- Java 编程语言是第一种设计成为全面支持国际化的语言。
- Java 从一开始就具备了进行有效的国际化所必需的一个重要特性:
- 使用 Unicode 来处理所有字符串。
- 由于支持 Unicode,在 Java 编程语言中编写程序来操作多种语言的字符串变得异常方便。
- 国际化一个程序所要做的事情绝不仅仅是提供 Unicode 支持。
- 在世界的不同地方,日期、时间、货币甚至数字的格式都不相同。
- 你需要用一种简单的方法来为不同的语言配置菜单与按钮的名字、消息字符串和快捷键。
一、locale
1、为什么需要 local
- 当你提供程序的国际化版本时,所有程序消息都需要转换为本地语言。
- 当然,直接翻译用户界面的文本是不够的,还有许多更细微的差异。
细微差异:
- 1、数字格式差异:小数点和十进制数的逗号分隔符的角色是相反的。
- 德国:123.456,78
- 英国:123,456.78
- 2、日期格式差异:在日期的显示上也有相似的变化。
- 美国:月/日/年。
- 德国:日/月/年。
- 中国:年/月/日。
- locale 捕获了像上面这类偏好特征。
- 无论何时,只要表示数字、日期、货币值以及其它格式会随 语言或地点 发生变化的项。
- 都需要使用 locale 感知的 API。
2、指定 locale
- 在 Java 中,Locale 是用于表示特定地理、政治、文化区域的类,其核心组成部分包括以下内容:
- 可以用 Locale.getlSOLanguages() 获取所有语言代码。
- 可以用 Locale.getlSOCountries() 获取所有 国家或地区 代码。
1. 语言(Language)-- 基于 ISO 639 标准 的语言 代码。
- 格式:2 或 3 个小写字母。
- 示例:
- en(英语)、zh(中文)、ja(日语)
- 作用:标识资源文件的基础语言。
2. 国家/地区(Country/Region)-- 基于 ISO 3166 标准 的国家或地区 代码。
- 格式:2 个大写字母 或 3 个数字表示。
- 示例:
- CN(中国)、US(美国)、DE(德国)、CH(瑞士)
- 作用:区分同一语言的不同地区变体(如 en-US 和 en-GB)。
3. 变体(Variant) – 自定义的附加标识符,用于进一步细分区域
- 格式:自由定义的字符串(通常大写字母或数字组合)。
- 示例:
- POSIX(表示 POSIX 兼容格式)、NY(纽约方言)、
- 1996(表示遵循 1996 年拼写改革)
- 作用:处理特殊场景(如:技术规范、方言、拼写规则)。
- 已经很少使用了。
4. 脚本(Script)-- 基于 ISO 15924 标准 的文字系统 代码。
- 格式:4 个字母(首字母大写,其余小写)。
- 示例:
- Hans(简体中文)、Hant(繁体中文)、Latn(拉丁字母)
- 作用:区分同一语言的不同书写系统(如:中文简繁体)。
5. 扩展(Extensions)-- 基于 BCP 47 标准 的附加键值对,用于表示区域的技术扩展。
- 格式:
- Unicode 扩展:以 u- 开头(如日历、货币格式)。
- 私有扩展:以 x- 开头(自定义用途)。
- 示例:
- u-ca-buddhist(使用佛历)
- x-lvariant-jp(自定义日本区域变体)
- 作用:处理复杂的区域需求(如:特殊日历、数字、货币规则)。
public class LocaleExample {
public static void main(String[] args) {
// 1、使用 Builder 模式创建包含所有部分的 Locale
Locale locale = new Locale.Builder()
// 语言(中文)
.setLanguage("zh")
// 国家(中国)
.setRegion("CN")
// 变体(POSIX 兼容)
.setVariant("POSIX")
// 脚本(简体中文)
.setScript("Hans")
// Unicode 扩展(佛历)
.setExtension('u', "ca-buddhist")
.build();
// 输出 Locale 详细信息
// Locale 完整标识: zh-Hans-CN-POSIX-u-ca-buddhist
System.out.println("Locale 完整标识: " + locale.toLanguageTag());
// 解析各组成部分
// 语言: zh
System.out.println("语言: " + locale.getLanguage());
// 国家: CN
System.out.println("国家: " + locale.getCountry());
// 变体: POSIX
System.out.println("变体: " + locale.getVariant());
// 脚本: Hans
System.out.println("脚本: " + locale.getScript());
// 扩展: ca-buddhist
System.out.println("扩展: " + locale.getExtension('u'));
}
}
3、默认 locale
- Locale.Category 包含以下两个枚举常量,分别对应不同的 本地化场景:
枚举常量 | 说明 |
---|---|
DISPLAY | 控制用户界面(UI)的本地化显示。(如:菜单、按钮、标签等) 依赖的资源文件(如:messages_xx.properties)。 |
FORMAT | 控制数据格式化的本地化行为。(如:日期、时间、数字、货币的格式化方式)。 |
- Locale 类的静态 getDefault 方法可以获得作为本地操作系统的一部分而存储的默认 locale。
- 可以调用 setDefault 来改变默认的 Java locale。
- 这种改变只对你的程序有效, 不会对操作系统产生影响。
public static void main(String[] args) {
// 1、获取操作系统默认语言环境
Locale defaultLocale = Locale.getDefault();
// zh_CN_#Hans
/*
zh:语言(中文)
CN:国家(中国)
Hans:脚本(简体中文)
*/
System.out.println(defaultLocale);
// zh_CN
System.out.println(Locale.CHINA);
// zh
System.out.println(Locale.CHINESE);
// 2、设置默认 Locale 为中文(中国)
Locale.setDefault(Locale.CHINA);
defaultLocale = Locale.getDefault();
// zh_CN
System.out.println(defaultLocale);
// 3、设置默认 Locale 为中文
Locale.setDefault(Locale.CHINESE);
defaultLocale = Locale.getDefault();
// zh
System.out.println(defaultLocale);
}
- 有些操作系统允许用户为显示消息和格式化指定不同的 locale。
- 如:生活在美国的说法语的人菜单是法语的,但是货币值是用美元来表示的。
- 即:界面显示为法语,但数据格式(日期、数字)使用美国标准。
public static void main(String[] args) {
// 1、设置 DISPLAY 类别为法语(界面显示)
Locale.setDefault(Locale.Category.DISPLAY, Locale.FRANCE);
// 2、设置 FORMAT 类别为美国英语(数据格式化)
Locale.setDefault(Locale.Category.FORMAT, Locale.US);
// 3、获取 Locale.Category.DISPLAY 类别的 Locale
displayLocale = Locale.getDefault(Locale.Category.DISPLAY);
// fr_FR
System.out.println(displayLocale);
// 4、获取 Locale.Category.FORMAT 类别的 Locale
forwatLocale = Locale.getDefault(Locale.Category.FORMAT);
// en_US
System.out.println(forwatLocale);
// 5、加载国际化资源文件<baseName> 包(使用 Locale.Category.DISPLAY 类别的 Locale)
ResourceBundle bundle = ResourceBundle.getBundle("messages");
// 输出: Bonjour!
System.out.println("本地化问候语: " + bundle.getString("greeting"));
// 6、格式化日期(使用 Locale.Category.FORMAT 类别的 Locale)
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
String formattedDate = dateFormat.format(new Date());
// 格式化日期: Thursday, May 22, 2025
System.out.println("格式化日期: " + formattedDate);
// 7、格式化数字(使用 Locale.Category.FORMAT 类别的 Locale)
NumberFormat numberFormat = NumberFormat.getNumberInstance();
String formattedNumber = numberFormat.format(1234.56);
// 格式化数字: 1,234.56
System.out.println("格式化数字: " + formattedNumber);
}
- 获取系统默认的偏好设置,可以调用。
public static void main(String[] args) {
Locale displayLocale = Locale.getDefault(Locale.Category.DISPLAY);
// zh_CN_#Hans
System.out.println(displayLocale);
Locale forwatLocale = Locale.getDefault(Locale.Category.FORMAT);
// zh_CN_#Hans
System.out.println(forwatLocale);
}
4、Locale 的应用
- 获取所有可用的 Locale 。
public static void main(String[] args) {
// 1、获取 Java 语言支持的所有的 Locale
Locale[] locales = Locale.getAvailableLocales();
for (Locale locale: locales) {
System.out.println(String.format("%10s\t\t%10s\t%10s", locale.getDisplayCountry(),locale.getLanguage(),locale.getCountry()));
}
}
- 创建 Locale 对象
// 1. 仅语言(如:中文)
Locale localeZh = new Locale("zh");
// zh
System.out.println(localeZh);
// 2. 语言 + 国家(如:中文-中国)
Locale localeZhCn = new Locale("zh", "CN");
// zh_CN
System.out.println(localeZhCn);
// 3. 语言+国家+变体(如:中文-中国-电话号码格式)
Locale localeZhCnPhone = new Locale("zh", "CN", "phonebook");
// zh_CN_phonebook
System.out.println(localeZhCnPhone);
// 4、获取操作系统默认语言环境
Locale defaultLocale = Locale.getDefault();
// zh_CN_#Hans
/*
zh:语言(中文)
CN:国家(中国)
Hans:脚本(简体中文)
*/
System.out.println(defaultLocale);
// 5、使用 Builder 模式创建包含所有部分的 Locale
Locale locale = new Locale.Builder()
// 语言(中文)
.setLanguage("zh")
// 国家(中国)
.setRegion("CN")
// 变体(POSIX 兼容)
.setVariant("POSIX")
// 脚本(简体中文)
.setScript("Hans")
// Unicode 扩展(佛历)
.setExtension('u', "ca-buddhist")
.build();
// 输出 Locale 详细信息
// Locale 完整标识: zh-Hans-CN-POSIX-u-ca-buddhist
System.out.println("Locale 完整标识: " + locale.toLanguageTag());
应用场景
- 2、日期格式适配
public static void main(String[] args) {
// 使用佛历(Unicode 扩展)
// 创建支持佛历的 Locale(泰国)
Locale thaiLocale = new Locale.Builder()
.setLanguage("th")
.setRegion("TH")
// Unicode 扩展指定佛历
.setExtension('u', "ca-buddhist")
.build();
// 获取当前日期(佛历)
ThaiBuddhistDate buddhistDate = ThaiBuddhistDate.now();
// 创建格式化器并应用佛历
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
.withLocale(thaiLocale)
.withChronology(buddhistDate.getChronology());
String date = formatter.format(LocalDate.now());
// 2568-05-21
System.out.println(date);
}
二、国际化
1、什么是国际化 – 本质就是:“查找,替换”
- 资源文件:负责为程序提供国际化消息。
- 资源文件的文件名必须满足 _语言代码_国家代码.properties
- 如果资源文件中包含非西欧字符,需要使用 native2ascii 工具类处理这个文件。
- native2ascii 要处理的文件生成的新文件名。
2、国际化用到的类
- Locale 类:代表语言、国家环境。
- ResourceBundle:负责加载国际化资源文件,而且帮我们查找和替换。
- MessageFormat:负责为消息中的占位符填充参数。
- format(String patern, Object… arguments)
- 该方法中 auguments 参数就负责一次替换每个占位符。
3、国际化的步骤:
- 1、提供资源文件。
- 负责为程序提供国际化消息。
- 2、使用 ResourceBundle 加载 国际化资源文件。
- getBundle(baseName, 语言代码_国家代码); 来搜索资源文件的顺序;
- baseName_语言代码_国家代码.properties
- baseName_语言代码.properties
- baseName.properties
- 调用 ResourceBundle 的 getMessage() 方法输出国际化消息。
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
public class I18NHello {
public static void main(String[] args) {
// 1、获取 Java 语言支持的所有的 Locale
Locale[] locales = Locale.getAvailableLocales();
for (Locale locale: locales) {
System.out.println(String.format("%10s\t\t%10s\t%10s", locale.getDisplayCountry(),locale.getLanguage(),locale.getCountry()));
}
// 2、获取当前计算机的语言、环境。
// 假如操作系统是简体中文环境,currentLocale就相当于返回了zh_CN
Locale currentLocale = Locale.getDefault();
System.out.println(currentLocale);
// 加载国际化资源文件 <baseName>_语言_国家.properties 文件
// 如:zxw_zh_CN.properties
//ResourceBundle bundle = ResourceBundle.getBundle("zxw",currentLocale);
ResourceBundle bundle = ResourceBundle.getBundle("zxw_zh_CN");
// 从 bundle 所加载的文件查找 hi 对应字符串进行替换。
System.out.println(bundle.getString("hello"));
// 李四,欢迎来到国际化!
System.out.println(MessageFormat.format(bundle.getString("welcome"),"张三","李四","王二"));
}
}
hello=欢迎您,国际化真简单\!
# {1} 表示:会使用传入的第二个参数,即 李四。
welcome={1},欢迎来到国际化!
hello=Hello World!
# {0} 表示:会使用传入的第一个参数,即 张三。
welcome={0},welcome to I18N.