当前页面: 开发资料首页 → Eclipse 专题 → 开放源码项目: 国际化 Eclipse 插件
摘要: 本文是为编写进入国际市场的 Eclipse 插件而准备的路线图。首先,我们将简短回顾国际化的动机和技术问题,然后逐步说明国际化插件的步骤。最后,我们将说明如何将这些步骤应用到 Eclipse Platform 本身的国际化过程中。
在国际化社区中,有一个流传了很久的笑话:
“会说三种语言的人称为“三语人”。会说两种语言的人称为“双语人”。那么把只会说一种语言的人称为什么呢?”
“美国人。”
现在,从可用性、质量、市场营销和(在某些情况下)法律的观点来看,只提供英语版本的软件已不再受欢迎了。让您的产品能够用于国际市场只是为了增加经济效益。这个启用过程比较简单,就象本文所呈现的一样。
在开始之前,要谈几点注意事项。由于 Eclipse Platform 采用了 Java SDK 提供的国际化实现,因此在继续阅读本文之前先阅读 Java Tutorial: Internationalization 系列会有所帮助。该教程很好地概述了过程中涉及的问题和步骤。我们将假设您已经阅读了该教程,因此我们可以在本文中着重说明要点,阐述其它值得注意的事项并涵盖 Eclipse 特定的问题和步骤。当遇到本文中不熟悉的术语或缩略语时,请立即查阅 词汇表。
国际化概述
国际化是创建适合于国际市场的软件的过程。除了经济利益之外,某些国家或地区要求产品在进入其市场之前通过政府设置的某些本地化要求。
国际化 Eclipse Platform 的过程分两步完成:
国际化影响什么?
只熟悉单一语言的人注意事项:这是灵敏度训练的开始。本文结尾处可能还会有测验。:-)
让我们从 Java 国际化教程(请参阅本文后面的 参考资料)中提供的与文化相关的数据列表摘录入手,该列表已根据一般开发人员遇到的可能性进行了重新排序,而且每项后面还附带了详细信息:
文本
消息、GUI 组件上的标签
将已翻译的字符串装入内存只是第一步。下一步是将它们传递给用于显示的适当的控件(如标签、文本域、菜单选项等)。页面设计人员和程序员必须合作以确保所选布局允许适当地重新调整对话框大小和重新布局对话框。标准小窗口工具箱(Standard
Widget Toolkit (SWT))库中的布局支持极大程度依赖于程序员指定对域大小做出适当反应的布局描述才能“正确行事”。eclipse.org
网站上的文章“Understanding Layout in SWT”(请参阅
参考资料)详细地讨论了实现问题。
由于在翻译期间文本长度会增加,因此这特别重要。英语短语通常比其意义相同的译文短,通常短 40% 左右。为了适应本地语言,可能还需要修改字体大小。
联机帮助(*.html),插件清单(plugin.xml)
以清单文件为例,它与类似命名的特性文件 plugin.properties 配对,只包含外向化的文本。对于 plugin.xml
和 fragment.xml 之类的清单文件必须特别小心,因为标记的属性可能包含已翻译和未翻译文本。考虑以下这个良好的示例: 这里,我们看到一个可翻译文本、不可翻译文本和“灰色区域”可翻译文本的混合体,它们都是标记属性。显然,
您也许会考虑翻译
provider-name 也不翻译,因为“Inc.”具有法律意义,因而难以精确翻译。在标识了需要外向化哪些文本之后,现在示例如下所示: 其中
这个简单示例演示了翻译不只是为文本提供意义相同的单词或短语;它还涉及到理解本地文化考虑事项和潜在的法律冲突。这就是为什么需要专职翻译人员以及翻译验证测试。 数据格式
数字、日期、时间、货币
电话号码、邮递地址
地区和个人称谓
尊称和个人头衔
度量衡
多媒体考虑事项
通常,产品应该选择与地域无关的声音、颜色、图形和图标。 这意味着 Homer Simpson 的“D'oh!”声音与错误消息没有关系。如果您认为不会有大的开发组织会做这样的事,那么就看看下面这个典型的被提议和拒绝的图标: 开发人员要使用 66 号公路(一条从芝加哥到洛衫矶,横穿美国的国道)的符号来比喻“IP 路由器”。大多数美国人都会明白这个隐喻;想想这给倒霉的非美国用户造成的混淆。 同样,下面的图像对于许多北美用户也许很直观: 但在识别性调查中,美国以外的其他用户都猜这是个鸟房。以下是一个更广为接受的邮件图像: 要避免会产生混淆和可能令人不快的图像,最好的做法是聘请了解文化问题的专职图形艺术家。 国际化步骤
现在,让我们着手国际化 Eclipse 插件的实际步骤: 我们将详细讨论每一步。 第 1 步:将可翻译字符串移到 *.properties 文件中
幸好,Eclipse 的 Java 开发工具为正确地分隔可翻译字符串提供了很多帮助。包上下文菜单中的
Find Strings to Externalize
选项显示了
Externalize Strings向导。这个向导将引导您完成一系列步骤,以便寻找代码中的硬编码字符串、将它们分成可翻译或不可翻译,然后在适当的情况下修改代码以使用资源束。
在使用该向导之
前,我们通过一个标准的“Hello World”来介绍
Externalize Strings向导:
从包上下文菜单中选择
Find Strings to Externalize
将显示一个选择对话框,其中显示了包中可能包含可翻译文本的所有编译单元。因为我们现在只有一个
Java 文件,因此从 HelloWorld.java 文件中直接选择
Externalize Strings 比较简单。它会显示
Externalize Strings 向导:
通过从表中选择一项,然后选择右边的一个按钮,可以将字符串标记成以下三种情况之一: 注:
这里,中间的参数被标志成无需 NLS。其它两个则被跳过。 回到我们的示例,请注意,在列表的下面汇总了每个类别的字符串总数。外向化字符串的键名称是根据字符串值自动生成的,但可以在表中直接对其重命名。此外,可以指定一个可选的前缀(在下面的示例中是
提示:单击给定行的第一列中的图标将前进到下一个选项:Translate、Never Translate 或 Skip。
既然我们已经标识了哪些字符串是可翻译的,那么继续到下一步,选择如何对其外向化。这里是选择
Next之后显示的页面;
Property file name和资源束取值方法
Class name被修改成包含比缺省值更特殊的值:
资源束取值方法类将包含用于装入特性文件的代码,以及从文件中取出字符串的静态方法。向导会生成这个类,或者您可以指定自己现有的备用实现。在后一种情况下,您也许要取消选择
Use default substitution choice,并指定用于检索外向化字符串的备用代码模式。如果取值方法类在包外部(例如,集中式资源束取值方法类),您可以有选择地对底层源代码指定
Add [an] import declaration。
Externalize Strings向导使用了 JDT Refactoring 框架,这样下两个页面看起来就很熟悉。首先,会显示一列警告:
最后,一一显示了建议的更改: 选择了
Finish之后,向导会执行源代码修改、创建资源束取值方法类,并生成初始的特性文件。以下是标准资源束取值方法类的代码:
该生成代码中的唯一变化是分配给 static final
管理资源束和特性文件的准则
这些准则的设计意图是: 要实现这些目标,我们建议以下准则: 1.
每个包使用一个特性文件,按类名称来限定键
例如,JDT 搜索组件的所有字符串都在 SearchMessages.properties 中,其中键/值对如下:
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td> 2.
使用专用静态资源束取值方法类
让
Externalize Strings 向导生成这个类。它应该与特性文件同名。因此在我们的示例中,它就叫作
SearchMessages。当需要创建格式化的字符串时,将便利方法添加到束取值方法类中。例如:
3.
不要使用经过计算的键
没有一种简便方法可以使代码中经过计算的键和特性文件中的键关联起来。尤其是要确定某个键是否不再使用时,这几乎不可能。 4.
键名称的约定是 示例:PackageExplorerPart.title 第 2 步:分离与显示相关的参数
并非所有的外向化文本都只是要翻译成目标语言的单词或短语。其中有些与您的插件实现密切相关。这些示例包括属性、首选项和缺省对话框设置。 这里有一些特殊示例,它们会用于特性文件中: 对于那些从
AbstractUIPlugin
中分类出来的插件,还可以在其
缺省首选项存储(pref_store.ini)和
对话框设置(dialog_settings.xml)中找到与本地语言相关的参数。Eclipse Workbench
本身并不使用缺省首选项存储,而是选择将缺省值存储到特性文件,然后通过 AbstractUIPlugin
的
第 3 步:使用正确的语言环境敏感的数据格式、替代 API
请参考
Java Tutorial: Internationalization系列中的详细介绍。
第 4 步:以本国语言进行测试
测试是否可以翻译产品是很重要的,但已超出了本文的范围。但是,以后的文章(“测试国际化的 Eclipse 插件”)将提供验证产品的本地语言敏感问题的策略。
第 5 步:创建初始已翻译插件片段
现在,我们可以只把本国语言特性文件复制到同名且带有特定语言环境后缀的文件中(例如,MyMessages_xx.properties,其中
xx 是语言),然后前进到第 6 步,
准备和发送本国语言材料以供翻译。在这种情况下,产品是与其代码以及它支持的任何语言都作为一个安装交付的。
但是,这种方法有一些缺点。首先,代码及其本地语言资源都混杂在同一个目录/JAR
文件中。如果翻译比代码交付晚,那么必须更新插件 JAR 文件,尽管底层代码并没有更改。其次,除特性文件之外的其它文件(例如,HTML、XML、图像)本质上并不对语言环境敏感,因此必须将它们分离到每种语言的单独目录中。 为了解决这些问题,Eclipse Platform 引入了另一个与插件相对应的可重用组件的概念,叫作
插件片段。插件片段向其目标插件提供了附加功能。在运行时,这些插件组成部分与所有相关的片段合并在一起。这些组成部分可以包括代码组成部分和与插件相关的资源组成部分,如特性文件和 HTML 文件。换句话说,插件通过其类装入器访问片段的内容。
如何以及为什么使用片段来提供可翻译信息
插件片段是分发已翻译 Eclipse 信息(包括 HTML、XML、INI 和位图文件)的理想方式。由于以非入侵方式交付翻译,所以将
Eclipse Platform 翻译封装到片段 JAR 文件中,然后将其添加到现有 Eclipse 安装中不会更改或修改任何原始的运行时元素。这就引出了
语言包(language pack)的概念。
Eclipse Platform 以用片段中的运行时元素增补原始目标插件的方法来合并插件片段。这不会以任何方式移动、除去或修改目标插件。由于类装入器会寻找片段的资源,所以插件开发人员无需知道是从插件的 JAR 文件还是其片段的某个 JAR 文件装入资源。 Eclipse 语言包 JAR
Java 语言带有资源束功能,因此支持语言包的概念。Java
资源束不要求修改应用程序代码来支持另一种语言。*.properties
文件名称空间通过以下命名约定避免冲突:
basename_lang_region_variant。在运行时,
ResourceBundle
功能会针对当前语言环境寻找适当的特性文件。
在片段中部署文件(如 HTML 和 XML 文件)与在 Java 资源束中部署文件略有不同,因为
Eclipse 片段使用目录结构来区分不同的语言版本。
示例片段内容
通常,会根据资源束规则给已翻译的 *.properties 文件添加后缀,并将它们部署到 JAR 文件中。可是,当一个视图需要一种输入文件类型,而文件名称不象资源束一样是语言环境敏感的(如
*.xml),我们就要为文件的每一个语言版本定义一个子目录结构。上面的 de 子目录就是这样一个示例,其中 de = 德语。
片段清单
用于描述本地语言片段的清单通常很简单,只需指定
构建片段
现在,让我们研究如何使用 PDE 构建用于本地语言翻译的片段。在给定片段中,对语言的数量没有实际的限制。那么片段就是包含一种或多种语言翻译的“语言包”的基础。但是,在这个示例中,我们将语言包限定为德语翻译。 要构建插件片段,启动 New Project 向导(
File>
New>
Project...),选择
Plug-in Development类别,然后选择
Fragment Project 类型。在 New Fragment 向导的第一页,输入项目名称。请记住,项目名称也将变成片段标识。例如,启动一个向
HelloWorld 示例添加本地语言支持的项目,我们将项目命名成“com.jumpstart.nls.example.helloworld.nl1”。结尾“.nl1”并不是必需的,但确实有助于区分表示“语言包”的片段和添加附加代码和功能的片段。
按
Next。在第二页上,会看到项目的源文件夹和运行时库的缺省值:
这些值看上去还算合理,所以再按
Next,就转到了“Fragment Code Generators”页面。选择第二个单选按钮,表示我们要从模板创建片段清单文件,然后从列表中选择
Default Fragment Generator向导。
按了
Next 之后,我们会看到“Simple Fragment Content”页面。这个页面上有两项用于指定某个现有插件上的片段。必须提供插件目标标识和版本。可以使用
Browse按钮来选择要展开的插件。
现在,让我们进入片段清单编辑器,它与插件清单编辑器一样都是多页面编辑器,带有 Overview、Runtime、Extensions、Extension Points
和 Source 页面。 请注意与片段 xml 文件的各个节对应的选项卡页面。我们将使用
Runtime
页面将片段类路径指向包含我们的翻译的库。
我们在新建片段向导中指定了 nl1.jar,因此这个片段的类路径已经包含了那个库。此时缺少的东西就是包含在
\nl 文件夹及其子文件夹的内容。要添加新的运行时库,可以从 Overview 页面的 Runtime Libraries 节中选择
More,或者转到 Runtime 页面,选择
New...,然后输入文件夹掩码 $
foldername$/。
看一看片段清单编辑器的 Source 页面,我们会见到 PDE 生成所有描述插件片段所必需的 XML。 第 6 步:准备和发送本国语言材料以供翻译
准确的翻译需要专门技能,而您必须为此付钱。(虽然您在高中里学了四年德语课,可惜还不合格!)有许多公司都乐意提供具有专业水准的翻译。 对于 Eclipse Platform,这一步分两个阶段完成。第一阶段涉及将所有外向化的文本发送到翻译中心。第一遍翻译是在“脱离环境”的情况下完成的。翻译人员没有看到运行中的产品,也没有特定的产品经验。他们拥有的工具有助于提高翻译速度和保证一致性,但最终还要依赖翻译测试人员来验证以目标语言运行的的产品(第二阶段)。 即将发表的文章(“测试国际化的 Eclipse 插件”)中将讨论脱离环境的翻译带来的风险和后果,其结果有时非常好笑。 第 7 步:重新封装和验证已翻译的材料
翻译了文件之后,我们将它们重新组合到相应的目录/JAR 文件中,这些目录/JAR 文件已在第 5
步
创建初始已翻译插件片段中描述过了。NL1 Fragment 文件夹包含了各种语言版本的
plugin.properties 文件。在将 HelloWorld.properties 文件翻译成德语之后,我们将它重命名成
HelloWorld_de.properties,并将它存储到 NL1 Fragment 源文件夹中。注:nl\de(德语)文件夹是新的,它并不是由
PDE 创建的,而是手工创建的。当我们不断添加各种翻译时,这些特定语言的文件夹隔离了各个版本的非特性文件(如下面显示的 hello.xml)。
请注意已翻译的特性文件很可能包含与代码页相关的重音字符,因此必须将特性文件转换成
PropertyResourceBundle
类所希望的 ISO 8859-1 代码页。native2ascii 实用程序(请参阅
参考资料)会处理代码页转换并插入任何必需的 Unicode 转义符(Unicode escape)。
需要详细说明一下术语
Unicode 转义符。Java SDK 包含的 native2ascii 转换实用程序允许进行源码编码,生成以
ISO 8859-1 编码的输出,并且它将此代码页之外的字符转换成叫作 Unicode 转义符的表示法。这种表示法是
\udddd,其中 dddd = Unicode 代码页中期望字符的代码点。
下面是一个示例。考虑一下法语短语“Son père est allé à l'h?tel”(他父亲去旅馆了)。它包含了四个不属于
Latin-1 代码页的重音字符。用 native2ascii 实用程序转换这条短语会得到:
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td> 现在不再有任何重音字符,而且组成字符串的所有字符所拥有的代码点都可以在 ISO 8858-1 中找到。但替代用的
关于使用 native2ascii 实用程序,有一个小的告诫:它假设源码编码与执行它的机器的活动代码页相同。但是,翻译人员通常会用其缺省国家或地区代码页保存翻译,而在每个国家或地区以及每个操作系统中,这个代码页是不同的。因此,负责集成翻译的人需要 (a) 知道翻译人员用哪种代码页保存文件,或者 (b) 要求他们用公共代码页保存文件。当调用 native2ascii 时,可以用指定
提示:如果不能确定源码代码页,可以对照本文后面的
表 3.
常见重音拉丁字符的 Unicode 代码点来抽查 native2ascii 的输出。如果在转换的文件中找到该表中没有的
\udddd 表示法(如 \u0205),那可能是您指定了错误的源码编码。DBCS
语言中没有同样的抽查,实际上已转换文件中的所有字符都是 Unicode 转义符。只要仔细一点,对照正在运行的产品进行验证。
会有一篇专门的文章介绍测试翻译。请等待后续文章(“测试国际化的 Eclipse
插件”),这篇文章将描述在最近的 Eclipse Platform 翻译验证期间学到的经验教训,而且会讨论执行特性文件翻译的快速检查(当然,是
Eclipse 插件)。 第 8 步:部署各个片段
片段源代码与插件源代码相似,也可以封装在 JAR 文件中。使用
PDE 生成 JAR 包,选择“fragment.xml”文件,并从弹出菜单中选择“
Create
Fragment JARs...”。向导会指导您创建一个构建脚本以便为片段生成所有必需的 JAR。
要部署这个示例片段,将 fragment.xml、\nl 目录和 JAR
复制到插件目录的 com.jumpstart.example.helloworld.nl1
子目录中。这样就完成了我们的示例以及国际化的步骤。下一节将具体介绍 Eclipse Platform
的可翻译元素、如何组织它们,以及引入另一种语言需要什么。 Eclipse Platform(V 1.0)的国际化
本节的重点不是 Eclipse 国际化的“详细指导”,而是回顾版本 1.0
实现及其与翻译相关的构建过程。在这些主题中,有一些是专门针对平台自身的,它们描述的过程与那些只希望用本地语言启用平台的插件的开发人员无关。本节针对的读者是那些希望了解如何使平台支持本地语言并且要浏览许多构成已翻译产品的文件、子目录和 JAR 的人。 Eclipse Platform 已经实现了国际化,它拥有输入双字节字符和支持双向数据的能力。它提供了以下语言版本: 在 IBM,把这些语言统称为
第一集团语言。
已翻译的各种元素是什么,在哪里?
Eclipse Platform 依赖于 Java SDK 中提供的国际化框架。因此,大多数可翻译文本位于
*.properties 文件中。但还有其它文件格式包括可翻译信息,如
HTML、XML 和位图(已在
表 2. Eclipse 特有的(非 Java)可翻译资源中概述)。
翻译是一门艺术,需要均衡时间、精力和成本。它考虑到开发产品的速度以及产品投入市场的速度。关于
必须翻译什么以及翻译成什么
好,始终都有分析和协调决策。
简而言之,Eclipse 将其翻译集中到三个主要领域: 涉及的文件有: Eclipse 插件:.properties 文件
在 Eclipse 中,每个产品插件都包含特性文件。翻译插件就意味着翻译其特性文件。处理特性文件有两个主要步骤:
与字体相关的 .properties 文件
注:只有那些正在添加新操作系统或语言的支持的 Eclipse Platform
开发人员才需要创建新的 jfacefonts* 特性文件。切勿修改另一个插件的子目录的内容,包括
Eclipse Platform 附带的 JFace 特性文件中指定的字体。
各种 Eclipse 字体都在 JFace 插件中定义。可以在以下位置找到缺省 Eclipse fonts.properties 文件: 每种语言都有四个 org.eclipse.ui jfacefonts
特性文件,都存储在同一个本地语言片段 JAR 例如,意大利语有 jfactfonts_
it.properties、jfactfonts_linux_
it.properties、jfactfonts_windowsnt_
it.properties 和
jfactfonts_windows2000_
it.properties。西班牙语有
jfactfonts_
es.properties、jfactfonts_linux_
es.properties、jfactfonts_windowsnt_
es.properties
和 jfactfonts_windows2000_
es.properties,等等。
要添加对新语言的支持,复制上述的基本 jfacefonts 文件,添加国家或地区代码(如
jfactfont_linux_
xx.properties,其中
"xx" 是国家或地区后缀),将新的 jfacefonts
文件插入本地语言片段 JAR 中。
最终用户 messages.properties 文件
Java
资源文件命名约定考虑到了语言、地区和变体(
basename_language_region_variant),而
Eclipse 只指定语言,只有三种情况例外(葡萄牙语、中文/中国和中文/中国台湾):
PropertyResourceBundle
在运行时会自动查找与当前语言环境相应的资源文件。例如,以德语为例,最初会将英语 plugin.properties
复制成 plugin_de.properties,以供翻译。
Eclipse 联机帮助:.HTML 和 .properties 文件
Eclipse 有五个联机帮助。基本语言文档是作为插件在其自己的子目录中交付,而翻译版本则作为插件片段交付。“文档”插件的位置如下: 表 1. Eclipse 文档插件的位置
<table border="1" cellpadding="3" cellspacing="0" width="100%"><tr valign="top"><td>
联机帮助
</td><td>
位置
</td></tr><tr valign="top"><td>Workbench User Guide</td><td>eclipse\plugins\org.eclipse.platform.doc.user</td></tr><tr valign="top"><td>Java Development User Guide</td><td>eclipse\plugins\org.eclipse.jdt.doc.user</td></tr><tr valign="top"><td>Platform Plug-in Developer Guide</td><td>eclipse\plugins\org.eclipse.platform.doc.isv</td></tr><tr valign="top"><td>JDT Plug-in Developer Guide</td><td>eclipse\plugins\org.eclipse.jdt.doc.isv</td></tr><tr valign="top"><td>PDE Guide</td><td>eclipse\plugins\org.eclipse.pde.doc.user</td></tr></table>
上述每个目录中的插件清单文件 plugin.xml 都定义了帮助组成部分。在同一个目录中还定义了必需的帮助
信息集和
接线文件。要翻译联机帮助:
如果将联机帮助翻译成多种语言,必须创建一个语言目录结构来保存针对国家和地区翻译的 HTML。这些文件就象特性文件一样,也不是语言环境敏感的,因此必须保留它们原来的文件名和扩展名。例如,要区分日语 .html
文件集和德语文件集,必须将它们保存在不同的目录名下,每个目录表示一个国家或地区。 例如,Workbench User Guide 的德语 *.html 文件集存储位于 eclipse\fragments\org.eclipse.platform.doc.user.nl1\nl\
de中。日语 welcome.xml 位于 eclipse\fragments\org.eclipse.platform.doc.user.nl1\nl\
ja中。
Eclipse 的“Help >Welcome”菜单选项:welcome.xml 文件
welcome.xml 是在 Workbench 第一次打开或用户选择
Help > Welcome时显示的介绍性视图。它在 eclipse\plugins\org.eclipse.sdk_1.0.0 中。
以下是翻译 welcome.xml 文件的步骤: 结束语和其它参考资料
让您的产品进入国际市场只是为了增加经济效益。上面的步骤表明这个过程比较简单。以下是我们在介绍时提到的测验:
假。实际上,IBM 软件收入中超过 50% 来自美国以外。 幸好,使用基于 Eclipse Platform 的产品的那些开发人员可以从已经翻译的基本产品中受益。剩下的就是遵循本文中概括的明确步骤,将您的基于
Eclipse 的产品推向国际市场! Eclipse 特有的(非 Java)可翻译资源
以下是前面提到的可翻译资源列表的摘要,以及如何处理它们的简要说明。 表 2. Eclipse 特有的(非 Java)可翻译资源
<table border="1" cellpadding="0" cellspacing="0" width="75%"><tr><td>
已翻译的内容
</td><td>
必需
或
可选
</td><td>
高级步骤
</td></tr><tr><td>插件文件</td><td>
必需
</td><td>
有关可翻译资源的更多信息,请参阅 eclipse.org 网站上由 Greg Adams 撰写的文章“Creating
Product Branding”(请参阅
参考资料)。
常见重音拉丁字符的 Unicode 代码点
表 3. 常见重音拉丁字符的 Unicode 代码点
<table border="1" cellpadding="0" cellspacing="0" width="39%"><tr bgcolor="#CCCCCC"><td align="center" bgcolor="#CCCCCC" colspan="2">
字符
</td></tr><tr><td width="48%">\u00e0</td><td width="52%">带沉音符号的 a</td></tr><tr><td width="48%">\u00e1</td><td width="52%">带尖音符号的 a</td></tr><tr><td width="48%">\u00c0</td><td width="52%">带沉音符号的 A</td></tr><tr><td width="48%">\u00c1</td><td width="52%">带尖音符号的 A</td></tr><tr><td width="48%">\u00c2</td><td width="52%">带音调符号的 A</td></tr><tr><td width="48%">\u00e2</td><td width="52%">带音调符号的 a</td></tr><tr><td width="48%">\u00c3</td><td width="52%">带腭化符号的 A</td></tr><tr><td width="48%">\u00e4</td><td width="52%">带分音符号的 a</td></tr><tr><td width="48%">\u00c4</td><td width="52%">带分音符号的 A</td></tr><tr><td width="48%">\u00e8</td><td width="52%">带沉音符号的 e</td></tr><tr><td width="48%">\u00c8</td><td width="52%">带沉音符号的 E</td></tr><tr><td width="48%">\u00e9</td><td width="52%">带尖音符号的 e</td></tr><tr><td width="48%">\u00c9</td><td width="52%">带尖音符号的 E</td></tr><tr><td width="48%">\u00ea</td><td width="52%">带音调符号的 e</td></tr><tr><td width="48%">\u00eb</td><td width="52%">带分音符号的 e</td></tr><tr><td width="48%">\u00cb</td><td width="52%">带分音符号的 E</td></tr><tr><td width="48%">\u00ea</td><td width="52%">带音调符号的 e</td></tr><tr><td width="48%">\u00ca</td><td width="52%">带音调符号的 E</td></tr><tr><td width="48%">\u00ef</td><td width="52%">带分音符号的 i</td></tr><tr><td width="48%">\u00ec</td><td width="52%">带沉音符号的 i</td></tr><tr><td width="48%">\u00ed</td><td width="52%">带尖音符号的 i</td></tr><tr><td width="48%">\u00cc</td><td width="52%">带沉音符号的 I</td></tr><tr><td width="48%">\u00cd</td><td width="52%">带尖音符号的 I</td></tr><tr><td width="48%">\u00ee</td><td width="52%">带音调符号的 i</td></tr><tr><td width="48%">\u00ce</td><td width="52%">带音调符号的 I</td></tr><tr><td width="48%">\u00f6</td><td width="52%">带分音符号的 o</td></tr><tr><td width="48%">\u00d6</td><td width="52%">带分音符号的 O</td></tr><tr><td width="48%">\u00e3</td><td width="52%">带腭化符号的 a</td></tr><tr><td width="48%">\u00f4</td><td width="52%">带音调符号的 o</td></tr><tr><td width="48%">\u00d4</td><td width="52%">带音调符号的 O</td></tr><tr><td width="48%">\u00f2</td><td width="52%">带沉音符号的 o</td></tr><tr><td width="48%">\u00d2</td><td width="52%">带沉音符号的 O</td></tr><tr><td width="48%">\u00f3</td><td width="52%">带尖音符号的 o</td></tr><tr><td width="48%">\u00d3</td><td width="52%">带尖音符号的 O</td></tr><tr><td width="48%">\u00f5</td><td width="52%">带腭化符号的 o</td></tr><tr><td width="48%">\u00d5</td><td width="52%">带腭化符号的 O</td></tr><tr><td width="48%">\u00f1</td><td width="52%">带腭化符号的 n</td></tr><tr><td width="48%">\u00d1</td><td width="52%">带腭化符号的 N</td></tr><tr><td width="48%">\u00f9</td><td width="52%">带沉音符号的 u</td></tr><tr><td width="48%">\u00d9</td><td width="52%">带沉音符号的 U</td></tr><tr><td width="48%">\u00fa</td><td width="52%">带尖音符号的 u</td></tr><tr><td width="48%">\u00da</td><td width="52%">带尖音符号的 U</td></tr><tr><td width="48%">\u00fb</td><td width="52%">带音调符号的 u</td></tr><tr><td width="48%">\u00db</td><td width="52%">带音调符号的 U</td></tr><tr><td width="48%">\u00fc</td><td width="52%">带分音符号的 u</td></tr><tr><td width="48%">\u00dc</td><td width="52%">带分音符号的 U</td></tr><tr><td width="48%">\u00df</td><td width="52%">带升半音符的 s</td></tr><tr bgcolor="#CCCCCC"><td align="center" bgcolor="#CCCCCC" colspan="2">
特殊符号
</td></tr><tr><td width="48%">\u00ba</td><td width="52%">阳性序数指示符</td></tr><tr><td width="48%">\u00a7</td><td width="52%">章节符号</td></tr><tr><td width="48%">\u00aa</td><td width="52%">阴性序数指示符</td></tr><tr><td width="48%">\u00ac</td><td width="52%">非符号</td></tr><tr><td width="48%">\u00b9</td><td width="52%">上标 1</td></tr><tr><td width="48%">\u00b2</td><td width="52%">上标 2</td></tr><tr><td width="48%">\u00b3</td><td width="52%">上标 3</td></tr><tr><td width="48%">\u00a3</td><td width="52%">英镑符号</td></tr><tr><td width="48%">\u00a2</td><td width="52%">分符号</td></tr><tr><td width="48%">\u00b0</td><td width="52%">度数符号</td></tr></table>
词汇表
代码点(Codepoint)
代码页(Codepage)
编码(Encoding)
国际化(Internationalization)(有时缩写成“I18N”)
单字节编码字符集(Single-Byte Coded Character Set (SBCS))
双字节编码字符集(Double-Byte Coded Character Set (DBCS))
本地化(Localization)(有时缩写成“L10N”)
混合字节字符集(Mixed-Byte Character Set)
NLS
Unicode
作者简介<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td>
Dan Kehn 是美国北卡罗莱纳州 Research Triangle Park 的 IBM 高级软件工程师。他对面向对象编程的兴趣要追溯到
1985 年,当时这种技术还不象现在这样广为接受。他拥有广泛的软件经验,从事过开发工具(如
VisualAge for Smalltalk)、操作系统性能和内存分析和用户界面设计。Dan
作为面向对象开发项目的顾问走遍了美国,并且还在欧洲工作过四年。他最近的兴趣包括面向对象分析/设计、编程工具,以及使用
WebSphere Application Server 进行 Web 编程。去年,他加入了 Eclipse Jumpstart 小组,这个小组的主要目标是帮助
ISV 创建基于 Eclipse Platform 的商业产品。在其余时间,Dan 撰写了关于几篇关于各种 Smalltalk 主题的文章,如元编程、团队开发和内存分析。可以在
Eye on SmallTalk 上找到这些文章。
Scott Fairbrother在美国北卡罗莱纳州 Research Triangle Park 的 IBM Eclipse Jumpstart 小组工作。他是一位拥有 20 多年经验的软件开发人员。他开发过面向对象的业务过程管理应用程序框架。他为 Windows 2000 上的 IBM 中间件编写了规范,还撰写了关于 Microsoft Visual Studio .NET 的文章。
Cam-Thu Le在 1983 年加入 IBM。Cam 的经验跨越了软件创建的许多方面:开发、测试以及本地语言支持(NLS)规划和协调。Cam 使 IBM 产品的本地语言版本可以全球同步上市,包括 4690 Point of Sales 产品和 VisualAge for Smalltalk。Cam 去年加入了 Eclipse Project,作为 NLS 联络人。Cam 协调 Eclipse Workbench 和 WebSphere Studio Workbench 本地语言版本的构建和测试。
资源束很好地处理了与语言有关的文本。其策略是将所有字符串一次装入
ResourceBundle
子类,或者单独检索它们。Eclipse Java 开发工具(Java Development Tooling (JDT))版本
2.0 提供了支持检测可翻译字符串的向导。我们马上将在
国际化步骤中讲到它们。
这些形式的文本内容比简单的面向键/值的特性文件涉及更多,因此它们的外向化步骤稍微复杂一些。
清单 1. 插件清单文件,翻译之前
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
id
和
class
属性是不可翻译的,因为它们表示编程标识。同样,可以肯定应该翻译
name
属性。
version
属性(由于与语言环境相关的十进制分隔符)或
provider
属性(由于与语言环境相关的法律属性“Inc.”),因为将向最终用户显示它们。但是,版本号通常是不翻译的,有两个原因:最终用户不关心数值的意义,程序员有时编写的代码希望版本号是类似于“3.5.4”的复合字符串。将版本信息存储成单独的数字(如主、次和服务更新)以避免需要解析版本字符串,这大概是一个更好的设计决策,但这个讨论已经超出了本文的范围。
清单 2. 插件清单文件,翻译之后
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
plugin.properties
包含了外向化的字符串,即与键
plugin.name
相关联的“Java Development Tools UI”。
Java 库包括可以处理数字(十进制分隔符、千分位分隔符、集合)、日期(MDY、DMY、工作周的第一天)、时间(12 或 24 小时制、分隔符)和货币(当地货币符号、作为后缀还是前缀、有无前导分隔符)的必需格式的一些类。
这些都是更细微且不常见的文本翻译问题,却仍然值得注意。许多应用程序只允许自由格式的电话号码项,因为各地的差异实在太多。邮递地址比较简单:添加“省/直辖市(State/Province)”字段并考虑到多个地址行通常就行了。
虽然在美国不常用,但适当使用尊称(先生、太太、博士)仍被看作是绝对必要的,以免失礼。
这些不常见到。这涉及到用相应的换算来替换度量表示(例如,英里与公里)。在许多情况下,用户需要同时以不同的单位或者以它们之间一种简便的换算方法显示一项度量。
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
清单 3. Hello world,翻译之前
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
package com.jumpstart.nls.example.helloworld;
public class HelloWorld {
static public void main(String[] args) {
System.out.println("Copyright (c) IBM Corp. 2002.");
System.out.println("Hello.");
System.out.println("How are you?");
System.out.println("Goodbye.");
}
}
图 1. Externalize Strings 向导
操作:将一项添加到特性文件中;自动生成的键和访问代码就代替代码中原来的字符串。已经用注释标记符(如“
// $NON-NLS-1$
”)将用于指定键的字符串标记成不可翻译的
操作:用注释标记符将字符串标记成不可翻译的。
Externalize Strings向导在以后的操作中不会把它标志成未翻译的。
操作:不做修改。
Externalize Strings向导后面的操作会将字符串标志成可能可以翻译。
// $NON-NLS-1$
注释标记符中的尾数表示在一行中有几个字符串的情况下,哪个字符串无需翻译。例如:
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>x.foo("Date", "TOD", "Time"); // $NON-NLS-2$
S_
)。
图 2. Externalize Strings 向导
图 3. Externalize Strings 向导
图 4. Externalize Strings 向导
图 5. Externalize Strings 向导
清单 4. 标准资源束取值方法类
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
package com.jumpstart.nls.example.helloworld;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
public class HelloWorldMessages {
private static final String BUNDLE_NAME =
"com.jumpstart.nls.example.helloworld.HelloWorld"; //$NON-NLS-1$
private static final ResourceBundle RESOURCE_BUNDLE =
ResourceBundle.getBundle(BUNDLE_NAME);
private HelloWorldMessages() {}
public static String getString(String key) {
try {
return RESOURCE_BUNDLE.getString(key);
} catch (MissingResourceException e) {
return '!' + key + '!';
}
}
}
BUNDLE_NAME
的值。在继续下一步之前,以下是 JDT 小组的 Erich Gamma 和 Thomas M?der 贡献的一些值得注意的准则。
</td></tr></table>
SearchPage.expression.pattern=(? = any character, * = any string) ShowTypeHierarchyAction.selectionDialog.title=Show in Type Hierarchy
清单 5. 静态资源束取值方法类
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
public static String getFormattedString(String key, Object arg) {
String format= null;
try {
format= RESOURCE_BUNDLE.getString(key);
} catch (MissingResourceException e) {
return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
}
if (arg == null)
arg= ""; //$NON-NLS-1$
return MessageFormat.format(format, new Object[] { arg });
}
public static String getFormattedString (String key, String[] args) {
return MessageFormat.format(RESOURCE_BUNDLE.getString(key), args);
}
initializeDefaultPreferences(IPreferenceStore)
方法初始化它们。
插件和插件片段位于可以在 eclipse 子目录下直接找到的单独子目录中。考虑一下我们的示例片段,它部署在德语系统上,我们会看到一个
\nl 文件夹、一个 fragment.xml 和一个 nl1.jar 文件。
图 6. 片段子目录
每个插件文件夹都可以随意地包含一个片段清单文件 fragment.xml。清单文件描述了插件片段,而且与插件清单文件(plugin.xml)几乎相同,只有以下两点除外:
class
属性,因为片段没有插件类。它们只遵循其目标的规范。
和
标记。以下是完整的示例片段清单文件:
清单 6. 片段清单文件
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
<?xml version="1.0" encoding="UTF-8"?>
标记属性是:
name
― 可向用户显示的扩展名称。
id
― 该片段配置的标识。用于唯一标识该片段实例。
plugin-id
― 对目标扩展点的引用。插件片段与此目标扩展合并。
plugin-version
― 片段插件的版本。
version
― major.minor.service 格式的版本规范。
部分包含了组成插件片段运行时的一个或多个库的定义。被引用的库由平台执行机制使用,在此机制中,插件装入、合并和执行它所需的正确代码。每个
子标记都有一个
name
属性。它指定了库文件或目录导出掩码。要将文件夹及其子文件夹的内容包括在库中,可以使用掩码
$foldername$/
,其中
foldername 是要添加到库搜索路径的目录。我们以后会了解到如何使用这个掩码来包括
\nl 文件夹的内容及其子文件夹的内容。
Eclipse Workbench 附带了一个在插件开发中使用的工具:
插件开发环境(Plug-in Development Environment
(PDE))。PDE 包含了对开发插件片段的支持。
图 7. 启动片段项目
图 8. 定义片段文件夹
图 9. 选择缺省向导
图 10. 为片段指定目标
图 11. 片段清单编辑器
图 12. 片段运行时信息
图 13. 片段源码
图 14. 重新组装的片段项目
</td></tr></table>Son p\u00e8re est all\u00e9 \u00e0 h\u00f4tel
\u00e8
、
\u00e9
、
\u00e0
和
\u00f4
是什么呢?它们是以 \udddd 表示的重音字符的 Unicode 代码点。
-encoding
参数指定源代码编码。
图 15. 选择 fragment.xml 文件
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
每个操作系统都使用不同的字体集,某些语言有其自己的字体集(最显著的就是 DBCS 语言)。操作系统推荐缺省字体,但页面设计人员的最佳布局却依赖于特殊字体,因此使用缺省值字体的结果可能会产生格式很差的页面。为了确保正确显示已翻译的文本,Eclipse Workbench
使用特殊的与每种语言/操作系统组合一致的字体集。
缺省 Eclipse fonts.properties 文件的位置
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
eclipse\plugins\org.eclipse.ui\workbench.jar
org\eclipse\jface\text\JFaceTextMessages.properties
org\eclipse\jface\resources\jfacefonts.properties (OS 缺省值)
org\eclipse\jface\resources\jfacefonts_linux.properties
org\eclipse\jface\resources\jfacefonts_windows2000.properties
org\eclipse\jface\resources\jfacefonts_windowsnt.properties
要翻译插件的最终用户消息:
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>真或假:IBM 遍及全球的软件销售收入的主要部分来自于美国。
</td></tr><tr><td>插件“关于”文件</td><td>可选</td><td>
</td></tr><tr><td>联机帮助</td><td>
必需
</td><td>
</td></tr><tr><td>Splash*</td><td>可选</td><td>
要本地化闪屏(splash screen),需要在 eclipse/splash 下创建语言环境子目录。这些目录的名称要遵循标准
Java 语言环境命名约定。例如,平台寻找美国英语语言环境(en_US)的闪屏的方式如下:
其中
</td></tr><tr><td>插件产品文件*</td><td>
必需
</td><td>
</td></tr><tr><td>许可证*</td><td>可选</td><td>
</td></tr></table>
可以由一个或多个字节信息表示的字符。代码点是分配给每个图形字符的十六进制值。
代码页是一个图形字符集或一组图形字符集中的每个图形字符的代码点的规范。在一个给定代码页中,代码点可能只有一个特定含义。可以用 CHCP 命令显示
Windows? 操作系统上的活动代码页(在任何给定时刻,只有一个代码页是活动的)。
与一段给定数据相关的代码页。只能以一个给定代码页对文件“编码”;例如,缺省情况下,在美国英语机器上,记事本以代码页
437 对其数据进行编码。
Save As对话框允许用户选择几种其它可能的编码,其中最著名的是 Unicode 和 UTF-8。
国际化是指在预先不知道要处理的语言、文化数据或字符编码方案的情况下开发程序的过程。在系统术语中,它是指提供一些接口,这些接口可以在运行时使国际化的程序更改其行为以适合特定语言操作。
在单字节编码字符集中,单字节代码点表示集合中的每个字符。通常,SBCS 用于表示英语、欧洲语言、西里尔语、阿拉伯语、希伯来语等语言的字符。
在双字节编码字符集(DBCS)中,双字节代码点表示集合中的每个字符。实质上,这些语言是由表意字构成,如日语、汉语、韩国语,它们拥有的符号超出了 256
个代码点可以表示的范围,因此需要双字节编码字符集。
本地化是指在特定于每种受支持的语言、文化数据和编码字符集组合的计算机系统中建立信息的过程。
混合字节编码字符集是包含单字节字符和双字节字符的字符集合。在 MBCS 中,必须检查数据的每个字节,看看它是双字节字符的首字节还是单字节字符的首字节。如果字节在某个范围中(例如,大于
X'80'),那么它就是双字节字符的首字节。
本地语言支持。
直接出自
http://www.unicode.org:“无论是对于何种平台、何种程序、何种语言,Unicode
为每个字符提供了一个唯一的编号。”
注:虽然 Java 文本操作类确实是以 Unicode 为核心的,但存储在程序的保护之外的数据通常却不属于这种情况。必要时,Java
程序员必须考虑通过执行本地代码页到 Unicode 转换进行数据编码。
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td></td><td></td><td width="100%">
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td></td><td></td><td width="100%">
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
↑返回目录
前一篇: 实现 Java 企业级应用的多语言(国际化)支持
后一篇: 使用 Eclipse 创建易访问的应用程序:介绍