System & Services 进阶 20m
搞定从右到左的语言支持
Get it right (to left)
2022年6月6日
一句话判断
阿拉伯语和希伯来语的 RTL 适配比你想象的复杂,但 Apple 的框架已经帮你处理了大部分工作——前提是你知道哪些地方需要手动介入。
这场 Session 讲了什么
阿拉伯语是 Apple 平台使用量前十的语言之一。RTL(Right-to-Left)语言不只是文字方向反转——它影响整个 UI 布局,从工具栏到侧边栏,从图标到数字显示。
Session 系统性地覆盖了 RTL 适配的六大领域:文本方向与对齐、图标镜像、控件方向、UI 布局、阿拉伯数字显示、以及测试方法。
好消息是,Apple 的 UI 框架默认帮你处理了大量 RTL 适配:CoreText 自动处理双向文本排版,UIKit/SwiftUI 自动设置文本方向和对齐,系统控件自动翻转。但图标、自定义布局和特定控件方向需要你手动处理。
值得深挖的点
- 自然对齐 vs 强制对齐:UI 控件默认使用”自然对齐”(Natural Alignment),在 RTL 语言下自动右对齐。如果你硬编码了
.left对齐,RTL 布局就会出错。应该使用.leading代替.left。 - 图标的 RTL 处理:在 Xcode 的 Asset Catalog 中,每个 Image Set 都有 RTL 变体。系统可以自动镜像图标,也可以手动提供 RTL 版本。关键规则是:有方向性的图标(如返回箭头)需要翻转,无方向的图标(如加号)不需要。
- 双向文本的复杂性:阿拉伯语段落中的数字仍然从左到右显示,英文人名也保持原始方向。这意味着一段文字可能包含多个方向的片段。CoreText 自动处理,但如果你用自定义布局引擎,需要自己处理 bidi 算法。
- 数字显示的区域差异:阿拉伯语区域不一定使用阿拉伯-印度数字。大部分阿拉伯语用户更习惯西阿拉伯数字(0-9)。Apple 的 NumberFormatter 根据具体区域自动选择。
代码片段
// SwiftUI 中使用语义化的方向 API
HStack {
Text(" Leading")
Spacer()
Text("Trailing ")
}
// 不要用 .left/.right,用 .leading/.trailing
// 检查当前布局方向
let isRTL = Locale.current.language.characterDirection == .rightToLeft
// 在 SwiftUI 中适配方向
Image(systemName: "chevron.right")
// 自动在 RTL 下翻转为 chevron.left
.environment(\.layoutDirection, .rightToLeft)
// Xcode Asset Catalog 中设置 RTL 变体
// 方法一:在 Imageset 编辑器中勾选 "Right-to-Left"
// 系统自动水平镜像
// 方法二:提供单独的 RTL 图片资源
// 数字格式化的 RTL 处理
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "ar_SA") // 沙特阿拉伯
formatter.string(from: 42) // 可能返回 "٤٢" 或 "42",取决于区域
// 日期格式化也自动适配
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "he_IL") // 以色列
dateFormatter.dateStyle = .long
最佳实践
- 永远使用
.leading/.trailing而非.left/.right - 有方向性的图标提供 RTL 变体,在 Asset Catalog 中配置
- 在自定义布局中检查
userInterfaceLayoutDirection属性 - 不要假设数字总是从左到右——用 NumberFormatter
- 测试时在系统设置中切换语言为阿拉伯语或希伯来语,全面检查 UI
- 用 SwiftUI Preview 配合
\.layoutDirection环境值快速验证 RTL 布局
还有什么值得关注
- Apple 支持 15 种 RTL 语言,不仅限于阿拉伯语和希伯来语
- SwiftUI 的 RTL 支持比 UIKit 更完善,新项目优先考虑 SwiftUI
- “Creating Great Localizations” 和 “Localization” 等文档是 RTL 之外的国际化参考
- Auto Layout 在 RTL 下的行为与 Leading/Trailing 约束一致,但如果你用了 Left/Right 约束就会出问题
WWDC 2022