你的流水线会检查 JavaScript 语法,会做类型检查,会跑测试、检查代码格式、审计依赖、扫描密钥。然后,它把一个没人读过的 de.json 文件直接推到了生产环境——这个文件由某个没人审计过的模型生成。
一个真实存在的翻译 bug
作者在测试语料中埋入了一个 bug,它很有代表性:英文原文是“你的更改已成功保存”,西班牙语译文却是“无法保存你的更改”。意思完全相反,不是近似错误,而是精确反转。同一个 key,两个字符串都没有占位符,JSON 格式合法,key 集合逐字节一致。
作者指出,所有已知的 i18n 检查工具都会放过这个文件。
现有工具只看结构,不看语义
标准工具的做法是:对比本地化文件的结构。取源语言树、目标语言树,分别展平,对比 key 集合的差异。这确实有用,能抓住最常见的 i18n 缺陷——周五有人新增了 key 但没人翻译。它运行只要几毫秒,确定性执行,零成本。
但问题在于:它检查的是 key,不是 value。上述所有检查都是关于结构的陈述,而上面那个 bug 不是结构性问题——结构完美,语义反转。
占位符漂移:结构工具能触及的第二层
结构工具能处理的第二类问题是占位符漂移。比如英文是“你好 {{name}},你有 {{count}} 条新消息”,西班牙语译文变成“你好,你有新消息”——两个占位符都消失了。根据 i18n 库和框架的不同,这可能渲染成丢失主语和数字的句子,可能在运行时抛错,也可能——最糟的情况——在线上把字面量 {{name}} 直接渲染给用户。
这件事比看起来难的地方在于,“占位符”不是单一语法。混合代码库里可能同时存在 {{...}}、%1$s 等多种写法。只认识 {{...}} 的检查器,面对满是 %1$s 的文件会报告“干净”。而位置型 printf 有它自己的坑:翻译者可能为了适配目标语言语法而合法地调整 %1$s 和 %2$s 的顺序,所以“占位符顺序不同”是正确的、不该被标记;但“%2$s 完全消失”就是 bug。
方向也很关键。源语言有、译文没有的占位符是错误;译文里凭空出现、源语言没有对应物的占位符是另一种更奇怪的问题——通常是模型幻觉——应该给警告而不是硬失败,因为偶尔这是有意为之。
结论:结构检查要做全,但语义检查是空白
作者的建议是:结构检查要做,要覆盖所有占位符语法,要正确处理方向和位置参数的情况。这能覆盖大多数缺陷,但覆盖不了语义反转这类问题。下一步显然需要让模型逐对阅读源语言和译文,判断它们是否真的表达同一个意思——而这正是当前 CI 流水线里缺失的一环。
热门跟贴