跳到主要内容

国际化工作流

这篇文档约束这个仓库以后新增文档或博客时同步完成英文翻译的标准方式。

站点使用 Docusaurus 内置 i18n:zh-Hans 是默认语言,英文通过 i18n/en/ 目录实现。根目录的 I18N.md 是完整的参考手册,这篇文档只关心"每次新增内容时该怎么做"。

核心原则

1. 中文放 docs/blog/,英文放 i18n/en/ 镜像路径

中文是默认语言,直接放在项目根目录的 docs/blog/ 下。英文翻译放在 i18n/en/ 下的对应插件目录,路径必须 1:1 镜像。

文档镜像规则:

docs/Linux/new-article.md
→ i18n/en/docusaurus-plugin-content-docs/current/Linux/new-article.md

docs/DataStructer/sorting-algorithms/sorting-basics.md
→ i18n/en/docusaurus-plugin-content-docs/current/DataStructer/sorting-algorithms/sorting-basics.md

博客镜像规则:

blog/2026-05-01-new-post.md
→ i18n/en/docusaurus-plugin-content-blog/2026-05-01-new-post.md

文件名必须完全一致,包括大小写和扩展名。

2. 只翻译人读的文字,不翻译代码和技术标识

翻译范围:

内容是否翻译
titledescription翻译
sidebar_positiontagsauthorsslug保持一致
正文段落、标题翻译
代码块(```不翻译
内联代码(`不翻译
图片链接、JSX 组件、import / export不翻译
LaTeX 公式不翻译

3. 新目录必须同步创建 _category_.json

如果新增了一个目录(不管是文档目录还是专题目录),英文镜像路径下必须有对应的 _category_.jsonposition 保持一致,labeldescription 翻译。

// docs/NewDir/_category_.json
{
"label": "新目录",
"position": 12,
"link": {
"type": "generated-index",
"description": "中文描述。"
}
}

// i18n/en/.../NewDir/_category_.json
{
"label": "New Directory",
"position": 12,
"link": {
"type": "generated-index",
"description": "English description."
}
}

4. 新标签必须同步更新 tags.yml

如果文章使用了新标签,英文的 tags.yml 也要更新。permalink 保持一致,label 翻译。

5. 更新任何文档都必须同步更新英文翻译

不管是新增还是修改 docs/ 下的文档,都必须同步更新 i18n/en/ 下对应的英文版本。这条适用于所有 docs/ 下的 .md.mdx 文件,包括工作流文档、LabNotes、技术指南等。

具体要求:

  • 修改了中文文档的某一段,英文对应段必须同步修改
  • 新增了一个章节,英文版必须新增对应章节
  • 删除了内容,英文版也必须删除
  • 不要只更新中文而留下英文版过时——这比不翻译更糟糕,因为读者会看到中英文不一致的文档

工作流类文档(如本文件、life-blog-writing-workflow.md)尤其重要,因为它们是后续工作的参考依据。如果中文版更新了流程但英文版没有,英文读者会遵循过时的流程。

# i18n/en/docusaurus-plugin-content-docs/current/tags.yml
New Tag:
label: New Tag
permalink: new-tag

标准流程:新增文档

第一步:创建中文文档

docs/ 下正常创建中文 .md.mdx 文件。

第二步:创建英文翻译

i18n/en/docusaurus-plugin-content-docs/current/ 下创建同名同路径文件,翻译 frontmatter 和正文。

frontmatter 对照:

# 中文
---
title: 自定义镜像与 Dockerfile 实践
sidebar_position: 6
description: 把临时调试、Dockerfile 构建整理成一套更稳妥的工作流。
---

# 英文
---
title: Custom Images and Dockerfile Practices
sidebar_position: 6
description: "Turning ad-hoc debugging and Dockerfile builds into a more reliable workflow."
---

注意:description 包含冒号时必须用引号包裹,否则 YAML 解析会报错。

第三步:如果是新目录,同步创建英文 _category_.json

参照上面"核心原则"第 3 条。

第四步:如果使用了新标签,同步更新英文 tags.yml

参照上面"核心原则"第 4 条。

第五步:如果新标签名是中文,刷新 sidebar 翻译

npm run write-translations:en

然后编辑 i18n/en/docusaurus-plugin-content-docs/current.json,找到新增的 sidebar 条目翻译。

标准流程:新增博客

第一步:创建中文博客

blog/ 下创建中文 .md.mdx 文件。

第二步:创建英文翻译

i18n/en/docusaurus-plugin-content-blog/ 下创建同名同路径文件:

blog/2026-05-01-new-post.md
→ i18n/en/docusaurus-plugin-content-blog/2026-05-01-new-post.md

博客的 authors.ymltags.yml 不需要翻译(已是英文或通用标识符)。

MDX 博客中的 export const 数据对象需要翻译其中的文字值(altcaption 等),但图片 URL 不变。

第三步:生成中文音频

使用独立项目 tts-blog-generator(位于项目父目录 D:\Code\tts-blog-generator\):

cd ../tts-blog-generator
python generate.py

脚本会自动扫描 blog/ 下所有 .md / .mdx 文件,提取正文、调用 TTS API、转为 MP3、上传到 OSS。已生成的音频会跳过,使用 --force 可强制重新生成。

第四步:生成英文音频

python generate.py --lang en --blog-dir "../Dev-Knowledge-Base/i18n/en/docusaurus-plugin-content-blog"

英文音频使用 Chloe 语音,文件名带 en_ 前缀,存储在 OSS 的 Audio/blog/en/ 目录下。清单文件中的 key 带 en/ 前缀(如 en/agent-harness)。

第五步:复制清单文件到项目

cp output/blog-manifest.json ../Dev-Knowledge-Base/src/data/blogAudioManifest.json
cp output/blog-manifest.json ../Dev-Knowledge-Base/static/audio/blog/manifest.json

src/data/blogAudioManifest.json 是播放器组件的静态导入数据源,static/audio/blog/manifest.json 是备用访问路径。两份都必须更新。 不要把 output/manifest.json 直接复制给博客播放器;它是博客和文档的总清单。博客播放器应该使用 output/blog-manifest.json

第六步:测试

cd ../Dev-Knowledge-Base
npm start

访问博客页面,确认:

  • 播放按钮可点击
  • 音频正常播放
  • 多段音频无缝衔接(如有)
  • 进度条显示正确总时长
  • 切换中英文后使用对应语音(茉莉 / Chloe)

文档音频 (Docs TTS)

文档分类下的文章(如 docs/LookAround/)也支持 TTS 音频播放。与博客不同,文档音频播放器自动注入DocItem/Layout 中,无需在 MDX 文件中手动引入组件。

完整维护流程见 文档音频工作流。这里保留新增或更新双语文档时最常用的检查项。

架构设计

  • DocsAudioPlayer 复用 BlogAudioPlayer 的播放逻辑,但读取 src/data/docsAudioManifest.json,并使用 keyPrefix="docs/"
  • 博客音频同样通过 src/theme/BlogPostPage/index.js 自动注入,无需在 MDX 中手动引入
  • src/theme/DocItem/Layout/index.js 自动为 LookAround 文档注入播放器,位置在面包屑和正文之间
  • docsAudioManifest.json 中,中文 key 为 docs/{slug},英文 key 为 en/docs/{slug}
  • 文档音频托管在 OSS 的 Audio/docs/Audio/docs/en/ 下,与博客音频 Audio/blog/ 分离

生成文档音频

cd ../tts-blog-generator

# 中文 LookAround
python generate.py --type docs --lang zh --force --article-jobs 2

# 英文 LookAround
python generate.py --type docs --lang en \
--blog-dir "../Dev-Knowledge-Base/i18n/en/docusaurus-plugin-content-docs/current/LookAround" \
--force --article-jobs 2

生成器的并发单位是文章,不是同一篇文章里的 chunk。同一篇文章内部始终按 _001_002 顺序串行生成,避免音频和正文顺序错位。

单篇修复

python generate.py --type docs --lang zh \
--include mercedes-benz-g-class-history-category-industry-position \
--force --article-jobs 1 --chunk-char-limit 1200

长中文文章如果出现短音频、截断或某个时间点读错内容,先用 generate.extract_text() 检查真正送给 TTS 的 UTF-8 文本,再降低 --chunk-char-limit 重生成。G-Class 中文音频就是按 1200 字左右重分成 15 段后修复的。

复制文档清单文件到项目

cp output/docs-manifest.json ../Dev-Knowledge-Base/src/data/docsAudioManifest.json
cp output/docs-manifest.json ../Dev-Knowledge-Base/static/audio/docs/manifest.json

不要把 output/manifest.json 直接复制给文档播放器;它是博客和文档的总清单。文档播放器应该使用 output/docs-manifest.json

测试文档音频

访问 LookAround 分类下的任意文章,确认:

  • 面包屑下方出现音频播放器
  • 播放按钮可点击,音频正常播放
  • 多段音频按 manifest 中的 URL 顺序衔接
  • 进度条显示正确总时长
  • 访问非 LookAround 文档时播放器不出现
  • 切换英文 locale 后加载 en/docs/{slug} 的英文音频

MDX 兼容性陷阱

英文翻译中最容易引入构建错误的几个地方:

1. 散文中的 <

MDX 把 < 当 JSX 标签开始。数学比较必须转义:

❌ Small data (<1M elements)
✅ Small data (≤1M elements)
✅ Small data (&lt;1M elements)

2. 散文中的 {

MDX 把 { 当 JSX 表达式。集合符号必须用 LaTeX 包裹:

❌ The set {a, b, c} has 3 elements.
✅ The set $\{a, b, c\}$ has 3 elements.

3. YAML frontmatter 中的冒号

description 包含冒号时必须用引号包裹:

❌ description: Three patterns: temporary, persistent, and troubleshooting.
✅ description: "Three patterns: temporary, persistent, and troubleshooting."

4. LaTeX 中的花括号

LaTeX 的 \{...\} 在 MDX 中仍会被解析为 JSX。确保在 $...$$$...$$ 分隔符内:

❌ $[a]_R = \{x \in A \mid (a,x) \in R\}$ ← 单行 $ 可能失效
✅ $$[a]_R = \{x \in A \mid (a,x) \in R\}$$ ← 用 $$ 更安全

5. 代码块内的中文注释

代码块里的注释也应翻译:

// ❌ int visited[MAX]; // 访问标记数组
// ✅ int visited[MAX]; // Visited marker array

自定义页面的国际化

Docusaurus 不会自动翻译自定义 React 页面和 theme override。这些页面需要通过 useDocusaurusContext().i18n.currentLocale 读取当前语言,在组件内维护对应文案。

当前受影响的页面:

  • 文档首页(src/components/DocsIntro/
  • Travel 首页
  • Blog 总览页
  • 站点首页

如果修改或新增了这些页面的可见文案,必须同步处理中英文。

构建验证

每次新增翻译后必须验证:

npm run build # 构建全部 locale(zh-Hans + en)
npm run build:en # 只构建英文,适合快速验证

构建必须零错误。常见错误来源:

  • YAML frontmatter 中未转义的冒号
  • MDX 中未转义的 <{
  • 英文镜像路径下缺少 _category_.json
  • 文件名大小写不一致(Windows 不区分大小写,但 CI 环境会区分)

发布前检查清单

每次新增内容并同步翻译后,至少检查:

文档和博客通用:

  • 中文文件放在 docs/blog/
  • 英文文件放在 i18n/en/ 对应镜像路径下
  • 文件名完全一致(包括扩展名)
  • sidebar_position 中英文一致
  • titledescription 已翻译
  • tagsauthorsslug 保持一致
  • 代码块内容未被翻译
  • 新目录有对应的英文 _category_.json
  • 新标签有对应的英文 tags.yml 条目
  • YAML description 中的冒号已用引号包裹
  • 散文中的 <{ 已转义或用 LaTeX 包裹

博客音频专用:

  • 中文音频已通过 python generate.py 生成
  • 英文音频已通过 python generate.py --lang en --blog-dir "..." 生成
  • 清单文件 blogAudioManifest.json 已复制到 src/data/static/audio/blog/
  • OSS 防盗链白名单包含当前域名(生产 + localhost)

文档音频专用:

  • 中文音频已通过 python generate.py --type docs --lang zh ... 生成
  • 英文音频已通过 python generate.py --type docs --lang en --blog-dir "..." 生成
  • 长文或修复文章已用 UTF-8 检查 TTS 抽取文本
  • 长中文文章必要时已使用更小的 --chunk-char-limit
  • 清单文件 docsAudioManifest.json 已从 output/docs-manifest.json 复制到 src/data/static/audio/docs/
  • DocItem/Layout 中已为对应文档分类添加播放器注入逻辑
  • 目标 OSS 音频 URL 可访问并返回音频内容

构建验证:

  • npm run build 通过
  • npm run build:en 通过(快速验证英文)