你用 Next.js i18n 的方式错了
JavaScript 里的国际化已经形成了一种有缺陷的惯例:把所有面向用户的字符串都抽取到一个 JSON 文件中,为它们分配 key,然后在组件里引用这些 key。用 t('home.hero.title') 代替文本本身。你的 UI 在一处,你的内容在另一处。
这种做法当然是可行的。成千上万的应用都是这么发布的。但这并不是很好的开发体验。
在代码评审里看到 t('checkout.summary.total') 几乎什么都看不出来——你得打开一个 JSON 文件才能知道哪里变了。Key 得被想出来、划分命名空间并保持同步。过期的翻译会不断堆积,因为没人知道哪些 key 还在被使用。这个问题足够普遍,以至于专门为此诞生了一整类工具:自动补全 key 的 IDE 插件、用来校验 key 的类型生成器、用来标记未使用 key 的 linter。这些工具解决的是一个由糟糕范式设计创造出来的问题。
<T> 组件
文案不应该脱离它的使用位置。一个渲染标题、段落和按钮的组件,本身就应该是这些元素文案的唯一事实来源,而不是去代理某个存放在别处的字符串。当代码和翻译变成两套平行系统时,它们迟早会产生偏差——这是必然的。
如果这个库反过来工作——由它来适配你的代码,而不是要求你去重构代码,会怎样?这才是 i18n 应该有的样子。
import { T } from 'gt-next';
function Hero() {
return (
<T>
<h1>将您的产品推向全球</h1>
<p>无需重写应用即可覆盖全球市场。</p>
</T>
);
}将 JSX 包裹在 <T> 组件中。英文文案就写在代码里原本的位置即可。当用户以西班牙语或日语访问时,<T> 内的内容会被自动翻译——连同结构和格式一并处理。
无需 key。无需 JSON 文件。无需交叉引用。你的代码就是唯一的事实来源。
设置
上面的语法出自 gt-next,一款用于 Next.js App Router 的开源 i18n 库。要开始使用,只需运行一条命令:
npx gtx-cli@latest init设置向导 会安装依赖项,用 withGTConfig 包装你的 Next.js 配置,把 GTProvider 添加到根布局中,创建包含语言环境(locale)列表的 gt.config.json,为翻译热重载设置开发环境 API 密钥,并以交互方式配置基于 CDN(内容分发网络)的翻译存储——以上步骤都会通过交互方式完成。
完成这些步骤后,将内容包裹在 <T> 中,运行开发服务器,并使用 <LocaleSelector> 组件在不同语言之间切换:
import { LocaleSelector } from 'gt-next';
function Header() {
return (
<header>
<nav>{/* ... */}</nav>
<LocaleSelector />
</header>
);
}在开发环境中会按需执行翻译,因此你可以立即以任意语言查看你的应用。
部署
在生产环境中,译文会预先生成。
-
从 dash.generaltranslation.com 获取生产环境 API 密钥。生产密钥以
gtx-api-开头(不同于本地使用的gtx-dev-密钥)。 -
在构建流程中添加 translate 步骤:
{
"scripts": {
"build": "npx gtx-cli translate --publish && next build"
}
}translate 命令会扫描你的代码库中所有 <T> 的使用,生成翻译并发布到 CDN(内容分发网络)。当你的应用构建时,每个语言环境都已就绪。