zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    mac c++编译出现segmentation fault :11错误
    ssh 连接缓慢解决方法
    237. Delete Node in a Linked List
    203. Remove Linked List Elements
    Inversion of Control Containers and the Dependency Injection pattern
    82. Remove Duplicates from Sorted List II
    83. Remove Duplicates from Sorted List
    SxsTrace
    使用CCleaner卸载chrome
    decimal and double ToString problem
  • 原文地址:https://www.cnblogs.com/hyb1/p/3041954.html
Copyright © 2011-2022 走看看