Apache Airflow UI 国际化翻译实战指南:从 Locale 注册到 Breeze 完整性校验
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
本篇指南基于 Airflow 仓库的翻译技能文档 SKILL.md 编写,系统讲解如何为 Airflow UI 新增或更新界面翻译:包括 locale 目录搭建、supportedLanguages与复数后缀等三处关键配置、breeze ui check-translation-completeness脚手架与校验命令、全局翻译规则(术语保留英文、占位符、复数形式、热键),并深入到 i18n 初始化代码 与 Breeze 校验命令源码 层,帮助你独立完成一次可合并的 Airflow 界面本地化任务。
一、任务判定:新增翻译还是更新已有翻译
翻译工作分为两类,判定方法很简单:检查目标语言的目录是否已存在于airflow-core/src/airflow/ui/public/i18n/locales/<locale>/下。
- 目录已存在→ 走「更新已有翻译」流程(本文第四节);
- 目录不存在→ 走「新增翻译」流程,需要先完成三处配置文件更新(本文第三节)。
当前仓库locales/目录下已包含 ar、ca、de、el、en、es、fr、he、hi、hu、it、ja、ko、nl、pl、pt、ru、th、tr、zh-CN、zh-TW 共 21 个语言目录,其中en/是默认语言(default locale),也是所有翻译的唯一权威来源。
二、翻译文件结构与 i18n 运行架构
2.1 命名空间(Namespace)JSON 文件
所有翻译文件都是 JSON,位于:
airflow-core/src/airflow/ui/public/i18n/locales/<locale-name>/每个语言目录包含一组与英文 locale(en/)镜像的命名空间 JSON 文件,当前共 10 个:
admin.json、assets.json、browse.json、common.json、components.json、 dag.json、dags.json、dashboard.json、hitl.json、tasks.jsonSKILL 文档中这两个文件清单被包裹在<!-- START namespace-files ... END namespace-files -->注释之间,说明该列表由工具自动同步更新,新增命名空间文件后无需手动修改文档。以 en/common.json 为例,可以看到嵌套 key、复数 key 与跨命名空间引用的典型形态:
{ "admin": { "Connections": "Connections", "Pools": "Pools" }, "asset_one": "Asset", "asset_other": "Assets", "assetEvent_one": "$t(common:asset_one) Event", "assetEvent_other": "$t(common:asset_one) Events", "dag_one": "Dag", "dag_other": "Dags" }2.2 运行时如何加载翻译:config.ts
UI 前端基于i18next + react-i18next + i18next-http-backend + i18next-browser-languagedetector构建。config.ts#L32-L54 中的supportedLanguages数组定义了 UI 语言切换器可展示的全部语言(code+ 该语言自称的name),defaultLanguage固定为"en"。
初始化逻辑(config.ts#L116-L161)有几个值得注意的设计:
- 语言检测顺序为
["localStorage", "navigator", "htmlTag"],即以用户手动选择(localStorage 缓存)优先,其次浏览器语言,最后<html>标签; - 回退语言
fallbackLng为en——任何缺失 key 或未支持的 locale 都回落到英文原文,这也是「英文 locale 是所有翻译来源」这一约定得以成立的前提; - 加载路径为
${basePath}/static/i18n/locales/{{lng}}/{{ns}}.json?v=<version>,?v=参数通过VersionService.getVersion()获取 Airflow 版本号作为缓存破坏器(cache buster),避免 CDN/浏览器长期缓存旧翻译包导致新增 key 缺失(config.ts#L158-L161); convertDetectedLanguage(config.ts#L79-L114)是一个精细的浏览器语言归一化函数:它按navigator.languages的顺序逐项把en-GB归约为en、pt-BR归约为pt;对中文这类带地区变体的语言,利用Intl.Locale.maximize()依据 CLDR 脚本推导把zh-HK/zh-Hant映射到zh-TW、zh-SG/zh映射到zh-CN,从而避免zh-CN/zh-TW被粗暴剥离成不支持的zh。
对翻译者的含义:你新增的语言code必须与supportedLanguages中的 code 严格一致,否则浏览器即使检测到该语言也不会加载对应的 JSON。
三、新增一种语言翻译
3.1 创建 locale 目录
mkdir -p airflow-core/src/airflow/ui/public/i18n/locales/<locale>/3.2 在supportedLanguages中注册语言
更新 airflow-core/src/airflow/ui/src/i18n/config.ts,把语言加入supportedLanguages数组,并保持数组现有的字母序:
{ code: "<locale>", name: "<native name>" },name使用语言的自称写法,例如{ code: "ja", name: "日本語" }。
3.3 配置复数后缀PLURAL_SUFFIXES
更新 dev/breeze/src/airflow_breeze/commands/ui_commands.py,在PLURAL_SUFFIXES字典中添加该语言的复数后缀(ui_commands.py#L75-L97):
"<locale>": ["<suffixes>"],后缀种类取决于语言的 i18next 复数规则,差异非常大。从源码中现有的配置可以直接读到真实案例:
| 语言 | 后缀 | 说明 |
|---|---|---|
ar(阿拉伯语) | _zero,_one,_two,_few,_many,_other | 6 种复数形式 |
pl(波兰语) | _one,_few,_many,_other | 4 种形式 |
es/fr(西/法语) | _one,_many,_other | 3 种形式 |
he(希伯来语) | _one,_two,_other | 3 种形式 |
it/pt(意/葡语) | _zero,_one,_many,_other | 含_zero |
ja/ko/th/zh-CN/zh-TW | 仅_other | 无复数区分 |
| 多数语言 | ["\_one", "\_other"] | 源码中以MOST_COMMON_PLURAL_SUFFIXES常量复用 |
若不确定目标语言需要哪些后缀,SKILL 文档建议查阅 i18next 官方的复数规则演示工具(jsfiddle demo)确认。
源码印证:PLURAL_SUFFIXES不仅用于生成模板,也是完整性校验的核心输入。expand_plural_keys 会把英文 locale 中每个复数基 key(如dagRun_one)按目标语言的后缀表展开成「必需 key 集合」;若某语言在PLURAL_SUFFIXES中查不到后缀,命令会直接报错退出(并提示去 i18next 规则工具查询)。此外该函数还会读取英文值中的{{count}}占位符与英文是否定义了多个复数形式来决定是否展开,避免把语言特定的复数 key 误判为 unused——这正是 3.3 节配置必须准确的原因。
3.4 配置 PR 自动打标:.github/boring-cyborg.yml
在 .github/boring-cyborg.yml 的labelPRBasedOnFilePath段落下按字母序添加:
translation:<locale>: - airflow-core/src/airflow/ui/public/i18n/locales/<locale>/*当前文件中已存在translation:default(对应locales/en/*)以及 ar 到 zh-TW 的完整语言打标规则(boring-cyborg.yml#L413-L477)。这样配置后,涉及某个语言翻译文件的 Pull Request 会自动被贴上translation:<locale>标签,便于维护者分发给对应的语言社区。
3.5 用 Breeze 生成翻译脚手架
三处配置就绪后,执行:
breeze ui check-translation-completeness --language <locale> --add-missing该命令会把英文 locale 下每个命名空间文件的全部 key 复制到新语言目录,每个值都填充为TODO: translate:前缀的英文原文桩(stub):
{ "allRuns": "TODO: translate: All Runs", "blockingDeps": { "dependency": "TODO: translate: Dependency", "reason": "TODO: translate: Reason" } }源码印证:add_missing_translations 的写入行为保证了几个工程细节——缺失 key 一律写成TODO: translate: <英文原文>;复数基 key 会按该语言后缀表一次性补齐所有形式;写入前用natural_sort_key(模拟 eslint-plugin-jsonc 的natural: true自然排序,如2 < 10、忽略大小写主排序)对字典递归排序,保证 JSON key 顺序与前端 lint 规则一致;文件以ensure_ascii=False, indent=2输出并补换行,非 ASCII 字符(中文、日文等)原样落盘。
四、翻译规则(全局)
以下规则全局适用;若目标语言存在 locale 专属指南(见第六节)且规定不同,以语言指南为准。
4.1 默认保留英文的术语
| 术语 | 保留原因 |
|---|---|
Airflow | 产品名 |
Dag/Dags | Airflow 约定:永远写作Dag,绝不写作DAG |
XCom/XComs | Airflow 跨任务通信机制名 |
Provider/Providers | Airflow 扩展包名 |
REST API | 标准技术术语 |
JSON | 标准技术格式名 |
ID | 通用缩写 |
PID | Unix 进程标识符 |
UTC | 时间标准 |
Schema | 数据库术语 |
语言指南可针对存在成熟本地惯例的个别条目做覆盖(例如中文指南要求「Dag 执行」这类中英混排时空格规则)。
4.2 变量与占位符
翻译字符串使用 i18next 的{{variable}}插值格式。规则:
- 永不翻译、永不删除
{{…}}内部的变量名; - 占位符可以为符合目标语言语序而调整位置;
- 变量名的大小写必须原样保留(如
{{dagDisplayName}}不能写成{{dagDisplayname}})。
4.3 复数形式
Airflow 使用 i18next 复数后缀(_one、_other,以及按需的_zero、_two、_few、_many)。你需要为该语言要求的所有后缀提供翻译——具体是哪几个由语言指南指定;若尚无语言指南,则查询 i18next 复数规则工具,且至少要提供_one与_other。
源码印证:完整性校验通过 expand_plural_keys 把「英文 key 集合 + 语言后缀表 + 英文值是否含{{count}}」换算成该语言的必需 key 集合,因此多交一个该语言不需要的后缀 key 会被判为 unused、少交一个则判为 missing。
4.4 热键
热键值(如"hotkey": "e")是字面按键绑定,不应翻译,除非语言指南另有规定。
五、更新已有翻译
当目标语言目录已存在,需要补漏、修订或清理陈旧 key 时:
- 先读语言指南(见第六节),建立术语表与格式约定;
- 通读该语言现有 JSON,学习既有术语。与既有翻译保持一致至关重要——某个词如果已经被翻译过,必须复用那个确切的译法;
- 检查完成度现状:
breeze ui check-translation-completeness --language <locale>- 若有missing(缺失)key,用桩补齐:
breeze ui check-translation-completeness --language <locale> --add-missing- 若有unused(冗余)key——即非必需 key(英文 locale 中不存在,或是该语言不需要的一种复数后缀形式)——删除之:
breeze ui check-translation-completeness --language <locale> --remove-unused源码印证:remove_unused_translations 会递归删除未要求 key,并顺带删除递归后变空的字典,避免留下空对象残骸;随后同样执行自然排序并格式化重写。
最后按语言指南翻译所有TODO: translate:条目(连同前缀一起替换为目标语言译文),然后进入第七节验证。
六、Locale 专属指南
翻译开始前应阅读目标语言对应的指南文件,其中包含该语言的术语表(glossary)、语气规则与格式约定;与全局规则冲突时以语言指南为准。SKILL 文档给出的指南索引(以下路径已转换为仓库根目录相对路径):
| Locale | 语言 | 指南文件 |
|---|---|---|
ar | 阿拉伯语 | locales/ar.md |
ca | 加泰罗尼亚语 | locales/ca.md |
de | 德语 | locales/de.md |
el | 希腊语 | locales/el.md |
es | 西班牙语 | locales/es.md |
fr | 法语 | locales/fr.md |
he | 希伯来语 | locales/he.md |
hi | 印地语 | locales/hi.md |
hu | 匈牙利语 | locales/hu.md |
it | 意大利语 | locales/it.md |
ja | 日语 | locales/ja.md |
ko | 韩语 | locales/ko.md |
nl | 荷兰语 | locales/nl.md |
pl | 波兰语 | locales/pl.md |
pt | 葡萄牙语 | locales/pt.md |
th | 泰语 | locales/th.md |
tr | 土耳其语 | locales/tr.md |
zh-CN | 简体中文 | locales/zh-CN.md |
zh-TW | 繁体中文 | locales/zh-TW.md |
若目标语言的指南文件尚不存在,则只遵循 SKILL 文档中的全局规则。
以 zh-CN.md 为例,语言指南的典型内容深度包括:
- 复数规则:简体中文不区分单复数,
_one与_other使用相同译文(如"dagRun_one": "Dag 执行"与"dagRun_other": "Dag 执行")——注意这与PLURAL_SUFFIXES中zh-CN: ["_other"]的配置共同决定了校验行为; - 空格规则:中文与相邻英文、数字、符号之间插入半角空格(正确示例
"Dag 执行"、"最近 12 小时"、"{{count}} 个连接";错误示例"Dag执行"、"最近12小时"); - 标点规则:中文语境用全角标点
,。:?!(),英文术语、JSON、代码内部用半角标点。
七、验证
翻译完成后依次执行两项检查。
7.1 完整性校验
breeze ui check-translation-completeness --language <locale>输出表应显示:0 missing、0 TODOs、0 unused、100% Coverage。
源码印证:进度表由 print_translation_progress 生成,逐文件统计「英文基础 key 数 / 复数展开 key 数 / 必需总数 / 已翻译数 / 缺失数 / 覆盖率 / TODO 数 / 冗余数」。其中「已翻译」的判定是 is_todo_value:值以TODO: translate开头即计为未翻译(TODO 计入 magenta 列而非 translated 列);行颜色语义为——有 missing 红色、仅 TODO/unused 黄色、全部干净加粗绿色。若所有语言都不干净,命令还会额外打印一张「Total Coverage per Language」汇总表与中位数覆盖率,覆盖率 ≥95% 绿色、>90% 黄色、其余红色。
7.2 运行 pre-commit 钩子
prek run --from-ref main --hook-stage pre-commit用于自动修复格式、许可证头、lint 等问题(JSON 文件即依赖此环节做排序与格式化兜底)。
八、完整工作流速查
新增语言(以<locale>代指)的端到端步骤:
mkdir -p airflow-core/src/airflow/ui/public/i18n/locales/<locale>/- config.ts 的
supportedLanguages追加{ code, name }(字母序) - ui_commands.py 的
PLURAL_SUFFIXES追加该语言后缀表 - boring-cyborg.yml 的
labelPRBasedOnFilePath追加translation:<locale>规则 breeze ui check-translation-completeness --language <locale> --add-missing生成TODO: translate:脚手架- 阅读语言指南(第六节索引),逐条翻译(含 4.1–4.4 全局规则)
breeze ui check-translation-completeness --language <locale>确认 0 missing / 0 TODOs / 0 unused / 100%prek run --from-ref main --hook-stage pre-commit
更新已有语言则跳过 1–5,直接从「读指南 → 读现有译文保证术语一致 → 检查完成度 →--add-missing/--remove-unused→ 翻译 → 验证」开始。
九、适用前提与限制
- 本文所有命令基于当前仓库中 dev/breeze 与 airflow-core UI 源码 的实际实现;
breeze ui check-translation-completeness支持--language/-l、--add-missing、--remove-unused、--verbose、--dry-run选项(ui_commands.py#L611-L637),且对英文 locale 本身禁止做完整性检查(命令会直接报错退出); - 从 config.ts 的
supportedLanguages看,代码中还注册了ru(俄语),其复数后缀为["_one", "_few", "_other"],但 SKILL 文档的指南索引表中暂无对应语言指南文件——从文档结构看,该语言目前仅遵循全局规则; namespaces常量(config.ts#L57-L66)与 locales 目录中的 JSON 文件清单存在细微差异(例如tasks.json存在于 locales 目录而不在该常量列表中),实际以en/目录下的文件为准——这正是「英文 locale 是所有翻译的主要来源」这一约定的落地方式。
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考