国际化策略
Monorepo 下的共享翻译与应用覆盖策略
本页解释国际化分层和覆盖策略;如果你需要查看 helper、导出和底层实现,再看 @01mvp/i18n 参考。
概览
项目使用 next-intl,并采用「共享翻译 + 应用覆盖」两层模型,适配 monorepo 多项目场景。
目录结构
packages/i18n/
├── src/messages.ts # 核心加载与合并逻辑
└── translations/shared/
├── en.json # 共享英文翻译
└── zh.json # 共享中文翻译
apps/01mvp-web/src/modules/i18n/
├── messages.ts # 01mvp-web 的 i18n 入口
└── translations/
├── en.json # 01mvp-web 私有覆盖
└── zh.json # 01mvp-web 私有覆盖合并与优先级
请求语言为 locale 时,最终消息按以下顺序合并(后者覆盖前者):
加载共享默认语言 -- shared/defaultLocale
加载共享目标语言 -- shared/locale(覆盖上一步的同名 key)
加载应用默认语言 -- app/defaultLocale(覆盖共享层的同名 key)
加载应用目标语言 -- app/locale(最高优先级)
缺失的翻译 key 会回退到 key 字符串本身(比如 documentation.title),不会中断页面渲染。这在开发阶段很方便,但上线前记得用 pnpm i18n:check 检查有没有漏翻。
Key 规范
翻译 key 必须使用嵌套结构按业务域分组,不要用扁平化 key(如 "loginTitle")。嵌套结构方便按模块管理和查找。
使用嵌套对象按业务域分组,不使用扁平化 key:
{
"auth": { "login": { "title": "登录" } },
"documentation": { "title": "文档" }
}组件调用:const t = useTranslations("documentation"); return <p>{t("title")}</p>;
新增翻译流程
在 shared/en.json(defaultLocale)增加新 key
在 shared/zh.json 补充对应翻译
如果某个 app 需要特殊文案,在 apps/<app>/src/modules/i18n/translations/*.json 里只覆盖对应节点
不要在 app 层重复复制 shared 的全量文案,只维护差异部分,否则后续更新翻译会变成噩梦。
多项目适用策略
- 多项目共用的 UI 文案放
packages/i18n/translations/shared - 项目品牌词、业务术语放各自 app 的
translations - 新项目接入时新增
apps/<new-app>/src/modules/i18n/messages.ts与两份 locale 覆盖文件
MDX 文档国际化
Fumadocs 根据当前语言自动选择 page.zh.mdx 或 page.en.mdx。
校验命令
在 apps/01mvp-web 下执行:pnpm i18n:check(检查代码中使用的 key 是否存在)、pnpm i18n:check:strict(严格模式,适合 CI)。
推荐工作流
开发时:跑 pnpm i18n:check,保证不会缺词
提交前或 CI:跑 pnpm i18n:check:strict,防止 key 漂移
发现缺失后:优先补 shared/en.json,再补 shared/zh.json