• Flex il8n 策略


    • 通过多番查阅资料和研究Flex下的组件编写、ResourceManager的使用,终于实现了Flex下国际化/多语言支持的完美解决方案:

          * 很容易实现编译时类型检查
          * 支持运行时实例化注入(延迟创建)
          * 可注入参数化的值 (参看 account.email)
          * 支持属性链
          * 模型的更新可以使用当前的本地化值触发更新
          * 支持皮肤和嵌入资源(图像,声音)--目前未提供,可自行修改方法getResourceManagerChain来扩充

      最开始准备使用参考文献中的amazing-i18n-solutions,发现引用了很多包、文件,略显繁琐,参考后实现了自己的方案,个人感觉更完美。

    • 实现思路
      • 最开始经典实现思路,可参考Tour de Flex中的例子 Localization,进行如下操作
        设置编译参数 -locale=zh_CN,en_US -source-path=locale/{locale} 建立资源文件:example.properties FIRST_NAME=First Name LAST_NAME=Last Name ADDRESS=Address CITY=City ZIP=Zip DOB=Date of Birth SAVE=Save SELECT_LANGUAGE=Select a language LOGO=Embed("assets/logo_en.png") DATE_FORMAT=MM/DD/YYYY 如果是Flex3,支持中文时还需要生成本地资源。Flex4跳过 Open a Command prompt/shell, navigate to the bin directory of your Flex SDK (for example: 

                    C:\Program Files\Adobe\Flex Builder 3 Plug-in\sdks\3.0.1\bin), and execute the following command: 
                
                    copylocale en_US zh_CN

        添加资源引用

                <fx:Metadata>
                         引用的资源:注意参数,必须与example.properties对应 
                        [ResourceBundle("example")]
                </fx:Metadata>
         将需要显示文字的地方,改用ResourceManager方法
         
         <mx:Button id="btnLogin" label="登录" />
         
         
         <mx:Button id="btnLogin" label="{resourceManager.getString('example', 'SELECT_LANGUAGE')}" />

        需要切换语言时,执行下面语言 resourceManager.localeChain = [ "zh_CN" ];//["en_US"]

      • 上述方法已经可以达到实时切换语言的操作,但是仍然有几个不太方便的地方 设计视图基本上不可用了,开发人员并不知道到底显示的标签是什么内容,而是一堆resoureManager.get...,“就像一滩滩的鸽子粪”……对已经开发完成的应用添加国际化支持时,需要修改大量的代码 为此希望能够能够简化一些
      • 首先实现了一个方法public static function injection(target:Object,bundleName:String=null):void,这个方法能够自动读取指定资源文件名(bundleName)的资源,并将键值属性绑定到相关的属性上,从而实现国际化支持的自动注入
      • 资源文件的编写方式:原来的资源文件类似与传统ini文件,key=value,使用injection方法注入时,需要注意key的编写方式,需要提供属性链式的方式。比如btnLogin.label=登录,表示将“登录”注入到组件target的按钮btnLogin.label上
      • 更进一步:通过调用静态方法已经可以实现国际化支持的注入,但是每次创建Object之后都需要调用这个方法,比较繁琐,希望自定义一个组件,放置到mxml组件上之后可以自动进行国际化支持的注入。于是编写了一个自定义组件,主要方法initialized,该方法在组件创建之后执行,通过addEventListener使得mxml组件(即document)在创建完成后调用国际化支持注入的方法doInjection

        public function initialized(document:Object, id:String):void
                        {
                                if(!mEnabled) return;
                                
                                //记录创建此对象的 MXML 文档
                                this.document = document;
                                
                                var ui:UIComponent = document as UIComponent;
                                ui.addEventListener(FlexEvent.CREATION_COMPLETE,this.doInjection);
                        }

    I implement a class named "com.cnblogs.wideweide.utils.I18nInjection".Created in mxml components,such as application/Panel,it will automatic load the resource and bind to properties like title,label.
    Haha,Is it very convenient?Just see the source code and examples.Note tested in Flash Builder 4 beta.

    package com.cnblogs.wideweide.utils
    {
        import flash.events.Event;
        import flash.events.EventDispatcher;
        
        import mx.core.IMXMLObject;
        import mx.core.UIComponent;
        import mx.events.FlexEvent;
        import mx.resources.*;
        import mx.binding.utils.BindingUtils;
        import mx.utils.ObjectUtil;
        import flash.utils.getDefinitionByName;
        import flash.utils.getQualifiedClassName;
        
        public class I18nInjection  extends EventDispatcher implements IMXMLObject
        {        
            
            /////////////////////////////////
            //
            //   继承自父类的方法
            //
            /////////////////////////////////
            private var document:Object;
            public function initialized(document:Object, id:String):void
            {
                if(!mEnabled) return;
                
                //记录创建此对象的 MXML 文档
                this.document = document;
                
                var ui:UIComponent = document as UIComponent;
                ui.addEventListener(FlexEvent.CREATION_COMPLETE,this.doInjection);
            }        
            
            /** 实现多语言支持注入 */
            public function doInjection(e:Event):void{
                if(!mEnabled) return;
                
                if(this.mTarget == null) this.Target = this.document;
                injection(this.mTarget,this.mBundleName);
            }
            
            
            
            //////////////////////////////
            //
            //  应用多语言的目标对象
            //
            //////////////////////////////
            protected var mTarget:Object;        
            public function set Target(value:Object):void{
                mTarget=value;
            }        
            public function get Target():Object{
                return mTarget;
            }
            
            
            /////////////////////////////
            //
            //    资源文件名
            //
            ////////////////////////////        
            protected var mBundleName:String;        
            public function set BundleName(value:String):void{
                mBundleName = value;
            }        
            public function get BundleName():String{
                return mBundleName;
            }
            
            ////////////////////////////
            //
            //     是否启用
            //
            /////////////////////////////
            protected static var mEnabled:Boolean=true;        
            public static function get Enabled():Boolean{
                return mEnabled;
            }
            public static function setEnabled(value:Boolean):void{
                mEnabled = value;
            }
            
            //////////////////////////
            //
            //   几个静态方法
            //
            //////////////////////////
            
            /**
             * Determine the object endpoint based on target and property values
             * e.g.    target="{healthCare}"  property="pnlQualification.txtSummary.text"
             *         object endpoint is healthCare.pnlQualification.txtSummary === txtSummary
             * 
             * @param target    Object instance
             * @param chain    Property or Property chain in target instance
             *  
             * @return Object     Reference to object instance whose property will be modified.
             * 
             */
            static public function resolveEndPoint(target:Object, chain:String):Object {
                var results : Object = target;
                
                if (results != null) {
                    var nodes : Array  = chain.split(".");
                    if (nodes && nodes.length > 1) {
                        // Scan all nodes EXCEPT the last (which should be the "true" property endPoint
                        for (var i:int=0; i<nodes.length-1; i++) {
                            
                            // Is this a standard or "indexed" node; 
                            // eg    frmRegister.registrationValidators[0].requiredFieldError has node 
                            //       'registrationValidators' as an indexed node
                            var pattern : RegExp = /(.)[(.)]/;
                            var matches : Array  = String(nodes[i]).match(pattern);
                            var node    : String = (matches && (matches.length > 2)) ? matches[1]         : nodes[i];
                            var j       : int    = (matches && (matches.length > 2)) ? int(matches[2])     : -1; 
                            
                            if (results.hasOwnProperty(node)) {
                                results = (j == -1) ? results[node] : results[node][j];
                                continue;
                            } else {
                                throw new Error(node);
                            } 
                            
                            
                            // The scope chain is not valid. This is an UNEXPECTED condition
                            if (results == null) throw new Error(node);
                        }
                    }
                }
                
                return results;
            }
            
            /**
             * Determine the "true" property to modify in the target endpoint
             * e.g.    "lblButton.label" --> resolved property === "label"
             *  
             * @param map         Current property chain 
             * @return String     Property key in the "endPoint" target
             * 
             */
            static public function resolveProperty(chain:String):String {
                var results : String = chain;
                if (results != "") {
                    var nodes : Array  = chain.split(".");
                    if (nodes && (nodes.length>0)) {
                        results = nodes[nodes.length-1];
                    }
                }
                
                return results;
            }
            
            static public function resolveBundleName(obj:Object):String{
                return getQualifiedClassName(obj).match(/[a-zA-z0-9]+$/i)[0];            
            }
            
            public static function getResourceManagerChain(bundleName:String, resourceName:String ,method:String="getString"):Object{
                var chain:Object = new Object();
                chain.name = method;
                chain.getter = function(rm:IResourceManager):String { return rm.getString( bundleName, resourceName ) };
                return chain;
            }
            
            public static function injection(target:Object,bundleName:String=null):void{
                if(target==null)return;            
                if(bundleName==null) bundleName = resolveBundleName(target);
                
                var rm:IResourceManager = ResourceManager.getInstance();
                var ar:Array = rm.getLocales();
                if(ar.length==0) return;
                var strLocale:String=ar[0];
                if(strLocale.length==0) return; 
                
                var bundle:IResourceBundle = rm.getResourceBundle(strLocale,bundleName);
                if(bundle==null) return;
                var props:Array = ObjectUtil.getClassInfo(bundle.content).properties as Array;
                for each (var key:String in props)
                BindingUtils.bindProperty( resolveEndPoint(target,key), resolveProperty(key), rm, getResourceManagerChain(bundleName,key) );
            }
        }
    }

    • 一个简单的应用实例:
      • 创建应用程序(i18n.mxml),添加对资源的引用、自定义组件,源码如下:

         <?xml version="1.0" encoding="utf-8"?>
         <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
                       xmlns:s="library://ns.adobe.com/flex/spark" 
                       xmlns:mx="library://ns.adobe.com/flex/halo"
                       xmlns:utils="com.cnblogs.wideweide.utils.*"
                       minWidth="1024" minHeight="768"
            <fx:Metadata>
                      引用的资源
                [ResourceBundle("i18n")]
            </fx:Metadata>
            <fx:Script>
                <![CDATA[
                    import mx.events.ListEvent;
                    protected function localeComboBox_changeHandler(event:ListEvent):void
                    {
                        resourceManager.localeChain = [ localeComboBox.selectedItem.code ];
                    }
                ]]>
            </fx:Script>
            <fx:Declarations>
                <!--  引入多语言支持注入控件 -->
                <utils:I18nInjection />
                
                <!-- 多语言支持列表 -->
                <fx:Array id="locales">
                    <fx:Object label="中文" code="zh_CN"/>
                    <fx:Object label="English" code="en_US"/>
                </fx:Array>
            </fx:Declarations>
            
            <mx:Form 
                width="100%" height="100%"
                defaultButton="{btnLogin}"
                >
                <mx:FormItem id="fiName" label="登录名称" required="true">
                    <mx:TextInput id="tiName" text="admin"/>
                </mx:FormItem>
                <mx:FormItem id="fiPassword" label="登录密码" required="true">
                    <mx:TextInput id="tiPwd" displayAsPassword="true" text="admin"/>
                </mx:FormItem>
                <mx:FormItem id="fiLocales" label="使用语言">
                    <mx:ComboBox id="localeComboBox" dataProvider="{locales}" change="localeComboBox_changeHandler(event)"/>
                </mx:FormItem>
                <mx:FormItem>
                    <mx:Button id="btnLogin" label="登录" />
                </mx:FormItem>
            </mx:Form>
         </s:Application>

    在项目的编译参数中设置语言和路径

     -locale=zh_CN,en_US -source-path=locale/{locale}

    添加并编写语言资源文件locale\zh_CN\i18n.properties
     
    fiName.label=登录名称 fiPassword.label=登录密码 btnLogin.label=登录 fiLocales.label=使用语言
     
    添加并编写语言资源文件locale\en_US\i18n.properties
     
    fiName.label= User Id fiPassword.label=Password btnLogin.label=Login fiLocales.label=Language

  • 相关阅读:
    关于windows CE Platform Builder中模拟器的限制
    送给每天都用电脑的人
    用eVC4开发SmartPhone、Pocket PC程序之 开发工具下载、安装、配置
    在手机上显示图片
    如何禁止回车的使用
    弹出窗口的方法
    Request.Form同Request.querystring的区别.txt
    找前几天
    手机上可移动的图片
    httphandler是做什么的?
  • 原文地址:https://www.cnblogs.com/hyb1/p/3041954.html
Copyright © 2020-2023  润新知