← 返回博客
语言: English 中文
Dev 2026-08-07 7 分钟

在用户之前发现缺失的翻译键和插值参数不匹配

开发者的语言包永远不会有缺失键,因为功能就是对着它写的。缺失键只出现在后来由翻译者添加的语言包里,表现为真实用户看到的原始键路径。结构化地比较 locale 文件,而不是靠肉眼,才能在发布前发现这些。

i18n本地化翻译键插值ICU MessageFormat

国际化 bug 的独特之处在于,引入它的人看不到它。你对着自己的语言包开发功能,每个键都在,功能正常。bug 只出现在缺失该键的语言包里,而那通常是你不会说、也不测试的语言包。结果就是真实用户看到原始键路径,比如 settings.profile.title,或一个因为参数被改名而渲染为空的插值。

为什么评审里很难发现缺失键

代码评审只有在评审者 diff 了 locale 文件时才能抓住缺失键,而大多数评审者不会。功能在开发语言包里工作正常,测试在开发语言包里通过,缺失键在一个评审者从不打开的文件里。bug 上线了,第一个迹象是用户截图里本该是翻译字符串的地方出现了原始键路径。

结构性问题在于 locale 文件各自独立增长。英文文件随功能增加新键。翻译者稍后把对应键加到其他语言包,或漏掉。缺失键不会产生编译错误,因为 locale 查找会回退到键本身。这个回退就是 bug。

结构化地比较 locale

解法是把 locale 文件作为嵌套键结构比较,而不是作为文本。文本 diff 显示哪些行变了,但嵌套对象里缺失的键不是行变化,而是缺失的行。结构化比较把两个文件拍平成键路径,报告哪些路径只在一方存在。

比较必须双向进行。英文可能有一个日文没有的键,这是明显的缺失键 bug。日文可能有一个英文没有的键,这通常是移除功能后遗留的陈旧键。陈旧键不对用户可见,但它是死重,且以后键名和新功能撞了时会藏住真实 bug。

插值参数不匹配

带插值的翻译字符串,比如 Hello, {name},期望一个叫 name 的参数。如果翻译者把字符串改成 Bonjour {prenom},参数就变成 prenom,而代码仍传 name。视 i18n 库而定,结果是空串、字面 {name},或运行时错误。

这类 bug 比缺失键更难抓,因为两个语言包都有这个键。结构化键比较能通过。不匹配只藏在字符串值内部的参数里,需要解析每个字符串的插值语法并比较参数集合。

ICU MessageFormat 让这更严格,它加了 {count, plural, one {...} other {...}} 结构,参数名出现多次,库还会校验 count。一个丢掉 count 参数或改名的翻译复数会在运行时坏掉。检测这个意味着要解析 ICU 语法,而不只是 grep 花括号。

嵌套对象与扁平键

locale 文件有两种形状:嵌套对象,settings.profile.titlesettings: { profile: { title: ... } };扁平键,同一字符串是单个键 settings.profile.title。比较工具必须两种都处理,因为项目会混用。一个扁平键文件和一个嵌套文件比较,是工具只处理一种形状时常见的误报来源。

更深的问题是两种形状间的键冲突。一个带 settings.profile 键的扁平文件和一个 settings: { profile: ... } 的嵌套文件,文本里看着不同,但视库而定可能解析到同一查找路径。比较前把两者归一化到规范路径列表能避免这个。

什么时候跑这个检查

只要 locale 文件变了就跑比较,实际上就是每个触碰翻译的 PR。检查快、确定,能抓住一类否则直到用户报告才可见的 bug。在 CI 里跑,而不仅是本地,意味着翻译者的缺失键在合并前、而非发布后被抓住。

加新语言包时这个比较也有用。把新 locale 文件和参考 locale 文件粘进工具,立刻得到新语言包缺什么,这就是翻译者的起步清单。

比较抓不到什么

缺失键是客观的。错误翻译不是。比较不能告诉你翻译是否准确,只能告诉你键和参数是否匹配。一个每个键都在、每个参数都匹配的语言包,仍可能翻译得完全错误,那是双语评审者的问题,不是 diff 工具的问题。

比较也抓不到文化问题,比如一个技术上正确但读起来别扭的日期格式,或一个对所在布局太长的字符串。这些需要对照真实 UI 人工评审。工具抓住机械性 bug,缺失键和参数不匹配,这样人工评审就能专注在需要判断的那些上。

本站 i18n 工具在浏览器里跑结构化比较和参数检查,粘贴两个 locale 文件就能拿到 diff,不必把文件发到任何地方。这是每次翻译合并前都该跑的检查。

主要参考资料

用于核对本文技术细节的标准与官方文档。

在本地找出缺失键和参数不匹配

本站的 i18n 工具比较 locale JSON 文件,列出双向缺失的键,并标记插值参数不匹配。全部在浏览器里运行,locale 文件不会离开你的机器。

打开 i18n 工具

相关文章

继续阅读同一主题领域的实践指南。

查看全部文章

Cookie 同意

我们使用 Cookie 来增强您的体验并展示相关广告。您可以自定义您的偏好。