news 2026/10/23 8:27:57

TeXlyre 国际化实战:i18next + Crowdin 多语言翻译流水线全解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TeXlyre 国际化实战:i18next + Crowdin 多语言翻译流水线全解

【免费下载链接】texlyre

A local-first LaTeX & Typst web editor with real-time collaboration & offline support

项目地址:https://gitcode.com/gh_mirrors/te/texlyre
点击查看免费下载

TeXlyre 国际化实战:本地优先的 LaTeX & Typst 协作 Web 编辑器 TeXlyre 如何基于 i18next 与 Crowdin,用一套约 2400 行的脚本流水线,支撑 5 种语言、2794 个翻译键的自动提取、覆盖率统计与社区翻译协作?本文带你完整拆解这条多语言翻译流水线的设计思路与落地细节。

为什么要做国际化:一个界面,五种语言

TeXlyre 是一款运行在浏览器里的 LaTeX / Typst 编辑器,面向全球用户。官方目前维护 5 种界面语言,全部集中在 translations/locales/ 目录下:

语言代码翻译覆盖率备注
Englishen100%源语言(基准)
Arabicar95%RTL 从右向左书写
简体中文zh94%zh-CN.json
Deutschde92%
Italianoit91%

这些覆盖率数字不是手工维护的,而是由脚本自动计算后写回 translations/locales.config.json。整个文件共2794 个翻译键——对一款 Web 编辑器来说,这是一个相当不小的国际化体量。

要支撑这个体量的国际化,TeXlyre 的流水线分为三层:运行时(i18next)、构建时脚本(scripts/i18n/)、社区协作(Crowdin)。下面逐层拆解。

运行时:i18next 初始化与语言切换

一个 46 行的初始化文件

运行时核心是 src/i18n.ts,它把 5 个 locale JSON 文件打包进i18next的resources,并做了两件关键配置:

  • lng: 'en'+fallbackLng: 'en':默认英文,缺词时回退英文,保证任何语言下界面不出现"裸 key";
  • 导出一个轻量的t()函数供全项目调用。

值得留意的是一个小细节:简体中文在resources中注册为zh(而非zh-CN),与locales.config.json中的语言代码对齐。

LocaleContext:把"语言"变成响应式状态

src/contexts/LocaleContext.tsx 负责把语言切换变成全局响应式行为:

  1. 从设置项language读取用户选择,调用i18next.changeLanguage();
  2. 同步更新<html>的lang属性;
  3. 处理 RTL:阿拉伯语等从右向左书写的语言,会自动给<html>设置dir="rtl",整个布局镜像翻转;
  4. 派发language-changed自定义事件,供其他组件(如日期、数字格式化)联动刷新。

方向策略也可在设置里手动指定:auto(跟随语言)、ltr或rtl。

语言设置项与覆盖率展示

语言选择 UI 注册在 src/settings/registerLocaleSettings.tsx 中。它直接读取locales.config.json生成下拉选项,标签格式为"原生名 (英文名)"(如简体中文 (Chinese (Simplified))),因此新增语言无需改 UI 代码——配置文件加一行即可出现在下拉菜单里,并自动显示该语言的翻译覆盖率。

翻译文件规范:用英文原句做键

打开 translations/locales/en.json 会看到一个非常规但高效的设计:翻译键就是完整的英文原句,而不是nav.home这类缩写键。

"Save": "Save", "{count} bytes": "{{count}} bytes", "{count} bytes_one": "{{count}} byte", "{count} bytes_other": "{{count}} bytes"

这种"句子即键"的做法有两个好处:

  • 翻译者看到的是自然英文,无需查键值对照表,降低 Crowdin 社区参与门槛;
  • 键永不与界面脱节——代码里的字符串改了什么,键就跟着变,工具链会自动检出差异。

代价是复杂的复数规则。TeXlyre 采用 i18next 的多后缀约定,后缀来自 CLDR 规范:_zero、_one、_two、_few、_many、_other。不同语言需要的形态不同(阿拉伯语需要 6 种,德语只需 2 种),CONTRIBUTING.md 明确要求:变量只支持{count},且所有标签(如<strong />)和占位符({{count}})必须原样保留。

还有一个专为 Crowdin 准备的产物:base-en.json。它由 scripts/i18n/generate-base-plurals.cjs 自动生成,把每个复数键缺失的形态全部补齐成种子条目,让翻译平台上的语言版本可以按形态独立勾选完成度。

构建时:约 2400 行的 i18n 脚本流水线

这是整条流水线最工程化的部分,全部位于 scripts/i18n/,共 15 个模块、约 2400 行 Node.js 代码,统一入口是 scripts/translate-pages.cjs。

1. 提取:AST 扫描而非正则

extract-translations.cjs 基于Babel 解析器对整个src/做 AST 遍历,收集 JSX 文本、placeholder/title/alt/aria-label属性、registerSetting()中的label/description等可翻译字符串,写入en.json。用 AST 而不是正则的好处:

  • 自动忽略 URL、邮箱、纯数字、常量(内置ignorePatterns);
  • 能自动识别files.length这类计数表达式并转换为{count}占位符,生成对应复数键。

2. 回写:把英文硬编码自动替换为 t() 调用

apply-translations.cjs 方向相反:扫描源码中仍是硬编码英文的 JSX 文本,自动改写成t("...")调用,并自动注入import { t } from '@/i18n'(由 import-manager.cjs 处理)。两个贴心的工程细节:

  • 支持--dry-run预览模式;
  • 默认生成.bak备份文件,回写失败不伤源码;
  • 改写后的代码会经 validate-transforms.cjs 二次语法校验。

设置项有独立的 apply-settings-translations.cjs,专门处理registerSetting()调用。

3. 体检:缺失键、复数与覆盖率

npm 命令作用对应脚本
npm run i18n:extract从源码提取英文文案extract-translations.cjs
npm run i18n:preview/i18n:fix预览 / 应用t()改写apply-translations.cjs
npm run i18n:coverage计算各语言覆盖率calculate-coverage.cjs
npm run i18n:missing找出源码用了但 JSON 里没有的键detect-missing-keys.cjs
npm run i18n:validate校验 locale JSON 合法性并定位错误行列validate-json.cjs
npm run i18n:detect检出"应转 i18n 的动态内容"detect-dynamic-content.cjs
npm run i18n:full一键跑完 预览 → 提取 → 覆盖率 → 回写预览—

其中复数审计值得单说:detect-missing-plurals.cjs 使用浏览器原生的Intl.PluralRules(CLDR 数据)判断每种语言需要哪些复数形态,只生成该语言实际需要的形态种子,避免人工判断语言规则出错。npm run build:prod中内置了i18n:coverage,保证每次发版前覆盖率数据都是最新的。

社区协作:Crowdin 如何接入

翻译本身不在代码仓库里发生,而是在Crowdin平台上由社区完成,流程如下:

  1. 仓库发布新版后,维护者把base-en.json(含全部复数形态种子)推送到 Crowdin 作为源文件;
  2. 社区译者按语言认领、在线翻译,标签与{{count}}变量必须原样保留;
  3. 每个版本发布时,人工把 Crowdin 上的译文手动同步回translations/locales/ 对应的 JSON 文件;
  4. 本地运行npm run i18n:coverage更新locales.config.json,设置界面里的覆盖率即随之刷新。

选择"发布时同步"而非 CI 自动拉取,配合i18n:validate等本地校验脚本,能在合并前拦住格式错误的译文文件。

如何给 TeXlyre 添加一种新语言

依据 CONTRIBUTING.md 的官方指引,只需四步,完全不需要动 UI 组件:

  1. 在 translations/locales/ 新建xx.json(可从en.json复制骨架);
  2. 在 src/i18n.ts 中import并注册到resources;
  3. 在 translations/locales.config.json 的languages数组加一条配置:code、name、nativeName、direction(RTL 语言填"rtl")、filePath;
  4. 运行npm run i18n:full完成排序、校验与覆盖率计算。

完成后,新语言会自动出现在设置面板的语言下拉框中,并带 0% 覆盖率的实时进度。

小结:可复用的国际化工程清单

TeXlyre 的国际化实践浓缩成一条可复用清单:

  • 句子即键:翻译键用英文原句,平台协作零门槛;
  • AST 驱动:提取与回写都基于 Babel 遍历,杜绝正则的误伤与漏网;
  • CLDR 兜底:复数形态交给Intl.PluralRules,不靠人脑记语言规则;
  • 覆盖率即数据:locales.config.json让翻译进度可见、可比、可发版;
  • 平台 + 仓库双轨:Crowdin 管翻译过程,i18n:validate/i18n:coverage管数据质量。

无论项目用什么前端框架,只要把"提取 → 回写 → 校验 → 覆盖率"四件事脚本化,就能以 2794 个键的量级依然保持多语言维护不失控。

【免费下载链接】texlyre

A local-first LaTeX & Typst web editor with real-time collaboration & offline support

项目地址:https://gitcode.com/gh_mirrors/te/texlyre
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 8:25:54

双 11 倒计时 15 天指挥室哨音:全链路压测熔断阈值最终校准手记

在双 11 大促的作战倒计时翻到“最后 15 天”的这一刻&#xff0c;大促作战指挥室里的键盘敲击声反而逐渐沉寂下来。各大核心战役的技术骨干们全部围坐在总指挥长桌旁&#xff0c;屏幕上投射出密密麻麻的表格与时序曲线。在正式封网前夕&#xff0c;我们迎来了一个最关乎系统生…

作者头像 李华
网站建设 2026/10/23 8:25:53

Typora正版授权、免费替代与Markdown写作配置迁移指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 8:25:29

动态规划Part06:从背包问题到建模思维的核心突破

坚持到 day33&#xff0c;动态规划 part06&#xff0c;这个进度条本身就说明你已经熬过了从 0 到 1 的阶段。前五天你在斐波那契、爬楼梯、不同路径这类线性递推题上反复练习&#xff0c;基本能闭眼写出转移方程&#xff0c;于是第六天开始接触真正的硬核主题&#xff1a;背包问…

作者头像 李华
网站建设 2026/10/23 8:23:41

Qwen3.5在昇腾NPU上部署实战:vLLM-Ascend全链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 8:20:58

股票买卖DP全解:从121到123题的状态设计演进

1. 121题打头阵&#xff1a;为什么买卖时机问题要拆成“持有”和“未持有”两个状态到了训练营第41天&#xff0c;动态规划的题目难度已经明显上来了。今天这三道题都是股票买卖系列&#xff0c;121是最基础的一次买入一次卖出&#xff0c;122放开了交易次数&#xff0c;123则限…

作者头像 李华
网站建设 2026/10/23 8:19:02

Windows 0x80070005拒绝访问错误:权限排查与修复全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华