在本系列的第 1 部分中,我讲解了如何通过把资源直接编译到应用程序中在 Flex 中启用本地化。在第 2 部分中,继续第 1 部分中的讨论,讲解另一种本地化方法,即单独编译资源属性文件并在运行时装载它们。还要讨论每种方法的好处和缺点,以及对所有应用程序进行本地化的一般方法。
通过创建属性文件并把它们编译到应用程序中,现在已经成功地在应用程序中启用了本地化。编译到应用程序中之后,属性文件被称为资源包 (resource bundle),Flex 资源管理程序通过访问它们提取本地化的值。在本教程的这一部分中,我将解释如何从应用程序中消除编译的资源包,改为把它们编译成资源模块 (resource module)。资源模块是资源包的集合,可以在运行时装载它们,供资源管理程序访问。
目录
要求
为了充分利用本文,您需要以下软件和文件:
Flash Builder 4
范例文件:
必备知识
要想完成本文中的步骤,您需要对 Flex 开发有一定的了解和经验,使用 Flash Builder(以前的 Flex Builder)或通过 SDK 使用命令行都可以。不需要具备本地化知识。
回顾
在继续讨论之前,先回顾一下在第 1 部分中完成的步骤。
- 设置项目和适当的目录结构。
- 为希望支持的地区创建属性文件。
- 确认本地化框架文件已经就位。
- 通过设置 Flex 编译器选项启用本地化并指定要支持的地区。
- 开始本地化!
继续
本系列的第 2 部分是第 1 部分的延续,因此继续使用第 1 部分结束时完成的项目。最初的项目 localization-part-ii-start 实际上就是第 1 部分最终的项目 localization-part-i-end。以这个项目作为起点,按本文中的步骤把属性文件外部化并在运行时装载它们。
完成第 1 部分中的步骤之后,只需再完成四个步骤就能够外部化并装载资源包。它们是:
- 判断项目应该包含哪些必需的资源包。
- 创建自己的资源模块(每个地区一个)并用它们替代属性文件。
- 修改编译器选项以包含适当的资源包,并指定只使用资源模块。
- 在运行时在应用程序中装载资源模块。
判断必需的资源包
应用程序在执行期间将使用许多资源包。即使没有与应用程序相关的本地化,许多核心 Flex 框架类也依靠资源包对运行时和编译时的警告和错误进行本地化。因此,在创建自己的资源模块(它们实质上是资源包的集合)之前,必须先查明要包含哪些资源 包。可以使用 Flex 编译器很轻松地完成这个步骤。只需在编译应用程序时使用 -resource-bundle-list 编译器选项,例如:
mxmlc -locale= -resource-bundle-list=used-resource-bundles.txt Main.mxml
注意:可以在通过命令行直接执行 mxmlc 时使用这个编译器选项,也可以在 Flash Builder 中的 Additional Compiler Arguments 设置中添加它。
bundles = collections components controls core effects layout resources skins styles textLayout
创建自己的资源模块
既然知道了应用程序要使用哪些资源包,现在就可以编译自己的资源模块了。为此,必须通过命令行执行 mxmlc。目前还不能在 Flash Builder 中编译资源模块。
要想编译资源模块,只需指定四个编译器选项:
- locale—这是要为其创建资源模块的地区。
- source-path—这个资源模块要使用的属性文件的位置。
- include-resource-bundles—在前一步中查明的应用程序必须使用的资源包列表。
- output—将生成的输出 SWF 文件。这就是您的资源模块!
注意:不指定要编译的 MXML 文件。
- 要想为示例应用程序编译地区 en_US 的资源模块,应该执行以下命令:
mxmlc locale=en_US source-path=locale/{locale} include-resource-bundles=collections,components,controls,core,effects,layout,resources,skins,styles,textLayout -output resources.swf
注意:可以按您喜欢的目录结构给资源模块命名,只要在应用程序中相应地装载它们即可,具体做法见在运行时装载自己的资源模块。
这会使用 locale/en_US/ 中的属性文件为地区 en_US 创建资源模块 resources.swf,其中包含前面查明的其他资源包。
- 为地区 en_US 创建 resources.swf 之后,只需把它放在 localeen_US 中,与 en_US 属性文件放在一起。注意:这个项目不再需要属性文件,但是最好保留它们,因为以后可能需要修改它们。
- 对于另外三个地区重复步骤 1 和 2,见图 1。图 1. 为每个地区创建一个 resources.swf 文件。注意:可能会在项目中看到以下错误:Unable to resolve resource bundle ‘resources’。这是因为现在引用了一个不再存在的资源包,它已经被刚才创建的资源模块替代了。在下一节中修改编译器选项时将纠正这个错误。
修改编译器选项
刚才创建的资源模块已经就位了,现在可以修改编译器选项以包含适当的资源包,并指定只使用资源模块。
- 要想包含适当的资源包,应该使用 -include-resource-bundles 编译器参数并指定前面生成的资源包列表。
- 要想指定只使用资源模块,应该使用 -locale 编译器标志并加上等号 “=”,后面不加任何参数。这个步骤是可选的,但是因为这个应用程序确实只使用资源模块,最好让编译器知道这一情况。
按以下步骤设置编译器选项:
- 右键单击项目并选择 Properties。
- 选择左边的 Flex Compiler,使用下面的代码作为 Additional Compiler Arguments 设置:
locale= -source-path ./locale/{locale} -include-resource-bundles collections components controls core effects layout resources skins styles textLayout
注意:当通过 Flash Builder 修改编译器选项时,值不是逗号分隔的。
注意:可能会收到一条警告,它指出地区的源路径是项目源路径的子目录。使用下面的编译器选项允许源路径重叠,就可以解决这个问题:-allow-source-path-overlap=true
在运行时装载自己的资源模块
最后一步是修改应用程序以在运行时装载资源模块。在第 1 部分中,已经在地区组合框上添加了更改事件处理函数,它会根据在这个组合框中选择的语言更改地区。这个事件处理函数的代码如下:
private function comboChangeHandler():void {
resourceManager.localeChain = [localeComboBox.selectedItem.locale];
}
可以这样做是因为必需的资源包(包括自己的定制 resources 资源包)被编译并装载到应用程序中了。因此,资源管理程序可以立即访问资源包。但是,现在资源包没有直接编译到应用程序中,所以必须手工装载它们。为此, 要调用 ResourceManager 的 loadResourceModule 函数。装载了资源模块之后,可以像上面的 comboChangeHandler 函数一样通过修改资源管理程序的 localeChain 属性更改地区。
让 comboChangeHandler 事件处理函数更改地区实际上只需要做一处修改,而且逻辑非常简单。首先,检查是否已经装载了想要的地区的资源模块。如果还没有,就装载它。否则,像正常情况下一样更改地区。
- 在 Source 视图中编辑 Main.mxml。
- 把现有的 comboChangeHandler 函数替换为以下代码:
import mx.events.ResourceEvent; private function comboChangeHandler():void { var newLocale:String = String(localeComboBox.selectedItem.locale); // 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 = "./locale/" + newLocale + "/" + "resources.swf"; var eventDispatcher:IEventDispatcher = resourceManager.loadResourceModule(resourceModuleURL); eventDispatcher.addEventListener(ResourceEvent.COMPLETE, completeHandler); } function completeHandler(event:ResourceEvent):void { resourceManager.localeChain = [localeComboBox.selectedItem.locale]; } }
代码首先用一个简单的 if 语句检查是否已经装载了所选的地区的资源模块。如果已经装载了,就调用 completeHandler 函数,这个函数使用与原来的 comboChangeHandler 函数相同的代码行更改资源管理程序的地区。如果还没有 装载资源,就装载它。首先,代码识别资源模块的位置,然后调用 resourceManager.loadResourceModule 函数。装载资源模块之后,调用 completeHandler 函数。
注意:不要在调用 loadResourceModule 之后马上使用资源模块。ResourceManager 的 loadResourceModule 函数异步地执行调用,所以当调用完成时资源模块可能还没有 装载,还无法使用。相反,一定要 连接一个事件处理函数以监听 ResourceEvent.COMPLETE 事件。只有当派发这个事件时,才能够确定资源模块已经准备好了。
- 运行应用程序。当装载应用程序时,您会注意到表单中根本没有显示文本。目前,当在组合框中选择一种语言时,这个应用程序会装载适当的资源模块并更改地区。但是,在应用程序最初运行时,没有装载资源模块,所以不显示文本。为了解决这个问题,只需在主应用程序中添加 creationComplete 事件处理函数。当调用这个函数时,它装载默认地区 en_US。因为组合框在默认情况下显示英语,所以只需通过调用 comboChangeHandler 函数模拟选择英语的情况。
- 在 Main.mxml 中添加以下函数:
private function init():void { comboChangeHandler(); }
- 最后,在主标记中添加 creationComplete=”init”。完成这些修改之后,示例应用程序的主 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/mx" creationComplete="init()"> <fx:Script> <![CDATA[ import mx.events.ResourceEvent; [Bindable] private var locales:Array = [{label:"English (United States)", locale:"en_US"}, {label:"German (Germany)", locale:"de_DE"}, {label:"French (France)", locale:"fr_FR"}, {label:"Japanese (Japan)", locale:"ja_JP"}]; private function init():void { comboChangeHandler(); } private function comboChangeHandler():void { var newLocale:String = String(localeComboBox.selectedItem.locale); // 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 = "./locale/" + newLocale + "/" + "resources.swf"; var eventDispatcher:IEventDispatcher = resourceManager.loadResourceModule(resourceModuleURL); eventDispatcher.addEventListener(ResourceEvent.COMPLETE, completeHandler); } function completeHandler(event:ResourceEvent):void { resourceManager.localeChain = [localeComboBox.selectedItem.locale]; } } ]]> </fx:Script> <fx:Metadata> [ResourceBundle("resources")] </fx:Metadata> <s:layout> <s:VerticalLayout horizontalAlign="center" verticalAlign="middle" /> </s:layout> <s:Panel title="{resourceManager.getString('resources','contact.title')}" color="black" borderAlpha="0.15" width="350"> <s:layout> <s:VerticalLayout horizontalAlign="center" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10" /> </s:layout> <mx:Form width="100%" color="0x323232"> <mx:FormItem label="{resourceManager.getString('resources','contact.field.name')}"> <s:TextInput /> </mx:FormItem> <mx:FormItem label="{resourceManager.getString('resources','contact.field.streetAddress')}"> <s:TextInput /> </mx:FormItem> <mx:FormItem label="{resourceManager.getString('resources','contact.field.city')}"> <s:TextInput /> </mx:FormItem> <mx:FormItem label="{resourceManager.getString('resources','contact.field.state')}"> <s:TextInput /> </mx:FormItem> <mx:FormItem label="{resourceManager.getString('resources','contact.field.zipCode')}"> <s:TextInput /> </mx:FormItem> <mx:FormItem label="{resourceManager.getString('resources','contact.field.country')}"> <s:TextInput /> </mx:FormItem> <mx:FormItem> <s:Button label="{resourceManager.getString('resources','contact.submit')}" /> </mx:FormItem> </mx:Form> </s:Panel> <mx:Spacer height="15" /> <s:HGroup width="350" verticalAlign="middle"> <mx:Spacer width="100%" /> <mx:Image source="{resourceManager.getString('resources','contact.flagImg')}"/> <mx:ComboBox id="localeComboBox" dataProvider="{locales}" change="comboChangeHandler()"/> </s:HGroup> </s:Application>
现在完成了!构建应用程序、运行它并选择不同的语言,应该会看到联系人表单在运行时装载适当的资源模块。如果遇到任何问题,请参考 localization-part-ii-end.zip 中完成后的项目文件。
好处和缺点以及适合 FLEX 开发人员的一般方法
现在,您已经学完了这个分两部分的教程!我们使用了两种方法在 Flex 框架中实现本地化:
- 把资源包直接编译到应用程序中。
- 在外部编译资源包,在运行时把它们装载到应用程序中。
既然已经了解了这两种方法,现在有必要比较一下每种方法的好处和缺点,这样就能够在下一个应用程序中实现本地化时采用适当的技术。
表 1. 直接编译的优点和缺点
优点 | 缺点 |
---|---|
|
|
表 2. 外部编译的优点和缺点
优点 | 缺点 |
---|---|
|
|
您可能想知道,对于自己的应用程序,应该采用哪种方法。一般来说,对于支持的地区比较少的应用程序,使用编译时资源是最合适的。对于支持许多地区、具有大量本地化字符串的大应用程序,使用资源模块更好。
但是,这并不意味着您只能采用某一种方法。对于许多应用程序,混合式方法非常有用。例如,对于默认地区或最常用的地区(比如英语和法语),可能希望 使用编译时资源。对于其他地区,可以使用资源模块并在运行时装载它们。这样就可以快速访问常用的两个地区,同时通过资源模块支持许多其他地区,而不会增加 应用程序 SWF 的大小。
后续研究
对于面向全球用户的任何应用程序,本地化都是重要的特性。为了确保您的应用程序不但支持适当的地区,而且高效地使用框架,一定要了解可用的两种方法以及实现它们的方式。应该了解这两种方法以及它们的好处和缺点,这会帮助您为应用程序选择最合适的方法。
关于作者: