OmniRoute 国际化(i18n)工程指南:30+ 语言的 UI 与文档翻译、增量管线与自动化 QA
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
本指南基于 OmniRoute 仓库的 i18n 工具链文档(docs/i18n/cs/docs/guides/I18N.md 及其英文主文档 docs/guides/I18N.md),系统梳理 OmniRoute 的多语言体系:从next-intl驱动的仪表盘 UI 翻译、CLI 独立 i18n 层,到基于 LLM 与 Google Translate 的文档自动翻译管线,再到覆盖键位校验、术语一致性、视觉回归的完整 QA 工具链。读完本文,你将掌握如何为 OmniRoute 添加一门新语言、运行增量翻译、校验翻译质量,以及在 CI 中建立防漂移门禁。
快速参考:i18n 常用命令一览
| 任务 | 命令 |
|---|---|
| 增量翻译文档(推荐,基于哈希) | npm run i18n:run |
| 仅翻译单个语言 | npm run i18n:run -- --locale=pt-BR |
| 翻译指定文件 | npm run i18n:run -- --files=CLAUDE.md,docs/architecture/ARCHITECTURE.md |
| 强制全量重译(昂贵) | npm run i18n:run -- --force |
| 预演(不调用 API、不写入) | npm run i18n:run:dry |
| CI 漂移门禁 | npm run i18n:check |
| 一键新增语言(配置→文档→CLI→站点) | npm run i18n:add-locale -- --code=el … |
| 翻译 UI 字符串 | npm run i18n:sync-ui -- --translate-markers --batch-size=40 |
| 校验单个语言环境 | python3 scripts/i18n/validate_translation.py quick -l cs |
| 检查代码内键位 | python3 scripts/i18n/check_translations.py |
| 生成静态 QA 报告 | node scripts/i18n/generate-qa-checklist.mjs |
| Playwright 视觉 QA | node scripts/i18n/run-visual-qa.mjs |
架构概览
数据源(Source of Truth)
OmniRoute 的 i18n 体系分为三块,各有明确的唯一权威:
- UI 字符串:
src/i18n/messages/en.json(英文源,约 2800 个键) - 语言环境文件:
src/i18n/messages/{locale}.json - 框架:
next-intl,基于 Cookie 的运行时语言解析 - 语言注册表:
config/i18n.json—— 这是唯一声明语言的地方,定义了全部 locale、RTL 集合、别名与排除项;src/i18n/config.ts只是它的类型化适配层(源码注释明确要求"不要在此手维护语言列表")
config/i18n.json的顶层结构(config/i18n.json):
{ "$schema": "./i18n-schema.json", "default": "en", "rtl": ["ar", "fa", "he", "ur"], "uiOnly": ["en"], "docsExcluded": ["en"], "locales": [ { "code": "cs", "label": "CS", "name": "Čeština", "flag": "🇨🇿", ... } ] }关键字段说明:
default:默认语言(en)rtl:从右向左排版的语言集合,当前为ar、fa、he、uruiOnly/docsExcluded:仅 UI 使用或从文档翻译中排除的语言locales[]:每个条目的code、label、name、native、english、flag,以及可选aliases(旧代码别名,如id声明"aliases": ["in"],zh-TW声明["zh-hk", "zh-mo", "zh-hant"])
运行时语言解析流程
仪表盘侧由src/i18n/request.ts驱动(request.ts),完整流程如下:
- 用户选择语言 → 写入
NEXT_LOCALECookie - 请求时先读
NEXT_LOCALECookie,为空则读x-locale请求头,再交给resolveRequestedLocale解析 - 解析规则(resolveRequestedLocale.ts):精确匹配 → 大小写不敏感匹配 → 别名匹配(如
in→id、uk→uk-UA)→ 回退en - 动态 import 加载
messages/{locale}.json - 组件通过
useTranslations("namespace")与t("key")消费
值得注意的细节:request.ts实现了EN 兜底合并。当活跃语言不是英文时,会通过deepMergeFallback深度合并缺失键,并用__MISSING__:前缀哨兵(由scripts/i18n/sync-ui-keys.mjs写入)识别"未翻译"的占位值,从而让英文原文优先兜底(request.ts)。同时还会对新加入的命名空间做整命名空间级别的浅合并,保证新增 UI 在翻译落地前始终以英文显示。
支持的语言环境
当前仓库的 config/i18n.json 共注册 42 个语言环境(含en)。下面按文档表格结构列出全部语言、RTL 标志与 Google Translate 代码:
| 代码 | 语言 | RTL | Google Translate 代码 |
|---|---|---|---|
ar | العربية | Yes | ar |
az | Azərbaycan dili | No | az |
bg | Български | No | bg |
bn | বাংলা | No | bn |
cs | Čeština | No | cs |
da | Dansk | No | da |
de | Deutsch | No | de |
en | English | No | en |
es | Español | No | es |
fa | فارسی | Yes | fa |
fi | Suomi | No | fi |
fr | Français | No | fr |
gu | ગુજરાતી | No | gu |
he | עברית | Yes | iw |
hi | हिन्दी | No | hi |
hu | Magyar | No | hu |
id | Bahasa Indonesia | No | id(别名in) |
it | Italiano | No | it |
ja | 日本語 | No | ja |
ko | 한국어 | No | ko |
mr | मराठी | No | mr |
ms | Bahasa Melayu | No | ms |
nl | Nederlands | No | nl |
no | Norsk | No | no |
phi | Filipino | No | tl(别名fil) |
pl | Polski | No | pl |
pt | Português (Portugal) | No | pt |
pt-BR | Português (Brasil) | No | pt |
ro | Română | No | ro |
ru | Русский | No | ru |
sk | Slovenčina | No | sk |
sv | Svenska | No | sv |
sw | Kiswahili | No | sw |
ta | தமிழ் | No | ta |
te | తెలుగు | No | te |
th | ไทย | No | th |
tr | Türkçe | No | tr |
uk-UA | Українська | No | uk(别名uk) |
ur | اردو | Yes | ur |
vi | Tiếng Việt | No | vi |
zh-CN | 中文 (简体) | No | zh-CN |
zh-TW | 中文 (繁體) | No | zh-TW(别名zh-hk等) |
注:早期版本文档(如 docs/i18n/cs/docs/guides/I18N.md)记录为 30 种语言;当前仓库的
config/i18n.json已扩展至 42 个语言环境,且fa、ur也加入了 RTL 集合。
添加一门新语言
推荐方式:一键add-locale.mjs
从当前仓库的英文主文档看,新增语言已收敛为一条命令(scripts/i18n/add-locale.mjs),会同时更新配置、国旗、仪表盘目录、文档镜像、CLI 目录、README 与索引、语言切换栏,并可选更新站点:
# 需要 .env 中配置 OMNIROUTE_TRANSLATION_API_URL / _API_KEY / _MODEL node scripts/i18n/add-locale.mjs --code=el --english=Greek --native=Ελληνικά --flag=🇬🇷 # 印度语言共用 in.svg 国旗文件 node scripts/i18n/add-locale.mjs --code=kn --english=Kannada --native=ಕನ್ನಡ --flag=🇮🇳 --flag-file=in.svg # 仅预演,不写入 node scripts/i18n/add-locale.mjs --code=el --english=Greek --native=Ελληνικά --flag=🇬🇷 --dry-run新增后执行校验:
node --import tsx/esm --test tests/unit/i18n-locale-surfaces-parity.test.ts npm run i18n:check-ui-coverage && npm run i18n:check-ratio && npm run check:docs-all && npm run check:cli-i18n对应的单元测试 tests/unit/i18n-locale-surfaces-parity.test.ts 双向守护"语言表面一致性":配置中声明的文档语言必须有一行索引记录,索引中的每一行必须能映射回配置。
传统手动步骤(旧流程,理解用)
早期文档记录的手动流程(在generate-multilang.mjs时代)包含以下步骤,仍可作为理解内部机制参考:
- 注册语言:在
src/i18n/config.ts的LOCALES数组添加"xx",在LANGUAGES数组添加{ code: "xx", label: "XX", name: "Language Name", flag: "🏳️" } - 加入生成器:在
scripts/i18n/generate-multilang.mjs的LOCALE_SPECS添加条目(含code、googleTl、label、flag、languageName、readmeName、docsName) - 生成初始翻译:
node scripts/i18n/generate-multilang.mjs messages,从en.json经 Google Translate 生成src/i18n/messages/xx.json - 人工复核:自动翻译只是起点,重点检查技术准确性、语境术语、占位符(
{count}、{value}等)是否妥善处理 - 校验:
python3 scripts/i18n/validate_translation.py quick -l xx与python3 scripts/i18n/validate_translation.py diff common -l xx - 生成翻译文档:
node scripts/i18n/generate-multilang.mjs docs
重要提醒:
generate-multilang.mjs(Google Translate 引擎)在 docs/guides/I18N.md 中已被标记为弃用(计划 v3.10 移除),新流程一律走基于 LLM 的npm run i18n:run。仅readme模式(根 README 多语言变体)暂时没有替代方案。
自动翻译管线
推荐管线:run-translation.mjs(哈希驱动的增量翻译)
当前主推的是 scripts/i18n/run-translation.mjs,它对文档做增量、确定性翻译,背后是任意 OpenAI 兼容的 LLM 端点(典型如 OmniRoute Cloud 的cx/gpt-5.4-mini):
# 增量运行——只动有变化的源文件 npm run i18n:run # 限定单一语言 npm run i18n:run -- --locale=pt-BR # 指定文件(仓库相对路径,逗号分隔) npm run i18n:run -- --files=CLAUDE.md,docs/architecture/ARCHITECTURE.md # 强制全量重译 npm run i18n:run -- --force # 预演 npm run i18n:run:dry # CI 门禁——状态漂移时以非零码退出 npm run i18n:check状态重建(re-bootstrap):npm run i18n:run -- --adopt可从磁盘上已有的镜像重建.i18n-state.json,全程不调用 API、不写任何.md。适用于"源文件改动但无需重译"(如链接列表、计数)或状态文件丢失的场景;可用--files=…/--locale=…限定子集,加--dry-run预演。--adopt --targets-only则只重新哈希磁盘上的镜像文件,保留全部source_hash,让机械性的镜像重写不再被误报为目标漂移。
后端配置(run-translation.mjs 与 docs/guides/I18N.md 中的环境变量表):
| 环境变量 | 用途 | 默认值 |
|---|---|---|
OMNIROUTE_TRANSLATION_API_URL | OpenAI 兼容 base URL(如…/v1) | 必填 |
OMNIROUTE_TRANSLATION_API_KEY | Bearer token(不写入日志) | 必填 |
OMNIROUTE_TRANSLATION_MODEL | 模型 ID,如cx/gpt-5.4-mini | 必填 |
OMNIROUTE_TRANSLATION_TIMEOUT_MS | 单请求超时 | 60000 |
OMNIROUTE_TRANSLATION_CONCURRENCY | 并发请求数 | 4 |
脚本内置轻量.env加载器(不依赖 dotenv),已设置的进程环境变量优先。调用 LLM 时使用temperature: 0.15、非流式/chat/completions,对 408/429/5xx 与超时/网络错误各重试一次。
状态跟踪:.i18n-state.json(已提交)按"源文件 + 每个语言"记录 SHA-256 哈希。i18n:check(check-translation-drift.mjs)只做状态与磁盘的确定性对比,不调用任何 API,严格模式任一漂移即退出码 1,可加--warn(仅告警,退出 0)或--json(机器可读报告)。
输出形态:每个翻译文件生成顶部# <heading>(<native>)、🌐 Languages: …语言栏、---分隔线,再跟翻译正文——这与 scripts/check/check-docs-sync.mjs 对llm.txt、CHANGELOG.md镜像的校验规则完全一致。翻译前先用 Prettier 的 markdown 解析器格式化,保证落盘内容与 lint-staged 产物一致,避免哈希漂移。
其他内部机制(可从 run-translation.mjs 源码验证):
- 分块:正文超过 6000 字符时按顶层
##标题拆块,逐块翻译再以空行拼接 - 术语归一:译文产出后调用
normalizeLocaleText(glossary-normalize.mjs)按语言环境的规范术语表做后处理,修正模型"会转字不会转词"的问题(如 zh-TW 输出被大陆用词污染) - 系统提示词:要求保留所有 Markdown 语法、不翻译源码/URL/路径/命令/环境变量名,只输出译文本身
遗留引擎一:generate-multilang.mjs(Google Translate)
主自动翻译引擎(已弃用)——使用 Google Translate 免费 API 生成 UI 字符串、README 与文档翻译:
node scripts/i18n/generate-multilang.mjs [messages|readme|docs|all]| 模式 | 行为 |
|---|---|
messages | 从en.json翻译src/i18n/messages/{locale}.json中缺失的键 |
readme | 把README.md翻译为根目录的README.{code}.md |
docs | 把DOC_SOURCE_FILES翻译到docs/i18n/{locale}/{docName} |
all | 依次执行以上三种模式 |
实现特性:
- 文本保护:翻译前遮蔽代码块、行内代码、Markdown 链接/图片(
text)、HTML 标签、表格与 ICU 占位符({count}、{value}、{total}等),翻译后还原 - 分块批处理:用
__OMNIROUTE_I18N_SEPARATOR__分隔符拼接多条字符串以减少 API 调用(单请求上限 1800 字符) - 内存缓存:会话内重复字符串不重复调用
- 重试逻辑:对 429/5xx 指数退避,最多 5 次(300ms × 尝试序号)
- 超时:单请求 20 秒
- 跳过已有文件:目标文件已存在则不覆盖
需要注意的行为:docs/i18n/README.md每次运行会被整体重新生成(它是自动生成的语言索引);根目录README.{code}.md仅在不存在时创建(跳过EXISTING_README_CODES中已有的语言);语言栏(🌐 **Languages:** ...)会自动插入/更新到所有翻译文档。
遗留引擎二:i18n_autotranslate.py(LLM 文档翻译)
次级翻译器(已弃用)——使用任意 OpenAI 兼容 LLM API(包括 OmniRoute 自身)翻译docs/i18n/下的 Markdown 文件,质量优于 Google Translate,适合润色或重译:
python3 scripts/i18n/i18n_autotranslate.py \ --api-url http://localhost:20128/v1 \ --api-key sk-your-key \ --model gpt-4o特性:扫描docs/i18n/中的英文段落;跳过代码块、表格与已翻译内容;以技术翻译系统提示词驱动;支持全部语言。
CLI 的独立 i18n 层
除 Next.js 仪表盘外,omnirouteCLI 拥有独立的 i18n 层(bin/cli/i18n.mjs、bin/cli/locales/),与仪表盘共享config/i18n.json作为语言权威:
- 所有用户可见字符串经
t("module.key", vars)输出 - 目录文件随包发布 42 个语言环境
- 任何缺失键回退到
en,因此部分翻译也是合法的
语言选择优先级
| 优先级 | 来源 | 示例 |
|---|---|---|
| 1 | --lang参数 | omniroute --lang de status |
| 2 | OMNIROUTE_LANG环境变量 | OMNIROUTE_LANG=ja omniroute providers |
| 3 | LC_ALL系统环境 | 按终端语言自动检测 |
| 4 | LC_MESSAGES系统环境 | 按终端语言自动检测 |
| 5 | LANG系统环境 | 按终端语言自动检测 |
| 6 | 回退 | en |
下划线形式(pt_BR)会被规范化为连字符形式(pt-BR);语言代码用/^[a-zA-Z0-9-]+$/校验,拒绝路径穿越。
持久化语言偏好与一次性覆盖
# 设置语言并保存到 ~/.omniroute/.env(跨会话持久) omniroute config lang set pt-BR # 查看当前语言 omniroute config lang get # 列出全部可用语言 omniroute config lang list omniroute config lang list --output json # 仅当前命令生效的一次性覆盖(不写环境文件) omniroute --lang de providers list偏好以原子方式写入~/.omniroute/.env,CLI 引导阶段在任何命令运行前加载。新增 CLI 语言环境的流程为:在config/i18n.json添加条目 → 运行node bin/cli/scripts/generate-locales.mjs生成语言文件 → 翻译键(或留空{}走 en 兜底脚手架)→ PR 至少保证en.json与pt-BR.json的字符串齐全。
验证与 QA 工具链
validate_translation.py:翻译校验器
将任意语言环境 JSON 与en.json对比并报告问题:
# 快速检查(仅计数) python3 scripts/i18n/validate_translation.py quick -l cs # 输出示例: # Missing: 0 # Untranslated: 0 # Ignored (UNTRANSLATABLE_KEYS): 236 # 按类别查看详细差异 python3 scripts/i18n/validate_translation.py diff common -l cs python3 scripts/i18n/validate_translation.py diff settings -l cs # 导出 CSV python3 scripts/i18n/validate_translation.py csv -l cs > report.csv # 导出 Markdown python3 scripts/i18n/validate_translation.py md -l cs > report.md # 完整报告(默认) python3 scripts/i18n/validate_translation.py -l cs检测四类问题:
- 缺失键:存在于
en.json但不在语言文件中 - 多余键:存在于语言文件但不在
en.json中 - 未翻译键:语言文件值与英文源相同(排除 allowlist)
- 占位符不匹配:ICU 占位符在源与译文之间不一致
退出码约定:
| 退出码 | 含义 |
|---|---|
| 0 | 正常 |
| 1 | 通用错误 |
| 2 | 缺失字符串(硬错误) |
| 3 | 未翻译警告(软性) |
环境变量:设置TRANSLATION_LANG=cs或使用-l cs参数。
check_translations.py:代码到 JSON 的键位检查
扫描src/**/*.tsx与src/**/*.ts中的useTranslations()调用,验证所有引用的键都存在于en.json:
python3 scripts/i18n/check_translations.py # 基本检查 python3 scripts/i18n/check_translations.py --verbose # 详细输出 python3 scripts/i18n/check_translations.py --fix # 自动补缺失键到 en.jsongenerate-qa-checklist.mjs:静态分析 QA
扫描 Next.js 页面文件,输出 i18n 风险度量的 Markdown 报告:
node scripts/i18n/generate-qa-checklist.mjs检查项:固定宽度 class 使用(溢出风险)、方向性 left/right class(RTL 风险)、易裁剪模式、与en.json的语言对齐(缺失/多余键)、重点语言(es、fr、de、ja、ar)README 的语言选择栏。输出到docs/reports/i18n-qa-checklist-{date}.md。
run-visual-qa.mjs:Playwright 视觉回归
对多个语言与视口下的全部仪表盘路由截图并评估页面健康:
# 默认:es, fr, de, ja, ar(英文主文档当前含 zh-CN)@ localhost:20128 node scripts/i18n/run-visual-qa.mjs # 自定义 base URL 与语言 QA_BASE_URL=http://staging.example.com QA_LOCALES=de,fr node scripts/i18n/run-visual-qa.mjs # 自定义路由 QA_ROUTES=/dashboard/settings,/dashboard/providers node scripts/i18n/run-visual-qa.mjs检测:文本溢出、元素裁剪、RTL 布局错位。输出docs/reports/i18n-visual-qa-{date}.md及 JSON 报告。
术语表:超越键位对齐的语义一致性
键位对齐(check-ui-keys-coverage.mjs)与 ICU 合法性(validate_translation.py)无法发现语义漂移——例如同一个英文概念 "provider" 在中文里被分别译成"提供商"与"提供者"。仓库由此增加了按语言维护的术语层(docs/guides/I18N.md 中的 Terminology Glossary 章节,源自 issue #8038,从 zh-CN 起步)。
术语表文件
- scripts/i18n/glossary/zh-CN.json(另有 zh-TW、ko):版本化的常见概念表(provider、connection、routing、fallback、quota、context window、reasoning、tool call、cache、circuit breaker 等)。每个概念有
canonical规范译法与可选synonyms列表;目录中发现任何同义词即标记为漂移。synonyms为空数组表示"已记录但暂不强制"(目录中仍存在合法混用,规范化是后续工作) - scripts/i18n/glossary/protected-terms.json:产品/厂商/模型/协议/CLI/环境变量/标识符名称(
OmniRoute、OAuth、MCP、A2A、DATA_DIR等)必须原样出现在任何译文值中。它按概念(concept)而非键路径(key path)检查,粒度与untranslatable-keys.json(整键排除)不同
校验入口
# 默认 zh-CN npm run i18n:check-glossary # 指定语言、JSON 报告或非失败报告模式 node scripts/i18n/check-glossary-consistency.mjs --locale=zh-CN node scripts/i18n/check-glossary-consistency.mjs --locale=zh-CN --json node scripts/i18n/check-glossary-consistency.mjs --locale=zh-CN --report核心函数checkGlossaryConsistency(localeMessages, glossary, protectedTerms)返回{ violations: [...] }:非规范术语产生glossary-synonym违规,被改写的受保护名称产生protected-term-altered违规。已接入 CI 的i18n-glossary-zhcn作业(.github/workflows/ci.yml)。
管理不可翻译键(untranslatable-keys.json)
文件位置:scripts/i18n/untranslatable-keys.json(当前约 236 个键)。
允许保持与英文源完全一致的键名单,用于避免validate_translation.py产生"未翻译"的误报:
{ "description": "Keys that should remain untranslated...", "keys": [ "common.model", "common.oauth", "health.cpu" ] }适合放入此名单的键类型:
- 品牌/产品名:
landing.brandName、common.social-github - 技术术语/缩写:
health.cpu、mcpDashboard.pid、settings.ai - ICU/格式化字符串:
apiManager.modelsCount、health.millisecondsShort - 占位符值:
providers.openaiBaseUrlPlaceholder、cliTools.baseUrlPlaceholder - 协议名:
common.http、common.oauth、providers.oauth2Label - 导航分区:
sidebar.primarySection、sidebar.cliSection
新增键只需编辑keys数组并重新运行校验。历史上这份名单从validate_translation.py内联的 Python set 迁移到外部 JSON 文件,便于维护,校验器在运行时加载。
CI 集成
CI 流水线(.github/workflows/ci.yml)在每次 push 与 PR 上守护全部语言:
i18n-matrix作业:动态发现全部语言文件(排除en.json)i18n作业:对每个语言并行运行validate_translation.py quick -l '<lang>'ci-summary作业:汇总结果到流水线摘要
# i18n-matrix:发现语言 LANGS=$(ls src/i18n/messages/*.json | xargs -n1 basename | sed 's/.json$//' | grep -v '^en$') # i18n:校验每种语言 python3 scripts/i18n/validate_translation.py quick -l '${{ matrix.lang }}'摘要输出示例:
## 🌍 Translations | Metric | Value | |--------|------| | Languages checked | 30 | | Total untranslated | 0 | ✅ All translations complete此外还有多条专职 i18n 作业(见 .github/workflows/ci.yml):i18n-ui-coverage(键位覆盖率,阈值 65,跳过草稿 PR)、i18n-glossary-zhcn(术语表)、i18n-translation-ratio(真实翻译占比/占位符比例棘轮)、check-ui-value-drift(陈旧翻译)、check-translation-drift(文档漂移,当前以--warn运行)、CLI i18n 一致性(npm run check:cli-i18n,硬门禁)。
文件结构
config/ └── i18n.json # 唯一语言权威(42 locales、RTL 集、别名、排除项) src/i18n/ ├── config.ts # 语言定义的薄类型化适配层 ├── request.ts # 运行时语言解析(Cookie → 请求头 → en 兜底) ├── resolveRequestedLocale.ts # 无依赖的 locale 解析(含别名) └── messages/ ├── en.json # 英文源(~2800 键) ├── cs.json # 捷克语翻译 ├── de.json # 德语翻译 └── ... # 42 个语言文件 scripts/i18n/ ├── run-translation.mjs # 推荐:哈希增量文档翻译(LLM 后端) ├── check-translation-drift.mjs # 漂移检查(CI 门禁,无 API) ├── add-locale.mjs # 一键新增语言 ├── sync-ui-keys.mjs # UI 键位回填/翻译 ├── check-ui-keys-coverage.mjs # UI 键位覆盖率 ├── check-ui-value-drift.mjs # UI 值漂移 ├── check-translation-ratio.mjs # 真实翻译占比 ├── check-glossary-consistency.mjs # 术语表一致性 ├── glossary/ # zh-CN/zh-TW/ko 术语表 + protected-terms.json ├── generate-multilang.mjs # 弃用:Google Translate 自动翻译 ├── i18n_autotranslate.py # 弃用:LLM 文档翻译器 ├── generate-qa-checklist.mjs # 静态分析 QA ├── run-visual-qa.mjs # Playwright 视觉 QA ├── untranslatable-keys.json # 校验用 allowlist ├── validate_translation.py # 翻译校验器 └── check_translations.py # 代码到 JSON 键位检查 bin/cli/ ├── i18n.mjs # CLI 翻译层 └── locales/ # 42 个 CLI 语言目录 docs/ ├── guides/I18N.md # 本文英文主文档(手写维护) ├── i18n/ # 自动生成的翻译文档镜像(每语言一份) │ ├── README.md # 自动生成的语言索引 │ └── cs/docs/guides/I18N.md # 本文的捷克语版本 └── reports/ # QA 报告 ├── i18n-qa-checklist-*.md └── i18n-visual-qa-*.md .i18n-state.json # 提交的翻译状态(源/目标哈希)最佳实践
编辑翻译时的守则
- 永远先改
en.json——它是唯一权威 - 运行
npm run i18n:sync-ui -- --translate-markers --batch-size=40把新键传播到各语言(旧流程为generate-multilang.mjs messages) - 人工复核自动翻译——自动引擎只是起点而非终点
- 提交前校验:
python3 scripts/i18n/validate_translation.py quick -l <lang> - 若某键应保持英文,更新
untranslatable-keys.json
占位符安全
- ICU 占位符(
{count}、{value}、{total}、{seconds})必须逐字保留 - 复数格式(
{count, plural, one {# model} other {# models}})必须保持结构 - 校验器会自动检测占位符不匹配
在代码中新增翻译键
// 使用命名空间键 const t = useTranslations("settings"); t("cacheSettings"); // 映射到 JSON 中的 settings.cacheSettings // 运行 check_translations.py 验证键存在 python3 scripts/i18n/check_translations.py --verboseRTL 注意事项
- 阿拉伯语(
ar)、希伯来语(he),以及当前配置中的波斯语(fa)、乌尔都语(ur)是 RTL 语言 - 避免硬编码
left/rightCSS——使用start/end逻辑属性 - 视觉 QA(
run-visual-qa.mjs)负责捕获 RTL 布局错位
已知问题与历史
in.json→hi.json修复
生成器最初为印地语使用code: "in"(已弃用的 Google Translate 代码)而非正确的 ISO 639-1hi,产生了孤立的in.json重复文件。已通过把generate-multilang.mjs中code: "in"改为code: "hi"并删除孤立文件修复。
遗留in语言的退役(2026-09-02)
in在该修复后以第二个印尼语语言的身份延续了一段时期——config/i18n.json称之为 "Indonesian (Legacy)",而 README 仍标注为印地语。现已从所有表面(配置、目录、docs/i18n/in/、README、索引、基线)移除。id现在声明"aliases": ["in"],因此已保存的NEXT_LOCALE=in或OMNIROUTE_LANG=in会自动解析到id(实现见 resolveRequestedLocale.ts)。
docs/i18n/README.md改为手写维护
它过去被generate-multilang.mjs docs整体重新生成,现在不再如此:npm run i18n:add-locale会就地插入新语言的索引行(并更新计数句),其余编辑均手动完成。tests/unit/i18n-locale-surfaces-parity.test.ts双向守护这一约束。
validate_translation.py的忽略计数输出
quick检查现在显示来自untranslatable-keys.json的忽略键数量:
Missing: 0 Untranslated: 0 Ignored (UNTRANSLATABLE_KEYS): <varies per release>架构示意图
上图为 i18n 管线的总体流程(源码:docs/diagrams/i18n-flow.mmd):从config/i18n.json的语言注册、en.json源字符串,到 LLM 增量翻译、.i18n-state.json状态跟踪,最终产出docs/i18n/{locale}/文档镜像与src/i18n/messages/{locale}.jsonUI 目录,并由 CI 中的校验与 QA 作业闭环守护。
总结
OmniRoute 的 i18n 体系是一个"单一权威 + 双翻译引擎 + 多层校验门禁"的完整工程:config/i18n.json统一声明 42 个语言环境;仪表盘侧next-intl提供 Cookie 级语言解析与 EN 兜底,CLI 侧有独立的--lang/OMNIROUTE_LANG选择链路;文档翻译以哈希驱动的npm run i18n:run增量管线为主,旧 Google Translate 引擎保留但弃用;质量保障由键位校验、术语表一致性、静态 QA、Playwright 视觉回归与 CI 矩阵共同构成。对于任何需要长期维护多语言、多端(Web + CLI)界面的团队,这套"以英文为唯一源、自动化翻译打底、确定性校验兜底"的实践都极具参考价值。
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考