内容


使用 Dojo 国际化 Web 应用程序

让应用程序使用本国语言

Comments

简介

Wikipedia 将国际化定义为在无需进行工程性更改的前提下让所设计的软件应用程序能够适应各种语言和地区。(参见 参考资料)。根据这个定义,为了得到国际化的软件,有三个元素必不可少:

  • 负责告知软件其所要运行的本地语言环境的一个元素
  • 可应用到软件当前的本地语言环境的一组翻译文件
  • 负责连接前两个元素的一个组件

在本文中,我们将创建一个简单的国际化 Web 应用程序并展示针对前两个元素的 Dojo 工具箱标准(更多有关 Dojo 的信息,参见 参考资料)。之后,我们将针对第三个元素仔细研究两个 Dojo 用例:html 文件或小部件。

在一个基于 Dojo 的应用程序内设置本地语言环境

本地语言环境是一个参数,用来定义应用程序用户界面应该使用的语言。有的时候,仅仅指定语言可能还不够。不同的国家有可能使用的是近乎相同的语言,只有一些细微差别,比如拼写不同(例如,在美语中的 “center”,在英语中则是 “centre”),再比如同一个意思的用词不同(比如美语中的 “Elevator”,在英语中则是 “Lift”)。 因此,本地语言环境参数除了语言代码之外还要包含国家代码,以此指定应该使用的这种特定的语言。

Dojo 提供了两种用来指定 Web 应用程序本地语言环境的机制。第一种是编程方式(在运行时或设计时),另一种是借助浏览器的默认本地语言环境(在浏览器安装期间定义)设置。

通过编程的方式设置本地语言环境,实际上是在全局对象 djConfig 内指定 “locale” 属性的值。清单 1 的示例展示了如何在设计时将本地语言环境设为 en-us

清单 1:在设计时使用编程方式设置本地语言环境
          <script type="text/javascript" SRC="dojo.js" djConfig="locale:'en-us'"></script>

另一种可能性是通过编程方式计算本地语言环境(例如,通过从 URL 抽取),然后再设置本地语言环境。清单 2 的示例展示了如何在运行时使用编程方式设置本地语言环境:

清单 2: 在运行时使用编程方式设置本地语言环境
<script type="text/javascript"> 
// Note: this must be done before dojo loads!!!
var djConfig = new Object(); 
var theLocale = getLocale(); // this function is responsible to determine
                            // the appropriate locale
djConfig.locale = theLocale; 

function getLocale()
{
	return “en-us”;
}
</script>

最后一种可能性就是不指定本地语言环境,而是使用浏览器的默认本地语言环境,而这个默认设置是 Dojo 从导航对象提取出来的。默认的行为,正如任何默认设置一样,在您能预见客户机的浏览器和机器配置的情况下非常有用。但这种情况很少见,相反,比较常见的是浏览器使用非用户母语的语言安装或应用程序不熟悉的语言安装(所以应该要避免使用这种方式)。

Dojo 应用程序内的翻译文件

理解了如何设置应用程序的本地语言环境之后,就可以开始学习如何为 Dojo 应用程序提供一组翻译文件,称为 “资源包(resource bundle)” 。

一个资源包是命名相同的一组文件,其中的每个文件都适用于特定的一个本地语言环境。而且每个文件都应该是 JSON 格式。对于我们的这个欢迎应用程序,资源包文件都被命名为 greeting.js。其内容如 清单 3 所示。

清单 3:针对不同英语语言环境的资源包
针对美式英语语言环境的内容为:


{
	PAGE_TITLE:"God bless America",
	GREETING: “What’s up”,
	PERSON: “dude”,
	GREETING_SENTENCE: “${greeting} ${personTitle}”
}
 
                      
      
针对英式英语语言环境的内容为: { PAGE_TITLE:"God save the queen", GREETING:”Hello”, PERSON:”dear”, GREETING_SENTENCE: “${greeting} ${personTitle}” }
针对澳大利亚英语语言环境的内容为: { PAGE_TITLE:"God bless the ozis", GREETING:”G’day”, PERSON:”mate”, GREETING_SENTENCE: “${greeting} ${personTitle}” }
针对法语本地语言环境的内容为: { PAGE_TITLE:"Vive la France", GREETING:”Bonjour”, PERSON:”monsieur”, GREETING_SENTENCE: “${greeting} ${personTitle}” }

现在需要将这些文件放入合适位置以便 Dojo 能够读取。为此,需要以 Dojo 期望的方式构造文件夹的层次结构。假设应用程序在位置 “../../sample/application” 定义了一个称为 “sampleApp” 的模块路径。那么所需创建的目录结构将类似 图 1

图 1. 目录结构
目录结构

基本上,名为 “nls” 的文件夹应该位于所定义的模块之下。它包含 locale 文件夹,使用小写格式,并用连字符分隔语言代码和可选的国家代码。

注意,在翻译文件内,我们已经将欢迎分成两个部分,这是因为不同的语言有不同的句子结构,欢迎词的构成也就有所不同。因此,我们需要以一种国际化的方式来从各部分构造欢迎词。为此,我们使用了 Dojo 的实用工具 dojo.string.substitute,它接收字符串的模板和参数化的值并用给定的值替换这些占位符。模板本身是资源包内的 GREETING_SENTENCE 项。

在 HTML 文件内连接各部分

为了连接之前定义的各部分,我们创建了包含此应用程序的 html 文件,如 清单 4 所示。

清单 4:应用程序的 HTML 文件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title id="titleId"></title>
    <script type="text/javascript">
      var djConfig = { modulePaths: {sampleApp: "../../sample/application"},
                       parseOnLoad: true,
                       locale:'en-us'};
    </script>
    <script type="text/javascript" src="dojoBase/dojo/dojo.js"></script>
    <script type="text/javascript">
      dojo.require("dojo.string");
      dojo.requireLocalization("sampleApp", "greeting");
      function init()
      {
        var nlsStrings = dojo.i18n.getLocalization("sampleApp","greeting");
        var greetingString = nlsStrings.GREETING; var personString = nlsStrings.PERSON;
        var greetingTemplate = nlsStrings.GREETING_SENTENCE;
        var finalGreeting = dojo.string.substitute(greetingTemplate,
                                                   {greeting:greetingString,
                                                    personTitle:personString});
        dojo.byId("helloDiv").innerHTML = finalGreeting;
        dojo.byId("titleId").text = finalGreeting;
      }
      dojo.addOnLoad(init);
    </script>
  </head>
  <body>
    <div id="helloDiv"></div>
</body>
</html>

此应用程序中最需要注意的一点是两个 Dojo 命令的使用,用它们来提取本地化了的信息。第一个是 dojo.requireLocalization("sampleApp", "greeting"),此调用负责从服务器请求位于所定义的模块 “sampleApp” 之下名为 “greeting” 的资源包。第二个是 var nlsStrings = dojo.i18n.getLocalization("sampleApp","greeting"),此调用负责将所收到的 “greeting” 资源包(位于 “sampleApp” 模块下)转变成 JavaScript 对象以便返回。有了此对象(nlsStrings 对象)之后,就可以引用它的任何字段以便用所需的字符串设置合适的 dom 元素。

利用定制创建的小部件连接各部分并显示内容

通常,如果应用程序的规模比示例应用程序稍微大一些,就可以引入定制创建的小部件。在本节,我们将使用定制创建的小部件重新创建这个示例应用程序。您将看到如何正确地从内容显示小部件分离所有国际化机制。

首先,创建负责处理所有国际化问题的小部件,从 “mixin” 开始,如 清单 5 所示,它负责所有的本地语言支持(NLS)操作。

清单 5:NLS mixin
dojo.require("dojo.i18n");
dojo.require("dojo.string");
dojo.requireLocalization("sampleApp", "greeting");
dojo.provide("sampleApp.widget.NlsMixin");

dojo.declare("sampleApp.widget.NlsMixin",null,
  {
    data: {__initialized: false},

    constructor: function()
    {
      if(!this.data.__initialized)
      {
        this.data.nlsStrings = dojo.i18n.getLocalization("sampleApp",                   
                                                         "greeting");
        this.data.__initialized = true;
      }
    },

    /**
    *
    * @param {String} key - the name of the key in the translation file
    * @param {Object or Array?} substitutes - in cases where the translated  
    *   string is a template for string substitution, this parameter
    *   holds the values to be used by dojo.string.substitute on that  
    *   template
    */
    getString: function(/*String*/ key,
                        /*Object or Array?*/ substitutes)
    {
      var str = this.data.nlsStrings[key];
      return (substituteValues)?  
                               dojo.string.substitute(str,substitutes):
                               str;
    },

    postMixInProperties: function()
    {
      this.inherited('postMixInProperties', arguments);
      this.initializeStrings();
    },

    initializeStrings: function(){}
  }
);

这个 mixin 中有三点需要注意:

  • 为了减少内存使用,资源对象是静态和单一的。
  • 使用这个 mixin 的小部件需要覆盖函数 ‘initializeStrings’,并通过调用 getString 函数填充它。此方法在调用 postMixinProperties 期间被调用,以便所有国际化了的字符串能在呈现这个小部件时可用(换言之,就是在小部件的模板内使用这些字符串)。
  • getString 函数接受两个参数。一个参数是资源包中的键。如果所返回的字符串需要由 dojo.string.substitute 进一步处理,那么第二个参数就用来提供替换对象或数组(根据模板在资源包内的定义方式)。

现在,我们需要向您展示如何创建这个简单的欢迎小部件,它应该被置于页面之内以欢迎应用程序的用户。参见 清单 6

清单 6:欢迎小部件的代码
dojo.provide("sampleApp.widget.Greeting");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("sampleApp.widget.NlsMixin");

dojo.declare("sampleApp.widget.Greeting",
    		[dijit._Widget,dijit._Templated ,sampleApp.widget.NlsMixin],
  {        
    templateString: "<div>${greeting}</div>",
    greeting:””,
    initializeStrings: function()
    {
      var greetingString = this.getString("GREETING");
      var personString = this.getString("PERSON");      
      this.greeting =   this.getString("GREETING_SENTENCE",    
                                            {greeting:greetingString, 
                                             personTitle:personString});
  }
}
);

正如您所见,此模板引用了一个变量,该变量在 initalizeStrings 调用期间获得一个 “real” 值,为了得到正确的国际化内容,initalizeStrings 调用使用了 getString 方法。

现在,所需做的就是在一个 HTML 文件内使用这个小部件来查看国际化了的内容,如 清单 7 所示。

清单 7:面向 en-au 本地语言环境的欢迎页
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <script type="text/javascript">
      var djConfig = { isDebug: false, 
                       modulePaths:
                       { sampleApp: "../../sample/application" },
                       parseOnLoad: true, 
                       locale:'en-au'
                     };
    </script>
    <script type="text/javascript" src="dojoBase/dojo/dojo.js.uncompressed.js"></script>
    <script type="text/javascript">
      dojo.require("sampleApp.widget.Greeting");
    </script>
  </head>
  <body>
    <div dojoType="sampleApp.widget.Greeting" id="helloDiv"></div>
  </body>
</html>

快速测试

将应用程序翻译成其他语言,而不是开发人员在开发期间使用的语言,这需要对此软件进行三类测试。

  • 第一个是测试翻译的质量,这通常很难实现,因为除翻译人员之外,还需要另外一个人熟知被翻译的语言。关于这类测试的介绍超出了本文的讨论范围。
  • 第二类测试是测试应用程序用户界面显示每种语言的方式。一种语言的句子和词语有可能比另一种语言的长或短,这种改变有可能会让 UI 看起来很不一样(i18n 的很多其他方面我们这里都没有讨论,比如有时因为文化的差异需要在不同的本地语言环境使用一组不同的颜色和图标)。这类测试由开发团队进行,通常需要为不同的本地语言环境使用不同的样式表。关于这类测试的介绍亦超出了本文的讨论范围。
  • 第三类测试是覆盖测试。它通常意味着开发人员需要验证应用程序展示给最终用户的所有字符串都不是硬编码的,而是源自合适的资源包。我们将给出进行这类测试的一种方式。当然,可能还有很多其他方式,但这里给出的这种方式很简单也很有效,所以没有必要引入更复杂的方法。

测试硬编码的元素

以下所示的是一个逐步指导,可用于执行对国际化内容的覆盖测试:

  1. 创建一个虚构的本地语言环境,称为 “en-num”(添加其他 locale 文件夹的一个兄弟文件夹)。
  2. 向此文件夹复制要进行测试的资源包。
  3. 手动或自动使用某个以自己偏爱的编程语言编写的小程序或脚本,将此文件内的所有字符串(不包括 dojo.string.substitute 的替换标记)更改为数字。
  4. 将此应用程序的本地语言环境设置为 “en-num” 并遍历应用程序的所有屏幕。如果看到有的字符串不是全部由数字组成,那么很可能此字符串不是来自资源包。

结束语

至此,您看到了如何利用 Dojo 工具箱在 Web 应用程序中执行国际化。本文给出了一种设计、实现和测试 i18n 翻译部分的方式。还有很多其他与本地化更为相关的方面,比如根据本地语言环境(不同国家使用不同的方式来显示数字、货币、时间和日期)格式化某些被显示的元素。这里所没有涉及的 i18n 的其他方面大都源自文化差异。在一个国家具有一种含义的图标在另一个国家可能意义完全相反。例如,在 UI 中使用猫头鹰图标表示聪明,在亚洲的一些国家可能就是不同的含义,因为在这些国家猫头鹰往往象征着愚蠢。

不过,i18n 的翻译部分是最需要资源的,要求软件开发人员进行大量工作。使用本文中给出的信息可能会节省您的一些时间和成本。如果真的如此,那么本文就是有价值的。


相关主题

  • 您可以参阅本文在 developerWorks 全球网站上的 英文原文
  • 阅读有关 Internationalization 的 Wikipedia 页。
  • Dojo toolkit 是一个 JavaScript 工具箱,具有很多支持 Ajax 应用程序开发的组件。
  • IBM 全球化 具有处理各种有关全球化问题的大量资源。
  • Blogamundo 是一个讨论各种国际化问题的优秀 blog。
  • Global by design 是一个讨论各种 Web 全球化问题的 blog。
  • Dojo 园区 内有很多与使用 Dojo 相关的很好的提示和 cookie。
  • 利用 developerWorks Web 开发专区 内有关 Web 技术的文章和教程扩展您的 Web 开发技能。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=333070
ArticleTitle=使用 Dojo 国际化 Web 应用程序
publish-date=08262008