原文链接 实战技巧:为Android应用设置独立的多语言
通常情况下多语言的设置都在系统设置中,应用需要做的就是提供本应用所使用的字串的多语言翻译,使用时使用R.string.app_name类似的引用,然后系统会根据用户在系统设置中的选项来选择合适的具体的语言。并且这是一个全局选项,也就是说选择一个多语言后整个手机都变了。但有些时候,应用想要自己能够设置一个独自的多语言,也就是说想要给自己的应用设置一个不同于全局的多语言。今天就来研究一下这个话题。
这样做的目的是为了提供更好的用户体验,因为一些仅支持某些特定语言的应用,可能会与系统全局选项冲突,或者只在某些固定地区发行的应用,单独的个性化的局部设置会更好一些。这个谷歌已经通过Jetpack中的appcomat库给与了比较好的支持,详细的可以参考这个文档,以及官方的Sample。我们在官方文档基础之上再叠加试验和理解,做进一步的总结。
主要分为两种方法,一是系统支持单独给应用设置多语言;二是应用中独自设置,这个也是更为通用的解法。
注意:这里的方法都是让某一个应用内部使用的多语言改变为相应的设置(一般情况下是与系统全局设置不一样的),但是需要注意仅仅局限于应用启动后的应用内部界面使用的语言。对于像桌面上应用的入口仍是系统全局设置为准,因为这个入口并不属于应用自己管辖范围内的。
系统设置中支持为应用设置单独多语言
谷歌官方的表述是从Android 13(Android T, SDK 33)开始,就支持了在系统中有一个入口,可以为每个应用单独设置多语言选项。这一部分里面描述的方法也都是支持这个系统入口的情况才能生效的。需要注意,虽然谷歌官方说从Android 13开始就支持了,但这个也要取决 于厂商的定制,目前看大部分国内厂商会把这个功能和入口给屏蔽掉,那么这里后面描述的方法也就都不会生效了。
入口在哪里
需要通过系统设置来进行,有两个入口:
- Settings(设置)> Additional Settings (更多设置) > Languages & Input (语言和输入) > (App Languages) 应用语言 > select an app(选择一个应用)
- Settings(设置)> Apps (应用)> select an app(选择一个应用) > Language(语言)
具体的方法,又分为两种,一是自动式的,二是手动式的。
自动添加
说是自动,其实也是利用IDE(即Android Studio)和编译打包时自动根据res下面的多语言生成一份配置而已。在build.gradle或者build.gradle.kts中的android下面添加generateLocaleConfig = true:
android {
androidResources {
generateLocaleConfig = true
}
}
然后在resl中增加一个名为resources.properties的文件,加入默认值配置:
unqualifiedResLocale=en-US
手动添加
在res/xml中增加文件locales_config.xml,加入需要支持的多语言选项,如:
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en-US"/>
<locale android:name="en-GB"/>
<locale android:name="fr"/>
<locale android:name="ja"/>
<locale android:name="zh-Hans-MO"/>
<locale android:name="zh-Hant-MO"/>
</locale-config>
然后在AndroidManifest.xml中的appllication加上这一个属性:
<manifest>
...
<application
...
android:localeConfig="@xml/locales_config">
</application>
</manifest>
因为还没有找到支持如此设置的手机,所以上述方法未经验证。
在应用内部设置多语言
这个是更为通用的做法,具体的UI就是可以随便弄了,弄个List或者DrowDownMenu都可以。重点是让设置生效的时候需要用到一个API,叫做setApplicationLocales()和getApplicationLocales()。并且在appcompat 1.6.0以后的版本,有比较方便的API可以直接使用。
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)
如果要还原使用系统设置中的全局多语言配置,可以用LocaleListCompat.getEmptyLocaleList()当作参数。
特别注意:此方法要想生效,宿主Activity必须是继承自appcompat中的AppCompatActivity,而不是其他 。
为了兼容以前的版本(Android 12,API level 32以前),还需要在AndroidManifest中添加一个额外的Service:
<application
...
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
...
</application>
需要注意,这个Service在appcomat库中已经定义好了,只需要在manfiest里添加一下就可以了。
另外需要注意的是,一般情况下,应用自己肯定 会保存一下当前用户所选择的语言。但如果系统也支持应用语言选择入口的话,那么通过系统入口也是可能会修改应用的多语文选项的,这时,就需要把系统的选项与应用内部的选项进行同步。可以通过AppCompatDelegate.getApplicationLocales来获取当前生效的语言选项,它是由appcompat库来维护的,肯定是最新的,所以应用自己保存的选项如果与这个API的结果不一致,就要重置为这个API的结果。
参考资料
- Per-app language preferences
- user-interface-samples/PerAppLanguages