最近,我继续给自己的 A Tour of Go 多语言项目增加新语言。
这一次做的是:
Arabic / العربية / 阿拉伯语
正式地址:
项目仓库:
和之前做的德语、法语、日语、韩语、西班牙语、意大利语、荷兰语、葡萄牙语、土耳其语、瑞典语、波兰语、繁体中文、印度尼西亚语、越南语相比,这一门语言有一个非常明显的区别:
这是项目里的第一门 RTL(Right-to-Left,从右到左)语言。
也正因为如此,这次真正花时间的,并不只是翻译。
如果只把一门新语言理解成“把英文变成另一种语言”,阿拉伯语可能看不出特别复杂。但当它真的进入网页、代码编辑器、导航、移动端布局、课程目录、SEO、广告和 Production 之后,RTL 会把很多原本隐藏在 LTR 页面中的布局假设全部暴露出来。
这篇文章主要记录这次第一次支持 RTL 的过程,以及实际遇到的几个问题。
一、先看最终效果
阿拉伯语首页现在已经按照 RTL 方向正常显示。

课程目录页同样采用了从右到左的阅读方向。

从表面来看,这似乎只是:
<html lang="ar" dir="rtl">然后让浏览器自己处理方向。
真正做起来,却远远没有这么简单。
二、翻译流程本身其实没有太大变化
阿拉伯语仍然复用了项目已经比较成熟的正式翻译流程。
当前一门语言大致需要经历:
locale 初始化
→ glossary
→ UI / article metadata
→ TranslationUnit generation
→ automatic validation
→ Candidate Snapshot
→ 独立 TranslationUnit Quality Check
→ revision
→ QC 全 A
→ promotion
→ Course SEO
→ Locale Surface Review
→ Preview
→ HUMAN visual gate
→ publish
→ Production
→ Google / Bing / IndexNowTranslationUnit 仍然是:
- 103 个 Page
- 19 个 Example
- 共 122 个 Unit
这一部分其实和越南语、印度尼西亚语等语言没有本质区别。
阿拉伯语真正开始变复杂,是在翻译完成以后。
三、第一次遇到真正的 RTL 布局问题
最明显的问题发生在桌面端课程页面。
A Tour of Go 的课程页面本身是典型的双栏布局:
课程内容 | 代码编辑器在普通 LTR 页面中:
左边:lesson
右边:editor过去这些年,这套布局一直工作正常。
但第一次切换为 RTL 后,页面直接暴露了一个问题:
课程内容区和代码编辑器发生了明显重叠。

问题并不是简单的文字 text-align:right。
原本布局中其实存在很多“物理方向”的假设,例如:
left: 50%;
float: left;这些规则在 LTR 页面中没有问题,但当整个文档进入 RTL 后:
- 内容流方向变了;
- 某些元素的定位逻辑却仍然按照物理 left/right;
- 两套方向逻辑叠在一起,就产生了重叠。
最后的处理方向是:
课程布局使用逻辑方向,而不是继续假定 lesson 永远在物理左侧、editor 永远在物理右侧。
最终:
LTR
lesson:左
editor:右
RTL
lesson:右
editor:左修复后,阿拉伯语桌面课程页面才恢复正常。

这也是我这次第一次比较明显地感受到:
RTL 支持不是“翻转文字”,而是在重新审视整个页面里哪些东西属于“阅读方向”,哪些东西属于“固定技术方向”。
四、课程目录和语言菜单也必须真正适应 RTL
课程目录页以及课程内部的目录菜单,也需要自然地从右侧开始阅读。

语言菜单也是一样。

这里有一个很容易被忽略的问题:
并不是所有东西都应该跟随 RTL。
例如:
- 阿拉伯语文本:RTL
- 页面导航:RTL
- 课程章节关系:RTL
- Go 源代码:仍然必须 LTR
- URL:通常仍然保持 LTR
- 技术标识符:不应该被浏览器按照普通阿拉伯文字重新排列
也就是说,一个页面内部实际上同时存在两套方向。
五、阿拉伯语页面是 RTL,但 Go 代码仍然必须是 LTR
这一点尤其重要。
代码编辑器不能因为整个页面设置了:
direction: rtl;就跟着变成 RTL。
Go 代码:
package main
import "fmt"
func main() {
fmt.Println("Hello")
}无论页面是什么语言,代码的逻辑方向都应该保持:
左 → 右因此 CodeMirror、源码区域和相关 editor 必须显式保持 LTR。

最终页面其实变成了一种混合布局:
页面结构:RTL
自然语言:RTL
代码编辑器:LTR这比单纯的“整个页面翻转”要合理得多。
六、移动端又出现了一个更隐蔽的问题
桌面端修完之后,我原本以为 RTL 基本处理完成了。
结果在移动端 HUMAN visual gate 中又发现了一个新问题:
代码编辑器只剩下大约 143px 宽。

这个问题一开始看起来很像 editor 本身缺少:
width: 100%;于是很容易直接给 editor 加一个宽度补丁。
但继续排查以后,发现真正的根因并不在 editor。
原本移动端设计里,editor 上方的几个操作按钮应该是:
一个按钮
一行
一个按钮
一行
一个按钮
一行原有 CSS 在 mobile breakpoint 中使用:
float: none;但 RTL 新增的一条高优先级规则又把部分按钮改成了:
float: left;结果这些 float 脱离了原本的布局。
随后 editor 为了避开浮动元素,被浏览器压缩到了很窄的一块区域。
也就是说:
看起来是 editor 宽度问题,真正的根因却是上方按钮在 RTL 下意外恢复了 float。
最终并没有靠:
#file-editor {
width: 100%;
}硬顶过去。
而是修正真正的响应式逻辑:
- 桌面 RTL 可以根据布局需要使用对应 float;
- mobile 始终恢复
float:none; - 继续保持一个按钮一行;
- editor 自然占满父容器。
修复后移动端恢复正常。

这个问题也是这次最典型的一次:
不要看到哪里变窄,就直接给哪里写 width:100%。
有时候真正的问题来自它前面的兄弟元素。
七、RTL 还会改变“左”和“右”的语言语义
这次还有一个比较有意思的问题。
A Tour of Go 的 Welcome 页面会告诉用户:
- 上一页在哪里;
- 下一页在哪里;
- 使用什么箭头进行导航。
原来的 Arabic TranslationUnit 翻译本身没有语言错误。
但最开始审核时,整个 Tour 仍然基本按照 LTR UI 运行,所以翻译里对“左箭头”“右箭头”的描述,是基于当时实际界面的。
后来真正完成 RTL 布局以后:
页面物理方向发生变化了。
这意味着原本已经通过翻译审核的一句话,又因为 UI 行为变化而重新变得不准确。
所以这次不得不重新走:
TranslationUnit revision
→ validation
→ Candidate Snapshot
→ 独立 QC
→ A
→ promotion随后因为 Page identity 改变,又继续触发:
Course SEO refresh
→ Locale Surface Review r2这件事给我的一个提醒是:
语言质量并不完全独立于 UI。
尤其是下面这些词:
left
right
above
below
previous
next
top-left
top-right只要布局变化,它们就可能从“正确翻译”变成“错误描述”。
八、第一次 RTL 也迫使我重新验证 LTR
共享 CSS 被修改以后,我没有直接继续上线阿拉伯语。
因为这次修改的是:
shared layout
shared app.css如果只验证 Arabic,是不够的。
于是我又单独启动了 French 完整 projection,对已有 LTR 页面进行了回归。
重点确认:
- desktop lesson/editor 没有被反向影响;
- mobile editor 宽度正常;
- Run / Format / Reset 正常;
- CodeMirror 正常;
- PageUp / PageDown 正常;
- existing LTR layout 不发生退化。
French 测试最终通过。
这一步虽然增加了一些时间,但我觉得非常有必要。
因为第一门 RTL 语言实际上已经不是单纯的:
“新增一个 locale”
而是在做:
“修改所有 locale 都会复用的布局基础设施”。
九、测试 PageUp / PageDown 时,又顺便发现了一个上游问题
在做 French 和 Arabic 的快捷键对比时,我又注意到一个现象:
有时候:
PageDown非常灵敏,每按一次就翻一页。
有时候却像失效了一样。
一开始我怀疑:
是不是 RTL 又带来了什么键盘事件问题?
后来做了自动测试后发现并不是。
在 French 和 Arabic 中:
连续 12 次 PageDown只要焦点保持在 editor / lesson 区域中,都可以做到:
12 / 12问题真正和焦点位置有关。
例如焦点位于:
CodeMirror textareaPageDown:
/tour/welcome/1
→ /tour/welcome/2正常。
但是当焦点位于顶部的:
Toggle theme或者:
A Tour of Go LogoPageDown 就不会翻页。
更有意思的是,我随后直接测试了官方:
结果同样可以稳定复现。
因此可以确定:
这不是 Arabic,也不是 RTL bug,而是当前官方 A Tour of Go 就存在的通用焦点依赖问题。
最后我向 Go 官方提交了:

本地项目暂时没有为了这个问题再维护一份 fork-specific patch,而是把它记录成 Deferred Issue,等待 upstream 处理。
这也是我现在比较倾向的一种维护方式:
能确认属于 upstream 的问题,就尽量先 upstream,而不是每遇到一个问题就在自己的 fork 中增加长期差异。
十、第一门 RTL 语言最终用了大约 7 小时
前一门越南语,我实际大约用了:
5 小时左右。
阿拉伯语则大约用了:
7 小时左右。
多出来约:
2 小时。
也就是大约增加了:
40%
不过这 2 小时并不是因为阿拉伯语文本本身难翻很多。
真正增加的主要是:
RTL desktop layout
RTL mobile responsive layout
direction-sensitive TranslationUnit revision
Course SEO refresh
第二轮 Locale Surface Review
LTR regression
PageUp/PageDown upstream diagnosis
shared-assets deployment另外 Production publish 时还碰到了一次本机 /tmp tmpfs quota:
disk quota exceeded最后清理了大约:
4.4G历史 Go build / gocache 临时目录后重新 publish 成功。
因此这次 7 小时更准确的理解应该是:
第一门 RTL locale + 一次性的 RTL 基础设施建设成本。
而不应该简单理解成:
阿拉伯语永远比越南语多花 40%。
十一、下一门 RTL 语言应该不会再花这么久
这次完成以后,项目已经第一次具备了比较完整的 RTL 基础。
包括:
HTML dir=rtl
桌面 lesson/editor 镜像布局
RTL splitter
mobile explorer controls
mobile editor full-width
CodeMirror LTR
RTL course navigation
RTL language selector
RTL browser regression
LTR shared-layout regression所以将来如果再增加:
- Hebrew
- Persian
- Urdu
这一类 RTL 语言,至少基础设施层面已经不需要从零开始。
当然,不同语言依然可能暴露新的问题。
但我预计下一门 RTL locale 的成本,应该会重新向普通 locale 靠近。
目前我自己的实际基线大致可以记成:
成熟 LTR locale:约 5 小时
第一门 RTL locale:约 7 小时后续如果再做两三门,就能进一步确认这个估算是否稳定。
十二、这次真正重要的不是“又上线了一门语言”
阿拉伯语上线以后,A Tour of Go 多语言项目当然又多了一个新的 production locale。
但对我来说,这一次更重要的是:
项目第一次真正跨过了 LTR-only 的边界。
在此之前,即使支持十几门语言,本质上大家的页面方向仍然都相同。
RTL 是第一次迫使我重新检查:
- CSS 中的 left/right;
- float;
- responsive breakpoint;
- editor direction;
- UI 空间关系;
- 文案中的方位词;
- shared assets;
- LTR regression;
- 浏览器交互;
- HUMAN visual gate。
很多长期存在但从未暴露的问题,也是在这个过程中第一次被看见。
这也是多语言项目做到后面越来越有意思的地方:
最开始是在做:
翻译。
继续做下去以后,逐渐变成:
国际化工程。
再继续往后,其实是在维护:
一套能够让不同语言、不同文字方向、不同搜索市场共同工作的长期发布系统。
而 Arabic,算是这个项目第一次真正走进 RTL 世界。
A Tour of Go 多语言翻译项目
本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。
项目入口:
✅ Brazilian Portuguese — Português (Brasil)
✅ Dutch — Nederlands
✅ French — Français
✅ German — Deutsch
✅ Italian — Italiano
✅ Japanese — 日本語
✅ Korean — 한국어
✅ Simplified Chinese — 简体中文
✅ Spanish — Español
✅ Turkish — Türkçe
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n
项目正在持续扩展更多语言版本,并长期维护翻译质量、生产发布与后续更新。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。

