核心概念

国际化策略

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 的全量文案,只维护差异部分,否则后续更新翻译会变成噩梦。

多项目适用策略

  1. 多项目共用的 UI 文案放 packages/i18n/translations/shared
  2. 项目品牌词、业务术语放各自 app 的 translations
  3. 新项目接入时新增 apps/<new-app>/src/modules/i18n/messages.ts 与两份 locale 覆盖文件

MDX 文档国际化

Fumadocs 根据当前语言自动选择 page.zh.mdxpage.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