一、 程序国际化简介
国际化是指应用程序运行时,可根据客户端请求来自的国家/地区、语言的不同而显示不同的界面
1.1 程序国际化的意义
全球化软件,意味着同一种版本的产品能够容易地适用于不同地区的市场。软件全球化意味着国际化和本地化。
国际化是指应用程序运行时,可根据客户端请求来自的国家/地区、语言的不同而显示不同的界面。
国际化的英文单词是 Internationalization ,简称 I18N ,I 是首字母,18 表示省略当中的字母个数,N 代表最有一个字母
一个国际化支持很好的应用,会随着在不同区域使用时,呈现出本地语言提示。这个过程被成为 Localization,即本地化。简称 L10N
java 内核基于 UNICODE 2.1 编码集,提供了对不同国家,不同语言的支持,它已经具有国际化和本地化的特征和 API,因此 java 程序的国际化相对比较简单。
1.2 Java 国际化的思路
Java 程序国际化思路是 将程序中的标签、提示等信息放在资源文件中,程序需要支持的国家/语言环境,则必须提供对应的资源文件。资源文件是 key-value 对,每个资源文件中的 key 是不变的,但 value 则随不同国家/语言变化
j ava 程序的国际化主要通过如下 3 个类完成 :
● java.util.ResourceBundle : 用于加载一个资源包
● java.util.Locale : 对应一个特定的国家/区域、语言环境
● java.text.MessageFormat : 用于将消息格式化
为了实现国际化,必须先提供程序所需要的资源文件。资源文件内容是很多 key-value 对。
资源文件的命名可以有如下 3 种形式
● baseName_language_country.properties
● baseName_language.properties
● baseName.properties
其中 baseName 是资源文件的基本名,用户可以自由定义,而 language 和 country 都不可随意变化,必须是 java 所支持的语言和国家
1.3 Java 支持的语言和国家
事实上,java 不可能支持所有国家和语言,如需要获取 java 所支持的语言和国家,可调用 Locale 类的 getAvailableLocale() 方法获取,该方法返回一个 Locale 数组,该数组包含 java 所支持的语言和国家
LocaleList.java
import java.util.*;
public class LocaleList
{
public static void main(String[] args)
{
//返回Java所支持的全部国家和语言的数组
Locale[] localeList = Locale.getAvailableLocales();
//遍历数组的每个元素,依次获取所支持的国家和语言
for (int i = 0; i < localeList.length ; i++ )
{
//打印出所支持的国家和语言
System.out.println(localeList[i].getDisplayCountry()
+ "=" + localeList[i].getCountry() + " "
+ localeList[i].getDisplayLanguage() + "="
+ localeList[i].getLanguage());
}
}
}
通过该程序,就可以获得 java 程序所支持的国家/语言环境。
1.4 完成程序国际化
如有如下简单程序:
public class RawHello
{
public static void main(String[] args)
{
System.out.println("Hello World");
}
}
为了让该程序支持国际化,则肯定不能让程序直接输出 "Hello World" ,为了让程序可以输出不同的字符串,此处绝对不能使用这种字符串常量。
为了让上面输出的字符串常量可以改变,我们将需要输出的各种字符串 (不同国家/语言环境对应不同的字符串)定义在资源包中。
为上面程序提供如下 2 个文件 :
① mess_zh_CN.properties
#资源文件的内容是 key-value 对 hello=您好!
② mess_en_US.properties
hello=Hello World!
对于包含非西欧字符的资源文件,Java 提供了一个工具来处理该文件: native2ascii ,这个工具可以在 %JAVA_HOME%/bin 路径下找到。
使用 格式如下 :
native2ascii 源资源文件 目的资源文件
如果我们在命令窗口输入如下指令:
native2ascii mess_zh_CN.properties aa.properties
上面命令将生成一个 aa.properties 文件,该文件才是我们需要的资源文件。该文件看上去包含很多乱码,其实是非西欧字符的 UNICODE 编码方式,这完全正常。将该文件重命名为 mess_zh_CN.properties 即可。
Hello.java
import java.util.*;
public class Hello {
public static void main(String[] args) {
// 取得系统默认的国家/语言环境
Locale myLocale = Locale.getDefault();
// 根据指定国家/语言环境加载资源文件
ResourceBundle bundle = ResourceBundle.getBundle("mess", myLocale);
// 打印从资源文件中取得的消息
System.out.println(bundle.getString("hello"));
}
}
Java 国际化的关键类是 ResourceBundle ,有如下方法:
● getBundle(String baseName, Locale locale);
上面方法将根据 Locale 加载资源文件,Locale 代表一个国家/语言。
例如:
● ResourceBundle bundle = ResourceBundle.getBundle("mess", myLocale);
上面代码会加载 baseName 为 mess 的系列资源文件的其中之一,到底加载哪个,取决于 myLocale,对于简体中文,则加载 mess_zh_CH.properties.
1.5 使用 MessageFormat 处理带占位符的消息
MyResource_en_US.properties
msg=Hello,{0}!Today is {1}.
MyResource_zh_CN.properties
msg=你好,{0}!今天是{1}。
如上,我们需要使用参数为 {0} ,{1} 两个占位符赋值
此时需要使用 MessageFormat 类,方法:
● format(String pattern , Object... values) : 返回后面的多个参数值填充前面的 pattern 字符串,其中 pattern 字符串就是一个带有占位符的字符串
PlaceHolderMsg.java
import java.util.*;
import java.text.*;
public class PlaceHolderMsg {
public static void main(String[] args) {
// 定义一个Locale变量
Locale currentLocale = null;
// 如果运行程序指定了两个参数
if (args.length == 2) {
// 使用运行程序的两个参数构造Locale实例
currentLocale = new Locale(args[0], args[1]);
} else {
// 否则直接使用系统默认的Locale
currentLocale = Locale.getDefault();
}
// 根据Locale加载语言资源
ResourceBundle bundle = ResourceBundle.getBundle("MyResource",
currentLocale);
// 取得已加载的语言资源文件中msg对应消息
String msg = bundle.getString("msg");
// 使用MessageFormat为带占位符的字符串传入参数
System.out.println(MessageFormat.format(msg, "yeeku", new Date()));
}
}
如果程序不能找到对应的资源文件,系统会如下搜索:(假设是简体中文环境)
① MyResource_zh_CN.properties
如果没有找到,即搜索如下文件:
② MyResource_zh.properties
如果没有找到,即搜索如下文件:
③ MyResource.properties
如果上面 3 个文件都找不到,则系统会出现异常。
1.6 使用类文件代替资源文件
使用 java 文件来代替资源文件,必须满足如下条件 :
● 类的名字必须为 baseName_language_country ,这与属性文件的命名相似
● 类必须继承 ListResourceBundle,并重写 getContents() 方法,返回 Object 数组,该数组的每个项都是 key-value对
MyResource_zh_CN.java
import java.util.*;
public class MyResource_zh_CN extends ListResourceBundle {
// 定义资源
private final Object myData[][] = {{"msg", "类文件消息:{0},您好!今天是{1}"}};
// 重写方法getContents()
public Object[][] getContents() {
// 该方法返回资源的key-value对
return myData;
}
}
上面文件如果在简体中文环境下,可以代替 MyResource_zh_CN.properties
如果系统中存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件 。
对于简体中文的 Locale , ResourceBundle 搜索资源的顺序 是:
① baseName_zh_CN.class
② baseName_zh_CN.properties
③ baseName_zh.class
④ baseName_zh.properties
⑤ baseName.class
⑥ baseName.properties
系统按上面的顺序搜索资源文件,如果前面的文件不存在,才会使用下一个。如果一直找不到对应的文件,系统将抛出异常