8.2.3 ResourceBundle类
ResourceBundle类表示本地环境的一个资源包。一个资源包含有一个或多个Java资源文件。这些资源文件的文件名和本地环境有关。一般可以使用Locale对象来描述本地环境的信息。因此,建立ResourceBundle对象需要提供相应的Locale对象。
1. 资源文件概述
在一个国际化程序中,需要将文本信息保存在文件中,当程序运行时从这些文件中读取相应的文本信息。当然,这个功能使用Java实现非常简单,但国际化程序不仅仅要求从文件中读出信息,还需要根据本地环境选择读取哪一个文件中的信息。如果本地环境是中文,则需要选择保存中文信息的文件。如果本地环境是英文,则需要选择保存英文信息的文件。这就需要这些保存信息的文件按着一定的规则命名,以便系统可以准确地知道哪些文件是保存中文信息的,哪些文件是保存英文信息的。这些保存各种语言信息的文件被称为国际化资源文件,也可以简称为资源文件。
选择资源文件的文件名通常由以下三部分组成:
l 文件基名(baseName)
2 语言(language)
3 国家(country)
文件基名可以是任何有效的文件名,语言是在ISO-639标准中定义的语言代码,国家是在ISO-3166标准中定义的国家代码。文件基名、语言和国家之间用下划线分隔。资源文件的扩展名通常的是properties。如MyResource_zh_CN.properties就是一个合法的资源文件名。该文件表示中文环境下使用的资源文件。MyResource_en_US.properties则表示在英文(美国)环境下使用的资源文件。
在资源文件的三个组成部分中,文件基名是必须的,而语言和国家是可选的。也就是说,一个资源名可以只包含文件基名,不包含语言和国家。或者只包含文件基名和语言,不包含国家。下面的资源文件名都是合法的:
l MyResource.properties
2 MyResource_zh.properties
对于这样的资源文件命名规则,虽然使用编程的方式可以解决,但JDK提供了更好的解决方案来选择并读取这些资源文件。这就是ResourceBundle类。在创建ResourceBundle对象时会根据提供的Locale自动选择相应的资源文件,并通过ResourceBundle类的相关方法读取资源信息。关于ResourceBundle类的详细内容,将在后面的部分介绍。
2. 资源文件的格式
资源文件的格式和java.util.Properties类要求的格式相同,也就是key-value值对。如下面的资源文件分别保存了英文菜单信息:
MyResource_en_US.properties
menu_help=Help
menu_file=File
由于资源文件不支持非西欧字符,如中文、日文字符。因此,在资源文件中保存这些字符时,需要使用“\uxxxx”格式的UCS2编码。其中xxxx表示二个字节的16进制形式。如要保存“帮助”和“文件”信息,就需要按如下的内容编写资源文件:
MyResource_zh_CN.properties
menu_help=\u5e2e\u52a9
menu_file=\u6587\u4ef6
实际上,“\u5e2e\u52a9”和“\u6587\u4ef6”也是Java支持的格式,也就是说,可以直接将“\uxxxx”格式作为Java字符串,Java编译器在处理“\uxxxx”格式时会自动将其转换成Java内部所使用的UCS2编码。如下面的代码所示:
String s = "file:\u6587\u4ef6";
System.out.println(s);
在运行上面的代码后,将输出如下的信息:
file:文件
3. 资源文件的搜索顺序
如果同一种语言的资源文件存在多个,系统会按着如下的顺序搜索:
l baseName_language_country_variant.properties
2 baseName_language_country.properties
3 baseName_language.properties
4 baseName.properties
其中variant是第三方软件、平台或浏览器扩展的标志。详见8.2.1节的内容。
实际上,系统会搜索上面所有的资源文件(如果这些资源文件存在的话),当在这些资源文件中有重复key时,如果在同一个资源文件中有重复,则以最后一个出现的key为准。如果在不同资源文件中的重复,则以搜索顺序靠前的资源文件中的key为准。如MyResource_zh.properties文件中有两个key值为file的资源信息,内容如下:
file = File1
file = File2
系统在读取file资源时会读出“File2”,而不是File1。如果在MyResource.properties文件中也有一个key值为file的资源信息,内容如下:
file = File3
由于MyResource_zh.properties文件比MyResource.properties文件先搜索到,因此,系统仍然会读取MyResource_zh.properties文件最后一个file值,也就是“File2”。
4. 装载和读取资源包
通常可以使用ResourceBundle类的静态方法getBundle来创建ResourceBundle对象实例。在创建ResourceBundle对象实例的过程中会加载相应的资源包。getBundle方法有如下三个常用的重载形式:
public static final ResourceBundle getBundle(String baseName)
public static final ResourceBundle getBundle(String baseName, Locale locale)
public static ResourceBundle getBundle(String baseName, Locale locale,ClassLoader loader)
其中baseName表示资源文件的基名,locale表示与要读取的资源文件相对应的本地信息(Locale对象)。loader表示指定资源文件的类装载器。
如果使用getBundle方法的第一种重载形式,locale和loader参数使用了默认的值。也就是说,locale参数值是使用Locale.getDefault方法获得的值,而loader参数值是使用this.getClass().getClassLoader()方法获得的值。
如果使用getBundle方法的第二种重载形式,则loader参数值用了默认的值,也就是this.getClass().getClassLoader()方法返回的类装载器对象。
getBundle方法在装载资源包时会按着上面所讲的规则。如果Locale对象没有语言和国家信息,则getBundle方法会忽略这些本地信息,而直接读取baseName.properties文件的内容。
当baseName参数值中含有“.”时,系统会自动将“.”替换成“/”,当然,也可以直接在baseName参数值中使用“/”。如下面的两个baseName是相同的:
l resources.system.MyResource
2 resources/system/MyResource
为了使系统能找到资源文件,必须将资源文件放在系统能找到的目录中,如.class文件的根目录、Web应用程序的WEB-INF\classes目录等。如果系统未找到任何一个资源文件,则系统会抛出java.util.MissingResourceException异常。
在创建了ResourceBundle对象后,可以使用ResourceBundle类的getString方法读取相应的资源信息。如下面代码所示:
java.util.Locale locale = new java.util.Locale("zh", "CN");
java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("MyResource", locale);
System.out.println(rb.getString("file"));
在创建Locale对象时,语言和国家也可以不是ISO-639和ISO-3166标准中定义的代码,如下面的代码所示:
java.util.Locale locale = new java.util.Locale("abc", "DDD");
java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("MyResource",locale);
System.out.println(rb.getString("file"));
如果在类加载器可搜索到的目录中有一个MyResource_abc_DDD.properties文件,上面的代码仍然可以输出该文件中的相应资源信息。当然,如果该文件不存在,也会抛出一个MissingResourceException异常。