Android 本地化适配:RTL(right-to-left) 适配清单

8,602 阅读11分钟

本文首发自公众号:承香墨影(ID:cxmyDev),欢迎关注。

一. 序

越来越多的公司 App,都开始淘金海外,寻找更多的机会。然而海外市场千差万别,无论是市场还是用户的使用习惯,都有诸多的不同。

当你接触一款出海 App 的时候,除了需要了解海外 Google Service 的整个生态圈,还要做好不同语言的适配。语言适配最通用的做法就是根据不同系统语言设定,配置不同的语言资源(strings.xml),而其中比较特殊的就是例如阿拉伯的 RTL 布局,它不仅改变了语言,还改变了 UI 布局和使用习惯。

我们常用的习惯,称之为 LTR(Left-To-Right),其意为我们的阅读和书写习惯,是从左向右延伸的。而 RTL(Right-To-Left) 则正好相反,它的阅读和使用的习惯都是从右向左,常见使用 RTL 习惯的语言有阿拉伯语、希伯来语等。

今天就来聊聊,一个成熟的 Android App,想要做 RTL 适配,需要关注什么,想要适配 RTL 有哪些任务清单。

如果你维护的 App 有国际化的要求,那这个问题是迟早需要面对的。

二. Android 支持 RTL

2.1 什么是 RTL?

正如前面介绍的,RTL 是 Right-to-left 的缩写,其意为阅读和书写的习惯,是从右向左延伸的。再对比一下我国人自身的使用习惯,都是 LTR 的,也就是从左向右。

RTL 可以简单理解是 LTR 的镜像,当需要适配 RTL 的时候,除了翻译语言本身,还需要做到的就是 UI 布局,从中轴上镜像反转。

虽然 RTL 不符合我们国人的使用习惯,但是全球范围内依然有一部分人保持着 RTL 的习惯,比较常见的就是阿拉伯语、希伯来语等。

就 Android 系统来说,Android 4.1 开始就在 TextView 和 EditView 中增加了对双向文本的优先支持,允许其文本内容从左向右(LTR)到从右向左(RTL)的显示和切换。而在 Android 4.2 开始,增加了对 RTL 镜像布局完全原生的支持。

也就是在 Android 4.2(Api Level 17)及之后,在 UI 上的布局镜像,是原生支持的。在这些系统版本上,只要用户系统语言切换到「RTL 系语言」,首先系统 UI 会直接左右镜像切换,此时如果你的 App 支持 RTL 镜像布局时,也会自动切换布局方向。

2.2 App 如何支持 RTL 镜像

正如前面介绍的一样,LTR 到 RTL 的切换,不是由开发者控制的,而通常是由系统语言来控制的。

当系统语言切换为「RTL 系语言」时,还需要你的 App 支持 RTL 镜像布局。

这里所谓的支持,其实只需要配置一个属性即可,就是 AndroidManifest.xml 配置文件中的一个清单元素。需要在 <applictaion> 标签下,配置元素 android:supportsRtl="true"

此时当系统语言切换的时候,你的 App 也会跟着切换 UI 布局为镜像后的效果。

除了需要开启 supportsRtl 属性之外,还需要一些布局属性的配合。

简单来说,就是将布局需要的所有 xxxLeft/xxxRight "替换"为 xxxStart/xxxEnd。

例如我们常用的 Padding 和 Margin,都有类似 paddingLeft 和 layout_marginRight 属性,这些就需要"替换"成 paddingStart 和 layout_marginEnd 属性。当然不止于此,还有一些 gravity、drawableLeft 等属性需要"替换"。原则上,所有 Left/Right 都需要变换为 Start/End 就好了。

这些属性,官方文档中已经帮我们列举出来了。

到这里应该了解了,Android App 支持 RTL 镜像的主要流程,就两步:

  1. App 增加 android:supports="true" 属性。
  2. 调整 UI 布局属性,从 left/rightstart/end 切换。

那么问题来了,我们在日常编码的过程中,应该使用 left/right 还是 start/end?还是两者都需要?

注意到我前面提到的 UI 布局属性的替换时,是打了引号的,你是否需要使用 start/end 来完全替换 left/right ,完全取决于 App 当前的 minSdkVersion 值。

正如前面所提到的,Android 对 RTL 的原生支持,是在 Android 4.2 中才具备的,也就是说,如果 App 的 minSdkVersion 大于等于 4.2,你只需要使用 start/end 属性,但如果还需要支持 4.2 以下的设备用户,那就需要保留 left/rightstart/end 两者。

在低于 4.2 的系统中,不识别 supportsRtlstart/end 属性,所以不会造成影响。但是需要注意,在适配完成之后,后续开发新页面时的编码习惯。

2.3 AS 助力调整布局属性

如果当前需要适配的是一个成熟项目,并且其中的布局习惯还是使用 left/right 系的属性,那么针对所有页面布局文件,进行手工调整就是一个非常大的工作量了。

所幸的是 AS 提供了自动化的支持。

你可以在 Refactor → Add RTL Support Where Possible 来开启 RTL 的自动调整。

它会自动将项目中所有的 left/right 属性都替换为 start/end 属性,如果想要适配 Android 4.2 以下的设备,需要保留两者,那么在 Run 之前,勾选 Relpace Left/Right Properties with Start/End Properties 选项即可。

早期的 AS 自动支持 RTL 布局的时候,效率会有一些问题,转换的时候如果布局过多,可能会卡死,但是新版的 AS 已经优化了很多,转换效率上还是可以接受的。

另外这毕竟是自动替换,在替换完成之后,还是需要每个页面都测试一遍,看看效果才算完,有时候还需要我们做一些微调的工作。例如 AS 自动替换 RTL 布局的时候,如果使用了 include 标签,其中用到的方向属性不会被替换。

自动化虽然方便了我们机械的重复,但也必须介入人工的干预符合预期。

三. RTL 细节调整

要做这种全全局的改动,必然会有一些细节需要微调的,这里简单写一些 RTL 布局中会需要使用到的细节调整技巧。

3.1 利用全局样式,批量修改属性

在适配 RTL 的过程中,无法避免的就是有一些属性必须要设置。例如 EditView 就需要设置以下属性。

android:textAlignment="viewStart"
android:gravity="start"
android:textDirection="locale"

那我们就可以将这些属性在 style.xml 中全局为 EditText 设置上。

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
       ...
       <item name="editTextStyle">@style/EditTextStyle.Alignment</item>
       ...
</style>

<style name="EditTextStyle.Alignment" parent="@android:style/Widget.EditText">
        <item name="android:textAlignment">viewStart</item>
        <item name="android:gravity">start</item>
        <item name="android:textDirection">locale</item>
</style>

同时 TextView 也需要设置 android:textDirection 属性,也可以采用相同的方法用 Style 的方式全局设置。

3.2 针对 RTL 的资源适配

除了布局上的适配之外,还有一些资源的适配,资源适配主要说两块内容:Drawable(mipmap) 以及 Layout 布局资源。

先来说说 Drawable 的适配。例如在不同方向的布局下,使用不同的图标。

上图就是个很典型的例子,在调整布局到 RTL 时,还需要注意返回「」的图标也需要替换成「」。

这里依然使用 Android 对资源使用的限定符的方式,可以创建 drawable-ldrtl 目录,将翻转后的图标,放在这个目录下。如果需要限定 dpi,可以在目录名后面追加。

res/
  drawable/
    a.png  
  drawable-ldrtl/
    a.png  // 对标 drawable/a.png 的 RTL 图标
  drawable-xhdpi/
    b.png  
  drawable-ldrtl-xhdpi/
    b.png  // 对标 drawable-xhdpi/b.png 的 RTL 图标

接下来再说说 Layout 布局的 RTL 布局效果适配。有些特殊的页面,可能光镜像化还不够,还需要针对性的做一些 UI 上的调整,那最简单的做法就是做两套布局,互不影响。

既然 Drawable 可以通过资源限定符的方式,设置 RTL 布局下使用的图标,其实布局也可以。

对于布局文件,可以在目录下追加限定符 layout-ldrtl/,如果想对某个语言做布局适配,也可以增加语言限定,例如阿拉伯语可以用 layout-ar/

res/
  layout/
    main.xml  // 默认布局
  layout-ar/
    main.xml  // 阿拉伯语布局
  layout-ldrtl/
    main.xml  // RTL 布局

针对 RTL 的 UI 布局规范,Material Design 下有一个规范的文档(material.io/design/usab…

3.3 代码判断是否 RTL?

有些控件的属性,是通过代码动态调整的,那在使用的过程中,就需要在代码中,判断当前的环境,是 RTL 还是 LTF,才可以确定后续的属性设置。

通过获取 Configuration 的 locale 来判断当前的环境,为了兼容,在 TextUtilsCompat 下也提供了类似的方法。

public boolean isRtl() {
	return TextUtilsCompat.getLayoutDirectionFromLocale(
		getContext().getResources().getConfiguration().locale) == 		ViewCompat.LAYOUT_DIRECTION_RTL;
}

isRtl() 方法可以直接拿来使用,依此判断结果,执行后续的操作。

3.4 不是所有控件都支持 RTL

虽说从 Android 4.2 开始,原生支持 RTL 方向布局,但是也有一些控件是不支持的,例如 ViewPager,就不支持 RTL 的方向。

这其实没有什么很好的办法,要么和产品商量对此处的容忍,要么找一些其他的解决方案。

针对 ViewPager 的 RTL 化,在 Github 就有对应的开源库 RtlViewPager(github.com/diego-gomez… 可供使用。其原理也是将数据进行倒序重排,没什么好说的,源码不多,有兴趣可以自己看看。

四. 适配 RTL 要如何估期?

再来聊聊适配 RTL 时,估算开发周期的问题。

除了 App 本身在设计研发之初,就是为了中东的土豪设计的之外,多数情况下,我们都是因为各种外部原因,需要在一款成熟的 App 上,适配 RTL 镜像布局。例如市场验证有大量「RTL 系语言」的付费用户,或者产品经理认为存在「RTL 系语言」的潜在用户。

什么时候适配 RTL,完全是由外部因素决定的。但是当需要适配的时候,我们作为开发者,最直观要面对的现实问题就是,适配 RTL 需要做哪些事?这个任务需要多少时间能够完成?需要哪些人来配合,哪些任务是可以并行的?

要精确估计 App 的 RTL 化,很难,因为工作量主要来自适配,说到适配,工作量就可大可小了。App 大量使用第三方控件的就比只使用原生控件的工作量大;产品经理和设计师,允许部分页面适配有差异的,也会比高要求还原的工作量大。

这些在适配完成之前,谁也不知道效果如何,既然没有什么好的方法,那就试一试吧,只需要三步,你可以拿着一个明确的镜面翻转效果,来估计适配的难度。

  1. 设置 android:supportsRtl="true",让 App 支持 RTL。
  2. AS 自动转换布局属性,支持 RTL 布局效果。
  3. 打开开发者选项中的「强制使用从右到左的布局方向」,强制 RTL 布局。

此时你基本上可以看到一个 80% RTL 化的 App,剩下的就是把页面都检查一遍,看看有没有用到哪些控件不支持 RTL,哪些 Drawable 需要替换、哪些布局需要微调。然后针对性的调整即可。

有一些控件不支持 RTL 就会比较麻烦,有源码的就改改源码,没源码的就看有没有地方可以 Hook 解决。

总之需要做什么,清单是固定的。有了明确的任务,自然就容易估计开发周期了。

列举一下适配 RTL 的任务清单:

  1. App 支持 RTL,AS 自动转换布局属性支持 RTL,从开发者选项里强制 RTL 布局方向。
  2. 按页面排查,检查出需要翻转的 Drawable 资源,打包交给设计师,反转后替换。
  3. 检查布局翻转后的效果,和设计师确定需要适配翻转后的 UI 效果。
  4. 找到不支持 RTL 化的控件,可以从源码的角度分析,能改源码的改源码,不能改源码的尝试 Hook 解决或找替代方案。
  5. 翻译 RTL 系语言资源 strings.xml,放入对应的资源目录,例如阿拉伯语需要放入 /values-ar/strings.xml 目录,将系统语言切换到阿拉伯语,排查所有页面文字与控件的匹配度。
  6. 整体验收,微调效果。

其中需要和产品、设计、翻译配合的,都可以提前准备,让任务并行化。当然在适配的过程中,还有一些实际的问题,就需要遇到问题再解决问题了。

五. 小结时刻

本文聊了如何在一个成熟的 App 上,适配 RTL 镜像效果,以及如何快速的适配。最后还列出了一个适配时,需要调整关注的清单列表,希望对你有所帮助。

本文就到这里,如果有所帮助,留言、转发、点好看是最大的支持,谢谢!

reference:

material.io/design/usab…

android-developers.googleblog.com/2013/03/nat…

developer.android.com/training/ba…


公众号后台回复成长『成长』,将会得到精心准备的学习资料。