20、Launcher的国际化(i18n):字符串资源管理、RTL布局适配、区域设置对排序的影响
国际化,简称 i18n。说白了就是让你的 Launcher 能说「各国语言」,能适应「不同书写习惯」。比如阿拉伯语从右往左读,中文从左往右读。
我刚开始做 Launcher 时,觉得国际化不就是翻译字符串吗?后来发现,坑多着呢。尤其是 RTL 布局,稍不注意界面就乱成一锅粥。今天咱们就聊聊这三个核心点:字符串资源管理、RTL 布局适配、区域设置对排序的影响。
20.1 字符串资源管理
这是国际化的基本功。Android 系统通过资源文件夹的「限定符」来区分不同语言。
20.1.1 资源文件夹命名规则
你需要在 res/ 目录下创建带语言限定符的文件夹。比如:
res/values/strings.xml—— 默认语言(通常是英语)res/values-zh/strings.xml—— 简体中文res/values-ar/strings.xml—— 阿拉伯语res/values-ja/strings.xml—— 日语
系统会根据用户的语言设置,自动加载对应的 strings.xml。
核心原则:永远不要硬编码字符串。所有用户可见的文字,都必须放在 strings.xml 中。
20.1.2 字符串资源的最佳实践
我个人习惯把字符串按模块分组。比如 Launcher 的字符串可以分成:
- 通用:app_name、search_hint
- 桌面:add_widget、remove_shortcut
- 抽屉:all_apps、recent_apps
- 设置:settings_title、grid_columns
举个例子,res/values/strings.xml 的内容:
<resources>
<string name="app_name">Launcher</string>
<string name="search_hint">Search apps</string>
<string name="add_widget">Add widget</string>
<string name="all_apps">All apps</string>
</resources>
对应的中文版本 res/values-zh/strings.xml:
<resources>
<string name="app_name">桌面启动器</string>
<string name="search_hint">搜索应用</string>
<string name="add_widget">添加小部件</string>
<string name="all_apps">所有应用</string>
</resources>
小技巧:字符串中如果有占位符,记得用 %1$s、%2$d 这种格式。不同语言的语序可能不同,占位符能帮你灵活调整。
20.1.3 字符串的复数形式
有些语言有单复数变化。比如英语中 "1 app" 和 "2 apps" 不同。Android 提供了 <plurals> 标签:
<plurals name="app_count">
<item quantity="one">%d app</item>
<item quantity="other">%d apps</item>
</plurals>
在代码中这样使用:
int count = 5;
String text = resources.getQuantityString(R.plurals.app_count, count, count);
嗯,这里要注意:中文没有复数变化,所以中文的 plurals 只需要一个 other 条目就行。
20.2 RTL布局适配
RTL 是 Right-to-Left 的缩写。阿拉伯语、希伯来语都是从右往左写的。如果你的 Launcher 不支持 RTL,那在这些语言下界面会完全颠倒。
20.2.1 启用 RTL 支持
首先,在 AndroidManifest.xml 中声明:
<application
android:supportsRtl="true">
...
</application>
这一步是必须的。否则系统不会自动镜像你的布局。
20.2.2 使用 start/end 代替 left/right
这是 RTL 适配的核心。在布局文件中,用 layout_marginStart 代替 layout_marginLeft,用 paddingEnd 代替 paddingRight。
举个例子:
<!-- 错误写法 -->
<TextView
android:layout_marginLeft="16dp"
android:paddingRight="8dp" />
<!-- 正确写法 -->
<TextView
android:layout_marginStart="16dp"
android:paddingEnd="8dp" />
为什么?因为 left 在 RTL 下仍然是左边,但 start 在 RTL 下会变成右边。系统会自动镜像。
我曾经踩过的坑:在 Launcher 的拖拽逻辑中,我用了 getLeft() 和 getRight() 来计算位置。结果在 RTL 模式下,拖拽方向完全反了。后来全部改成 getStart() 和 getEnd() 才解决。
20.2.3 布局镜像
有些布局需要整体镜像。比如 Launcher 的底部导航栏,在 LTR 下从左到右排列,在 RTL 下应该从右到左排列。
你可以用 android:layoutDirection="locale" 让布局跟随语言方向:
<LinearLayout
android:layoutDirection="locale"
android:orientation="horizontal">
...
</LinearLayout>
或者,在代码中动态判断:
int layoutDirection = ViewCompat.getLayoutDirection(view);
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
// RTL 下的特殊处理
} else {
// LTR 下的处理
}
20.2.4 图标和图片的镜像
有些图标在 RTL 下需要镜像。比如返回箭头,在 LTR 下指向左边,在 RTL 下应该指向右边。
你可以在 res/ 下创建 drawable-ldrtl/ 文件夹,放入镜像后的图标。系统会自动加载。
或者,用 autoMirrored="true" 属性:
<vector
android:autoMirrored="true"
... />
这样系统会自动镜像矢量图,省去你手动准备两份资源的麻烦。
20.3 区域设置对排序的影响
这个点容易被忽略。不同语言对「排序」的理解完全不同。
20.3.1 字母排序的差异
比如英语中 "apple" 排在 "banana" 前面。但在德语中,"ä" 可能排在 "a" 后面,也可能排在 "z" 后面,取决于区域设置。
中文更复杂。是按拼音排序?还是按笔画排序?不同区域设置下结果不同。
我建议使用 Collator 类来进行排序:
import java.text.Collator;
import java.util.Locale;
Collator collator = Collator.getInstance(Locale.CHINA);
List<String> appNames = new ArrayList<>();
// 添加应用名称...
Collections.sort(appNames, collator);
Collator 会根据指定的 Locale 自动处理排序规则。比直接用 String.compareTo() 靠谱得多。
20.3.2 Launcher 中的应用排序
在 Launcher 中,应用列表的排序直接影响用户体验。我一般这样处理:
- 获取当前系统的 Locale
- 根据 Locale 创建对应的 Collator
- 用 Collator 对应用名称进行排序
- 如果应用名称相同,再用包名排序作为 fallback
代码示例:
Locale currentLocale = getResources().getConfiguration().getLocales().get(0);
Collator collator = Collator.getInstance(currentLocale);
Collections.sort(appList, (app1, app2) -> {
int result = collator.compare(app1.getLabel(), app2.getLabel());
if (result == 0) {
return app1.getPackageName().compareTo(app2.getPackageName());
}
return result;
});
注意:不要用 Locale.getDefault()。在 Android 上,getResources().getConfiguration().getLocales() 才是用户当前设置的语言列表。
20.3.3 数字和特殊字符的处理
不同语言对数字和特殊字符的排序位置也不同。比如英语中数字排在字母前面,但有些语言中数字排在字母后面。
你想想看,如果用户安装了上百个应用,排序规则不对,找个应用得翻半天。所以一定要用 Collator,别自己写排序逻辑。
20.4 知识体系总览
下面这张图总结了 Launcher 国际化的核心知识点:
20.5 总结
国际化不是简单的翻译。它涉及资源管理、布局适配、排序逻辑等多个方面。
我个人经验是:从项目一开始就把国际化考虑进去。不要等到所有功能做完了再回头适配 RTL,那会改到你怀疑人生。
最后送大家一句话:国际化做得好,用户遍布全球;国际化做得差,Bug 遍布全球。
公众号:蓝海资料掘金营,微信deep3321