Files
mai-bot/docs/i18n.md

8.2 KiB
Raw Blame History

i18n Guide

MaiBot 现在使用 JSON + Crowdin + Babel 的国际化方案,不依赖 gettext 的 .po/.mo 运行时。

仓库翻译策略

  • zh-CN 是仓库内唯一的 source language也是唯一的 GitHub 侧 source of truth。
  • zh-CN 的目标语言文件是同步产物和可评审输出,不是常规长期编辑面。
  • 仓库中已经提交过的目标语言文件,需要先通过一次 bootstrap 同步进 Crowdin避免 Crowdin 把这些历史翻译当成“未翻译”并用 source 文本导出覆盖。
  • bootstrap 完成后,目标语言的常规维护应在 Crowdin 中完成,而不是直接在 GitHub 中持续编辑。
  • GitHub Actions 是仓库与 Crowdin 之间唯一允许的同步方式;不要把 Crowdin 的原生 GitHub integration 当作第二条回写路径。
  • 翻译回流仍然通过 l10n_* pull request 完成,不直接写回 mainr-dev

目录结构

翻译文件位于 locales/<locale>/*.json,当前默认语言是 zh-CN

建议按模块拆分文件:

  • core.json
  • startup.json
  • config.json
  • prompts.json

长 Prompt 模板使用单文件本地化目录:

prompts/
  zh-CN/
    replyer.prompt
    planner.prompt

注意:

  • prompts/zh-CN/**/*.prompt 是唯一中文 source 模板目录。
  • 不要把 zh-CN 原文整批复制到 prompts/en-US/ 后直接提交。
  • 目标语言 prompt 文件应该由 Crowdin 下载生成;在本地还没有目标文件时,运行时会自动回退到 zh-CN

Dashboard WebUI 的 locale 文件位于 dashboard/src/i18n/locales/*.json

dashboard/src/i18n/locales/
  zh.json
  en.json
  ja.json
  ko.json

注意:

  • dashboard/src/i18n/locales/zh.json 是 dashboard 在 Git 中的 source of truth。
  • dashboard 运行时继续使用 zh / en / ja / ko 这组短 locale code但 Crowdin 侧仍把 zh.json 当作仓库里的 zh-CN source 资产来同步。
  • en.json / ja.json / ko.json 是同步产物和可评审输出,不是常规长期手工编辑面。

在代码中使用

统一从 src/common/i18n/__init__.py 导入:

from src.common.i18n import t, tn

logger.info(t("startup.launching_script", script_file=script_file))
logger.info(tn("core.tasks_cancelled", count))

可用能力:

  • t(key, locale=None, **kwargs):普通翻译
  • tn(key, count, locale=None, **kwargs)plural 翻译
  • set_locale(locale) / get_locale():设置或读取当前默认 locale
  • format_datetime_localized(...)
  • format_number_localized(...)
  • format_decimal_localized(...)

Prompt 模板统一从 src/common/prompt_i18n.py 加载:

from src.common.prompt_i18n import load_prompt

template = load_prompt("replyer")
rendered = load_prompt("replyer", identity="Mai", bot_name="麦麦")

Prompt 加载规则:

  • 优先读取 prompts/<当前 locale>/
  • 找不到时回退到 prompts/zh-CN/

locale 优先级

运行时按以下顺序决定 locale

  1. 显式传入 locale
  2. 当前上下文中的 locale 覆盖(如使用 use_locale(...)
  3. 环境变量 MAIBOT_LOCALE
  4. 默认值 zh-CN

key 规范

  • 使用稳定的点分 key例如 startup.env_created
  • 全部小写
  • 不要把中文原文直接当 key

日常翻译流程

  1. 先在 locales/zh-CN/*.jsonprompts/zh-CN/**/*.promptdashboard/src/i18n/locales/zh.json 添加或修改 source 内容。
  2. 在代码中用 t() / tn() / load_prompt() 替换硬编码字符串。
  3. 运行 python scripts/i18n_validate.py 校验结构。
  4. 把 source 变更推送到 mainr-dev,或手动触发 crowdin-sync.yml
  5. 目标语言翻译在 Crowdin 中完成。
  6. GitHub Actions 下载当时 Crowdin 中可用的翻译结果,并通过 l10n_main / l10n_r-dev pull request 回流到仓库。

对于非 zh-CN 的目标 locale

  • 下面这两条是本仓库的 repository-specific 校验策略,不是 Crowdin 默认行为。
  • 不要手工把中文 source 文案直接复制进目标语言文件后提交。
  • 英文 locale 文件中不应保留中文字符;这类残留会被校验脚本拦截。
  • Python 项目后续新增的 ko locale 也遵循同样规则。
  • dashboard/src/i18n/locales/en.json / ja.json / ko.json 也遵循同样规则。

什么时候可以直接改目标语言文件

以下场景才适合直接在 GitHub 中改非 zh-CN 文件:

  • 需要把仓库里已经存在的历史目标语言文件一次性 bootstrap 到 Crowdin。
  • 需要做紧急修复,而且你确认后续会把同样的修改补回 Crowdin避免下一次同步被覆盖。

除了上面这些例外,不要把目标语言文件当作常规编辑入口。

校验脚本

运行:

python scripts/i18n_validate.py

校验内容包括:

  • JSON 语法是否合法
  • 是否存在重复 key
  • 是否存在空字符串 key
  • 各语言 key 集合是否与 zh-CN 对齐
  • dashboard 嵌套 JSON 的 key 集合是否与 dashboard/src/i18n/locales/zh.json 对齐
  • 占位符集合是否一致
  • dashboard i18next {{placeholder}} 占位符集合是否一致
  • plural 结构是否一致
  • zh-CN locale 是否直接保留了包含中文字符的 source 文案
  • prompt 模板已存在时,其占位符集合必须与 prompts/zh-CN/ 对齐

对于 prompt 模板:

  • 缺少目标 locale 文件只会给 warning不会阻断因为运行时有 fallback
  • 目标 locale 文件如果存在但占位符漂移,会直接校验失败

候选扫描

如果你想继续做下一批迁移,可以运行:

python scripts/i18n_extract_candidates.py

这个脚本会扫描仓库中的 Python 文件,输出仍然包含中文字符串常量的位置,方便人工挑选下一批适合迁移到 i18n 的文案。

Crowdin

项目根目录的 crowdin.yml 现在会上传三类 source

  • locales/zh-CN/*.json
  • prompts/zh-CN/**/*.prompt
  • dashboard/src/i18n/locales/zh.json

对于 Python 项目:

  • locales/zh-CN/*.json 的目标语言现在也包含韩语,不再排除 ko
  • prompts/zh-CN/**/*.prompt 的目标语言现在也包含韩语,不再排除 ko

对于 dashboard

  • Crowdin 下载结果会回写到 dashboard/src/i18n/locales/en.jsonja.jsonko.json
  • 其中英文文件名保持为 en.json,但在 Crowdin 配置里仍映射到 en-US

GitHub Actions 中的 crowdin-sync.yml 是日常稳态同步入口:

  • push 到 main / r-dev 时,只有 source 资产(包括 dashboard 的 zh.json source 文件)和 crowdin.yml 会触发正常上传。
  • workflow 运行时会上传 source并下载当时 Crowdin 中可用的翻译结果。
  • 下载结果通过 l10n_* pull request 回流,而不是直接写回 main / r-dev

crowdin-bootstrap.yml 是一次性或例外场景使用的 bootstrap 入口:

  • 只能手动触发。
  • 会把仓库当前已提交的目标语言文件上传到 Crowdin用来保留历史翻译。
  • 不会作为日常 workflow 持续上传 GitHub 中的目标语言改动。
  • 这个 workflow 必须先存在于仓库默认分支后,才会在 GitHub 网页端出现,也才能被 gh workflow run 调用。

常用命令:

python scripts/i18n_validate.py
gh workflow run crowdin-sync.yml --ref main
gh workflow run crowdin-bootstrap.yml --ref main -f base_branch=r-dev -f confirm_bootstrap=yes-bootstrap-current-target-translations
gh run list --workflow crowdin-sync.yml --limit 5
gh pr list --head l10n_r-dev

更完整的 GitHub 侧操作说明见 docs/github-actions-crowdin-workflow-report.md

当前迁移范围

这一批已经覆盖:

  • bot.py 启动、重启、退出与协议确认提示
  • src/config 中第一批配置加载、热重载、校验异常提示
  • src/main.py 的主要启动链路提示
  • src/prompt/prompt_manager.py 的 locale 感知 Prompt 加载
  • prompts/<locale>/ 的单文件 Prompt 模板结构

仍然可以继续做但这次没有全量翻译的内容:

  • 所有 Prompt 模板的高质量英文翻译
  • 内部协议字段
  • debug-only 文案