ICU MessageFormat 落地笔记:六个 CLDR 复数类别、=N 优先级与 MF2 技术预览(ICU 75.1)
ICU MessageFormat 用来写可翻译字符串,处理复数、性别、条件文本,语法是 {variable, type, style}。类型有 plural、select、selectordinal、date、time、number。
文档:
大小括号与转义
花括号在「字面模式」和「代码模式」之间切换。不在字面模式时,空白(包括换行)会被忽略,所以消息串可以换行排列也不影响结果。
{ 和 } 必须用单引号转义;在 plural 语句内部,# 也要转义。配套有 MessageFormat.escape 辅助方法。
plural 与 CLDR 六个复数类别
{ COUNT, plural,
=0 {There are no results.}
one {There is one result.}
other {There are # results.}
}
COUNT:0→There are no results.COUNT:1→There is one result.COUNT:100→There are 100 results.
CLDR 定义了六个复数类别:
| 类别 | 语言示例 |
|---|---|
| zero | Arabic、Latvian、Welsh |
| one | English、French、German |
| two | Arabic、Hebrew、Slovenian |
| few | Russian、Polish、Czech |
| many | Russian、Polish、Arabic |
| other | 所有语言,必填 |
英语只需要 one 和 other,但 other 在所有语言里都必填,作为兜底分支。=N 是精确数字匹配,优先级高于类别匹配:写了 =0 就走 =0,不会再去算类别。
select 与嵌套顺序
按性别选分支:
{gender, select,
female {She liked your post}
male {He liked your post}
other {They liked your post}}
other 同样必填。
把性别和复数组合起来时需要嵌套。建议嵌套控制在两层以内:外层放 select(固定选项集),内层嵌 plural,且最多嵌一个。复杂参数应当作为最外层的结构,子消息里写完整句子,而不是把片段拼来拼去。
selectordinal 与 offset
selectordinal 处理序数词 1st/2nd/3rd,产出 1st place、2nd place、3rd place、4th place。
plural 支持 offset:判断复数类别之前先减掉 offset,典型例子是 "You and 4 others added this"。字面/精确匹配在应用 offset 之前先测试。
number、date、time 与 skeleton
日期、时间、数字格式化走 Intl.NumberFormat 和 Intl.DateTimeFormat。预定义样式有 short/medium/long/full,数字还有 integer/currency/percent。
以 :: 开头的是 skeleton,例如:
{T, date, ::hamszzzz}
skeleton 与语言环境无关,推荐使用。
string pattern 不推荐:它依赖语言环境,译者很容易写出无效的 ICU 语法导致报错,享受不到 CLDR 精选模式的收益,而且要在多个国家之间通用也很麻烦。
与 gettext ngettext 的取舍
gettext 的 ngettext 只支持两种形式。ICU 在单条字符串里就能覆盖全部 CLDR 复数类别,还能叠加性别、格式化和嵌套。ICU 表达力更强,代价是学习曲线更陡。
Symfony 里 ICU 消息文件需要带 +intl-icu 后缀,比如 messages+intl-icu.en.yaml、messages+intl-icu.fr_FR.xlf。旧的 %name% 占位符不再有效,必须改名。
MF2 技术预览与 ICU 75.1
MF2(MessageFormat 2.0)在 ICU4J 和 ICU4C 中有技术预览实现,类名是 MessageFormatter。规范草案在 LDML 45。ICU 75.1 实现了 number、datetime、string 选择器;pattern 不推荐使用,后续会被移除;skeleton 受支持。
MF2 作为继任者,建立在 25 年的经验之上,结构更模块化,并为新的格式化器留了扩展点(区间、相对时间、列表、度量单位、人名),也考虑语法性别和屈折变化。截至 2025 年,MF2 仍是技术预览,生产库中还没有广泛采用。
实现库
- FormatJS / intl-messageformat(react-intl 使用)
- messageformat / messageformat
常见的 i18n 失误
硬编码字符串、默认假设 LTR、忽略复数化、单位与格式不本地化、不用伪语言环境检查文本膨胀。