安卓程序的本地化

· 1796字 · 4分钟

安卓程序的本地化 🔗

1. 什么是本地化/国际化 🔗

全球有着巨大的手机使用群体,由于地区不同,这些用户的语言和文化也有所不同。本地化(Localization)就是在APP中为不同语言和地区的人群分别展示不同的界面,从而覆盖更多的用户群体,由此提高商业收益。安卓的官方文档Localize your app | Android Developers中针对应用本地化已经有了较为详尽的介绍和最佳实践,在此我仅记录自己的心得体会和经验。

2. 安卓程序本地化的原理 🔗

在了解如何做本地化之前,让我们先来了解安卓应用本地化的原理。安卓应用的本地化基于安卓资源的加载方式,开发者除了在代码中硬编码字符串,还可以在程序的资源文件夹中通过XML等方式配置应用字符串、图片、字体等资源,而在代码中通过R类的R.stringR.drawable等访问这些资源。安卓应用的资源文件在${project}/src/res中,一个应用可以为不同的限定条件(Qualifier)配置不同的资源文件夹,默认的资源文件夹为该文件夹下的valuesdrawable等子文件夹,我们也可以为不同地区的用户单独添加文件夹。资源文件夹的命名格式为"${resource}-${qualifier}",例如我们可以为大陆地区的qualifier是"zh-rCN",那么该地区对应的资源文件夹名称则为"values-zh-rCN"

安卓程序运行时如果是访问程序资源,会根据当前的配置加载对应的资源。例如程序通过stringResource(R.string.greeting)加载名称为greeting的字符串资源时,会根据系统配置中国大陆得到qualifier zh-rCN,那么就会通过qualifier定位到values-zh-rCN/strings.xml,访问其中名为greeting的资源。如果文件夹或者strings.xml不存在,或者strings.xml中没有配置名为greeting的字符串资源,那么就会退回到用户设置的第二语言,继续执行该流程。最后如果所有的方案都试过了仍然无法找到资源,那么APP会加载默认的资源,即values/strings.xml中的greeting字符串资源。依赖于安卓的这种资源加载的机制,安卓程序就可以实现本地化了。

创建的中国大陆的字符串资源文件

3. 实现安卓程序的本地化 🔗

既然我们已经了解了安卓程序本地化的原理,现在假设我们实现本地化的思路就很清晰了:在资源文件夹中维护默认资源,并针对性为我们本地化的目标用户创建各自的资源文件夹并维护相应的资源。我们已经知道安卓是通过资源文件夹的后缀qualifier区分资源文件夹的,当我们想要为香港和台湾做本地化时难道还需要去找对应的qualifier名称然后创建文件夹吗?这种易出错且完全可以自动化的事情,IDE已经帮我们做好了,在我们右击资源文件夹时选择创建安卓资源文件夹,Android Studio会通过可视化界面让我们快速准确地创建资源文件夹。

点击创建安卓资源目录

Android Studio支持通过多种限定条件创建资源文件夹,由于我们是要做本地化,所以我们通过地区(Locale)筛选中文。选择后我们可以看到有很多可供筛选的语言,而选择语言之后还可以选择对应的区域。不过经过我在本地环境的测试,选择中文不限定语言的话系统调成英文并没有使用到我的中文资源文件,而是退回到了默认资源。因此如果要为中国大陆、香港和台湾做本地化,建议分别创建对应地区的文件夹。

创建资源目录可以按照限定符生成相应的资源文件夹

创建资源文件夹我们就可以维护资源了,Android Studio贴心地为我们提供了字符串翻译工具,我们打开字符串资源文件就会得到提示,点击“Open editor”会弹出一个翻译框,可供我们翻译字符串为目标语言,也可以在图形窗口中特别维护某些字符串的值。

Android Studio提供了可视化的字符串资源翻译工具

Android Studio提供了可视化的字符串资源翻译工具

3. 注意点 🔗

3.1 资源一定保证完整 🔗

在本地化时一定要保证资源条目在所有地区都是一致的,不要出现缺漏的情况,尤其是默认资源一定不要缺漏。因为默认资源是兜底的资源,在加载到默认资源时就已经没有后备方案了,这个时候如果缺少相应资源就会出现错误。

3.2 尽量使用资源,避免硬编码 🔗

开发初期时就要考虑到本地化问题,避免硬编码,因为这些内容无法被国际化。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            DemoTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(modifier = Modifier.padding(innerPadding)) {
                        // 硬编码的内容无法本地化
                        Text(text = "你好")
                        
                        // 资源内容可以被本地化
                        Text(text = stringResource(R.string.greeting))
                    }
                }
            }
        }
    }
}
comments powered by Disqus