messageformat.format 依赖严格索引对齐、类型匹配和单引号转义:占位符须从0连续编号,参数类型需与{index,type,style}声明一致,单引号必须成对写为'',配合resourcebundle实现本地化。

MessageFormat.format 是 Java 中处理带变量参数的本地化文本最常用的方式,但它不是简单“填空”,而是依赖严格模式匹配和类型适配。用错一个占位符、漏一个单引号、传错参数类型,都会导致静默丢参、格式错乱甚至运行时异常。
占位符必须按索引严格对齐
模板里的 {0}、{1}、{2} 不是“任意顺序占位”,而是硬性绑定参数数组下标。索引必须从 0 开始、连续使用。比如写成 "{0} 和 {2}",但只传两个参数(new Object[]{"A", "B"}),{2} 就会因越界被忽略,不报错但内容消失。
- 正确做法:占位符索引连续,且参数数组长度 ≥ 最大索引 + 1
- 避免跳号或重复,例如不用 {0}、{2},改用 {0}、{1}
- 多语言资源文件中建议统一用 {0}、{1}… 而非语义名(如 {userName}),因为 MessageFormat 不支持命名参数
日期、数字等类型必须显式声明并匹配参数实际类型
写 {1,date,yyyy-MM-dd} 意味着你承诺第二个参数是 Date 类型;写 {2,number,currency} 就要求第三个参数是 Number(如 Double、BigDecimal),传 String 会直接抛 IllegalArgumentException。
- 常见陷阱:数据库查出的金额是字符串 "99.99",但模板写了 {0,number,currency} → 必须先转成 Double.parseDouble("99.99")
- 时间戳字段建议统一用
java.util.Date或java.time.Instant(需配合自定义 Format 子类,原生不支持 java.time) - 若不确定类型,可用默认格式
{0},它调用对象 toString(),最安全但无格式控制
单引号是最大隐形杀手,必须成对转义
MessageFormat 把单引号 ' 当作字面量保护符。模板里想输出一个真实单引号,必须写两个:''。如果漏了,整个解析就乱套——比如模板 "用户'{0}'已登录" 实际会被当成“保护 {0}”+“已登录”,结果可能变成 用户{0}已登录 或直接报错。
- 所有出现在模板中的单引号,不管是否在花括号外,都要双写
- 想输出
'Hello',模板要写成'''Hello'''(开头三个单引号:第一个是转义符,后两个是字面单引号;结尾同理) - 资源文件(.properties)中尤其注意:IDE 可能自动补单引号,但 MessageFormat 规则不会因此改变
与 ResourceBundle 配合实现真本地化
单独用 MessageFormat 只是格式化,真正支持多语言靠的是 ResourceBundle 加载不同 locale 的 .properties 文件。关键点在于:模板结构一致,仅内容随语言变。
- 英文资源(messages_en.properties):
login.success=Welcome, {0}! Logged in at {1,date,HH:mm}. - 中文资源(messages_zh.properties):
login.success=欢迎,{0}!您于 {1,date,HH:mm} 登录。 - Java 中加载:
ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.getDefault());
再调用MessageFormat.format(bundle.getString("login.success"), userName, new Date()) - 注意:不同语言中日期/数字习惯不同(如中文用“年月日”,英语用“MMM dd, yyyy”),应让 FormatStyle 适配 locale,而非硬编码格式串











