十五种语言不等于十五份翻译
翻字符串是所有人都会给预算的那部分。真正会坏的是字符串**周围**的一切 —— 元数据、兜底、以及那个顽固地留在英文的链接。

十五种语言不等于十五份翻译 🌍
计划书上写着:抽出字符串、发出去翻、放回来、上线十五种语言。这计划没错,只是它描述的只是这件事的一小部分。真正吃掉时间的,是字符串周围那圈表面积 —— 那些在旁边一切都在变的时候,悄悄守着某个默认值的地方。
下面是我们反复发现的东西,大致按它让我们难堪的顺序排列。
词典不等于整个表面
一份翻译文件覆盖的是组件渲染出来的文本。它不覆盖:
- 页面元数据。
<title>和描述常常是在某个 layout 或generateMetadata里拼出来的,而没人把它当成 UI。一个完全翻译过的页面,仍然可能在浏览器标签、搜索结果、以及任何人分享出去的链接预览里,用英文自报家门。这是最常见的遗漏,因为页面看上去是对的。 - 导航和页脚条目,如果它们定义在配置文件而不是组件里。配置是数据,数据不会被"禁止硬编码字符串"的 lint 规则抓到,于是一个导航标签能在所有语言里以未翻译状态待上几个月。
- alt 文本和 aria 标签。 对视觉评审不可见,对读屏软件完全可见。
- 错误态和空态。 评审时很少被渲染到,这正是它们能存活的原因。
- 任何服务端生成的东西 —— 邮件、收据、通知文案。用户的语言必须从请求一路传到模板,而通常总有一跳传丢了。
规律很一致:一个东西被翻译的概率,正比于有人盯着它看的频率,而不是用户遇到它的频率。
缺 key 在开发时该很吵,在生产时该看不见
两种失败模式,修法相反。
如果缺 key 时应用渲染出原始 key 路径,你就把 nav.products.subtitle 发给了真实用户。如果它静默渲染成空,你就发出去一片空白,而且一两个版本里没人会注意到。
管用的做法是:生产环境回落到默认语言的字符串,开发环境让它尖叫。 用户拿到英文 —— 不完美,但是一句真话。开发者拿到一条控制台错误,或者更好:一个构建期检查,确认每种语言都有默认语言拥有的每一个 key。这个检查二十行代码,是你能写的投入产出比最高的一块 i18n 基础设施。
文本有尺寸,而尺寸会变
同一句话的德语版本通常比英语长 30%。中文和日文往往短得多但更高,而且断行规则不一样。韩语介于中间,有自己的间距行为。
你不需要一套排版理论,你只需要别再造那些假设英文长度的布局:
- 别按标签的宽度定死按钮;按内容加内边距,让它自己长。
- 别把两个固定宽度的列并排放,然后指望中间那点空隙。
- 别在用户必须读完才能做决定的内容上用省略号截断。
- 检查布局时看最长的那种语言,不是最短的。德语过得去,其他都过得去。
一句翻译可以合乎语法,同时是错的
花掉我们最多评审时间的那个,不是误译。是一句韩语,在一次文案改动之后,出现了两个连续的连接词尾 —— 语法上能解析,但读起来像有人把两个半句用订书机钉在了一起。修法是拆成两句,而这是任何术语表或术语一致性检查都不会提出的建议。
这是"翻字符串而不是翻意思"的结构性上限。当你改英文文案时,另一种语言里对应的改动有时候不是同一个改动。把两个从句连起来在一种语法里成立,在另一种语法里读着像结巴。
实际的防线不是加流程,而是:当文案改动改变了句子结构时,标记它去做一次母语通读,而不是逐 key 替换。 加一个限定从句恰好就是这类编辑,而它同时也恰好是那种"太小了,不好意思麻烦别人"的编辑。
下一次我们会先建什么
按投入产出排序:
- 构建期的 key 完整性检查,覆盖所有语言。用最少的代码抓住最多的真实缺陷。
- 第一天就做元数据本地化,包括
<title>和所有 Open Graph 字段。事后补等于要动每一条路由。 - 一条"配置文件也要翻译"的规则 —— 或者更好:用户可见的字符串永远不要住在配置里。
- 把"用最长语言过一遍布局"并入任何 UI 改动,而不是单独排一轮。
这些都和翻译质量无关。它们说的是另一件事:"加一种语言"是应用的架构属性,而字符串从来都是容易的那部分。