Using resource modules

Resource modules are SWF files, separate from your application SWF file, that contain resources bundles for a single locale. Your application can preload one or more resource modules as it starts up, before it displays its user interface. It can also load resource modules later, such as in response to the user selecting a new locale. The ResourceManager interacts with all resource bundles the same, whether the bundles it manages were originally compiled into the application or loaded from resource modules.

Resource modules can be a better approach to localization than compile-time resources because you externalize the resource modules that can be loaded at run time. This creates a smaller application SWF file, but then requires that you load a separate SWF file for each resource module that you use. The result can be an increased number of network requests and an aggregate application size that is larger than if you compiled the locale resources into the application. However, if you have many locales, then loading them separately should save resources in the long run.

You are limited as to when you can use resources in resource modules during the application initialization sequence. The earliest that you can access a resource bundle in a resource module is during the application’s preinitialize event handler. You should not try to access a resource at class initialization time or during preloading.

Creating resource modules

To create a resource module, you must do the following:

  • Determine the required resource bundles

  • Create the resource module SWF file

The following sections describe these tasks.

Determining the required resource bundles to include in a resource module

Before you can compile a resource module, you must know which resource bundles to put into it. In other words, you must know which resource bundles your application — and all of its framework classes — actually require. This includes not just the custom resource bundles that you create, but also the framework resource bundles that are required by the application.

To determine which resource bundles an application needs, you use the resource-bundle-list compiler option. When you compile an application, this option outputs a list of the needed bundles by examining the [ResourceBundle] metadata on all of the classes in your application.

The resource-bundle-list option takes a filename as its argument. This filename is where the compiler writes the list of bundles. On the Macintosh OS X, you must specify an absolute path for this option. On all other operating systems, you can specify a relative or absolute path.

When using the resource-bundle-list option, you must also set the value of the locale option to an empty string.

The following command-line example generates a resource bundle list for the application MyApp:

mxmlc -locale= -resource-bundle-list=myresources.txt MyApp.mxml

In this example, the compiler writes a file called myresources.txt. This file contains a list similar to the following:

bundles = RegistrationForm collections containers controls core effects skins styles

In Flash Builder, you add the locale and resource-bundle-list options to the Additional Compiler Arguments field on the Flex Compiler pane in the project’s properties. On Windows, the output file’s location is relative to the Flash Builder directory, not the project’s directory. You can specify an absolute path instead. On Macintosh, the option must be an absolute path.

If you use custom resource bundles, those will be included in this list. In this example, the name of the resource properties file is RegistrationForm. The others are bundles containing framework resources.

You use this list to instruct the compiler which resource bundles to include when you compile a resource module.

Compiling a resource module

To compile a resource module, you must use the mxmlc command-line compiler. You cannot compile resource modules by using Flash Builder. The output is a SWF file that you can then load at run time.

To compile a resource module on the command line, use the following guidelines:

  • Do not specify an MXML file to compile.

  • Specify the include-resource-bundles option.

  • Specify the locale by using the locale option.

  • Add the locales to the source path.

The include-resource-bundles option takes a comma-separated list of resource bundles to include in the resource module. You generated this list of required resource bundles in Determining the required resource bundles to include in a resource module.

When compiling a resource module, you must also specify the locale by using the locale option.

The following example compiles a resource module for the en_US locale:

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

You can compile the resources for only a single locale into each resource module. As a result, you cannot specify more than one locale for the locale option.

You should use a common naming convention for all of your locales’ resource modules. For example, specify the locale in the SWF file’s name, but keep the rest of the file name the same for all locales. This is because when you load the resource module, you want to be able to dynamically determine which SWF file to load. In the previous example, the resource module for the en_US locale is named en_US_ResourceModule.swf. If you then generated a resource module for the es_ES locale, it would be named es_ES_ResourceModule.swf.

Loading resource modules at run time

To load a resource module at run time, you use the ResourceManager’s loadResourceModule() method. After the resource module has finished loading, you set the value of the ResourceManager’s localeChain property to the newly-loaded locale.

The ResourceManager’s loadResourceModule() method asynchronously loads a resource module. It works similarly to the loadStyleDeclarations() method of the StyleManager, which loads style modules.

The loadResourceModule() method takes several parameters. The first parameter is the URL for the resource modules’s SWF file. This is the only required parameter. The second parameter is update. You set this to true or false, depending on whether you want the resource bundles to immediately update in the application. For more information, see Updating resource modules. The final two parameters are applicationDomain and securityDomain. These parameters specify the domains into which the resource module is loaded. In most cases, you should accept the default values (null) for these parameters. The result is that the resource module is loaded into child domains of the current domains.

The loadResourceModule() method returns an instance of the IEventDispatcher class. You can use this object to dispatch ResourceEventtypes based on the success of the resource module’s loading. You have access to the ResourceEvent.PROGRESS, ResourceEvent.COMPLETE, and ResourceEvent.ERROR events of the loading process.

You should not call the loadResourceModule() method and then immediately try to use the resource module because the resource module’s SWF file must be transferred across the network and then loaded by the application. As a result, you should add a listener for the ResourceManager’s ResourceEvent.COMPLETE event. When the ResourceEvent.COMPLETE event is dispatched, you can set the localeChain property to use the newly-loaded resource module.

The following example loads a resource module when the user selects the locale from the ComboBox control. It builds the name of the SWF file (in this case, it is either en_US_ResourceModule.swf or es_ES_ResourceModule.swf), and passes that file name to the loadResourceModule() method.
<?xml version="1.0"?>
<!-- resourcebundles/ResourceModuleApp.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;
        import mx.events.ResourceEvent;

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

        private function initApp():void {
            /* Set the index to -1 so that the prompt appears 
               when the application first loads. */
            localeComboBox.selectedIndex = -1;            
        }

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

        private function comboChangeHandler():void {
            var newLocale:String = String(localeComboBox.selectedItem);

            /* Ensure that you are not loading the same resource module more than once. */
            if (resourceManager.getLocales().indexOf(newLocale) != -1) {
                completeHandler(null);
            } else {
                // Build the file name of the resource module.
                var resourceModuleURL:String = newLocale + "_ResourceModule.swf";
                
                var eventDispatcher:IEventDispatcher = 
                    resourceManager.loadResourceModule(resourceModuleURL);
                eventDispatcher.addEventListener(ResourceEvent.COMPLETE, completeHandler);
            }            
        }
        
        private function completeHandler(event:ResourceEvent):void {
            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>

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

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

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

    <mx:ComboBox id="localeComboBox" 
        prompt="Select One..."
        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:

To compile an application that uses only resource modules, do not specify any locales to be compiled into the application. You do this by setting the value of the locale option to an empty String. For example, on the command line, you can compile an application named MyApp.mxml with the following command:

mxmlc -locale= MyApp.mxml

In Flash Builder, you add the following option to the Additional Compiler Arguments field on the Flex Compiler pane in the project’s properties:

-locale=

If you compile an application that uses both compiled-in resources and resource modules, then you specify the locale(s) for the compiled-in resources with the locale option.

You should try to avoid loading the same resource module more than once in the application. To do this, you can use the ResourceManager’s getLocales() method. This method returns an Array of locales. You can compare this list against the resource module’s locale that you are about to load.

You can find out which resource bundles exist for a specified locale by using the getBundleNamesForLocale() method.

You can get a reference to a particular locale’s resource bundle by calling the getResourceBundle() method. Once you have a reference to a ResourceBundle, you can use a for-in loop to iterate over its content object. For more information, see Enumerating resources.

Loading remote resource modules

Loading a remote resource module typically requires a crossdomain.xml file that gives the loading application permission to load the SWF file. You can do without a crossdomain.xml file if your application is in the local-trusted sandbox, but this is usually restricted to SWF files that have been installed as applications on the local machine. For more information about crossdomain.xml files, see Using cross-domain policy files.

Also, to use a remote resource module, you must compile the loading application with network access (have the use-network compiler option set to true, the default). If you compile and run the application on a local file system, you might not be able to load a remotely accessible SWF file.

Updating resource modules

The second parameter of the loadResourceModule() method is update. Set the update parameter to true to force an immediate update of the resource bundles in the application. Set it to false to avoid an immediate update of the resource bundles in the application. The resources are updated the next time you call this method or the unloadResourceModule() method with the update property set to true.

Each time you call the loadResourceModule() method with the update parameter set to true, Adobe Flash Player and AIR reapply all resource bundles in the application, which can degrade performance. If you load multiple resource modules at the same time, you should set the update parameter to false for all but the last call to this method. As a result, Flash Player and AIR only apply the resource bundles once for all new resource module SWF files rather than once for each new resource module SWF file.

Preloading resource modules at run time

You can load a resource module when the application starts up by calling the loadResourceModule() method from your application initialization code, and then specifying the value of the localeChain property after the module loads. This is useful if you have a default locale that you want all users to start the application with. However, you can also specify the locale that the application should load on startup by passing flashVars properties in the HTML wrapper. This lets you specify a locale based on some run time value such as the Accept-Language HTTP header or the Capabilities.language property in ActionScript.

The following table describes the flashVars properties that you pass to set the preloaded resource modules:

flashVars property

Description

localeChain

A comma-separated list of locales that initializes the localeChain property of the ResourceManager class. If the localeChain property is not explicitly set, then it is initialized to the list of locales for which the application was compiled, as specified by the locale compiler option.

resourceModuleURLs

A comma-separated list of URLs from which resource modules will be sequentially preloaded. Resource modules are loaded by the same class as RSLs, but are loaded after the RSLs.

The URLs can be relative or absolute.

As with URL parameters, you must separate these values with an ampersand (&). You must also ensure that the values are URL encoded.

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’s resource module is preloaded when the application launches:

<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='ResourceModuleApp.swf'/> 
    <param name='flashVars' 
        value='resourceModuleURLs=es_ES_ResourceModule.swf&localeChain=es_ES'/> 
    <embed name='mySwf'  
        src='ResourceModuleApp.swf'  
        pluginspage='http://www.adobe.com/go/getflashplayer'  
        height='100%'  
        width='100%'  
        flashVars='resourceModuleURLs=es_ES_ResourceModule.swf&localeChain=es_ES' 
    /> 
>

If you are using the SWFObject 2 template that Flex uses by default, 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.resourceModuleURLs="es_ES_resourceModule.swf" 
     flashvars.localeChain="es_ES" 
            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> 

Using a combination of compile-time resources and resource modules

You might have a default locale that you want all users of your application to start with. You can compile this locale’s resources into the application. You can then load external resource modules if the user changes the locale.

To do this, you compile the resource module as you normally would. When you compile the application, rather than specifying an empty String for the locale option, you set it to the locale that you want to compile into the application. This compiled-in locale’s resource bundles become the default bundles when the application starts. You can then load a resource module SWF file as you normally would at run time.

Unloading resource modules at run time

You can unload a resource module when you are no longer using it. For example, if the user changes to a different locale, you can unload the resource module for one locale after loading the resource module for the new locale. This can reduce the memory footprint used by the application.

To unload a resource module, you can use the ResourceManager’s unloadResourceModule() method. This removes the resource module’s SWF file from memory. Its resources cannot be used until that module is reloaded.

Resource module SWF files are cached by the browser just like any other SWF file. As a result, if you unload a resource module, and then later want to reload it, Flash Player and AIR will load it from the browser’s cache rather than make another network request. If the browser’s cache was cleared, though, then Flash Player and AIR will load the resource module’s SWF file with a network request again.

You can unload individual resource bundles rather than the entire resource module. You do this with the ResourceManager’s removeResourceBundle() method. This method takes a locale and the resource bundle’s name, which you can access with the getBundleNamesForLocale() method. Rather than iterate over the resource bundle names, you can use the removeResourceBundlesForLocale() method, which removes all resource bundles for a locale.