Internationalizing Flex 2 Apps Pt 2

As the 2nd part of my 3 part series on internationalizing Flex Applications, this article will explore the use of multiple instances of the ResourceBundle class to allow for run time switching of locales.  As you may recall from the first article on this subject, the native use of the ResourceBundle class requires separate compiled swfs for each language.  This is not always desirable, and there are times when you may want to allow for switching of languages at run time.  One strategy I've used successfully for this is to trick the flex compiler and have several different properties in the same locale folder, and to create separate instances of the ResourceBundle class for each of them.  This way, its a fairly simple process to determine what the current locale is, and to pull the labels from that ResourceBundle.  To start, I took 3 properties files and placed them in a single directory. 

Here is a simple example of how to get it working:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="vertical" creationComplete="doLangChange()">
 <mx:Script>
  <![CDATA[
   import mx.formatters.DateFormatter;
  
   import mx.resources.ResourceBundle;
   [ResourceBundle("helloWorld_us")]
   private var rb_us:ResourceBundle;
   [ResourceBundle("helloWorld_uk")]
   private var rb_uk:ResourceBundle;
   [ResourceBundle("helloWorld_fr")]
   private var rb_fr:ResourceBundle;
   [Bindable]
   private var today:Date = new Date();
   [Bindable(event="langChange")]
   private function geti18nText(key:String):String{
    return this["rb_"+lang.selectedItem.lang].getString(key);
   }
   [Bindable(event="langChange")]
   private function geti18nDate(dt:Date):String{
    var formatter:DateFormatter = new DateFormatter();
    formatter.formatString = geti18nText("dtformat");
    return formatter.format(dt);
   }
   private function doLangChange():void{
    var e:Event = new Event("langChange");
    this.dispatchEvent(e);
   }
  ]]>
 </mx:Script>
 <mx:DateFormatter id="smeNme" formatString="MM/DD/YYYY"/>
 <mx:ApplicationControlBar dock="true">
  <mx:ComboBox id="lang" change="doLangChange()">
   <mx:dataProvider>
    <mx:Object label="US English" lang="us"/>
    <mx:Object label="UK English" lang="uk"/>
    <mx:Object label="French" lang="fr"/>
   </mx:dataProvider>
  </mx:ComboBox>
 </mx:ApplicationControlBar>
 <mx:Label fontSize="50" text="{geti18nText('hello')}"/>
 <mx:Label fontSize="50" text="{geti18nText('welcome')}"/>
 <mx:Label fontSize="50" text="{geti18nDate(today)}"/>
</mx:Application>

I named each file based on the language it was there to support (helloWorld_fr.properties, helloWorld_uk.properties, helloWorld_us.properties).  Notice that there is ResourceBundle instance for each of the three files.  I've also added some simple functions to get the data from these files (geti18nText, geti18nDate).  Bare in mind, this is a simplistic example.  In real world apps, I tend to have a singleton responsible for embedding and retrieving the data from the files.  But, even in this simple case, you can see the power of it, as simply switching the selected language in the combo box instantly translates the labels and dates to the appropriate format.

Remember to add a compiler argument to specify the proper directory for the locale files.  In my case, all three files where in a locales/multi directory, so i added the argument:  -sp ../locales/multi

 

Internationalizing Flex 2 Applications

I'll be speaking at the first ever meeting of the New York Flex User Group, this thursday 9/14, on the subject of "Strategies for Internationalizing Flex Applications."  I'm also in the midst of writing a 3 part blog series on the topic.  For those of you who can't wait for parts 2 and 3, come on down to the meeting on Thursday, otherwise, you'll have to wait until i finish writing the other two.

Part 1: Use of the ResourceBundle class

Part 2: Allowing run-time locale switching with the ResourceBundle class

Part 3: Loading locale specific XML at run-time.

Internationalizing Flex apps Pt 1

This is part 1 of a 3 part series on Internationalizng flex apps.


One of the many features available in Flex 2.0 is a ResourceBundle class, which allows for a standardized approach for internationalizing applications.  Many recent projects of mine have had requirements that we build applications so that they can easily be ported to other languages.  Traditionally, I've used a series of XML files for this, one for each of the various languages which need to be supported.  This strategy is still viable, and I still use it on some of my projects.  I've recently discovered a different approach, which is available natively in Flex.  Flex provides a ResourceBundle class, which allows you to set up your text in .properties files (identical structure that you would use for internationalizing java applications).  These properties files are arranged in a folder structure, relating to the language and country, so, the properties file for US English would be in a folder called en_US, the file for the UK would be en_UK, while the French would be in fr_FR.  The strucutre of the files is very simple, like this:

hello = Hello World
welcome = Welcome!

or, for the french version

hello = Bonjour Monde
welcome = Bienvenue

To use these in an application, you have two options:  you can use the @Resource command each time you need a value, or, you can declare a variable for the ResourceBundle, and use the getString(), getNumber(), getBoolean(), etc methods.  In this example, you can see both being used:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
 <mx:Script>
  <![CDATA[
   import mx.resources.ResourceBundle;
   [ResourceBundle("helloWorld")]
   private static var rb:ResourceBundle;
   private function geti18nText(key:String):String{
    return rb.getString(key);
   }
  ]]>
 </mx:Script>
 <mx:Label fontSize="50" text="@Resource(key='hello', bundle='helloWorld')"/>
 <mx:Label fontSize="50" text="{geti18nText('welcome')}"/>
</mx:Application>

So, here you can see the top label uses the @Resource directive to specifically pull the hello key from the helloWorld bundle.  If the application is compiled for en_US, that key will show "Hello World," compile the same app for fr_FR, and it will read "Bonjour Monde"

The remaining trick is to tell the compiler which language to use, and where to find the files.    You can do this from the command line like this:

  mxmlc -locale en_UK -sp ../locales/{locale} -o HelloWorld_en_UK.swf I18N_HelloWorld.mxml
  mxmlc -locale fr_FR -sp ../locales/{locale} -o HelloWorld_fr_FR.swf I18N_HelloWorld.mxml

or, you can specifiy compiler arguments in flexbuilder

-locale en_US -sp ../locales/{locale}

I'll put a zip of the files to use this up here over the weekend.

In my next article, I'll explore how i often use xml files as an alternative to this...

 

BlogCFC was created by Raymond Camden. This blog is running version 5.9.001.