Java资源国际化


一、java实现国际化概述

    不同的国家和地区人们有各自不同的文化习惯,例如货币、时间、数字等的表现格式各不相同。所谓软件的国际化就是让软件能支持多个国家和地区的用户的使用习惯,让文字、货币、时间、数字等按照各国本地的文化习惯和格式来显示。“国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。如中文本地化信息既有中国大陆地区的中文,又有中国台湾、中国香港地区的中文,还有新加坡地区的中文。Java通过java.util.Locale类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。Java.text包中包含将数字、货币、日期和时间按照特定的本地格式进行显示的类,如NumberFormat类、DateFormat类。将表示本地信息的Local对象以参数的形式传递给这些类之后,就可以按相应的本地文化习惯来显示和处理本地敏感数据了。Java既然作为一个跨平台的语言就必然要在各种不同的语言环境中使用, 为了解决这个问题Java给我们提供了一个工具类ResourceBundle, 帮助我们实现Java的国际化, 核心的思想就是, 对不同的语言提供一个不同的资源文件。最原始的静态文本是硬编码到程序中(可能是一大串的判断语句),但是这样就将程序代码和易变的locale信息捆绑在一起,以后如果需要修改locale信息或者添加其他的local信息,你就不得不重新修改代码。而资源包可以帮你解决这个问题,它通过将可变的locale信息放入资源包中来达到两者分离的目的。应用程序可以自动地通过当前的locale设置到响应的资源包中取得所要的信息。


二、java国际化涉及到的类

Java国际化主要通过如下几个类完成


java.util.ResourceBundle:用于加载一个资源包

java.util.Locale:对应一个特定的国家/区域、语言环境。

java.text.MessageFormat:用于将消息格式化

java.text.NumberFormat:用于数字格式化

java.text.DateFormat:用于日期格式化


ResourceBundle类

     你可以把资源包看作为一个由许多成员(子类)组成的大家庭,其中每个成员关联到不同的locale对象,那它是如何完成关联功能的呢?资源包中的每个成员共享一个被称作基名(base name)的名称,然后在此基础上根据一定的命名规范进行扩展。下面就列出了一些成员的名称:    LabelResources         LabelResources_de         LabelResources_de_CH         LabelResources_de_CH_UNIX可见这些子类依据这样的命名规范:baseName_language_country_variant,其中language等几个变量就是你在构造Locale类时所使用的。而资源包正是通过这个符合命名规范的名称来和locale进行关联的,比如LabelResource_de_CH就对应于由德语(de)和瑞士(CH)组成的locale对象。当你的应用程序需要查找特定locale对象关联的资源包时,它可以调用ResourceBundle的getBundle方法,并将locale对象作为参数传入。

Locale currentLocale = new Locale("de", "CH", "UNIX");

ResourceBundle myResources = 

            ResourceBundle.getBundle("LabelResources", currentLocale);

如果该locale对象匹配的资源包子类找不到,getBundle将试着查找最匹配的一个子类。具体的查找策略是这样的:getBundle使用基名,locale对象和缺省的locale来生成一个候选资源包名称序列。如果特定locale对象的语言代码、国家代码和可选变量都是空值,则基名是唯一的候选资源包名称。否则的话,具体locale对象(language1,country1和variant1)和缺省locale(language2,country2和variant2)将产生如下的序列:


baseName + "_" + language1 + "_" + country1 + "_" + variant1

baseName + "_" + language1 + "_" + country1 

baseName + "_" + language1 

baseName + "_" + language2 + "_" + country2 + "_" + variant2 

baseName + "_" + language2 + "_" + country2 

baseName + "_" + language2 

baseName 

然后,getBundle方法按照产生的序列依次查找匹配的资源包子类并对结果子类初始化。首先,它将寻找类名匹配候选资源包名称的类,如果找到将创建该类的一个实例,我们称之为结果资源包。否则,getBundle方法将寻找对应的资源文件,它通过候选资源包名称来获得资源文件的完整路径(将其中的“.”替换为“/”,并加上“.properties”后缀),如果找到匹配文件,getBundle方法将利用该资源文件来创建一个PropertyResourceBundle实例(ResourceBandle是一个抽象类,getBundle返回的是它的一个具体实现子类PropertyResourceBundle),也就是最终的结果资源包。与此同时,getBundle方法会将这些资源包实例缓存起来供以后使用。

创建了具体的资源包子类实例以后,就需要获得具体的信息。信息在资源包中是以键值对的方式存储的.可以调用ResourceBundle类的getString方法来得到对应的值。


抽象类ResourceBundle具有两个子类:ListResourceBundle和

PropertyResourceBundle,它们表示资源包子类两种不同的实现方式。

ListResourceBundle和PropertyResourceBundle子类

PropertyResourceBundle是和资源文件配对使用的,一个属性文件就是一个普通的文本文件,你只需要为不同的locale设置编写不同名称的资源文件。但是,在资源文件中只能包含字符串,如果需要存储其它类型对象,你可以使用ListResourceBundle。ListResourceBundle是将键值对信息保存在类中的列表中,而且你必须实现ListResourceBundle的具体子类。如果ListResourceBundle和PropertyResourceBundle不能够满足你的需要,你可以实现自己的ResourceBundle子类,你的子类必须覆盖两个方法:handleGetObject和getKeys。为了简化自定义的ResourceBandle子类的编写,JDK中定义了一个ListResourceBandle类,ListResourceBandle类是ResourceBandle类的一个抽象子类。

使用资源文件步骤

使用资源包最简单的方法就是利用资源文件,利用资源文件一般需要以下几个步骤:1、创建一个缺省的资源文件为了防止找不到资源文件,你最好实现一个缺省的资源文件,该文件的名称为资源包的基名加上.properties后缀。2、创建所需的资源文件为你准备支持的locale设置编写对应的资源文件。3、设置locale你必须在程序中的某个地方提供locale的设置或者切换功能,或者将其放入配置文件中。4、根据locale设置创建资源包ResourceBundle resource =        ResourceBundle.getBundle("LabelBundle",currentLocale);5、通过资源包获取locale相关信息String value = resource.getString("welcome");

注意:在使用基名的时候,特别要注意给出完整的类名(或者路径名),比如你的应用程序所在的类包为org.javaresearch.j2seimproved.i18n,而你的资源文件在你的应用程序下的resource子目录中,那你的基名就应该是org.javaresearch.j2seimproved.i18n.

Resource.LabelBundleBundle而不是resource.LabelBundleBundle。


使用ListResourceBundle使用ListResourceBundle和使用资源文件的步骤基本上一样,只不过你需要用ListResourceBundle子类来替换相应的资源文件。比如你的应用程序的基名是LabelBundle,而且准备支持Locale("en","US")和Locale("zh","CN"),那你需要提供以下几个Java文件,注意类名和locale的对应关系。LabelBundle_en_US.javaLabelBundle_zh_CN.javaLabelBundle.java(缺省类)


下面列出的是LabelBundle_zh_CN.java的源代码,相对于资源文件中“key = value”的写法,在此文件中你首先利用键值对来初始化一个二维数组,并在getContents方法中返回该数组。      LabelBundle_zh_CN.java

public class LabelBundle_zh_CN extends ListResourceBundle {    

  public Object[][] getContents() {      

    return contents;    

  }    


  private Object[][] contents = {       

    {"title", "称谓"},       

    {"surname", "姓"},       

    {"firstname", "名"},    

  }; 

创建完资源类以后,同样需要设置locale以及根据locale来创建资源包。在通过资源包获取具体值的时候,你不能再使用getString方法,而应该调用getObject方法,而且由于getObject方法返回一个Object对象,你还需要进行正确的类型转换。其实,为了你的程序通用性,我们建议在使用资源文件的时候你也应该调用getObject方法,而不是getString方法。

String title = (String)resource.getObject("title");

六、Local类

      一个local实例对象代表一个特定的地理、政治和文化上的区域。


     得到一个Local实例方法有两种:

      一种是通过构造方法,另一种是通过Local类的常量。

getDefault()方法返回代表操作系统当前设置的本地信息的Locale实例对象


MessageFormat类  



利用资源文件来分离代码和可变的信息。但是在实际过程中,有些信息并不能够完全事先定义好,其中可能会用到运行时的一些结果,最典型例子的就是错误提示代码,比如提示某个输入必须在一定范围内。利用上面所讲的资源文件并不能够很好地解决这个问题,所以Java中引入了MessageFormat类。MessageFormat提供一种语言无关的方式来组装消息,它允许你在运行时刻用指定的参数来替换掉消息字符串中的一部分。你可以为MessageFormat定义一个模式,在其中你可以用占位符来表示变化的部分,比如你有这样一句话:您好,peachpi!欢迎来到Java研究组织网站!当前时间是:2003-8-1 16:43:12。其中斜体带下划线的部分为可变化的,你需要根据当前时间和不同的登录用户来决定最终的显示。我们用占位符来表示这些变化的部分,可以得到下面这个模式:您好,{0}!欢迎来到Java研究组织网站!当前时间是:{1,date} {1,time}。占位符的格式为{ ArgumentIndex , FormatType , FormatStyle },详细说明可以参考MessageFormat的API说明文档。这里我们定义了两个占位符,其中的数字对应于传入的参数数组中的索引,{0}占位符被第一个参数替换,{1}占位符被第二个参数替换,依此类推。最多可以设置10个占位符,而且每个占位符可以重复出现多次,而且格式可以不同,比如{1,date}和{1,time}。而通过将这些模式定义放到不同的资源文件中,就能够根据不同的locale设置,得到不同的模式定义,并用参数动态替换占位符。

占位符的三种书写方式

{argumentIndex}: 0-9 之间的数字,表示要格式化对象数据在参数数组中的索引号{argumentIndex,formatType}: 参数的格式化类型{argumentIndex,formatType,FormatStyle}: 格式化的样式,它的值必须是与格式化类型相匹配的合法模式、或表示合法模式的字符串。

MessageFormat模式(主要部分): 

FormatElement:

         { ArgumentIndex }

         { ArgumentIndex , FormatType }

         { ArgumentIndex , FormatType , FormatStyle }

 FormatType: (指定使用不同的Format子类对入参进行格式化)

         number:调用NumberFormat进行格式化

         date: 调用DateFormat进行格式化

         time: 调用DateFormat进行格式化

         choice: 调用ChoiceFormat进行格式化

 FormatStyle: (设置FormatType中使用的格式样式)

         short

         medium

         long

         full

         integer

         currency

         percent

         SubformatPattern(子模式)