18ways 博客

i18n 已经坏了:别再写翻译键了

一种在 Next.js 中进行 i18n 的 AI 原生方式,不会破坏 SEO。

国际化(通常缩写为 i18n)、本地化(l10n)以及 多语言支持,都在描述同一个概念:让你的产品适配到人们可以用自己的语言使用。

i18n 还是 2005 年的那套架构

每家企业最终都会意识到自己需要支持多种语言。到了那时,产品团队会像修车工一样往后一靠,咂着嘴吸一口凉气,然后说:“我们说的是多少页面?那至少得做四个冲刺。”

一种对传统 i18n 工作成本和耗费的机械式反应

我们实现 i18n 的底层方式在 20 年里一直没变。大多数 i18n“最佳实践”形成于现代 Web 架构出现之前,而此后的一切不过是在它们之上层层叠加。

I18n 是现代网页开发中唯一仍然涉及导出文件并发送给别人的部分。如果不是通过邮件发送,而是通过 API 上传,那就算是一种现代化配置了。

在 CI 流水线和以分钟而不是天计的部署时代,我们大概该庆幸自己不用去传真任何东西。

为什么 i18n 总感觉不对劲

现代 Web 开发已经以前所未有的速度快速演进。

我们有:

但国际化工作流至今仍像是属于另一个时代。

即便是相对小型的应用,很快也会开始同时应付:

开发者并不是一开始就打算构建这样的系统。他们往往是在跌跌撞撞中走进来的:常见的 i18n 难题会绊倒那些不熟悉不同语言在句子结构上完全不同的开发者……随后不得不勉强接受 20 年前就定下的现状。

随着时间推移,国际化会变成技术栈中最复杂的部分之一。


现代 UI 无法塞进翻译字符串里

来看看一个简单的 UI:

jsx
<h1>Hello world!</h1>
<p>
  <a href="#/">Click here</a> to view your <b>{serverResponse.productDescription}</b>
</p>

这很容易理解。它很简单。

在某个时刻,我们决定把它国际化。所以,我们把文本提取到一个翻译文件中:

json
{
  "mainPage": {
    "greeting": "Hello world",
    "cta": "<0>Click here</0> to view your <1>{{description}}</1>"
  }
}

我们在代码中对此进行了引用:

jsx
<h1>{t('mainPage.greeting')}</h1>
<p>
  <Trans key="mainPage.cta">
    <a href="#/">Click here</a> to view your <b>{{ description: serverResponse.productDescription }}</b>
  </Trans>
</p>

但是,等等,我们现在在代码里和翻译文件里都有英文文本?是的。不同的 i18n 库只是换了层包装,但试图把部分已格式化的文本拆出来,放进一个可供人阅读的 JSON 文件里再去翻译,这是一场注定失败的战斗。

现代 UI 是结构化的,但翻译系统却建立在扁平字符串的基础上。

一旦标记、变量和动态内容介入,抽象层就开始泄漏。

动态内容和用户生成内容打破了传统 i18n

当内容不是静态的时候,事情就会变得更难。

再看这个例子:

jsx
<b>{serverResponse.productDescription}</b>

这个字符串来自一个 API。我们该怎么把它变成一个翻译键?

剧透:你不会。现在你的后端也得支持国际化了。

你的后端现在必须管理:

译者突然也需要访问后端系统。

而当用户生成内容介入时,问题只会进一步加剧。

评价。评论。市场列表。论坛。

传统的 i18n 流水线假设开发者掌控所有文本。而越来越多的时候,事实并非如此。

像 Amazon 这样的公司在 2026 年开始试验翻译后的用户评论,这一事实足以说明 i18n 生态系统进化得有多慢。

区域设置检测依然一团乱麻

甚至判断用户想要哪种语言都出乎意料地复杂。

浏览器通过 Accept-Language 头发送语言偏好:

text
Accept-Language: en-GB,en;q=0.9,fr;q=0.8

这会告诉服务器:

  1. 偏好英式英语
  2. 然后,任何形式的英文
  3. 然后是各种法语

在实践中,要正确实现这一点涉及:

许多框架把这项逻辑的大部分都交给了开发者。

即使是最基本的区域设置检测,往往也会变成定制的应用代码。

语言切换只是事后补上

翻译基础设施只解决了一半的问题。

用户仍然需要一种切换语言的方式。

大多数 i18n 库只提供翻译层,而把问题的其余部分留给开发者:

这些细节听起来很小,但很快就会变成大量的应用“胶水”工作;尤其是如果你对 i18n 的所有复杂之处并不熟悉的话。大多数公司和开发者都不熟悉。

捷径:自动网站翻译

鉴于这一切的复杂性,有些团队转而使用自动网站翻译工具。这些平台承诺只需极少的工作量,就能自动翻译你的网站。

许多公司正在打造这个想法的日益复杂的版本。不幸的是,它们引入了另一组问题:

结果往往是:一个网站在技术上支持多种语言,但实际体验明显更差,而且开发者用起来就像黑箱。

我们是如何走到这一步的

我们一开始使用的是某种美妙而简单的东西:

jsx
<h1>Hello world!</h1>
<p>
  <a href="#/">Click here</a> to view your <b>{serverResponse.productDescription}</b>
</p>

结果就变成了翻译键、JSON 词典、破碎的流水线、分离的后端和前端本地化层,以及外部翻译工具。

现代 Web 应用是极其复杂的系统。可 i18n 工具仍然假设的是另一个时代设计的工作流,就像一辆法拉利还得靠手摇曲柄启动引擎。

重新思考模型

如果国际化看起来更像这样呢?

jsx
<h1><T>Hello world!</T></h1>
<p>
  <T>
    <a href="#/">Click here</a> to view your <b>{serverResponse.productDescription}</b>
  </T>
</p>

没有翻译键。

没有 JSON 词典。

没有人工流水线。

只是文本。

另一种做法

这个想法是 18ways 的基础。

18ways 将翻译视为一种优化过的运行时问题。提取翻译键和管理翻译文件是机器的问题,不是人的问题。

开发者只需标记出他们希望翻译的文本。

在幕后,18ways:

一切仍然对 SSR 友好,并且对 SEO 安全。

上下文感知翻译

传统的 i18n 系统会丢失上下文。

译者可能会看到这样的内容:

text
home

但这是什么意思?

在德语里,这些意思需要完全不同的词:

如果你必须把“home”翻译成德语,你更想看到哪种?一个孤立的字符串,还是它真正出现的页面?

将翻译一个孤立的 JSON 字符串与在完整网站上下文中进行翻译进行比较

只能看到孤立字符串的翻译系统,很难处理这些差异。

18ways 会在实际 UI 的上下文中分析翻译。

我们的后端代理会审阅翻译在真实页面上的呈现,并针对以下方面进行优化:

对于熟悉德语复合名词的人来说,别担心,这里也包含溢出检测。

赋能人工翻译人员

所有能帮助 AI 生成更好译文的东西,也同样能帮助人工译者。

18ways 为翻译人员提供与我们的 AI 代理相同、具备上下文感知的环境。

翻译人员不再编辑 JSON 文件,而是直接在文本出现的 UI 上审校。

这会大幅提升翻译准确性和工作流效率。

更好的 i18n 是什么样的

国际化不需要再加一层翻译键、字符串文件和流水线胶水。

现代应用本就已经具备我们更好地完成这件事所需的结构、上下文和运行时信息。

下一代 i18n 应当为服务端渲染应用、动态内容、AI 原生工作流,以及并肩协作的人类译者而构建。

这正是 18ways 的方向。