ICU MessageFormat 是一项 Unicode 标准,用来编写一个能根据数量、性别等变量自适应的可翻译字符串。它至今仍然难以翻译,是因为这套语法是为代码设计的,而不是为负责本地化的人设计的。这份规范已经存在十多年,最容易在它上面栽跟头的,是对着嵌套花括号发愁的非技术译者。globalize.now 是由 AI 驱动的本地化基础设施,作用在这个问题的下一层:它负责提取字符串、生成存放这些 ICU 模式的语言文件,并在每次 Git 推送时保持同步。

什么是 ICU MessageFormat?

ICU MessageFormat 是一种语法,用来表达最终措辞取决于运行时数据的消息。你不必在代码里拼接片段,只需写一个模式,由格式化器按语言区域选择正确的分支。

最常见的用法是处理复数:

{count, plural,
  one {You have # message}
  other {You have # messages}
}

# 会被替换为具体数字,one / other 分支对应语言的复数类别。英语有两种,波兰语、俄语和阿拉伯语则更多。ICU 还通过 select 处理性别和其他选择:

{gender, select,
  male {He liked your post}
  female {She liked your post}
  other {They liked your post}
}

这就是 ICU 成为事实上的复数处理标准的原因:它把语法复杂度放进可以按语言变化的数据里,而不是放进只适用于编写时所用语言的分支逻辑里。FormatJS、i18next 和 next-intl 等运行时库都支持它。

ICU MessageFormat 为什么这么难翻译?

难翻译是因为这套语法是为开发者设计的,而真正翻译字符串的人通常不是开发者。

译者打开语言文件,看到的不是“你有 3 条消息”。他们看到的是花括号、关键字 plural 和 select、可能完全不认识的复数类别,以及一个看起来像笔误的 # 占位符。删掉一个括号,或者改了一个类别名,字符串在运行时就会抛错而无法渲染。让 ICU 对工程师很强大的这套结构,在语言学家手里恰恰变得脆弱。

本地化行业多年来一直默默忍受着这个缺口。大多数工具的处理方式,要么是直接展示原始模式,指望译者别碰错字符;要么是把复数形式藏到单独的编辑器后面,但译者仍然需要理解复数类别。没有哪家厂商真正拉近了规范的复杂度与非技术译者认知模型之间的距离。

MessageFormat 2.0 带来了什么变化?

MessageFormat 2.0 是对语法的彻底重新设计,并在 2025 年从草案变成了稳定标准。

MF2 于 2025 年 3 月达到 Final Candidate 状态,现已成为 CLDR 的稳定组成部分,作为 Unicode LDML 技术标准的一部分发布,并在 LDML 47 和 48 版本中不断完善。参考实现与 2026 年初发布的 LDML 48.2 版规范保持一致。新语法把输入与匹配逻辑分离,并支持具名的格式化函数,复杂消息因此更易读:

.input {$count :number}
.match $count
one {{You have {$count} message}}
*   {{You have {$count} messages}}

目标是得到更易扩展、读起来也没有原版嵌套花括号那么费劲的模式。

MessageFormat 2.0 解决了译者的难题吗?

还没有,原因有两点。

第一,MF2 改善的是语法的易用性,但它仍然是语法。译者依然会遇到 .match、复数键和花括号。认知负担比 MF1 低,却没有消失,非技术译者改错一个标记,照样会弄坏整条消息。

第二,几乎没有人在生产环境中使用它。直到 2026 年,采用范围依然有限:JavaScript 一侧的 TC39 流程要求先有大约十几个组织在生产环境中使用 MF2,才会继续推进,而这个门槛至今没有达到。实际上,你今天交付的大多数应用,仍然通过 FormatJS、i18next 等库运行在原版 ICU MessageFormat 上。因此,面向译者的问题不是靠一份规范解决的,而是取决于语法在哪里生成、由谁维护。

ICU MessageFormat 在 AI 生成的应用中会在哪里出问题?

问题出现得很早,因为 AI 编程工具很少一开始就输出正确的 ICU。

用 Cursor、Claude Code 或 Lovable 构建应用时,生成的代码往往用最朴素的方式处理数量:

// What an AI coding tool often generates
const label = count + " " + (count === 1 ? "message" : "messages");

这段逻辑对英语是对的,对大多数其他语言却是错的,因为它假定只有两种复数形式。它还把整句话写死在应用代码里,语言文件中根本没有 ICU 模式可供译者本地化。在有人于生产环境发现语法错误之前,本地化层完全看不到这个字符串。AI 工具还会生成重复的翻译键,并把硬编码文本散落在各个组件中,所以即便团队想用 ICU,最后也会有一些字符串始终没进入可翻译的文件。globalize.now 开发者概览 的设计思路,正是在发布之前拦截这类字符串。

2026 年,开发者该如何处理 ICU MessageFormat?

把 ICU 当作自动生成的基础设施,而不是手写的内容。

实践中的规则是:开发者和 AI 工具不应该在代码里写复数拼接,译者也不应该手动编辑原始的 ICU 花括号。ICU 模式应当放在由系统自动生成并持续更新的语言文件里,再由 FormatJS、i18next 或 next-intl 等库在运行时渲染。这样,语法在生成的那一刻就是正确的,并且随着应用变化,在每个语言区域之间始终保持一致。如果你在权衡 ICU 与运行时层该如何配合,globalize.now 与 i18next 对比 介绍了基础设施与运行时如何分工。

这就是 globalize.now 所处的层级。它从代码库中提取硬编码字符串,生成翻译键和存放 ICU 模式的语言文件,并在每次 Git 推送时重新同步,无需手动导出,结构也始终保持正确。它并不打算成为译者的编辑器,也不会替代你的运行时库;它只负责确保这些工具依赖的 ICU 格式文件真实存在并保持同步。vibe coder 指南介绍了为 Lovable、Bolt 和 Replit 构建的应用如何完成同样的配置,完整工作流见 globalize.now 首页。

globalize.now 把硬编码的应用文案转换为可直接翻译的语言文件,并在你持续发布时保持同步更新。

免费试用 globalize.now