作者:martindontpanic,iOS 开发者,目前就职于TikTok
Sessions: https://developer.apple.com/videos/play/wwdc2020/10219/
如果想让自己的应用服务国际市场,本地化就是一个需要认真对待的问题。本地化不仅仅是将UI文案翻译成多国语言,还需要在布局上针对不同的语言进行适配。这个 session 介绍了一些布局的准则和利用 Xcode 帮助布局适配的技巧。
不管是使用手动计算布局、Auto Layout 还是 Swift UI,一些布局准则是通用的。
按钮上一句很短的英文,翻译成另一种语言可能会很长,需要更多的宽度。另外,有些语言字符的上下会有一些修饰符号,让整个字符比英语字符更“高”,比如缅甸语。因此控件高度最好也不要固定。
下面是一段缅甸语文本和相同字号的英语:
ဤလင့်ခ်ကို ပံ့ပိုးမပေးပါ This link isn't supported
使用 Auto Layout 来计算文本控件的尺寸,不要设置固定长度约束。如果使用 Swift UI,避免显式设置 frame 为绝对值。
既然包含文本的控件尺寸是可变的,如果控件尺寸更大,间距就应该相应地更小,保证整个布局合适。
对于横向空间放置多个文本控件的场景,常见的布局方式是两边的控件到屏幕边缘设置固定的间距,中间的控件确定要“跟随”哪边来布局,最后留出一个可变间距,使用 greaterOrEqualTo 设置一个间距最小值。如图:
手机屏幕的横向空间本身很有限,如果文本本身较长,就需要允许文本折行布局。
众所周知,UILabel
默认是单行文本布局,设置 numberOfLines = 0
即可支持多行布局。
例如在横向空间内放置多个文字按钮,文字稍长就容易因为空间不足而被截断。
Xcode 为开发者提供了一组调试工具,可以让开发者方便地调试布局在多种语言下的表现。
Document Preview 可以在多种屏幕尺寸、屏幕方向和语言条件下预览 Storyboard 布局的效果。
我们随便用 Storyboard 搭个界面:
在菜单中依次点击 Editor -> Preview ,即可打开 Document Preview。
点击右下角按钮可以切换预览语言:
分割线上方是项目支持的所有语言,不用多说。下面的几个 Pseudolanguage 是用来方便调试布局效果的“伪语言”。
第一个 Double-length Pseudolanguage 会让文本重复两次,方便预览在长文本下的布局适配效果。
我们尝试切一下,所有文本都买一送一了。
可以看到大标题被截断了,我们把这个 UILabel
的 numberOfLines
属性设置为0,让它自动折行。
其他的几种 Pseudolanguage 也很有趣,Emotional Pseudolanguage 会把所有的文本变成 Emoji:
Accented Pseudolanguage 会在字符上下加一些符号,这个在 Unicode 中叫做 Combining Diacritical Marks(组合附加符号)。有些语言(比如上面提到的缅甸语)会有这种符号,如上文所说,文本控件的高度也需要针对不同语言动态适配。
对于使用代码自定义的布局效果,Document Preview 就有些力不从心了,我们需要把 App 实际跑起来验证。
我们看一下官方视频中提供的一个例子:
界面上四个按钮横向排开,用一个 UIStackView 包裹。横向空间很紧张,如果按钮文字再长一些势必被截断。用 Double-length Pseudolanguage 预览一下看:
这里苹果的工程师创建了一个 UIStackView 子类,当判断出横向空间不足以容纳所有子视图时,自动调整为纵向布局。
class ReadjustingStackView: UIStackView {
...
override func layoutSubviews() {
adjustOrientation()
}
@objc
func adjustOrientation() {
// Always attempt to fit everything horizontally first
axis = .horizontal
alignment = .firstBaseline
let desiredStackViewWidth = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
if let parent = superview {
let availableWidth = parent.bounds.inset(by: parent.safeAreaInsets).width - (leadingConstraint.constant * 2.0)
if desiredStackViewWidth > availableWidth {
axis = .vertical
alignment = .fill
}
}
}
}
将 Storyboard 上的 UIStackView
改成这个 ReadjustingStackView
,然后打开Edit Scheme -> Options,将 App Language 设置为 Double-length Pseudolanguage,再启动调试:
和预期中一样,按钮变成了纵向布局。
这里也体现了上文提到的布局原则4,在有限的空间内不要放置太多控件。如果 UI 需要横向排列多个按钮,可以考虑用图标代替文字,这样就不会有多语言适配问题了。
运行时 App 语言还可以设置为 Right-to-Left Pseudolanguage,可以模拟从右到左书写的语言,比如阿拉伯语、希伯来语。苹果官方的设计规范要求,当界面语言为从右到左语言时,整个界面布局需要左右镜像反转,原先左对齐的文本排版也要改为右对齐。这个伪语言选项在调试从右到左语言适配效果时非常有用。我们用这个选项把第一个 Demo 跑起来试一试:
可以看到 UITextField
中输入的文本已经是右对齐了。但是两个 UILabel
的文本还是左对齐。我们将它们的 text alignment 设为 natural,这样当界面语言是 RTL 语言时就会自动右对齐。重新跑起来,是我们期望的效果了:
动态字体是苹果在 iOS 7 就引入的特性,可以让 App 的字体大小跟随用户在 iOS 设置中选择的大小。这个特性对视力减退的用户还是很友好的,你看那些老人机的界面字号都特大。老实说作为常年对着电脑屏幕的程序员,尝试把手机字号调大以后也觉得贼舒服。
如果你的应用支持了动态字体,界面自然也需要根据字号动态布局。Xcode 也提供了几种预览动态字体效果的功能。
一个是 Environment Override,在启动应用调试之后可以在 Debug 窗口上面的工具栏点开。这个功能除了调试动态字体之外还可以调试 Dark Mode 和其他的 Accessibility 选项。
另一个是 Accessibility Inspector,在菜单栏选择 Xcode -> Open Developer Tool -> Accessibility Inspector 即可打开。
打开以后选中想要控制的 Target,选中设置 Tab 就可以设置字体了。
想进一步了解动态字体,可以看2017年的WWDC Session Building Apps with Dynamic Type[1]。
很多出海应用都不会放过阿拉伯语市场,毕竟沙特是真的土豪。如前文提到的,适配阿拉伯语这种从右到左的语言,需要将整个界面左右镜像翻转,左对齐的文本需要变为右对齐。
下面介绍一些适配从右到左语言的方法,主要编译自苹果官方文档 Internationalization and Localization Guide[2] 的 Supporting Right-to-Left Languages 章节。
只要使用了 Auto Layout,且确保横向约束使用 leading/trailing 而不是 left/right 创建,界面布局就会自动镜像翻转。如果应用针对 iOS 9 以上系统链接,UIKit 控件也会自动翻转。
以下的界面元素不需要翻转:
视频控制按钮和时间轴
无指向性的图片
时钟
音符与乐谱
图表
在 iOS 中使用 UIView
的一个类方法 userInterfaceLayoutDirectionForSemanticContentAttribute:
获取布局方向。例如:
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
...
}
在 iOS 9以上环境下,默认的文本对齐方式是 NSNaturalTextAlignment
,在英语、中文这类从左到右语言环境下是左对齐,在从右到左语言下是右对齐。
如果一个文本控件希望在从左到右语言下是右对齐,从右到左语言下是左对齐,就需要获取一下布局方向然后手动设置了。
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) {
label.textAlignment = NSTextAlignmentLeft;
} else {
label.textAlignment = NSTextAlignmentRight;
}
如果一段文本中阿拉伯语和英语字符混在一起,阿拉伯语字符应当从右到左排列,英语字符则是从左到右,这种就是双向文本。
iOS 的文本控件已经帮我们处理好了双向文本的排版。文本整体的书写方向是通过运行时检查文本前几个字符来确定的。
但这种自动确定方向的机制有时会不符合预期。比如一段文本大部分是阿拉伯语,但开头几个单词是英语,这给时整个文本的方向就会判定为从左到右。这种情况下想要强制指定文本的书写方向,可以使用下文介绍的 Unicode 标记字符。
对于遵循 UITextInput
协议的文本输入控件,可以使用 setBaseWritingDirection:forRange:
方法来指定文本书写方向。
Unicode 提供了一组不可见的字符用于标记文本的书写方向。
举个例子,电话号码在任何语言都应该是从左到右书写的。可以在电话号码的首尾两边加上 U+202A 和 U+202C 来保证在任何语言文本中都正确显示。
// Wrap the plus (+) prefix and phone number in left-to-right directional markers
NSString *phoneNumber = @"408-555-1212";
NSString *localizedPhoneNumber = [NSString stringWithFormat:@"\u202A%@\u202C", phoneNumber];
如果一段文本的起始是一个变量,比如是一个用户名,此时首位字符是不确定的。要将文本的书写方向固定,就可以使用这两个字符。
在 iOS 9 之后,可以使用 UIView
的属性 semanticContentAttribute
来指定在从右到左语言环境下应该怎样展示。
例如视频进度条不应当在从右到左语言下翻转,可以设置为 UISemanticContentAttributePlayback
。更多选项可以查看官方文档:UISemanticContentAttribute[3]
在从左到左语言下翻转图片,有以下几种方法:
UIImage
的imageFlippedForRightToLeftLayoutDirection
方法将图片翻转。在视频末尾,session 主讲人特地强调了人工测试的重要性。尽管苹果为我们提供了多种工具,但有些本地化问题还是要靠 native speaker 的人工测试来发现。例如术语翻译的前后不一致、文本截断和缩略、脱离上下文的不适当翻译。尤其在提供新的语言支持、或做了大的功能变动的时候,人工测试是很有必要的。
无论开发者使用哪种语言,国际用户的数量都是占多数的。用户看到应用针对自己的语言做了适配会很开心,本地化做得好的应用会留给用户正面的第一印象,有助于在当地市场构建良好的用户关系和声誉。在进行 UI 设计时,需要考虑多种语言对界面外观的影响,遵循本地化设计的几个布局原则。如果在设计和编码阶段就充分考虑了本地化,最后只需要很少的调整就能让应用完美支持多种语言。最后,由说当地语言的人员进行测试,是保证应用良好的本地化质量的重要步骤。
感谢阅读~
Universal Links 新变化:让触达更广更快
我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。
关注有礼,关注【老司机技术周报】,回复「2020」,领取学习大礼包。
这篇文章的内容来自于 《WWDC20 内参》。在这里给大家推荐一下这个专栏,专栏目前已经创作了 108 篇文章,只需要 29.9 元。点击【阅读原文】,就可以购买继续阅读 ~
WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。
Building Apps with Dynamic Type: https://developer.apple.com/videos/play/wwdc2017/245/
[2]Internationalization and Localization Guide: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/Introduction/Introduction.html#//apple_ref/doc/uid/10000171i-CH1-SW1
[3]UISemanticContentAttribute: https://developer.apple.com/documentation/uikit/uisemanticcontentattribute
[4]Adding Additional Resources You Want to Localize: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LocalizingYourApp/LocalizingYourApp.html#//apple_ref/doc/uid/10000171i-CH5-SW17