【免费下载链接】texlyre
A local-first LaTeX & Typst web editor with real-time collaboration & offline support
TeXlyre 国际化实战:本地优先的 LaTeX & Typst 协作 Web 编辑器 TeXlyre 如何基于 i18next 与 Crowdin,用一套约 2400 行的脚本流水线,支撑 5 种语言、2794 个翻译键的自动提取、覆盖率统计与社区翻译协作?本文带你完整拆解这条多语言翻译流水线的设计思路与落地细节。
为什么要做国际化:一个界面,五种语言
TeXlyre 是一款运行在浏览器里的 LaTeX / Typst 编辑器,面向全球用户。官方目前维护 5 种界面语言,全部集中在 translations/locales/ 目录下:
| 语言 | 代码 | 翻译覆盖率 | 备注 |
|---|---|---|---|
| English | en | 100% | 源语言(基准) |
| Arabic | ar | 95% | RTL 从右向左书写 |
| 简体中文 | zh | 94% | zh-CN.json |
| Deutsch | de | 92% | |
| Italiano | it | 91% |
这些覆盖率数字不是手工维护的,而是由脚本自动计算后写回 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 负责把语言切换变成全局响应式行为:
- 从设置项
language读取用户选择,调用i18next.changeLanguage(); - 同步更新
<html>的lang属性; - 处理 RTL:阿拉伯语等从右向左书写的语言,会自动给
<html>设置dir="rtl",整个布局镜像翻转; - 派发
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平台上由社区完成,流程如下:
- 仓库发布新版后,维护者把
base-en.json(含全部复数形态种子)推送到 Crowdin 作为源文件; - 社区译者按语言认领、在线翻译,标签与
{{count}}变量必须原样保留; - 每个版本发布时,人工把 Crowdin 上的译文手动同步回translations/locales/ 对应的 JSON 文件;
- 本地运行
npm run i18n:coverage更新locales.config.json,设置界面里的覆盖率即随之刷新。
选择"发布时同步"而非 CI 自动拉取,配合i18n:validate等本地校验脚本,能在合并前拦住格式错误的译文文件。
如何给 TeXlyre 添加一种新语言
依据 CONTRIBUTING.md 的官方指引,只需四步,完全不需要动 UI 组件:
- 在 translations/locales/ 新建
xx.json(可从en.json复制骨架); - 在 src/i18n.ts 中
import并注册到resources; - 在 translations/locales.config.json 的
languages数组加一条配置:code、name、nativeName、direction(RTL 语言填"rtl")、filePath; - 运行
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
相关推荐
Lichess多语言与国际化:Crowdin社区翻译系统
Lichess多语言与国际化:Crowdin社区翻译系统 Lichess作为全球最大的免费开源国际象棋平台,通过精心设计的国际化架构和Crowdin社区翻译系统
后端游戏开发即时通讯CANN/asc-devkit AI CPU调试打印API
printf<a name="ZH CN_TOPIC_0000002507683569" </a 产品支持情况 Ascend 950PR/Ascend 950D
人工智能深度学习高性能计算CANN戴森球计划工厂蓝图库:从零到宇宙帝国的终极建造指南
戴森球计划工厂蓝图库:从零到宇宙帝国的终极建造指南 想象一下,你站在一个荒芜的星球上,手中只有基础工具,却要在浩瀚宇宙中建造一个能包裹恒星的戴森球。这听起来像是
数据集
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考