首页
/ LobeHub 国际化实战:基于 react-i18next 的扁平 Key 命名与自动化翻译流水线

LobeHub 国际化实战:基于 react-i18next 的扁平 Key 命名与自动化翻译流水线

2026-09-06 17:59:59作者:彭桢灵Jeremy

LobeHub 是一套面向 7×24 小时 Agent 运营平台的多语言体系,采用 react-i18next 作为框架,默认语言为英语(en-US)。本文基于仓库中的 i18n 技能文档 完整展开,并结合 .i18nrc.jsscripts/i18nWorkflow/auto-i18n.yml 等真实实现,讲清 Key 命名规范、组件用法、新增文案的五步工作流,以及“默认 locale 生成 + LLM 自动翻译 + 每日定时 PR”的完整流水线,读完即可在 LobeHub 中规范地添加、维护与发布国际化文案。

一、目录职责:单一事实源与生成产物分离

LobeHub 的 i18n 目录结构有一条铁律:只能编辑 packages/locales/src/default/ 下的文件,绝不要直接修改 locales/ 下的 JSON 文件(手工维护的 en-US/zh-CN 预览文案除外)。这一约束在 SKILL.md 中被明确标注,其工程含义是:

  • packages/locales/src/default/{namespace}.ts:中文默认 locale(源语言),开发者手写,是所有文案的单一事实源(source of truth)
  • locales/{locale}/{namespace}.json:由 lobe-i18n 翻译工具生成(或每日自动工作流更新),属于生成产物,直接手改会被下次翻译覆盖;
  • locales/en-US/locales/zh-CN/ 允许手工写入,用作开发阶段的即时预览。

packages/locales/src/default/index.ts 可以看到,每个 namespace 都是一个独立的 TS 模块(import chat from './chat'),最终聚合为 resources 常量导出。当前仓库实际注册了 50 余个 namespace,包括 agentagentGroupauthchatcolorcommoncomponentsdesktop-onboardingdevicediscovereditorelectronerrorevalfilehomehotkeyimageknowledgeBaselabsmarketAuthmemorymessengermetadatamigrationmodelProvidermodelRuntimemodelsnotificationoauthonboardingopStatusTrayopenInApppageSharepluginportalprojectprovidersragEvalselfLearningsettingspendsubscriptionsuggestQuestionsthreadtooltopicuiverifyvideowelcome 等——可以看到命名上允许连字符(desktop-onboarding)与驼峰(knowledgeBase)两种风格,导出时以字符串键对齐('desktop-onboarding': desktopOnboarding)。

框架依赖方面,根 package.json 中声明了 react-i18next ^16.6.6i18next ^25.10.10i18next-browser-languagedetectori18next-resources-to-backend,翻译 CLI 为 @lobehub/i18n-cli ^1.27.0

二、Key 命名规范:扁平点号键而非嵌套对象

LobeHub 的文案 Key 统一采用扁平的点号命名(flat keys with dot notation),而不是嵌套对象结构:

// ✅ 正确:扁平 key
export default {
  'alert.cloud.action': '立即体验',
  'sync.actions.sync': '立即同步',
  'sync.status.ready': '已连接',
};

// ❌ 避免:嵌套对象
export default {
  alert: { cloud: { action: '...' } },
};

命名模式为 {feature}.{context}.{action|status},即「功能.上下文.动作/状态」三段式,例如 alert.cloud.action 表示「alert 功能下 cloud 上下文中的操作按钮」。扁平结构的工程收益是:Key 即字符串路径,可被脚本按行扫描、按前缀分组统计(scripts/i18nWorkflow/ 中的 flattenLocaleKeys.tsanalyzeUnusedKeys.ts 等工具正是基于这种可扁平化的结构做未使用 Key 分析),也便于 i18next 按命名空间字符串精确寻址。

插值参数使用 {{variableName}} 语法:

'alert.cloud.desc': '我们提供 {{credit}} 额度积分',

避免 Key 冲突

扁平键还有一个必须遵守的约束:不要让一个 Key 恰好成为另一个 Key 的前缀,否则在工具按前缀聚合、或按段拆分时会产生歧义冲突:

// ❌ 冲突:solve 与 solve.backup.title 前缀重叠
'clientDB.solve': '自助解决',
'clientDB.solve.backup.title': '数据备份',

// ✅ 解法:给短 Key 补一层语义(action)
'clientDB.solve.action': '自助解决',
'clientDB.solve.backup.title': '数据备份',

规则可以概括为:当一个 Key 需要“拥有子 Key”时,必须给它换一个不与子路径重合的名字,通常补 .action.label.title 之类的末端语义段。

三、组件中使用:useTranslation、命名空间与插值

组件层的调用方式完全遵循 react-i18next 的标准 API:

import { useTranslation } from 'react-i18next';

const { t } = useTranslation('common');

t('newFeature.title');
t('alert.cloud.desc', { credit: '1000' });

// 多命名空间
const { t } = useTranslation(['common', 'chat']);
t('common:save');

要点:

  1. useTranslation('common') 传入单个命名空间后,t('newFeature.title') 直接以 Key 寻址;
  2. 插值以第二个参数对象传入,{{credit}} 被替换为 1000
  3. 需要跨命名空间时传入数组 ['common', 'chat'],此时必须用 命名空间:Key 前缀语法(t('common:save'))显式指定来源。

使用频率最高的三个命名空间是 common(共享 UI 文案)、chat(聊天功能)与 setting(设置页),绝大多数通用按钮、加载态、错误提示都应优先放入 common,避免在业务 namespace 中重复定义。

四、新增文案的五步工作流

向 LobeHub 添加一条新文案的完整流程(对应 SKILL.md 的 Workflow 章节):

  1. 添加 Key:在 packages/locales/src/default/{namespace}.ts 中新增条目,遵循扁平命名与插值规范;
  2. 导出命名空间:如果是新建的 namespace 文件,必须在 packages/locales/src/default/index.ts 中 import 并加入 resources 对象(注意连字符命名要用字符串键,参考 'desktop-onboarding': desktopOnboarding 的写法);
  3. 开发预览:手工翻译 locales/zh-CN/{namespace}.jsonlocales/en-US/{namespace}.json,保证本地开发时两种语言可见;
  4. 其余语言交给自动化:由 .github/workflows/auto-i18n.yml 每日定时翻译并自动开 PR,开发者无需手工处理其他 15 种语言;
  5. 立即需要时才手动跑bun run i18n 仅在分支必须立即拿到全量翻译时使用——该命令执行缓慢,且依赖 OPENAI_API_KEY

第 5 步背后的实现,可以从 package.json 的 scripts 拆解出来:

"i18n": "npm run workflow:i18n && lobe-i18n && prettier -c --write \"locales/**\"",
"workflow:i18n": "tsx ./scripts/i18nWorkflow/index.ts",
"i18n:unused": "tsx ./scripts/i18nWorkflow/analyzeUnusedKeys.ts",
"i18n:unused-clean": "tsx ./scripts/i18nWorkflow/cleanUnusedKeys.ts",
"docs:i18n": "lobe-i18n md && npm run lint:mdx"

bun run i18n 实际是三段流水线:本地预处理脚本 → lobe-i18n LLM 翻译 → prettier 格式化 locales 产物。预处理入口 scripts/i18nWorkflow/index.ts 依次执行 genDiff()(DIFF ANALYSIS,对比当前 locale 与默认 locale 的差异,确定需要翻译的增量 Key)和 genDefaultLocale()(GENERATE DEFAULT LOCALE,把 packages/locales/src/default/ 的 TS 源同步为默认 locale 基准文件),同目录还有 flattenLocaleKeys.tsprotectedPatterns.tsi18nConfig.ts 等配套模块,其中 i18nConfig.ts 负责加载根目录的 .i18nrc 配置。

五、翻译配置解析:.i18nrc.js 中的语言矩阵与 LLM 参数

翻译行为由 .i18nrc.js 定义,这是 @lobehub/i18n-cli 的入口配置,核心参数如下:

参数 取值 含义
entry / entryLocale locales/en-US / en-US 翻译基准入口与基准语言
output locales 生成产物输出目录
outputLocales arbg-BGzh-CNzh-TWru-RUja-JPko-KRfr-FRtr-TRes-ESpt-BRde-DEit-ITnl-NLpl-PLvi-VNfa-IR 共 17 个目标语言,与仓库根 locales/ 下的目录一一对应
modelName gpt-4o 翻译所用的 LLM 模型
temperature 0 零温度采样,保证翻译输出稳定可复现
saveImmediately true 每完成即落盘,避免长任务中断丢失进度
experimental.jsonMode true 以 JSON 模式约束模型输出,防止格式污染 locale 文件

配置中还有一个独立的 markdown 段,负责文档与 README 的翻译(对应 docs:i18n 脚本):

  • entry 覆盖 ./README.md./docs/**/*.md./docs/**/*.mdx,基准语言 en-US,目标语言仅 zh-CN
  • reference 注入 docs/glossary.mdx 作为术语表,并要求“保持 mdx 组件格式、输出不包裹代码块”,保证专有名词与组件标记在翻译中一致;
  • exclude 排除已有的 README.zh-CN.md*.zh-CN.mdx 产物,防止自我翻译;
  • outputExtensions 自定义产物后缀:mdx 文件翻译为 .{locale}.mdx(如 start.zh-CN.mdx),普通 md 翻译为 .{locale}.md——这正是 docs/changelog/ 中成对出现 *.mdx / *.zh-CN.mdx 的由来。

六、每日自动翻译工作流:auto-i18n.yml

.github/workflows/auto-i18n.yml 是整套体系的“兜底引擎”,其关键设定:

  • 触发cron: '0 0 * * *' 每日 0 点定时执行,另支持 workflow_dispatch 手动触发;
  • 执行bun run i18n,注入 OPENAI_API_KEYOPENAI_PROXY_URL 两个 secrets,任务超时 30 分钟;
  • 产物:通过 peter-evans/create-pull-request@v7 仅提交 locales/**/*.json 的变更,自动创建 style/auto-i18n 分支 PR,标题为 🤖 style: update i18n,打上 i18n / automated / style 标签并在合并后删除分支;
  • 可追溯:PR 描述内嵌 i18n_update.log 执行日志,便于审阅本次翻译覆盖了哪些 Key。

这也解释了 SKILL 文档中“Leave all other locales to the daily workflow”这句话的完整语义:开发者只需要维护默认 locale,其余 17 种语言的 JSON 由该工作流每日增量翻译并以独立 PR 合入,把翻译成本从“每次发版阻塞项”变成了“异步流水线”。

七、未使用 Key 的治理闭环

扁平 Key 带来的可扫描性还支撑了文案瘦身。package.json 提供两个配套命令:

对一个 50+ 命名空间、每种语言 50+ 个 JSON 文件的仓库而言,这套“分析—清理”闭环能有效控制翻译体量(翻译费用与文件体积都与 Key 数量线性相关),与 protectedPatterns.ts 等防护配置共同避免误删动态拼接 Key。

八、实践要点总结

  1. 只写默认 locale:新文案一律加到 packages/locales/src/default/{namespace}.ts,新 namespace 必须同步登记到 packages/locales/src/default/index.tsresources
  2. 扁平点号 Key:遵循 {feature}.{context}.{action|status},插值用 {{var}},子 Key 前缀冲突时给短 Key 补语义段;
  3. 组件内用 useTranslation:单命名空间直接寻址,多命名空间用 'common:save' 前缀语法;
  4. 翻译走流水线:日常开发只手工补 locales/zh-CNlocales/en-US 两个预览文件;合入 main 后由每日 auto-i18n.yml 自动开翻译 PR;仅在紧急场景手动 bun run i18n(需 OPENAI_API_KEY,耗时较长);
  5. 文档翻译独立成线:README 与 docs 通过 docs:i18nlobe-i18n md)翻译,术语以 docs/glossary.mdx 为参照,产物带 .zh-CN 后缀与源文件成对存在;
  6. 定期瘦身:用 i18n:unused 分析、i18n:unused-clean 清理废弃 Key,维持 17 种语言 × 50+ 命名空间的维护成本可控。

按以上规范操作,即可在 LobeHub 的多语言体系中完成从“写一条 Key”到“全语言自动上线”的完整闭环。

登录后查看全文
热门项目推荐
相关项目推荐