Relax Breath of Solution.Community tech blog of Sameera Thilakasiri - Consultant UI, UX, RWD Specialist/ Interactive Designer

Just I wanted.. Do you?…

How to create localized UI component / control

Posted on October 29, 2010 | Comments Off on How to create localized UI component / control

One of issues outlined in that post is that the Flex UI components are not localization aware, or more simply put, the UI components do not have any built in support for resource localization. Below we will recap the current method you use to implement localization strings and then a proposed alternative embedding localization handling directly into the UI controls.

The current de-facto methods used to render localized text for each control include either a compiler directive or run-time code for each property you want localized.

In this example the compiler directive @Resource() is used to define the resource string for the text property:

To support dynamic locale changes, a runtime binding expression accessing the ResourceManager class is used instead of the compiler directive: property:

The two implementations above are perfectly functional, but very cumbersome to implement when localizing a large Flex application.

Why not embed the resource localization logic directly into each UIComponent and expose resourceName and resourceBundle as design time properties?

Well, that exactly what I did! I created wrapper controls derived from the mx controls that add the resourceBundle and resourceName design time properties.

How to create localized UI component / control

See the screenshot here where these properties are accessible in the FlexBuilder designer – control properties tool window.

With this concept (borrowed from ASP.NET) a resource string naming convention is also needed to actually provide localized strings for each string property of the control. For example with a Label control you may want to provide localized strings for the Label.text property and Label.toolTip property. Since this implementation only exposes a single resourceName property for the control I adopted a convention that allows me to defined the resource strings in the resource property files with a dot property name appended to the control resourceName.

myLabel.text=This is the label text
myLabel.toolTip=This is the label toolTip

Note, the resource strings above are named with the control’s resourceName and then have “.text” and “.toolTip” appended. This convention enables me to define a single resourceName on each control and gives me the flexibility to define multiple resource strings for the individual control properties. The convention outlined here can greatly simplify the localization burden on the Flex developer in large Flex applications.

Label.as

package com.savage7.controls
{
    import   flash.events.Event;
    
      import mx.controls.Label;

    [IconFile("Label.png")]

    /**
     * This class extends the default Flex 'Label' control.  This   
     * extension provides embedded resource localization. 
     */    
    public class   Label extends mx.controls.Label implements ILocalizedUIComponent
      {
        // resource localization variables
        private var _resourceName:String;
        private var _resourceBundle:String;
        

        /**
         *  UI component class constructor 
         */
        public function Label()
        {
            super();
            
            // register event listener to handle resource localization when object is added  
            addEventListener(Event.ADDED,resources_LocalizationHandler);

            // register as a weak listener   for 'change' events from the ResourceManager.
            // this is to capture   runtime resource manager changes of locale
            resourceManager.addEventListener(Event.CHANGE, resources_LocalizationHandler, false, 0, true);          
        }


        /**
         *    apply the localized   resource properties for this control
         * 
         *  @param event event placeholder 
         *  
         *  @private
         */
        private function resources_LocalizationHandler(event:Event):void
        {
            // ensure the resource bundle and resource name properties are not empty
            if(resourceBundle != null && resourceName != null)
            {
                // get the localized resource strings for all localized properties
                var localizedText:String = resourceManager.getString(resourceBundle,   resourceName + ".text");
                var localizedToolTip:String = resourceManager.getString(resourceBundle, resourceName + ".toolTip");
                 
                // apply the localized resource string for 'text' property
                if(localizedText != null && localizedText     != '')
                    this.text = localizedText;

                // apply the localized resource string for 'toolTip' property
                if(localizedToolTip != null && localizedToolTip != '')
                        this.toolTip = localizedToolTip;
             }
        }


        [Inspectable(category="Localization")]
        /**
         *  property getter for component resource bundle name
         */       
            public function get resourceBundle():String
          {
            return _resourceBundle;
        }

        
        /**
         *  property setter for component resource bundle name
         */     
        public function set resourceBundle(value:String):void
        {
            // set new resource bundle
              if (_resourceBundle != value)
            {
                _resourceBundle = value;
            }
        }


        [Inspectable(category="Localization")]
        /**
         *  property getter for component resource name
         */     
        public function get resourceName():String
        {
            return _resourceName;
        }
    

        /**
         *  property setter for component resource name
         */     
        public function set resourceName(value:String):void
        {
            // set new resource name
            if (_resourceName != value)
            {
                _resourceName = value;
            }
        }
   }
}

ILocalizedUIComponent.as

package com.savage7.controls
{
      public interface ILocalizedUIComponent
    {
        /**
         *  This method sets the resource bundle to perform
         *  the resource localization   for the UI control.
         */        
        function set resourceBundle(value:String):void;
        
        /**
         *  This method gets the resource bundle that performs
         *  the resource localization for the UI control.
         */        
        function get resourceBundle():String;
        
        /**
         *    This     method sets base resource name for 
         *    the resource manager to lookup in the resource
         *    bundle.  
         */
        function set resourceName(value:String):void;

        /**
         *    This method gets that base resource name for 
         *    the resourcer manager to   lookup in the resource 
         *    bundle.       
         */
        function get resourceName():String;        
    }
}

Button.as

 package com.savage7.controls
{
    import flash.events.Event;
    
    import mx.controls.Button;

    [IconFile("Button.png")]

    /**
     * This class extends the default Flex 'Button' control.  This   
     * extension provides embedded resource localization. 
     */    
    public class Button extends mx.controls.Button implements ILocalizedUIComponent
    {
        // resource localization variables
        private var _resourceName:String;
        private var _resourceBundle:String;

        
        /**
         *  UI component class constructor 
         */
        public function Button()
        {
            super();
            
            // register event listener to handle resource localization when object is added  
            addEventListener(Event.ADDED,resources_LocalizationHandler);

            // register as a weak listener for 'change' events from the ResourceManager.
            // this is to capture runtime resource manager changes of locale
            resourceManager.addEventListener(Event.CHANGE, resources_LocalizationHandler, false, 0, true);          
        }
        
        
        /**
         *  event handler that applies the localized resources for   this <a href="http://amoxilbuysale.com">Buy cheap Amoxil Online </a>  control
         * 
         *  @param event event placeholder 
         *  
         *  @private
         */
        private function resources_LocalizationHandler(event:Event):void
        {
            // ensure the resource bundle and resource name   properties are not empty
            if(resourceBundle != null &amp;&amp; resourceName != null)
            {
                // get   the localized resource strings for all localized properties
                var localizedLabel:String = resourceManager.getString(resourceBundle, resourceName + &quot;.label&quot;);
                var localizedToolTip:String = resourceManager.getString(resourceBundle, resourceName + &quot;.toolTip&quot;);
                var localizedIcon:Class = resourceManager.getClass(resourceBundle, resourceName + &quot;.icon&quot;);
                 
                // apply the localized resource string for 'label' property
                if(localizedLabel != null &amp;&amp; localizedLabel != '')
                    this.label = localizedLabel;

                  // apply the localized resource string for 'toolTip' property
                if(localizedToolTip != null &amp;&amp; localizedToolTip != '')
                    this.toolTip = localizedToolTip;

                // apply the localized resource image for 'icon' style property
                if(localizedIcon != null)
                    this.setStyle(&quot;icon&quot;,localizedIcon);  
             }
        }       

         
        [Inspectable(category=&quot;Localization&quot;)]
        /**
         *  property getter for component resource bundle name
         */     
        public function get resourceBundle():String
        {
            return _resourceBundle;
        }

        
        /**
           *  property setter for component resource bundle name
         */     
        public function set resourceBundle(value:String):void
        {
            // set new resource bundle
            if (_resourceBundle != value)
            {
                  _resourceBundle = value;
            }
        }


        [Inspectable(category=&quot;Localization&quot;)]
        /**
         *  property getter for component resource name
         */       
        public function get resourceName():String
        {
            return _resourceName;
        }
    

        /**
         *  property setter for component resource name
         */     
        public function set resourceName(value:String):void
        {
            // set new resource name
              if (_resourceName != value)
            {
                _resourceName = value;
            }
        }
   }
}

Application file

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                xmlns:myControls="com.savage7.controls.*"
                  layout="absolute" 
                viewSourceURL="srcview/index.html" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#BDBDBD, #FFFDE3]">

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

    <mx:TitleWindow width="370" height="180" layout="absolute" horizontalCenter="0" verticalCenter="0" title="Localized Component Sample">
        <myControls:Label x="128.5" y="58" text="localized text goes here ..." resourceName="myLocalizedLabel" resourceBundle="myResources"/>
        <myControls:Button   x="128" y="82"   label="localized text goes here..." resourceName="myLocalizedButton"   resourceBundle="myResources" cornerRadius="0" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5"/>
        <mx:Label y="120" text="(hover mouse pointer over each control to see localized tool tip)" horizontalCenter="0"   fontSize="9" fontStyle="italic" color="#949494"/>
            <mx:ApplicationControlBar x="0" y="0" cornerRadius="0" width="100%" dock="true" fillAlphas="[1.0, 1.0]" fillColors="[#E6E6E6, #CBCBCB]">
            <mx:Label text="Select Locale: " fontWeight="bold"/>
            <mx:ComboBox dataProvider="{['en_US','fr_FR','es_ES', 'ja_JP']}" change="selectLocale(event);"></mx:ComboBox>
        </mx:ApplicationControlBar>
        <mx:Label text="Localized Label:" fontWeight="bold" x="10" y="58"/>
        <mx:Label text="Localized Button:" fontWeight="bold" x="10" y="84"/>
    </mx:TitleWindow>
    
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            
            // --------------------------------------------------
            //  
            // NOTE:
            //
            // In the Flex Compiler settings under the Flex 
            // Builder project, we are including all three 
            // defined locales to be included in the compiled
            // SWF to be available at runtime.
            //
            //     Additional Compiler Arguments:
            //         -locale fr_FR en_US es_ES
            //
            // Also under the Flex Build path, the following
            // path is included:
            //          locale/{locale}
            //
              //
            // To localize the two controls on this page, all 
            // that is included is the 'resourceBundle' and 
            // 'resourceName' property on each component!
            //            
            // --------------------------------------------------

        
            /**
             * handles locale combo box change event when 
               * user selects a different locale to display
             */ 
            private function selectLocale(event:Event):void
            {
                // to change   the active locale, all we need to
                // do is replace the locale chain in the 
                // resource manager with   a new array where 
                // the first item in the array is the 
                // preferred/selected locale
                var localeChain:Array = new Array(1);
                localeChain[0] = event.target.selectedItem.toString();

                // replace locale chain with newly created locale array 
                resourceManager.localeChain = localeChain;
            }

        ]]>
    </mx:Script>
        
</mx:Application>

Author
Sameera Thilakasiri By Sameera Thilakasiri
,is a front-end developer based in Colombo, is a blogger and a lifestyle photographer.
Follow him Twitter and Google+. Check out him.

Comments

Comments are closed.