)
如何一次性支持38种语言Proton WebClients 国际化 i18n 体系完整拆解ttag 翻译工作流指南【免费下载链接】WebClientsMonorepo hosting the proton web clients项目地址: https://gitcode.com/gh_mirrors/we/WebClientsProton WebClients 是 Proton 官方的前端 Monorepo集中管理了 Mail、Drive、Pass、Calendar、Account 等所有 Web 客户端。它的国际化i18n体系用一套轻量方案支撑了38 种语言开发者在代码里用ttag标记可翻译文本再用 Monorepo 内置的 packages/i18n/ CLI 工具一键提取、校验翻译文件最后通过 Crowdin 平台分发给全球翻译者。本文带你从零拆解这套 ttag 翻译工作流的完整链路。一、i18n 文件布局每个应用一个 locales 目录Monorepo 采用应用自治策略每个应用在自己的根目录下维护语言包而不是堆在一个全局目录里。以 Mail 为例路径作用applications/mail/locales/*.json各语言翻译文件共 30 种语言applications/mail/locales/config/locales.json语言代码 → 母语名称的映射如fr_FR: Français驱动 UI 语言切换器applications/account/locales/*.jsonAccount 应用的语言包覆盖 38 种语言含ar_SA、kab_DZ等长尾语种打开任意一个语言文件如 fr_FR.json能看到统一的三段式结构{ headers: { language: fr_FR, plural-forms: nplurals2; plural(n 2); }, contexts: { account: { Mail forward stopped: [Le transfert automatique de messages a été arrêté.] } } }headers语言元信息重点是plural-forms复数规则——德语、俄语、波兰语的复数规则各不相同这一行让翻译引擎按目标语言的语法规则处理数量词contexts按模块分区的键值对。同一个文案在不同模块如账户设置 vs日历邀请允许有不同的译文靠 context 命名空间隔离避免一词多义被错误统一。二、代码中如何标记可翻译文本ttag 模板字符串Proton 的 React 项目统一使用 ttag 来标记文本核心就是一个叫trans的模板字符串import { trans } from lingui/core; // 基本用法模板字符串里的英文即翻译源文本 const msg transMail forward stopped; // 带变量${ name } 会被翻译工具识别为占位符翻译者不能改动变量名 const msg trans{ name } sent you a file; // 指定 context 复数形式 const msg trans({ context: account, defaults: { one: 1 other potential leak detected, other: ${ n } other potential leaks detected }, });几个关键点源码即模板trans的默认参数就是英文原文提取工具扫描代码即可生成翻译模板不需要单独维护 key 字典变量受保护${ name }这类占位符会被校验脚本提取比对翻译中变量缺失或数量不一致会直接报错context 必填没有 context 的翻译在 CI 校验阶段会被判定为不合规见下文。在组件库里实际调试时可以借助 Storybook 预览组件的文案渲染效果组件库位于 packages/components/三、proton-i18n CLI提取与校验的三条命令packages/i18n/ 是一个 npm 内部包proton/i18n暴露proton-i18n命令行官方说明见 Readme.md$ npx proton-i18n help Available commands: - validate type # 校验翻译检查 context、变量、ttag 格式 - extract type # 从代码中提取全部翻译到模板文件1️⃣ extract如何做到只提取真正会发布的文案提取逻辑见 scripts/extract.sh。它的巧妙之处在于不在源码上提取而在构建产物上提取先用 Webpack 构建出dist包经过 tree-shaking被删除的死代码里的文案不会出现用 extract-sourcemaps.mjs 从 sourcemap 还原出实际打进了 bundle 的源码调用 ttag-cli 执行ttag extract输出 gettext 标准的.pot模板文件。这样翻译平台上永远不会出现代码已删除、文案还留着的僵尸词条模板文件与线上真实内容严格一致。2️⃣ validate三重质量门禁lib/validate.js 用gettext-parser解析模板文件后执行三层检查检查项实现拦截的问题无 context 检查validateWithoutContext翻译者/开发者漏写 context 导致文案串区变量匹配检查validateVariables单数/复数句中${ n }占位符数量不一致重复变量检查validateContextAndVariables同一 context 下两条文案变量结构完全相同却语义不同任意一项失败都会抛出N translations without context!类错误直接卡住合并。3️⃣ lint-functionsttag 写法规范检查validate --lint-functions会运行 scripts/linter.mjs检查源码里trans是否使用了正确格式比如是否误把函数调用结果传进去、模板字符串是否带 context把问题拦在代码提交阶段而不是翻译阶段。四、从代码到 Crowdin翻译工作流全链路把上面的环节串起来就是一条完整的自动化流水线开发者写代码trans标记文案 contextCI 跑 lintlint-functions保证 ttag 写法规范构建后提取extract生成po/template.pot上传翻译平台CLI 通过 .env 中的I18N_TEMPLATE_FILE识别模板路径同步到 Crowdin翻译者协作各语言译员在 Crowdin 上提交.po译文回写语言包译文落地为各应用locales/目录下的 JSON格式即上文第二段合入前校验validate三重门禁确保变量、context 完整。整个链路里没有任何手工拷贝粘贴的环节38 种语言 × 10 个应用、数以万计的词条全部靠这套 CLI 保证一致性。五、如何新增一种语言三步走想为某个应用增加新语言例如vi_VN越南语步骤非常清晰建文件在applications/app/locales/下新建vi_VN.jsonheaders里填对该语言的plural-forms规则登记语言在 locales/config/locales.json 中追加vi_VN: Tiếng Việt语言切换器即刻生效同步词条在 Crowdin 上创建对应项目跑proton-i18n validate确认无缺失 context 后即可发布。得益于模板文件自动提取 CI 校验新增语言的成本基本只剩翻译本身。总结为什么这套 i18n 体系值得借鉴Proton WebClients 的国际化方案没有引入沉重的框架而是抓住了三件事用ttag模板字符串让英文原文成为唯一事实来源代码、模板、界面永不脱节基于构建产物提取词条消灭僵尸文案用 CLI CI 把 context、变量校验变成硬性门禁翻译质量自动化。对于同样要支持多语言、多应用的 Monorepo 项目这套 ttag gettext 的轻量组合是一个可直接抄作业的成熟范式。核心工具源码packages/i18n/lib/extract.js、packages/i18n/lib/validate.js提取脚本packages/i18n/scripts/extract.sh语言包示例applications/mail/locales/fr_FR.json【免费下载链接】WebClientsMonorepo hosting the proton web clients项目地址: https://gitcode.com/gh_mirrors/we/WebClients创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考