2. ResourceBundle 基础
ResourceBundle 是 Java 中管理国际化(i18n)资源的核心工具。它能让我们轻松实现多语言应用,让不同地区用户都能用母语操作程序。
核心要点:
- ✅ 所有资源文件必须放在同一目录下,且共享基础名称
- ✅ 文件命名规则:
基础名[_语言[_国家[_平台]]].扩展名
- ❌ 避免硬编码界面文本(如按钮、标签)
典型文件命名示例:
ExampleResource // 默认文件
ExampleResource_en // 英语
ExampleResource_en_US // 美国英语
ExampleResource_en_US_UNIX // 美国英语+UNIX平台
2.1 属性文件方式 (PropertyResourceBundle)
属性文件以 .properties
结尾,采用键值对存储数据。支持三种格式:
# 注释方式1:井号开头
continueButton continue
# 注释方式2:感叹号开头
! 标签区域
helloLabel:hello
# 等号方式(最常用)
cancelButton=cancel
⚠️ 注意:键值对区分大小写,注释以 #
或 !
开头
2.2 Java 文件方式 (ListResourceBundle)
需要继承 ListResourceBundle
并重写 getContents()
方法:
public class ExampleResource_pl_PL extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][] {
{"currency", "polish zloty"},
{"toUsdRate", new BigDecimal("3.401")},
{"cities", new String[] { "Warsaw", "Cracow" }}
};
}
}
两种方式对比:
- 🏆 Java 文件优势:可存储任意对象类型(非仅字符串)
- 🏆 属性文件优势:修改后无需重新编译
3. 资源包使用实战
使用资源包只需三步:
// 1. 定义目标语言环境
Locale locale = new Locale("pl", "PL");
// 2. 加载资源包
ResourceBundle exampleBundle = ResourceBundle.getBundle(
"com.example.ExampleResource",
locale
);
// 3. 获取资源
assertEquals(exampleBundle.getString("currency"), "polish zloty");
assertEquals(exampleBundle.getObject("toUsdRate"), new BigDecimal("3.401"));
assertArrayEquals(exampleBundle.getStringArray("cities"),
new String[]{"Warsaw", "Cracow"});
常用方法:
getString(key)
:获取字符串getObject(key)
:获取任意对象getStringArray(key)
:获取字符串数组
4. 资源文件匹配规则
当请求特定语言环境时,JVM 按以下顺序查找文件:
Label_pl_PL_UNIX // 最精确匹配
Label_pl_PL // 国家匹配
Label_pl // 语言匹配
Label_en_US // 回退到默认环境(美国英语)
Label_en // 默认语言
Label // 终极回退(无语言标识)
⚠️ 重要原则:
.java
文件优先于.properties
文件- 找不到匹配文件时抛出
MissingResourceException
- 默认语言环境由 JVM 设置决定
5. 资源继承机制
资源包支持属性继承:子文件会自动继承父文件的键值对。
示例配置:
# resource.properties(父文件)
cancelButton = cancel
# resource_pl.properties(继承父文件)
continueButton = dalej
# resource_pl_PL.properties(继承前两者)
backButton = cofnij
当加载 Locale("pl", "PL")
时:
- ✅ 同时获取三个文件的所有键值对
- ❌ 不会回退到默认语言环境
5.1 跨类型继承限制
重要踩坑:属性文件和 Java 文件之间不支持继承!
// CustomListResourceBundle.java
public class CustomListResourceBundle extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][] {
{ "customMessage", "This is a custom message." }
};
}
}
测试验证:
@Test
public void givenListResourceBundle_whenUsingInheritance_thenItShouldNotInherit() {
ResourceBundle listBundle = ResourceBundle.getBundle(
"com.example.CustomListResourceBundle",
Locale.ENGLISH
);
// 只能获取自身定义的键
assertEquals("This is a custom message.",
listBundle.getString("customMessage"));
// 尝试获取属性文件中的键会抛异常
assertThrows(MissingResourceException.class,
() -> listBundle.getString("greeting"));
}
6. 自定义加载控制
通过继承 ResourceBundle.Control
可自定义加载行为:
public class ExampleControl extends ResourceBundle.Control {
@Override
public List<Locale> getCandidateLocales(String s, Locale locale) {
// 强制只返回波兰语环境
return Arrays.asList(new Locale("pl", "PL"));
}
}
应用自定义控制:
ResourceBundle bundle = ResourceBundle.getBundle(
"com.example.ExampleResource",
locale,
new ExampleControl()
);
7. UTF-8 编码处理
JDK 8 及更早版本的坑:
PropertyResourceBundle
默认使用 ISO-8859-1 编码- 非拉丁字符(如中文、波兰语)需转义为
\uxxxx
处理方案:
# 使用 native2ascii 工具转换
native2ascii -encoding UTF-8 utf8.properties nonUtf8.properties
转换示例:
# 转换前
polishHello=cześć
# 转换后
polishHello=cze\u015b\u0107
✅ JDK 9+ 的福音:默认支持 UTF-8 编码,无需转换!
8. 总结
ResourceBundle 是 Java 国际化的核心解决方案,关键优势:
- ✅ 灵活存储:支持属性文件和 Java 类两种方式
- ✅ 自动继承:子文件自动继承父文件资源
- ✅ 动态加载:修改属性文件无需重新编译
- ✅ 精确匹配:智能的语言环境查找机制
使用建议:
- 优先使用
.properties
文件(修改无需编译) - 复杂对象或 JDK 8 环境考虑 Java 类方式
- JDK 9+ 直接使用 UTF-8 编码
- 通过自定义 Control 实现特殊加载逻辑
通过合理使用 ResourceBundle,可以轻松构建支持多语言的健壮应用,同时保持代码的可维护性。