国际化接入
为应用添加语言支持、管理翻译文件和配置多语言内容的实操指南
概览
项目使用 next-intl(Next.js 官方推荐的国际化方案)实现多语言支持,当前内置两种语言:
| 语言 | Locale 代码 | 货币 |
|---|---|---|
| 简体中文(默认) | zh | CNY |
| English | en | USD |
翻译文件采用两层模型:共享翻译(所有应用共用)+ 应用级覆盖(某个应用需要特殊文案时使用)。更详细的分层策略说明请参阅 国际化策略。
翻译文件结构
packages/i18n/translations/shared/— 跨应用共用的 UI 文案,如按钮、导航、通用错误提示等。apps/01mvp-web/src/modules/i18n/translations/— 仅在当前应用中使用的文案,如品牌词、业务术语。
应用层翻译文件只维护与共享层的差异部分,不要把共享层的完整内容复制过去,否则后续更新翻译会非常麻烦。
新增语言
在 config 中添加 locale
编辑 apps/01mvp-web/src/lib/config/index.ts,在 i18n.locales 对象中新增语言条目:
i18n: {
enabled: true,
locales: {
en: { currency: "USD", label: "English" },
zh: { currency: "CNY", label: "简体中文" },
ja: { currency: "JPY", label: "日本語" }, // 新增
},
defaultLocale: "zh",
defaultCurrency: "CNY",
localeCookieName: "NEXT_LOCALE",
},创建翻译文件
在共享层和应用层分别新建对应的 JSON 文件:
# 共享翻译
touch packages/i18n/translations/shared/ja.json
# 应用覆盖(按需)
touch apps/01mvp-web/src/modules/i18n/translations/ja.json可以先从 en.json 复制一份再逐条翻译。
验证 next-intl 路由识别
项目的路由配置从 config.i18n.locales 读取 locale 列表(见 apps/01mvp-web/src/modules/i18n/routing.ts),无需额外修改路由配置。新增的 locale 会自动生效。
使用 LocaleSwitch 测试
页面右上角的语言切换器(LocaleSwitch 组件)会自动读取 config 中的 locale 列表。新增的语言会自动出现在下拉菜单中。切换后页面刷新即加载新语言。
添加翻译文案
在共享翻译文件中添加 key
翻译 key 使用嵌套结构按业务域分组,不使用扁平化 key:
{
"auth": {
"login": {
"title": "Sign In",
"submit": "Continue"
}
}
}在 packages/i18n/translations/shared/en.json 和 shared/zh.json 中同时添加对应的 key。
在组件中使用 useTranslations() hook
import { useTranslations } from "next-intl";
export function LoginForm() {
const t = useTranslations("auth.login");
return (
<div>
<h1>{t("title")}</h1>
<button>{t("submit")}</button>
</div>
);
}useTranslations("auth.login") 指定命名空间后,后续调用 t("title") 实际读取的是 auth.login.title。
运行检查命令验证覆盖
cd apps/01mvp-web && pnpm i18n:analyze该命令检查代码中使用的翻译 key 是否在所有语言文件中存在,缺失的 key 会输出为警告。
如果某个翻译 key 在目标语言中缺失,页面不会报错,而是回退显示 key 字符串本身(如 auth.login.title)。开发阶段很方便,但上线前务必用 pnpm i18n:analyze 检查。
翻译覆盖规则
当用户请求语言为 locale 时,最终翻译消息按以下顺序加载,后者覆盖前者:
- 共享默认语言 —
shared/{defaultLocale}.json(即shared/zh.json) - 共享目标语言 —
shared/{locale}.json(同名 key 覆盖上一步) - 应用默认语言 —
app/{defaultLocale}.json(覆盖共享层的同名 key) - 应用目标语言 —
app/{locale}.json(最高优先级)
对于非默认语言,如果目标语言缺少某个 key,系统会自动回退到默认语言的值。这意味着你只需要翻译有差异的 key。
在邮件模板中使用翻译
邮件模板的翻译 key 统一放在 mail 命名空间下。邮件发送时会自动加载对应 locale 的翻译:
// apps/01mvp-web/src/lib/mail/send.ts
import { getMessagesForLocale } from "@01mvp/i18n";在邮件模板中通过 messages.mail.* 访问翻译内容。邮件翻译的 key 格式示例:
{
"mail": {
"common": {
"useLink": "请使用以下链接:",
"openLinkInBrowser": "或复制以下链接到浏览器中打开:"
},
"magicLink": {
"body": "您请求了一次魔法链接登录。",
"login": "立即登录"
}
}
}邮件模板的翻译独立于 UI 翻译,使用 Proxy 实现优雅的 key 缺失回退——即使某个邮件翻译 key 不存在,也不会导致发送失败,而是显示 locale.mail.key 格式的提示文本。
LocaleSwitch 组件
LocaleSwitch 是一个下拉菜单组件,位于 packages/ui-shared/src/LocaleSwitch.tsx,提供语言切换功能。
工作原理:
- 自动读取
config.i18n.locales中定义的语言列表 - 使用
useLocale()hook 获取当前语言 - 切换时调用
onLocaleChange回调(用于更新 locale cookie),然后刷新页面
放置位置:通常放在页面头部导航栏中。如果应用使用了 ConfigContext 提供配置,LocaleSwitch 会自动读取可用语言列表,无需额外传参。
import { LocaleSwitch } from "@01mvp/ui-shared";
export function Header() {
return (
<nav>
{/* 其他导航内容 */}
<LocaleSwitch />
</nav>
);
}常见问题
相关资源
- 国际化策略(概念) — 分层模型与合并策略的详细说明
- @01mvp/i18n 参考 — 包的 API 与导出说明
- next-intl 官方文档