Localization

Localization refers to enabling an application to display different resources at different locations. The most obvious and common example of localization is displaying text in different languages based on location or user selection. Localization is different from building entirely unique applications for each locale. Instead, the same code base is used for all locales, but different resource bundles are used for different locales. There are two basic variations on how this is implemented: compile time and runtime.

Compile-time localization means that you create a unique .swf for each locale. For example, if you want to support both French and Spanish, with compile-time localization you must create two .swf files: one for French and one for Spanish. Although with compile-time localization you create unique .swf files for each locale, each file uses the same code base. That means you don’t have to build and maintain separate application code bases. What is different between the .swf files is the resource bundle(s) that gets compiled into the .swf. We’ll talk more about resource bundles in just a minute.

Runtime localization allows you to create and deploy just one .swf containing your main application architecture. Runtime localization still uses resource bundles just like compile-time localization. However, rather than compiling the resource bundles for just one locale into the application runtime localization, necessitating different .swf files for each locale, runtime localization means one of two things: compiling in all resource bundles for all locales (allowing for changes between locales at runtime), or loading resource modules at runtime. We’ll talk more about resource modules later in this chapter when we talk about the details of loading resources at runtime.

Resource Bundles

All Flex applications use resource bundles, whether you are aware of them or not. The Flex framework has a bunch of resource bundles for things such as containers, controls, effects, skins, and styles. These resource bundles are compiled into an application by default. You can also create your own custom resource bundles to use for things such as localization. You create a resource bundle by creating a new UTF-8 encoded text file and populating it with keys and values. Each key and value is delimited by an equals sign, and each key-value pair should be on its own line in the file. The following is an example of such a file:

instruction=Select a value
color=Color
send=Send

The keys in this example are instruction, color, and send. You probably noticed that all the values are strings. However, you can also embed objects and classes in a resource bundle using the Embed() and ClassReference() directives.

The following example not only uses string values, but also embeds an image:

instruction=Select a value
color=Color
send=Send
flag=Embed('../../usa.jpg')

Note

All resource bundle files should be saved using UTF-8 encoding. This is important because it allows any character in the Unicode standard to be accurately stored. That means you can store and use characters outside the 128 characters used in the US-ASCII standard, allowing for use of characters in other languages and character sets.

You must save resource bundle files using the .properties extension. For example, you can save a resource bundle file as languageResources.properties. You will almost always want to save resource bundle files maintaining parity in naming, but placing them in subdirectories each named by locale. For example, if you want to support both French and Spanish, you might create two resource bundle files, both called languageResources.properties, but place them in different subdirectories called fr_FR and es_ES, respectively. Generally, you should place all the localization resource bundle files in subdirectories of one parent directory. For example, in the preceding scenario, the paths to the files might be locale/fr_FR/languageResources.properties and locale/es_ES/languageResources.properties. You could continue this pattern for as many locales as you need to support. For example, you could support English in the United States by adding a new file called locale/en_US/languageResources.properties. Usually, each file will contain the same set of keys, but with different, locale/language-specific values. For the examples that follow in the next few sections, we’ll use the resource bundle files shown in Example 5-2, Example 5-3, and Example 5-4. For each resource bundle we’ll assume that the files are saved in subdirectories of a locale directory that is a sibling of the src directory for the Flex project.

The code in Example 5-2 is saved in locale/en_US/languageResources.properties. This also assumes that there’s an assets directory in the project root, and that the .jpg files referenced in the following code examples are stored in that assets directory.

Example 5-2. U.S. English resource bundle

instruction=Select a value
color=Color
send=Send
flag=Embed('../../assets/usa.jpg')

The code in Example 5-3 is saved in locales/en_CA/languageResources.properties.

Example 5-3. Canadian English resource bundle

instruction=Select a value
color=Colour
send=Send
flag=Embed('../../assets/canada.jpg')

The code in Example 5-4 is saved in locales/es_ES/languageResources.properties.

Example 5-4. Spanish (Spain) resource bundle

instruction=Seleccione un valor
color=Color
send=Envíe
flag=Embed('../../assets/spain.jpg')

As we mentioned earlier, Flex includes a bunch of resource bundles that it uses when compiling most Flex applications. For example, there are bundles for controls, containers, styles, and effects. Some of these bundles are locale-specific, and the Flex SDK has only the bundles for the locale for which the SDK was intended. As an example, in the United States some of the Flex resource bundles are stored in the frameworks/locale/en_US directory of the SDK because they are specific to U.S. English. If you want to use resource bundles other than the locales already supported by the version of the SDK you have, you will need to create copies of the existing resource bundles, saving them to directories within frameworks/locale with the locale names you intend to use. For example, if you want to support Spanish (from Spain), you'll need to make sure you have a frameworks/locale/es_ES directory in your SDK containing all the necessary resource bundles (compiled and packaged as .swc files). The simplest way to accomplish this is to use the copylocale utility provided in the bin directory of the SDK. The copylocale utility will copy an existing locale’s resource bundles to a new locale. The syntax is as follows:

copylocale existingLocale newLocale

For example, if you already have the en_US locale and you want to support French, you could run copylocale as follows:

copylocale en_US fr_FR

Note

The preceding code assumes you are running the command from a command prompt and that you have added the bin directory of your SDK installation to your system’s path.

For the examples that we’ll see in the following sections, we’ll assume that we’ve already run copylocale as necessary to ensure that we have the following locales in the SDK: en_US, en_CA, and es_ES.

Using ResourceManager

One of the many manager classes that are included in the Flex framework is the ResourceManager class. As the name implies, this class is responsible for managing resources from resource bundles. In the next few sections, we’ll see ways in which you can tell a ResourceManager what resource bundle(s) to use. However, first we’ll look at how to access a ResourceManager as well as how to access the data from resource bundles via a ResourceManager.

The ResourceManager class uses the Singleton design pattern; therefore, there is just one instance per application. You can access that instance in one of two ways:

  • Use the ResourceManager.getInstance() method.

  • Use the resourceManager property of a UIComponent instance, including an Application instance.

A ResourceManager object contains all the data for all the resource bundles that have been loaded into an application. You can retrieve that data in two ways:

  • Use the Resource() directive.

  • Use the get methods of the ResourceManager instance.

The Resource() directive allows you to access data once, at application startup. That means you should use the Resource() directive only for compile-time localization. The directive requires that you pass it two attributes: bundle and key. The bundle is the name of the resource bundle file (minus the .properties extension) in which the key is defined. The key is the name of the key from the file for which you want the value. For example, the following uses the Resource() directive to retrieve the color value from the languageResources resource bundle:

<mx:Label text="@Resource(bundle='languageResources', key='color')" />

Note

When using the Resource() directive for an attribute value in an MXML tag, you must precede the directive with an @ character.

If you would like to retrieve a value from ResourceManager using ActionScript or in a data binding expression, you will need to use one of the following get methods of ResourceManager: getString(), getNumber(), getInt(), getUint(), getBoolean(), getObject(), getStringArray(), or getClass(). All of these methods require two parameters: the resource bundle name and the key name. For example, the following code retrieves the value for the color key from the languageResources resource bundle:

var color:uint = ResourceManager.getInstance().getInt("languageResources", "color");

As you’ve already seen, most data is stored in a .properties file as strings. The exceptions to that are embedded assets (using the Embed() directive) and classes (using the ClassReference() directive). Since there is no formal way to distinguish values as string, number, integer, Boolean, and so on, when storing the values in a .properties file, the responsibility of differentiating is left to the ResourceManager and you, the developer. You can use the get methods to specify how you’d like the ResourceManager to coerce the value for a key. For example, getBoolean() will attempt to coerce the value to a Boolean value whereas getInt() will attempt to coerce the value to an integer. You should use the getClass() method to retrieve all values stored using the Embed() or ClassReference() directive.

Compile-Time Localization

When you want to implement compile-time localization, you need to do two things:

  • Add the Resource() directive or appropriate ResourceManager get method call at the point in the code where you need to retrieve the value from the resource bundle.

  • Tell the compiler what resource bundles to include in the .swf.

The following code illustrates how an MXML document might look when using compile-time localization:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Form>
         <mx:FormHeading label="@Resource(key='instruction', bundle=
'languageResources')" />
         <mx:FormItem label="@Resource(bundle='languageResources', key='color')">
              <mx:ColorPicker id="color" />
         </mx:FormItem>
         <mx:FormItem>
              <mx:Button label="@Resource(bundle='languageResources', key='send')" />
         </mx:FormItem>
    </mx:Form>
    <mx:Image source="@Resource(bundle='languageResources', key='flag')" />
</mx:Application>

When compiling the application, you must tell it what resource bundles to use by adding a locale attribute to the compiler options. You also need to tell the compiler where it can find custom resource bundle .properties files by adding the directory for the resource bundle files to the source path. The following code tells the compiler to compile an application using the en_CA resource bundle, and it tells the compiler where it can find custom resource bundle files:

mxmlc -locale en_CA -source-path=../locale/{locale} Example.mxml

Note

The {locale} in the preceding code is a variable that tells the compiler to use the value of the locale attribute.

If you are using Flex Builder, you should do the following:

  1. Open the properties for the Flex project (Project→Properties).

  2. Select the Flex Compiler option from the list of tabs appearing on the left side of the window.

  3. In the “Additional compiler arguments” field, specify something such as the following:

    -local en_CA -source-path=../locale/{locale}

Runtime Localization

Runtime localization occurs in one of two ways:

  • Two or more resource bundles get compiled into the .swf.

  • No resource bundles are compiled into the .swf because they are loaded at runtime from external resource modules.

We’ll look at each option in the following sections.

Compiling multiple resource bundles into an .swf

You can compile two or more resource bundles into an .swf by adding a space-delimited list of locales to the locale compiler attribute. For example, if you’d like to compile the en_US, en_CA, and es_ES resource bundles into an .swf, the compiler attributes might look like the following:

-locale en_US en_CA es_ES -source-path=../locales/{locale}

That tells the compiler to include all the resource bundles for the locales in the main application .swf file. Then, at runtime, you can use ActionScript to change which locale is currently being used. The localeChain property of the ResourceManager instance controls which of the available resource bundles the Flex application should use. The localeChain property is an array of strings. Therefore, if you want to use the en_US resource bundles, you should set the localeChain property as follows:

resourceManager.localeChain = ["en_US"];

Note

If you specify more than one locale in the localeChain array, any key-value pairs not found in the first locale will be retrieved from the next locale.

When you allow for runtime selection of locales, you cannot use the Resource() directive to retrieve resource bundle values. You must use only the get methods of the ResourceManager instance. Because of this, the Flex application must also specify one more piece of information via a metadata tag. That piece of data is the name of the resource bundle(s) to use. The ResourceBundle metadata tag will tell the compiler to include a particular custom resource bundle.

The following is an example of such a metadata tag in an MXML document:

<mx:Metadata>
    [ResourceBundle("languageResources")]
</mx:Metadata>

The following code illustrates how you can build an application that allows the user to select a locale at runtime. In Example 5-5, the user selects a locale and the language of the application changes.

Example 5-5. Selecting a locale at runtime

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Metadata>
         [ResourceBundle("languageResources")]
    </mx:Metadata>
    <mx:Script>
         <![CDATA[

              private function selectLanguage():void {
                   resourceManager.localeChain = [language.value];
              }

         ]]>
    </mx:Script>
    <mx:ComboBox id="language" change="selectLanguage();">
         <mx:dataProvider>
              <mx:ArrayCollection>
                   <mx:Object label="English (US)" data="en_US" />
                   <mx:Object label="English (Canada)" data="en_CA" />
                   <mx:Object label="Espanol (Espana)" data="es_ES" />
              </mx:ArrayCollection>
         </mx:dataProvider>
    </mx:ComboBox>
    <mx:Form>
         <mx:FormHeading
label="{resourceManager.getString('languageResources', 'instruction')}" />
         <mx:FormItem
label="{resourceManager.getString('languageResources', 'color')}">
              <mx:ColorPicker id="color" />
         </mx:FormItem>
         <mx:FormItem>
              <mx:Button
label="{resourceManager.getString('languageResources', 'send')}" />
         </mx:FormItem>
    </mx:Form>
    <mx:Image
source="{resourceManager.getClass('languageResources', 'flag')}" />
</mx:Application>

Loading resource modules at runtime

Resource modules are resource bundles compiled into .swf files that are external to the main application .swf file. Resource modules allow you to build applications that can load resources at runtime. This allows users to change the locale at runtime, just as we saw in the preceding section. Resource modules are ideal when the number and size of the resource bundles are significant enough that compiling them all into the main .swf file would unnecessarily increase the main .swf file size beyond acceptable limits. Resource modules allow you to keep the main .swf file relatively small. Resource modules are loaded only when requested, as we’ll see shortly.

Resource modules include not only custom resource bundles, but all the framework resource bundles for a locale as well. That means that to create a resource module you must know which resource bundles the application is using. To determine that, run mxmlc with the locale attribute set to an empty string using the resource-bundle-list attribute. The resource-bundle-list attribute specifies a file to which the compiler will write a list of resource bundles. The following is an example:

mxmlc -locale= -resource-bundle-list=listOfResourceBundles.txt Example.mxml

The preceding line of code will write to the specified file (e.g., listOfResourceBundles.txt) a space-delimited list of resource bundles, such as the following:

bundles = collections containers controls core effects languageResources skins styles

Next, you can use that list of resource bundles to compile the resource module using mxmlc. The syntax is as follows:

mxmlc -locale=locale -source-path=locale/{locale} -include-resource-
bundles=commaDelimitedListOfResourceBundles -output resourceModuleName.swf

The following is an example:

mxmlc -locale=en_US -source-path=locale/{locale}
-include-resource-bundles=languageResources,collections,containers,
controls,core,effects,skins,styles -output en_US_ResourceModule.swf

You must create the resource modules for each locale. Then, in the application, you can use the loadResourceModule() method of ResourceManager to load a resource module. The loadResourceModule() method returns an instance of an event dispatcher object that you can use to register a listener for a complete event to receive notification when the resource module has loaded. At that point, you can set the localeChain property.

When you compile an application using resource modules, you have available two basic strategies:

  • You can compile with locale set to an empty string to compile no resource bundles into the application.

  • You can compile with locale set to a default locale. The resource bundles for that locale will get compiled into the application, and all other locales can be loaded at runtime using resource modules.

If you're using the first strategy, consider that no resource bundles will be available by default on application initialization. That can cause errors if your application depends on resource bundles on startup. If you’d like to load a default module automatically on startup, you can specify that via the FlashVars variables localeChain and resourceModuleURLs. The following FlashVars value will tell the Flex application to load a resource module named en_US_ResourceModule.swf and set the localeChain to en_US:

localeChain=en_US&resourceModuleURLs=en_US_ResourceModule.swf

Note

If you’re not familiar with FlashVars, you can read more about it in Chapter 20.

The following example is an application that loads resource modules at runtime when the user selects a locale from a combo box. The example assumes that the default locale and resource module are specified via FlashVars.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Metadata>
         [ResourceBundle("languageResources")]
    </mx:Metadata>
    <mx:Script>
         <![CDATA[

              import mx.events.ResourceEvent;

              private var _selectedResourceName:String;

              private function changeResourceHandler(resourceName:String):void {
                   _selectedResourceName = resourceName;
                   var eventDispatcher:IEventDispatcher = resourceManager.
loadResourceModule(resourceName + "_ResourceModule.swf");
                   eventDispatcher.addEventListener(ResourceEvent.COMPLETE,
languageModuleLoadedHandler);
              }

              private function languageModuleLoadedHandler(event:Event):void {
                   resourceManager.localeChain = [_selectedResourceName];
              }

              private function selectLanguage():void {
                   changeResourceHandler(language.value as String);
              }

         ]]>
    </mx:Script>
    <mx:ComboBox id="language" change="selectLanguage();">
         <mx:dataProvider>
              <mx:ArrayCollection>
                   <mx:Object label="English (US)" data="en_US" />
                   <mx:Object label="English (Canada)" data="en_CA" />
                   <mx:Object label="Espanol (Espana)" data="es_ES" />
              </mx:ArrayCollection>
         </mx:dataProvider>
    </mx:ComboBox>
    <mx:Form>
         <mx:FormHeading
label="{resourceManager.getString('languageResources', 'instruction')}" />
         <mx:FormItem label="{resourceManager.getString('resources', 'color')}">
              <mx:ColorPicker id="color" />
         </mx:FormItem>
         <mx:FormItem>
              <mx:Button
label="{resourceManager.getString('languageResources', 'send')}" />
         </mx:FormItem>
    </mx:Form>
    <mx:Image
source="{resourceManager.getClass('languageResources', 'flag')}"
width="60" height="40" />
</mx:Application>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset