Java平台I18N 支持概述

一、国际化和 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 网站
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值