首页
/ lazygit 国际化工作流:翻译文件的生成、Crowdin 同步与运行时加载机制详解

lazygit 国际化工作流:翻译文件的生成、Crowdin 同步与运行时加载机制详解

2026-09-04 14:53:29作者:咎岭娴Homer

本文围绕 pkg/i18n/translations/README.md 展开,讲解 lazygit 多语言翻译体系的完整闭环:英文源文本如何从 Go 代码导出为 JSON 并上传到 Crowdin 翻译平台、社区翻译成果如何通过脚本回灌到仓库、以及翻译文件如何被编译进二进制并在运行时按需加载。读完本文,你将掌握维护 lazygit 翻译文件的规范操作、理解 en.jsonpkg/i18n/english.go 的主从关系,并能从源码层面解释语言回退(fallback)与自动检测语言的实现原理。

核心原则:翻译 JSON 是机器生成的,禁止手工编辑

阅读 pkg/i18n/translations/README.md 的第一句话就能抓住整个目录的纪律:

The JSON files in this directory are machine-generated; please do not edit.

也就是说,pkg/i18n/translations/ 目录下的 ja.jsonko.jsonzh-CN.jsonzh-TW.json 等文件全部由工具链生成,任何人(包括贡献者)都不应直接修改它们。这条原则背后有两个事实源:

  • 英文源文本:所有界面文本的英文版本维护在 pkg/i18n/english.go 中。该文件定义了一个巨大的 TranslationSet 结构体(约 2300 行),每个字段对应一条界面文案,如 FilesTitleCommitTooltipCredentialsPassword 等,并提供了 EnglishTranslationSet() 构造函数。
  • 其他语言的译文:存放在 Crowdin 翻译平台(lazygit 项目)上,由社区译者在线完成,最终通过脚本导出回本仓库的 translations/ 目录。

这种"代码即源文本 + 平台即译文"的设计,保证了英文文案随功能开发自然演进,而翻译进度与代码仓库解耦。

运行时如何加载翻译:embed 编译 + 英文兜底合并

README 只讲了文件从哪来,而 pkg/i18n/i18n.go 则回答了文件如何被使用。

翻译文件编译进二进制

关键在第 69~70 行:

//go:embed translations/*.json
var embedFS embed.FS

通过 Go 的 embed 包,所有翻译 JSON 在编译期直接嵌入二进制,运行时不需要外部文件。getSupportedLanguageCodes() 从内嵌文件系统中列出 translations/ 目录下的 JSON 文件名(去掉 .json 后缀)得到受支持的语言码列表;readLanguageFile() 则按 translations/%s.json 读取并反序列化为 TranslationSet

语言选择与回退逻辑

NewTranslationSetFromConfig() 实现了完整的语言决策链(见 i18n.go 第 18~48 行):

  1. language: auto:调用 detectLanguage(),基于 jibber_jabber 库检测 IETF 语言标签(读取 LANG/LC_ALL 等环境变量)。若检测结果与任一已支持语言码前缀匹配则使用该语言;检测出未翻译语言不视为错误,直接回退英文。
  2. language: en:直接返回英文翻译集。
  3. 显式指定某语言码:若该码在支持列表内则加载对应文件;指定了未翻译的语言则返回错误Language not found: <code>),提示用户配置有误。

newTranslationSet() 中有一个关键细节(第 50~67 行):非英文翻译集加载后,会用 mergo.Merge(baseSet, *translationSet, mergo.WithOverride) 与英文基础集合并。这意味着翻译文件中缺失的键会自动用英文值填充——这正是后文"必须删除空字符串"操作在运行时层面的意义:JSON 中只保留已翻译的条目,未翻译条目靠英文兜底。

测试验证的语言场景

i18n_test.go 用表驱动测试完整覆盖了上述分支:

场景 配置 language 环境变量 LANG 预期结果
显式指定支持语言 nl en_US 加载荷兰语
显式指定不支持语言 xy en_US 报错
自动检测且无 LANG auto 回退英文
自动检测荷兰语 auto nl_NL 加载 nl
自动检测简体中文 auto zh-CN 加载 zh-CN
自动检测未翻译语言 auto xy_XY 回退英文

这些用例同时印证了"auto 下检测失败是静默回退、显式配置错误才会报错"的双轨语义(该测试在 Windows 上会跳过,因为 Windows 不遵循 LANG 环境变量约定)。

上传英文源文件到 Crowdin

README 的"Uploading the English file to Crowdin"一节给出了明确流程:当 pkg/i18n/english.go 中的文本发生变化(新增功能、修改文案)后,需要把最新英文版同步到 Crowdin,供译者参照翻译。

操作步骤:

go run cmd/i18n/main.go

这一命令的行为定义在 cmd/i18n/main.go 中:调用 i18n.EnglishTranslationSet() 取得英文翻译集,经 json.MarshalIndent 格式化为两空格缩进的 JSON,追加一个尾部换行符,写入仓库根目录的 en.json

需要注意 README 中强调的两个后续动作:

  1. 将生成的 en.json 上传到 Crowdin 的 lazygit 项目源文件区;
  2. 上传完成后必须把 en.json 从工作副本中删除——它是一个非版本化(unversioned)的临时产物,不应出现在提交中。

这种"临时导出文件 + 人工上传"的方式,是因为 Crowdin 的 CLI 工具在该项目上尚未打通(scripts/update_language_files.sh 头部的注释也提到 crowdin-cli 一直没能跑通,因此流程偏手工)。

从 Crowdin 拉取翻译:update_language_files.sh 全流程

这是 README 的核心章节,对应实现为 scripts/update_language_files.sh。标准流程三步:

  1. 从 Crowdin 下载全部翻译的 zip 包;
  2. 解压到一个临时目录;
  3. 将解压目录作为唯一参数运行脚本:
scripts/update_language_files.sh <download_dir>

脚本内部做了四件关键事情(见脚本第 21~38 行):

第一步:语言码归一化。 Crowdin 导出葡萄牙语目录名是 pt-PT,但 lazygit 统一使用 pt(同时覆盖巴西葡语与欧洲葡语)。脚本中无法在 Crowdin 端修改该命名,于是在本地重命名:

[ -d "$download_dir/pt-PT" ] && mv "$download_dir/pt-PT" "$download_dir/pt"

第二步:剔除未翻译的空字符串。 对每个语言目录执行:

jq 'del(..|select(. == ""))' < "$d/en.json" > pkg/i18n/translations/$(basename "$d").json

这条 jq 表达式递归删除 JSON 中所有值为空字符串的字段。注释解释了原因:未翻译的条目在导出时就是空串,Crowdin 虽有"导出时跳过空条目"的选项,但对 JSON 格式不生效,只能本地清洗。这一步与运行时 mergo 合并逻辑严丝合缝——留下的都是有效译文,缺省键由英文兜底。(脚本要求 jq 1.7 或更高版本。)

第三步:重新生成下游自动文件。 翻译变化会波及按键绑定速查表,因此脚本最后执行:

go generate ./...

第四步:容错设置。 脚本以 set -e 开头,任何一步失败都会立即中止;参数数量不为 1 时打印用法并退出码为 2。

下游产物:按键速查表的多语言生成

go generate ./... 触发的核心逻辑在 pkg/cheatsheet/generate.go。它调用 i18n.GetTranslationSets()(该函数除英文外逐一读取每个语言 JSON,返回以语言码为键的完整映射),然后对每种语言分别构造一个应用实例,把 gui.language 配置设为该语言,取回本地化后的标题与按键描述,渲染出 Keybindings_<lang>.md 写入 docs-master/keybindings/ 目录——这正是仓库中能看到 Keybindings_ja.mdKeybindings_zh-CN.md 等多份速查表的来源。

这也解释了为什么 README 要求翻译更新后必须跑 go generate:翻译 JSON 与按键文档是"一源多产物"的关系,只更新 JSON 不重新生成会导致速查表滞后。

用户侧配置:language 选项

对普通用户而言,上述流程的终点体现在配置项上。pkg/config/user_config.go 定义了该字段:

Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`

docs/Config.md 中给出的默认值为 language: auto。结合源码可确认其取值语义:

  • auto:按 LANG/LC_ALL 等环境变量自动检测,检测不到或未收录时静默使用英文;
  • en 或其他已支持语言码(zh-CNzh-TWjakonlplrupt 等,以 translations/ 目录实际包含的 JSON 为准):强制使用对应语言,配置了不支持的语言码会在启动时报错。

维护者操作速查

场景 操作 依据
修改/新增英文界面文本 直接编辑 pkg/i18n/english.go,再执行 go run cmd/i18n/main.go 生成 en.json,上传 Crowdin 后删除该临时文件 README"Uploading"节、cmd/i18n/main.go
同步社区最新翻译 从 Crowdin 下载 zip → 解压 → scripts/update_language_files.sh <解压目录>(脚本内部自动完成 pt-PTpt 重命名、jq 清洗空串、go generate README"Updating"节、scripts/update_language_files.sh
验证语言加载行为 运行 go test ./pkg/i18n/,重点看 TestNewTranslationSetFromConfig 的六种场景 pkg/i18n/i18n_test.go
确认某语言是否受支持 查看 pkg/i18n/translations/ 目录下的 JSON 文件名,或 config.schemalanguage 的枚举 pkg/i18n/i18n.gogetSupportedLanguageCodes()

小结

lazygit 的国际化体系是一条清晰的双向流水线:英文文本以 Go 结构体为唯一源头,经 cmd/i18n/main.go 导出为 en.json 送往 Crowdin;社区译文再以 zip 形式回流,经 update_language_files.sh 清洗空串、归一语言码后落盘为机器生成的 JSON,最后 go generate 刷新多语言按键速查表。运行时则通过 go:embed 将全部译文编译进二进制,以英文集为兜底底版做 mergo 合并,并配合 auto 自动检测与显式配置的差异化错误处理。对贡献者最重要的纪律只有一条:translations/ 目录下的 JSON 一律不许手改——一切修改都应走"改 english.go 或同步 Crowdin"两条正道。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384