首页 > 学院 > 开发设计 > 正文

Java平台I18N 支持概述

2019-11-18 14:11:31
字体:
来源:转载
供稿:网友

  一、国际化和 java 编程语言

和大多数使用其它语言的程序员不同,Java 程序员是大量构建在 JDK 中提供 I18N 支持的标准代码的受益者。大部分代码最初来自 IBM 的 Taligent 子公司(自从合并进 IBM 以后),代表了许多人年的工作成果,比大多数公司独安闲其产品中提供的代码要切实可行得多。

这些代码及其远见并不总是完美的;例如,请看一下 java.util.Date 类中许多弃用的(dePRecated)方法。我们中的许多人可能记得太平洋标准时间(Pacific Standard Time)显然也是 Java 世界时间(Java World Time)。然而,即使在“错误的旧时代”,其它语言也没有能与这种内置的功能匹敌的东西,即便有,也很少。这一节的页面简要地讨论了 Java 平台支持的一般国际化领域。


二、Unicode 支持

Java 语言字符集是 Unicode,而且相应地,原始 char 数据类型的长度是两个字节(16 位),以容纳 Unicode 值。由于大家熟悉的 String 由 char 组成,因此 String 也是基于 Unicode 的。Unicode 本身是这样定义的:值 0 到 127 匹配标准 ASCII,0 到 255 匹配 ISO 8859-1 (Latin-1) 标准。由于这一起始值的一致性,不使用 I18N 功能或不需要面对 I18N 问题的程序员可以编写他们的 Java 程序而无需理解或知道 Unicode。然而,考虑到 Windows 的普遍使用,该平台的程序员应该知道标准 ISO 8859-1 和 Windows Latin-1 (cp1252) 之间的差异。

16 位 char 长度答应 0 到 65535 之间的值。提供了 Unicode 转义以在本地平台不支持实际字符时仍然答应输入。其格式是“u”后跟 0000 到 FFFF 的四个十六进制数字。例如,下面两行代码是等价的:


char c1 = ´a´;
char c2 = ´u0061´;


JDK/JRE 的 1.3 版本支持 Unicode 2.1;1.4 版本支持 Unicode 3.0。更多关于 Unicode 和称为 UniBook 的 Unicode 显示程序的信息,请参阅参考资料中到 Unicode Consortium 的链接。


三、字符集转换和流输入/输出

上一页提到过 Java 字符集是 Unicode,但并不是所有平台都支持 Unicode。那么,这个戏法是怎么完成的呢?答案是:所有支持字符的输入和输出流 ? 即 java.io.Reader 和 java.io.Writer 层次结构 ? 自动调用在平台的本地编码和 Unicode 之间执行转换的隐藏代码层。请注重,本地编码是假设的。假如数据不是缺省编码的,您将不得不自己转换数据。幸运的是,java.io.InputStreamReader、java.io.OutputStreamWriter 和 java.lang.String 类具有答应使用受支持的编码的转换规范的方法。您可以在 JDK 文档(可以从参考资料访问)的 Internationalization 节中的 Supported Encodings 下面找到它们。请注重,JDK 1.4 现在对泰国语和印地语提供支持。

有趣的是,Java 对数字的大尾数格式提供保证,而对于 char 数据类型却不支持这一保证。缺省格式同平台有关。例如,在 NT 4.0 上,系统特性“sun.io.unicode.encoding”被设置成“UnicodeLittle”。假如因为某种原因您想自己指定该格式,那么您可以根据文档来选择 UnicodeBig、UnicodeBigUnmarked、UnicodeLittle、UnicodeLittleUnmarked、UTF8 或 UTF-16。


四、字符分类与 Character 类

除了以标准方式为多种语言定义字符之外,Unicode 也为每个字符定义了几个特性。这些特性标识诸如一般类别、双向性、大写、小写以及该字符是数字还是控制字符等事情。在可以从 Unicode Consortium 网站上获得的 UnicodeData 文件中定义了这些特性。

Java Character 类提供获取这些特性的方法。虽然特定实例是不变的,但是许多方法是静态的,答应实时访问字符的特性。

该类有用性的一个示例来自一个典型的 ASCII 编程算法:许多程序员利用了这样一个事实,假如字符值在 0x41 和 0x5A 之间,那么它是大写字母(A-Z)。加上 0x20,您就得到小写字母(a-z)。遗憾的是,假如处理的语言包含有超出 ASCII 范围的字符时,该算法会失效。 解决方案是使用 Character.isUpperCase() 和 Character.toLowerCase(),它们在任何情况下都起作用。另外一个示例是 Character.isDigit(),它也用于表示 ASCII‘0’到‘9’以外的数字的字符。


五、语言环境

在 Java 语言中,语言环境(locale)仅仅是一个标识符,而不是一组本地化的属性。java.util.Locale 类的一个实例表示一个特定的地理政治区域,使用表示语言、区域以及国家或地区的参数创建。每个与语言环境相关的类都维护着它自己的一组本地化属性,并且确定如何对含有 Locale 参数的方法请求做出响应。

按照以前的陈述,很明显,没有关于程序员可能怎样对含有 Locale 参数的方法请求做出响应的约束。然而,在 Sun 的参考 Java 2 平台和其它一致实现中,有一组一致的受支持的本地化实现。更多信息,请参阅 JDK 文档(可以从参考资料访问)中的 Internationalization 一节中 Supported Locales。应该注重,该文档将多种语言环境列为“也提供了,却未测试(also provided, but not tested)”。我个人看见这一“未测试”问题出现在 JDK 1.3.1 中的 Finnish (fi_FI) 语言环境;买主自行当心。


六、AWT/Swing Name 和 Locale 属性

java.awt.Component 类包含 Name 和 Locale 属性的读方法和写方法。虽然文档也讨论了 Component 的构造器及其使用 Name 参数的子类,但我显然需要倍加小心,因为我以前从未找到它们。Component 位于大多数 Swing 类的层次结构中,它们也自动支持这些属性。

Name 属性是一个您可以通过编程进行赋值的不可本地化的 String。这有助于国际化 ? 听起来可能有些希奇,但是随着大多数数据根据语言环境改变时,Name 提供了一个标识组件的设置锚点。当然,在一个给定的类里,为对象等同性测试对象引用可以达到相同的目的。虽然每种技术都有极好的理由,但我通常在 actionPerformed() 方法中使用对象等同性测试,如同您在代码示例中看到的那样。文档声明:假如不通过编程设置 Name,那么将赋予一个缺省值,但不给出值或模式。在我编写的代码中,假如在调用 Component.setName("aName") 之前调用了 Component.getName(),它将返回 null。当然,作为未在文档中记录的行为,结果可能不一致,并且可能会在将来发生改变。因此,当将使用 Name 属性时,良好的编程实践要求将所有组件的 Name 属性设置成标准值(也就是“取消设置”),然后适当地设置想要的组件。

Locale 属性答应组件跟踪它自己的语言环境,即便是应用程序的其余部分正在使用不同的语言环境。在某些情况下,该项技术非常有用,虽然对于具有文本值的 Component,可以在将文本发送给 Component 之前对它执行本地化,而无需设置特定的 Component Locale。


七、本地化的资源

java.util.ResourceBundle 是一个为存储和定位由应用程序使用的资源提供机制的抽象类。资源通常是本地化的 String,但也可以是任何 Java 对象。ResourceBundle 以一种层次结构建立,它以一个具有基础名称的一般 ResourceBundle 开始,然后通过向另外的 ResourceBundle 的基础名添加语言和国家或地区标识(它们在 JDK 文档 Internationalization 一节的 Supported Locales 中有定义,可以从参考资料访问这一节),使这种层次结构变得更为特定。ResourceBundle 的三大优点是:

类装入器机制用于定位 ResourceBundle,因此无需额外的 I/O 代码。


ResourceBundle“知道”如何通过使用 static getBundle(String baseName) 或 getBundle(String baseName, Locale locale) 方法,按照从特定到一般的顺序,搜索层次结构以寻找适合于语言环境的实例。


假如在特定实例中没有找到资源,那么将使用来自更一般实例的资源。
好消息/坏消息是:ResourceBundle 实例一旦被装入,将被以性能优化的名义进行高速缓存;这一高速缓存从不会被更新,并且没有操作该高速缓存的正式方法。

ResourceBundle 有两个子类:

ListResourceBundle,它是另一个抽象类,因此您必须提供自己的实现。首先,您必须覆盖 getContents(),它返回二维 Object 数组(Object[][])。这种 ResourceBundle 可以返回任何类型的 Object。


PropertyResourceBundle,它是一个由 java.util.Properties 文件支持的具体类,它只能返回 String。
您也可以提供您自己的定制子类。在这种情况下,您必须覆盖并实现 handleGetObject() 和 getKeys(String key)。

ResourceBundle 使用键/值对,并提供 getString(String key) 和 getObject(String key) 方法。您也可以使用 getKeys() 来获得可用键的 Enumeration。


八、日历与时区支持

最初打算将 java.util.Date 用来处理日期与时间操作,但是内在的缺陷导致其只能以时间的形式表示具体时刻。JDK 1.1 中引入了抽象类 java.util.Calendar 及其具体子类 java.util.GregorianCalendar 来处理 java.util.Date 的不足。Calendar 类具有获取所有日期与时间字段以及执行日期与时间运算的方法。

抽象 java.util.TimeZone 类及其具体子类 java.util.SimpleTimeZone 维护全球统一时间(Universal Coordinated Time(缩写为 UTC,而不是您期待的 UCT;由于历史原因这一缩写取自法语形式))的标准时及夏令时的偏差值。此外,TimeZone 也含有获取本机及本地化时区显示名称的方法。


九、格式化与解析

数字、货币、日期、时间以及程序消息都受到文化及地区差异的影响,并且对于本地化需要大量的格式化与解析工作。创建了抽象类 java.text.Format 及其子类来处理这一 I18N 领域的问题。所有这些子类都有与语言环境相关的 format() 和 parse() 方法来以与语言环境相关的方式操作值。碰到非法值,parse() 方法将抛出 ParseException。具体子类 java.text.SimpleDateFormat 和 java.text.DecimalFormat 答应模式及对实例的适当符号的访问。通常,抽象父类拥有返回适当本地化的对象的 getInstance() 和 getXXXInstance() 静态工厂方法。

下面是 java.text.Format 的直接子类的列表:

抽象 java.text.DateFormat 类及其具体子类 java.text.SimpleDateFormat,由 java.text.DateFormatSymbols 类支持,用于处理日期与时间值。


抽象 java.text.NumberFormat 类及其具体子类 java.text.ChoiceFormat 和 java.text.DecimalFormat,由 java.text.DecimalFormatSymbols 类支持,用于处理数字、货币及百分数。


java.text.MessageFormat 答应“软编码的”位置及格式化要插入本地化的消息的值。
对于 JDK/JRE 1.4,已经添加了 java.util.Currency 以使得可以独立于语言环境使用货币。java.text.NumberFormat 拥有处理货币和整数的新方法。


十、与语言环境相关的 String 操作

作为开发人员,我们经常需要操作、搜索 String 以及对其排序。当涉及多种语言,这项工作的难度简直令人难以置信。Java 平台提供下列类以供帮助:

抽象 java.text.Collator 类及其具体子类 java.text.RuleBasedCollator 答应对与语言环境相关的 String 进行比较。


java.text.CollationElementIterator 类以给定的整理顺序遍历 String 的每个字符并返回其有序的优先级。


java.text.CollationKey 类表示一个由特定 Collator 治理的 String,它答应相对较快的排序比较。


java.text.BreakIterator 类以与语言环境相关的方式实现了定位断行、断句、断词和断字符的位置的约定。


java.text.StingCharacterIterator 类对 Unicode 字符提供双向遍历,用于搜索 String 内的字符。


十一、输入法

实际上,以上所有讨论都涉及操作或显示数据。然而,必须以某种方式输入数据。对于最终用户,最常用的是键盘。但是,假如键盘不支持某种语言输入所需的字符,您该怎么办呢?

输入法(Input method)是答应数据输入的软件组件的一个技术术语。Java 平台既答应使用主机 OS 输入法也答应使用基于 Java 语言的输入法。假如您需要实现输入法,您可以使用输入法框架(Input Method Framework)。您可以在 JDK 文档中 Internationalization 一节中的 Input Method Framework(可以从参考资料中访问该文档)中找到输入法客户机 API(Input Method Client API)及输入法引擎 SPI(Input Method Engine SPI)的规范、参考和教程。

--摘自IBM 网站

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表