Apache Airflow UI 荷兰语(nl)本地化指南:翻译原则、术语约定与 i18n 加载实现解析
Airflow 的 Web UI 现已内置完整的荷兰语(Dutch, nl)翻译,并在 语言选择器 中作为官方支持语言提供(Nederlands)。本文以仓库中面向翻译维护者的 荷兰语翻译指南 为核心,系统讲解荷兰语本地化的术语选择原则、一致性校验方法,并结合前端 i18n 源码说明这些翻译文件是如何被加载、探测与回退的。读完本文,你可以独立评估、补充或修正 Airflow UI 的荷兰语翻译,并理解其背后的 i18n 架构。
一、荷兰语翻译文件的仓库位置与结构
Airflow UI 的前端源码位于 airflow-core/src/airflow/ui,其国际化资源按 语言代码/命名空间 两级目录组织,位于:
airflow-core/src/airflow/ui/public/i18n/locales/
├── en/ # 英文基准(默认语言)
├── nl/ # 荷兰语
├── zh-CN/ # 简体中文
├── zh-TW/ # 繁体中文
└── ... # 共 21 种语言
其中 nl/ 目录内容与英文基准 en/ 逐文件对齐,共 10 个命名空间文件加 1 份指南:
| 文件 | 对应 UI 区域 |
|---|---|
| common.json | 全局通用词条(按钮、状态、弹窗、导航、表格) |
| dags.json | DAG 列表页与 DAG 操作 |
| dag.json | DAG 详情页 |
| dashboard.json | 仪表盘页 |
| assets.json | Assets(数据资产)页 |
| browse.json | Browse(浏览)菜单 |
| admin.json | Admin 管理区域 |
| components.json | 通用组件 |
| hitl.json | Human-in-the-loop 相关界面 |
| tasks.json | 任务相关界面 |
| README.md | 荷兰语翻译指南(本文核心) |
这份 README 正是翻译维护者的"准入文档":它规定荷兰语翻译应遵守的术语口径,任何新翻译都必须先对照它再落笔。
二、荷兰语翻译的四大核心原则
1. Airflow 专有术语一律保留英文
Dag、XCom、Asset 等是 Airflow 生态中被广泛认可的技术对象,翻译后反而会破坏用户的既有认知,因此指南明确要求保留英文原词,并且统一拼写为 Dag(而非全大写的 DAG),以与 Airflow 自身的命名约定保持一致。
这在翻译文件中可以找到大量印证:
"dag_one": "Dag"、"dag_other": "Dags"(common.json)"asset_one": "Asset"、"asset_other": "Assets"(common.json)- 导航栏中的
"dags": "Dags"、"assets": "Assets"(common.json)
2. 对用户使用非正式称呼 "je/jouw"
指南规定荷兰语翻译统一采用非正式的 "je/jouw"(你/你的) 语域,而不是正式的 "u"。这一选择顺应了荷兰语技术软件本地化的普遍趋势,让 UI 更现代、更亲切。例如 common.json 中的登出确认语:
"logoutConfirmation": "Je staat op het punt om uit te loggen uit de applicatie."
以及权限拒绝提示(common.json):
"errors": {
"forbidden": {
"description": "Je hebt geen permissies om deze actie uit te voeren.",
"title": "Toegang geweigerd"
}
}
3. 翻译一致性:Task 固定译为 taak
基于维护者共识,Task 在所有位置统一翻译为 taak(句首时首字母大写为 Taak),而不是像其他技术对象那样保留英文。从 common.json 可以看到复数形式也保持一致:
"task_one": "Taak",
"task_other": "Taken"
以此派生出的复合词同样遵守该约定:Taakgroep(Task Group)、Taakinstantie(Task Instance)、Taak documentatie(Task documentation)等。
4. 既定 UI 映射表
指南给出了以下已经定案、不可随意更改的界面词映射:
| 英文 | 荷兰语 |
|---|---|
Schedule |
Planning |
Queue |
Wachtrij |
State |
Status |
Run |
Run(数据工程领域的标准术语,保留英文) |
这些映射在实际翻译文件中均有对应实现,例如:
- DAG 详情页的调度字段
"schedule": "Planning"(common.json) - 任务实例的队列字段
"queue": "Wachtrij"(common.json) - 状态总称
"state": "Status"(common.json) - Run 相关的
"dagRun_one": "Dag Run"、"runId": "Run ID"(common.json)
三、一致性校验:以 common.json 为唯一基准
指南的 Consistency 一节给出了一条硬性规则:
所有新翻译都必须对照
common.json进行校验,确保按钮(如Opslaan、Annuleren、Verwijderen)与状态(如Lopend、Mislukt、Succesvol)使用标准的荷兰语术语。
也就是说,common.json 是整个荷兰语本地化的术语基准库。任何新词条若涉及按钮或状态,都应先查基准库,而不是凭个人偏好新造译法。从源码中可以提取出当前基准库中的核心标准译法:
按钮与操作(common.json):
"modal": {
"add": "Toevoegen",
"cancel": "Annuleer",
"confirm": "Bevestig",
"delete": { "button": "Verwijder", "...": "..." },
"save": "Opslaan"
}
值得注意的细节:指南中举例按钮标准译法为
Annuleren,而当前 common.json 中modal.cancel的实际值为Annuleer。这正是"必须对照基准库核实"这条规则存在的意义——维护文档与词条文件之间偶有细微出入,翻译者应以仓库中实际生效的 JSON 为准。
任务状态全集(common.json):
"states": {
"awaiting_input": "Wachtend op invoer",
"deferred": "Uitgesteld",
"failed": "Mislukt",
"no_status": "Geen status",
"none": "Geen status",
"open": "Open",
"planned": "Gepland",
"queued": "Wachtend",
"removed": "Verwijderd",
"restarting": "Herstartend",
"running": "Lopend",
"scheduled": "Gepland",
"skipped": "Overgeslagen",
"success": "Succesvol",
"up_for_reschedule": "Wachtend op herplanning",
"up_for_retry": "Wachtend op een nieuwe poging",
"upstream_failed": "Upstream mislukt"
}
这套状态译法是所有页面(DAG 列表、Dag Run 详情、任务实例详情)共用的,新增翻译时状态类词条必须与之一致。
四、源码视角:荷兰语翻译是如何被加载的
理解了"翻译什么",再看"翻译怎么生效"。Airflow UI 使用 i18next + react-i18next 构建国际化层,核心配置在 airflow-core/src/airflow/ui/src/i18n/config.ts:
- 命名空间(namespaces):
common、dashboard、dags、admin、browse、assets、components、hitl,与nl/目录下的 JSON 文件一一对应(dag.json、tasks.json等按需加载); - 默认语言与回退:
defaultLanguage = "en",即某个键在荷兰语文件中缺失时自动回退英文,避免 UI 出现空白; - 加载路径:
${basePath}/static/i18n/locales/{{lng}}/{{ns}}.json?v={{version}},翻译文件作为静态资源随 UI 分发,并带版本号参数做缓存失效(cache busting),防止部署新版本后浏览器/CDN 继续使用旧翻译; - 语言探测顺序:
localStorage→navigator→htmlTag,用户在设置中手动选择的语言会持久化到 localStorage; - 区域码归约:
convertDetectedLanguage会把nl-BE之类的浏览器区域码归约为受支持的nl,同时保证zh-CN/zh-TW这类需要保留区域后缀的语言不被误折叠。
这套机制意味着:只要 nl/ 下的 JSON 词条完整、键名与英文基准 en/ 对齐,荷兰语 UI 就能自动生效,无需改动任何组件代码。对应的语言探测逻辑还有专门的单元测试覆盖,见 airflow-core/src/airflow/ui/src/i18n/config.test.ts。
此外,日期、时间等会随语言切换的本地化内容,由 registerDayjsLocaleSync 与 i18next 的 languageChanged 事件联动同步(config.ts),保证切换到荷兰语后时间显示也符合荷兰语习惯。
五、从实际词条看荷兰语本地化的覆盖范围
荷兰语翻译并非只覆盖少量高频词,而是深入到了 Airflow UI 的各个业务模块:
DAG 列表与操作(dags.json) 覆盖了排序、筛选、收藏、删除等交互,例如:
"dagActions": {
"delete": {
"button": "Verwijder Dag",
"warning": "Dit zal alle metadata gerelateerd aan de Dag verwijderen, inclusief Runs en Tasks."
}
},
"filters": {
"paused": { "active": "Actief", "all": "Alles", "paused": "Gepauzeerd" }
},
"sort": {
"displayName": { "asc": "Sorteer op weergavenaam (A-Z)", "desc": "Sorteer op weergavenaam (Z-A)" }
}
仪表盘(dashboard.json) 覆盖健康检查、统计卡片、截止期限等,例如:
"health": {
"dagProcessor": "Dag Processor",
"metaDatabase": "MetaDatabase",
"scheduler": "Scheduler",
"triggerer": "Triggerer",
"healthy": "OK",
"unhealthy": "Fout"
},
"stats": {
"activeDags": "Actieve Dags",
"failedDags": "Mislukte Dags",
"runningDags": "Lopende Dags",
"queuedDags": "Dags in de wachtrij"
}
复数与插值机制:荷兰语词条大量使用 i18next 的复数形式(_one/_other)与插值变量,例如 "pendingDagRun_one": "{{count}} Wachtende Dag Run"、"pendingDagRun_other": "{{count}} Wachtende Dag Runs"(common.json)。新增翻译时必须保留这些键名后缀与 {{变量}} 占位符,否则页面会显示原始键名或丢失动态数据。
六、给翻译维护者的实操建议
结合指南与源码,梳理一份可执行的荷兰语翻译工作流:
- 以英文基准为底稿:对照 en/ 目录下同名 JSON,确保键名完全一致、无遗漏键;
- 先读指南再动笔:通读 nl/README.md,遵守四大原则——专有名词(
Dag/XCom/Asset/Run)保留英文、非正式je/jouw语域、Task统一为taak、既定映射表不可推翻; - 以 common.json 为准绳:涉及按钮与状态时,从 common.json 复用标准译法,保证跨页面一致;
- 保留结构占位符:不破坏
{{count}}、{{resourceName}}等插值变量,不省略_one/_other复数键; - 留意潜在笔误:仓库现有词条中仍存在个别拼写问题,例如 common.json 中
"Taaakgroep documentatie"(多了一个a),这类问题可以作为贡献者着手修正的低风险切入点; - 验证加载路径:翻译属于静态资源,通过
static/i18n/locales/nl/{{ns}}.json按版本号分发,改动后需配合版本刷新观察 UI 效果。
总结
Airflow UI 的荷兰语本地化由"指南 + 词条文件 + i18n 运行时"三层构成:nl/README.md 定口径,nl/ 下 10 个 JSON 文件承载词条,i18n/config.ts 负责按语言探测、按命名空间加载并以英文兜底。对开发者而言,理解了这套结构与术语约定,无论是为荷兰语贡献翻译、审查 PR,还是借鉴这套"语言目录 + 翻译指南"模式搭建其他语言的本地化流程,都能直接上手、有据可依。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00