Using resource bundles

After you create a resource properties file, you can use it in your Flex application as a resource bundle (the compiler converts properties files to subclasses of the ResourceBundle class when you compile your application). You can either bind values from the resource to an expression, or you can use methods of the ResourceBundle class to access those values.

There are two ways to access the values in resource bundles:

  • @Resource directive

  • Methods of the ResourceManager class

Using the @Resource directive to include resource bundles in your application is the simplest method. In the directive, you specify the name of the properties file and the key that you want to use. This method is simple to use, but is also restrictive in how it can be used. For example, you can only use the @Resource directive in MXML and you cannot change locales at run time. In addition, this directive only returns Strings.

The other way to access resource bundles is through the ResourceManagerclass. You can use the methods of the ResourceManager class in ActionScript, whereas you can only use the @Resource directive in MXML. These methods can return data types other than Strings, such as ints, Booleans, and Numbers. You can also use this class to switch locales at run time, so if you compiled multiple locales into the same application, you can change from one locale to the other.

The following sections describe how to use the @Resource directive and the ResourceManager class to access resource bundles in your Flex application.

Using the @Resource directive

In MXML, you can use the @Resource directive to access your resource bundles. You pass the @Resource directive the name of the resource bundle (the name of the properties file without the .properties extension) and the name of the key from the key/value pairs in the properties file. The directive returns a String of the key’s value from your resource bundle.

The following example creates a form; the values for the labels in the form are extracted from the RegistrationForm.properties resource properties file.
<?xml version="1.0"?>
<!-- resourcebundles/LocalizedForm.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <s:Form>
        <s:FormItem label="@Resource(key='personname', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='street_address', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='city', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='state', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='zip', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
    </s:Form>
</s:Application>

The executing SWF file for the previous example is shown below:

Because this application only uses a single locale, you compile it with the following compiler options:

-locale=en_US -allow-source-path-overlap=true -source-path=locale/{locale}

Accessing the values in a resource bundle with the @Resource directive is restrictive in that the directive can only be used in an MXML tag. In addition, you cannot change the locale at run time.

To use a class, such as a programmatic skin, as a resource, you use the ClassReference() directive in your properties file. The following example embeds the MyCheckBoxIcon_en_US class in the properties file:

CHECKBOXSKIN=ClassReference("MyCheckBoxIcon_en_US")

You then reference that class in your style properties, as the following example shows:

<mx:CheckBox selected="true" 
    selectedUpIcon="@Resource(key='bundle1', 'CHECKBOXSKIN')" 
    selectedDownIcon="@Resource(key='bundle1', 'CHECKBOXSKIN')" 
    selectedOverIcon="@Resource(key='bundle1', 'CHECKBOXSKIN')"/>

To use a binary asset such as an image, you embed the image in the resource properties file. You can then use the asset anywhere that you might use an embedded image. To embed an image in the properties file, you use the Embed directive in your properties file, as the following example shows:

flag=Embed("images/unitedstates.gif")

The location of the image is relative to the location of the properties file. In this case, the images directory is under locale/en_US.

In your application, you use the @Resource directive anywhere a String might supply the location of the image. The following example uses the image as a source for the <s:Image> tag:
<?xml version="1.0"?>
<!-- resourcebundles/LocalizedFormResourceWithImage.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark"> 

     <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

   <s:Image source="@Resource(key='flag', bundle='RegistrationForm')"/>

    <s:Form>
        <s:FormItem label="@Resource(key='personname', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='street_address', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='city', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='state', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="@Resource(key='zip', bundle='RegistrationForm')">
            <s:TextInput/>
        </s:FormItem>
    </s:Form>
</s:Application>

The executing SWF file for the previous example is shown below:

Using the ResourceManager

To use resource bundles in ActionScript, you use the methods of the ResourceManager class, such as getString() and getClass(). Using the ResourceManager is more flexible than using the @Resource directive because it lets you dynamically rather than declaratively set the values of properties from resources. It also lets you change locales at run time so that all localized resources in your application can be updated at once.

When using the ResourceManager, you must specify metadata that defines the resource bundles for your application. The syntax for this metadata is as follows:

<fx:Metadata> 
    [ResourceBundle("Resource_file_name")] 
</fx:Metadata>

For example:

<fx:Metadata> 
    [ResourceBundle("RegistrationForm")] 
</fx:Metadata>

For multiple resource bundles, add each one on a separate line inside the same <fx:Metadata> tag, as the following example shows:

<fx:Metadata> 
    [ResourceBundle("RegistrationForm")] 
    [ResourceBundle("StyleProperties")] 
    [ResourceBundle("FormatterProperties")] 
</fx:Metadata>

In an ActionScript class, you apply the metadata above the class name, as the following example shows:

[ResourceBundle("RegistrationForm")] 
public class MyComponent extends UIComponent {  
    ... 
}

You can also bind expressions to the ResourceManager. These expressions are updated any time the locale changes.

The following example uses ActionScript to set the value of the Alert message, and binds the labels in the form to resources by using the ResourceManager’s getString() method.
<?xml version="1.0"?>
<!-- resourcebundles/LocalizedFormWithBinding.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <fx:Script><![CDATA[
        import mx.resources.ResourceBundle;
        import mx.controls.Alert;

        private function registrationComplete():void {
            Alert.show(resourceManager.getString('RegistrationForm', 'thanks'));
        }  
    ]]></fx:Script>

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Metadata>
        [ResourceBundle("RegistrationForm")]
    </fx:Metadata> 

    <s:Form>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','personname')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','street_address')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','city')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','state')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','zip')}">
            <s:TextInput/>
        </s:FormItem>
    </s:Form>
    <s:Button id="b1" 
        label="{resourceManager.getString('RegistrationForm','submit_button')}" 
        click="registrationComplete()"/>
</s:Application>

The executing SWF file for the previous example is shown below:

To use a binary asset such as an image with the ResourceManager, you embed the image in the resource properties file. You can then use the asset anywhere that you might use an embedded image. To embed an image in the properties file, you use the Embed directive in your properties file, as the following example shows:

flag=Embed("images/unitedstates.gif")

The location of the image is relative to the location of the properties file. In this case, the images directory is under locale/en_US.

To use the image in your application, you use the ResourceManager’s getClass() method. The following example uses the GIF file as both a skin for the Button control and a source for the <s:Image> tag:
<?xml version="1.0"?>
<!-- resourcebundles/LocalizedFormBindingWithImages.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="initApp()"> 

    <fx:Script><![CDATA[
        import mx.resources.ResourceBundle;
        import mx.controls.Alert;

        private function initApp():void {
            b1.setStyle("downSkin", resourceManager.getClass("RegistrationForm", "flag"));            
        }

        private function registrationComplete():void {
            Alert.show(resourceManager.getString('RegistrationForm', 'thanks'));
        }  
    ]]></fx:Script>
    
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Metadata>
        [ResourceBundle("RegistrationForm")]
    </fx:Metadata> 

    <s:Image source="{resourceManager.getClass('RegistrationForm', 'flag')}"/>

    <s:Form>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','personname')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','street_address')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','city')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','state')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','zip')}">
            <s:TextInput/>
        </s:FormItem>
    </s:Form>
    <s:Button id="b1" 
        label="{resourceManager.getString('RegistrationForm','submit_button')}" 
        click="registrationComplete()"/>
</s:Application>

The executing SWF file for the previous example is shown below:

To use a class, such as a programmatic skin, as a resource, you use the ClassReference() directive in your properties file. The following example embeds the MyCheckBoxIcon_en_US class in the properties file:

CHECKBOXSKIN=ClassReference("MyCheckBoxIcon_en_US")

You can bind the value of the getClass() method for programmatic skins, as the following example shows:

<mx:CheckBox selected="true" 
    selectedUpIcon="{resourceManager.getClass('bundle1','CHECKBOXSKIN')}" 
    selectedDownIcon="{resourceManager.getClass('bundle1','CHECKBOXSKIN')}" 
    selectedOverIcon="{resourceManager.getClass('bundle1','CHECKBOXSKIN')}"/>

For more information about the ResourceManager, see About the ResourceManager.

Changing locales at run time with the ResourceManager

A common use of localization is to provide multiple locales for a single application, and to let the user switch the locale at run time. You can compile all possible locales into the application and then choose from among them. This solution is not very flexible because you can only select from locales that you added to the application at compile time. This solution can also lead to larger applications because the resource properties files and all of their dependencies must be compiled into the application.

You can also compile resource properties files into resource module SWF files, and then dynamically load those SWF files at run time. These modules provide all the resources for the locales. The advantage to this approach is that the application SWF file is smaller because the resources are externalized, but it requires an additional network request for each resource module that the client loads. For information on using resource modules, see Using resource modules.

You change the locale at run time by changing the value of the ResourceManager’s localeChain property. This property takes an Array as its value. The Array’s first element is the current locale (such as en_US or es_ES).

To be able to change the locale at run time without using resource modules, you compile all the available locales into the application at compile time by including them as part of the locale option. This compiler option takes a comma-separated list of locales. If you add a second locale, such as es_ES, change the locale option to the following:

-locale=en_US,es_ES

The Flex application uses the list of locales in the localeChain property to determine precedence when getting values from resource bundles. If a value does not exist in the first locale in the list, the Flex application looks for that value in the next locale in the list, and so on.

Before compiling additional locales into an application, you must generate the framework resources for that locale, if they are not already created. You do this with the copylocale command-line utility. For more information, see Adding new locales.

The order of the locales on the command line can be important. The application defaults to the first locale in the list if you do not specifically set the value of the ResourceManager’s localeChain property when the application initializes.

The following example lets you select a new locale from the ComboBox control. When you change that value, the application updates the localeChain property. The application’s locale-specific assets, such as the form labels, flag image, and alert message should change to the new locale.
<?xml version="1.0"?>
<!-- resourcebundles/BasicLocaleChain.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="initApp()"> 

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script><![CDATA[
        import mx.resources.ResourceBundle;
        import mx.controls.Alert;

        [Bindable]
        private var locales:Array = [ "es_ES","en_US" ];

        private function initApp():void {
            b1.setStyle("downSkin", resourceManager.getClass("RegistrationForm", "flag"));            
    
            // Initialize the ComboBox to the first locale in the locales Array.
            localeComboBox.selectedIndex = locales.indexOf(resourceManager.localeChain[0]);
        }

        private function registrationComplete():void {
            Alert.show(resourceManager.getString('RegistrationForm', 'thanks'));
        }  

        private function comboChangeHandler():void {
            // Set the localeChain to either the one-element Array
            // [ "en_US" ] or the one-element Array [ "es_ES" ].
            resourceManager.localeChain = [ localeComboBox.selectedItem ];
            
            // This style is not bound to the resource bundle, so it must be reset when 
            // the new locale is selected.
            b1.setStyle("downSkin", resourceManager.getClass("RegistrationForm", "flag"));            
        }
    ]]></fx:Script>

    <fx:Metadata>
        [ResourceBundle("RegistrationForm")]
    </fx:Metadata> 

    <s:Image source="{resourceManager.getClass('RegistrationForm', 'flag')}"/>

    <mx:ComboBox id="localeComboBox" 
        dataProvider="{locales}"
        change="comboChangeHandler()"/>

    <s:Form>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','personname')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','street_address')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','city')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','state')}">
            <s:TextInput/>
        </s:FormItem>
        <s:FormItem label="{resourceManager.getString('RegistrationForm','zip')}">
            <s:TextInput/>
        </s:FormItem>
    </s:Form>
    <s:Button id="b1" 
        label="{resourceManager.getString('RegistrationForm','submit_button')}" 
        click="registrationComplete()"/>
</s:Application>

The executing SWF file for the previous example is shown below:

When you compile this example application, you must add both locales to the locale option, as the following list of compiler options shows:

-locale=en_US,es_ES -allow-source-path-overlap=true -source-path=locale/{locale}

Because this application uses multiple locales, you must prepare properties files for each one. You should have the following files in your project to use this example:

/main/BasicLocaleChain.mxml 
/main/locale/en_US/RegistrationForm.properties 
/main/locale/en_US/images/unitedstates.gif 
/main/locale/es_ES/RegistrationForm.properties 
/main/locale/es_ES/images/spain.gif

The contents of the properties files for this example should be similar to the following:

# /locale/en_US/RegistrationForm.properties 
registration_title=Registration 
submit_button=Submit Form 
personname=Name 
street_address=Street Address 
city=City 
state=State 
zip=ZIP Code 
thanks=Thank you for registering! 
flag=Embed("images/unitedstates.gif") 
 
# /locale/es_ES/RegistrationForm.properties 
registration_title=Registro 
submit_button=Enviar el formulario 
personname=Nombre 
street_address=Dirección de calle 
city=Ciudad 
state=Estado 
zip=Código postal 
thanks=¡Gracias por inscribirse! 
flag=Embed("images/spain.gif")

If you do not bind the properties in your application to your resources, then those values are not updated when the locale changes.

When you compile an application for multiple locales, the ResourceManager's localeChain property is initialized to the locales specified by the locale compiler option. For example, if the locale option is -locale=en_US,es_ES, the application defaults to the English resources because en_US is listed first. You can override this default initial value at run time by specifying the value of the localeChain as an application parameter in the flashVars variable in the HTML template.

If you write your own HTML template, you can pass variables as flashVars properties in the <object> and <embed> tags. The following example specifies that the es_ES locale is the first in the localeChain property’s Array:

<object id='mySwf'  
    classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'  
    codebase='http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab'  
    height='100%'  
    width='100%'> 
    <param name='src' value='BasicLocaleChain.swf'/> 
    <param name='flashVars' value='localeChain=es_ES,en_US'/> 
    <embed name='mySwf'  
        src='FlashVarTest.swf'  
        pluginspage='http://www.adobe.com/go/getflashplayer'  
        height='100%'  
        width='100%'  
        flashVars='localeChain=es_ES,en_US' 
    /> 
>

If you are using SWFObject 2, the default template that Flex uses, define and pass the flashVars object to the embedSWF() JavaScript method, as the following example shows:

<script type="text/javascript" src="swfobject.js"></script> 
        <script type="text/javascript"> 
            var swfVersionStr = "0"; 
            var xiSwfUrlStr = ""; 
            var flashvars = {}; 
            flashvars.localeChain="es_ES,en_US" 
            var params = {}; 
            params.quality = "high"; 
            params.bgcolor = "#ffffff"; 
            params.allowscriptaccess = "sameDomain"; 
            var attributes = {}; 
            attributes.id = "TestProject"; 
            attributes.name = "TestProject"; 
            attributes.align = "middle"; 
            swfobject.embedSWF( 
                "TestProject.swf", "flashContent", 
                "100%", "100%", 
                swfVersionStr, xiSwfUrlStr, 
                flashvars, params, attributes); 
</script> 

This initializes the value of the localeChain property to ["es_ES", "en_US"].

For more information about using flashVars properties, see Passing request data with flashVars properties.

About the ResourceManager

The ResourceManager manages all resource bundles, regardless of their source. They can be loaded as resource modules, compiled into the application, or created programmatically.

The ResourceManager class is a Singleton. All components that extend UIComponent, Formatter, or Validator have a resourceManager property, which lets you access this manager. If you create other classes that need to use the ResourceManager, you can call the ResourceManager.getInstance() method to get a reference to it.

Whenever you set the value of the localeChain property, the ResourceManager dispatches a change event. This event causes values that are bound to resource properties to be updated. You can manually dispatch this event by calling the ResourceManager’s update() method. The change event causes the following to occur:

  • Binding expressions involving resource-access methods of ResourceManager such as getString() are updated.

  • Components that extend UIComponent, Formatter, or Validator execute their resourcesChanged() method. This is how components such as CurrencyFormatter can update their default value for resource-backed properties such as currencySymbol. If you are writing another kind of class that needs to respond to resource changes, you can listen for change events from the ResourceManager.

The localeChain property is an Array so that the ResourceManager can support incomplete locales. For example, suppose you localize an application for English as spoken in India (by using the "en_IN" locale). Most of the resources are the same as for U.S. English (the en_US locale), so there is no reason to duplicate them all in the en_IN locale’s resources. The en_IN locale’s properties files need only to have the resources that differ between the en_US and en_IN locales. If the ResourceManager has bundles for both en_US and en_IN and you set the value of the localeChain property to ["en_IN", "en_US"], the ResourceManager searches for a resource first in the en_IN bundle. If the resource is not found there, then the ResourceManager searches for the resource in the en_US bundle. If the ResourceManager does not find the resource you specify in any locale, its methods return null.

The most commonly used method of the ResourceManager for resource access is the getString() method. However, the ResourceManager supports other data types. You can get resources typed as Numbers, Booleans, Uints, and ints by using other methods, as the following example shows:

resourceManager.getNumber("myResources", "PRICE"); // Returns a Number like 19.99. 
resourceManager.getInt("myResources", "AGE"); // Returns an int like 21. 
resourceManager.getUint("myResources", "COLOR"); // Returns a Uint like 0xFF33AA. 
resourceManager.getBoolean("myResources", "SENIOR") // Returns the Boolean like true.

You can also use methods such as getClass(), getString(), getObject(), and getStringArray().