应使用 ResourceBundle 管理多语言异常提示语,避免硬编码;将提示语存于 messages_zh_CN.properties、messages_en_US.properties 等资源文件,通过 key 标识,结合 Locale 自动匹配,并用 MessageFormat 填充占位符,再封装为可复用的 I18nException 工具类。

异常消息不写死,用 ResourceBundle 管理多语言
异常提示语不能硬编码在 throw new RuntimeException("用户名已存在") 里,否则换语言就失效。正确做法是把提示语抽出来,放在 messages_zh_CN.properties、messages_en_US.properties 这类资源文件中,用 key 唯一标识,比如:
user.exists=\u7528\u6237\u540D\u5DF2\u5B58\u5728
user.exists=User already exists
ResourceBundle 按 Locale 自动匹配资源文件
Java 会根据传入的 Locale 对象,按优先级查找匹配的 properties 文件:
– 先找 messages_{语言}_{国家}(如 messages_zh_CN)
– 再找 messages_{语言}(如 messages_zh)
– 最后 fallback 到 messages.properties(默认文件)
调用方式示例:
ResourceBundle rb = ResourceBundle.getBundle("messages", locale);
其中 locale 可来自 HTTP 请求头 Accept-Language、用户偏好设置,或线程上下文中的 LocaleContext。
用 MessageFormat 动态填充占位符
实际异常常带变量,比如“用户 {0} 在 {1} 时间已被锁定”。这时需在 properties 中定义含占位符的字符串,并用 MessageFormat 填充:
– 资源文件中写:
user.locked=用户 {0} 在 {1} 时间已被锁定
– Java 中解析:
String pattern = rb.getString("user.locked");
String msg = MessageFormat.format(pattern, "zhangsan", "2026-04-29 16:20");
注意:占位符索引从 0 开始,个数必须与 args 数组长度一致,否则抛 IllegalArgumentException。
封装成可复用的国际化异常工具类
避免每次手动调用 ResourceBundle + MessageFormat,建议封装一个工具方法或基类:
– 提供静态方法:I18nException.of("user.notfound", userId)
– 内部自动获取当前线程 Locale,加载对应资源,格式化占位符,再包装为 RuntimeException 子类
– 全局异常处理器(@ControllerAdvice)捕获该异常后,直接返回 format 后的 message 给前端,前后端语言保持一致

















