locale必须显式传入api(如resourcebundle.getbundle)才生效,不能依赖locale.setdefault();resourcebundle按zh_cn→zh→base精确匹配文件名,大小写和编码需规范;小语种可能因jdk缺失本地化数据而静默回退英文。

Locale对象怎么创建才真正生效
直接用 new Locale("zh", "CN") 或 Locale.CHINA 看似正确,但很多场景下它只是“存在”,并不自动影响资源加载或格式化行为。关键在于:Locale 必须被显式传递给依赖它的 API,比如 ResourceBundle.getBundle()、NumberFormat.getInstance()、DateFormat.getDateInstance(),否则系统会 fallback 到 JVM 启动时的默认 Locale(通常是 OS 设置,不可靠)。
常见错误是只设了 Locale.setDefault(new Locale("fr")) 就以为全局生效——这仅影响未显式传 Locale 的少数 API(如 Calendar.getInstance()),对 ResourceBundle 完全无效。
- 推荐做法:每个需要本地化的操作都显式传入
Locale实例,不依赖默认值 - Web 应用中,从 HTTP 请求头(
Accept-Language)解析出Locale,再传给业务逻辑 - 避免在多线程环境里调用
Locale.setDefault(),它修改的是全局静态状态,会干扰其他线程
ResourceBundle加载路径和命名规则必须严格匹配
ResourceBundle.getBundle("messages", locale) 能否找到对应文件,完全取决于类路径下是否存在符合命名规范的 properties 文件。不是“有中文就找 zh_CN”,而是按精确的继承链查找:比如 locale = new Locale("zh", "CN") 会依次尝试加载:
messages_zh_CN.properties messages_zh.properties messages.properties
一旦某一级命中,就停止向上回退。所以 messages_zh.properties 不是“简体中文通用”,而是“所有 zh 语言变体的兜底”,若你只提供 messages_zh_CN.properties 却没提供 messages_zh.properties,那么 new Locale("zh", "TW") 就会直接 fallback 到 messages.properties(即 base bundle),而非你预期的简体中文内容。
立即学习“Java免费学习笔记(深入)”;
- 文件名中的语言码必须小写(
zh),国家码必须大写(CN),否则无法识别 - properties 文件需保存为 UTF-8 编码,并用
native2ascii工具转义中文(或改用 .properties 文件的 Unicode 转义格式,如key=\u4f60\u597d) - Spring 等框架封装了 ResourceBundle,但底层仍遵循这套查找逻辑,调试时可开启
-Djava.util.ResourceBundle.Control=java.util.ResourceBundle.Control.INSTANCE查看加载过程
日期/数字格式化为什么总显示英文
用 DateFormat.getDateInstance(DateFormat.LONG, locale) 返回的格式器,确实会按 locale 选择星期、月份名称和顺序,但前提是该 locale 在 JDK 中有完整支持。像 new Locale("yue", "HK")(粤语)或某些小语种,JDK 可能只提供语言码而无本地化数据,此时会静默 fallback 到 ROOT 或 en_US,导致“明明传了 locale,却还是英文”。
验证方式:调用 DateFormat.getDateInstance().getCalendar().getDisplayName(Calendar.MONTH, Calendar.LONG, locale),如果返回 null 或空字符串,说明该 locale 的本地化数据缺失。
- 优先使用 JDK 内置标准 locale,如
Locale.JAPAN、Locale.FRANCE、Locale.TRADITIONAL_CHINESE - 不要用
new Locale("zh-Hans-CN")这类 BCP 47 格式(Java 7+ 才部分支持),老版本 JDK 会直接忽略 - Android 环境下 Locale 行为与标准 JDK 不同,特别是 Android 7.0 前对多语言支持较弱,需额外测试
Spring Boot里LocaleResolver配置容易漏掉的关键点
Spring Boot 默认用 AcceptHeaderLocaleResolver,它只读请求头,不支持 URL 参数或 Cookie 切换语言。很多人配了 SessionLocaleResolver 却发现切换无效,问题常出在:没有配套配置 LocaleChangeInterceptor,或者拦截器没注册进 WebMvcConfigurer。
更隐蔽的问题是:Thymeleaf 模板里用 #{key} 语法时,背后依赖的是 Spring 的 MessageSource,它和 LocaleResolver 是两套机制——前者决定“用哪个 ResourceBundle”,后者决定“当前请求的 Locale 是什么”。两者必须协同,否则页面显示语言和表单提交后校验提示语言可能不一致。
-
LocaleChangeInterceptor的参数名(如lang)必须和前端请求参数一致,且要确保该拦截器在拦截链中生效(检查WebMvcConfigurer.addInterceptors()是否被正确重写) -
ReloadableResourceBundleMessageSource支持热刷新,但 baseName 必须写成classpath:i18n/messages(不能带 .properties 后缀),否则找不到文件 - 若用
CookieLocaleResolver,注意 Cookie 的 path 和 maxAge 配置,否则跨子路径或浏览器重启后失效
new Locale("zh", "cn")(国家码小写),或 properties 文件名写成 messages_zh_CN.utf8.properties(多加了后缀)。这些细节不报错,只默默 fallback,最终表现为“语言没切过去”。










