2020-02-02
关键字:自动切换语言、高版本下应用内多语言切换
在 Android 应用开发中,最简单的多语言实现就是直接在 res 目录下将你需要的不同语言的资源以 values-xx 子目录的形式存放。
例如,res 目录下默认只有一个 values 目录,这个目录下存放的资源就是应用默认使用的资源,包括文字、色彩值、尺寸、样式等等等等。
如果你默认目录下使用的是中文,现在想要增加一个英文,则只需要自行创建多一个目录,以名称 values-en 命名即可。然后将 values 目录下的资源文件翻译好后拷贝过去即可。
这种情况下应用选择的语言环境是跟着 Android 系统走的,即系统的显示语言是什么它就读取哪个目录下的资源。当我们的应用中没有与系统对应的资源时,就使用默认的 values 目录下的资源。
想象一下,这种可以将不同语言分目录保管,并自动加载的机制是不是可以让我们的应用更容易做到“全球化”?
举个例子来说就是,我们不说文字,这个太肤浅。假如你正在开发的一款应用,它的其中一个页面的基本色调是天蓝色。而偏偏有一个民族的人非常忌讳天蓝色,他们更喜欢大红色。那我们有这套机制解决起这个问题来岂不易哉。代码完全不用改,只需要准备两套基本色调配置表即可。这种机制真是太妙了。
言归正传。世界上不同语言种族这么多,我们很难记得住每个国家地区的对应缩写是什么。那我们要如何来创建这个 values-xx 目录呢?
如果你是使用 Android Studio 来开发应用的,那这个就不难。只需要按照如下步骤操作即可由 Android Studio 自动帮你创建对应目录:
当然,其实也完全可以手动创建目录与资源文件,如果你知道它的对应缩写的话。
而 Android Studio 中也有很方便的多语言资源编辑工具,可以让开发者很方便地以对照式来编辑不同语言的资源。其操作步骤如下图所示:
以上是跟随 Android 系统来切换显示语言的情况。但有些应用可能还需要能够摆脱操作系统的设置,自行选择显示语言。这该怎么办呢?
其实也不难。只需要在上述步骤的基础之上,再在应用启动时自行设置一下显示语言就可以了。
为了全局统一,一般是在自定义的 Application 类的初始化中来设置。
这里简单说一下笔者的一个比较简单的解决方案。
因为是应用自行控制语言选项,因为应用需要自行保存当前选择的显示语言。笔者采用的是 SharedPreferences 来保存。
从 SharedPreferences 中读取出要显示的语言以后,再通过如下代码设置即可:
Configuration conf = context.getResources().getConfiguration();
conf.locale = isThai ? new Locale(LOCALE_THAI) : Locale.SIMPLIFIED_CHINESE;
context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());
上述代码已将关键的一行以加粗标红表示出来了。需要注意的是,Locale 类默认提供的语言并不全面。如果恰巧你需要的语言各类它没有默认提供,正如笔者上面所需要的泰语言一样,那就需要你自行实例化一个 Locale 对象。实例化时直接将对应语言的缩写作为参数传入即可。如笔者上面的实例化泰语言时,它的写法就是:new Locale("th");。
如此即可。
对了,这种方式似乎是针对较低版本的 Android 系统的,这段代码笔者最高在 Android7.0 上运行通过过。其它更高版本的系统似乎还有另外一种写法,那种写法笔者没有去研究过,就不贴出来了,仅在此提一下。
最后,笔者不得不提一下应用多语言显示与 AppCompact 主题之间的冲突。
这个冲突当初可是困扰了笔者一整天。
具体现象就是在正确设置好 values-xx 资源目录以后,发现应用怎么都不能切换至对应语言风格去显示。无论是用跟随系统显示语言的方式还是手动指定显示语言的方式。
当时笔者真是各种加打印,各种百度谷歌都找不到原因。
在几近绝望的时候偶然发现在另外一个 Activity 中能够将语言给切换过来。(注:笔者开发的是一款需要登录的应用,且这个应用有免密登录功能。笔者先前保存过登录信息,因此应用每次打开都只显示一个固定的界面。)
唯独那个主 Activity 的语言死活不能切换。
有了这个对照,至少证明了笔者的资源配置没有错,甚至也可以证明手动切换显示语言的代码也没有错。
那就只能在这两个 Activity 中找差异了。
经过对比,笔者最终发现是因为这两个 Activity 所使用的主题样式,就是 style 不同而导致另一个 Activity 不能切换显示语言的。
这个不能让应用切换显示语言的主题就是 AppCompact 主题:
<style name="App_Compact_Theme" parent="Theme.AppCompat.Light.NoActionBar"/>
笔者的主 Activity 使用到了 Fragment,因此最开始继承了 AppCompactActivity 类,而这个 AppCompactActivity 类又要求其主题必须是 AppCompact 的主题。至于为什么这个主题会不能切换显示语言,笔者倒是没有去深究,也觉得没有必要。
最终笔者将主 Activity 的父类换成 FragmentActivity,再将 style 更换成 <style name="Normal_theme" parent="android:Theme.Light.NoTitleBar"/> 以后成功地解决了问题。
以上就是笔者关于Android应用多语言开发的心得与笔记。
关于Android8.0及以上版本系统下的应用内多语言切换
在Android8.0及以后的版本系统下,上述的语言切换功能将失效。原因是Android自那以后改变了系统语言控制架构。
因此,我们的软件中关于显示语言功能需要针对高低版本做两套兼容代码。
因绝大多数情况下仅Activity需要展示UI,所以这里仅针对Activity来讲解。
首先,我们要将应用内所有的Activity的Context实例做个自定义,即在每个Activity实例化之初给它一个已经指定了显示语言种类的Context实例。
怎么做呢,给我们的Activity重写 attachBaseContext() 方法:
@Override
protected void attachBaseContext(Context newBase) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1){
super.attachBaseContext(getSelfContext());
}else {
super.attachBaseContext(newBase);
}
}
这里有个小坑要注意的,在这个方法刚执行时,其参数中的 newBase 将是 null。因为Activity生命周期中调用这个方法就是为了获得Context实例的,此时它当然还没有任何可以使用的Context对象的了。
其次,根据自己的业务逻辑,准备相应的 Context 实例给到上面的方法,即上面 getSelfContext() 方法的实现:
public Context getSelfContext(Context ctx){
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
Configuration conf = getApplicationContext().getResources().getConfiguration();
LocaleList ll = new LocaleList(isThai ? new Locale(LOCALE_THAI) : Locale.SIMPLIFIED_CHINESE);
conf.setLocales(ll);
return new ContextWrapper(getApplicationContext().createConfigurationContext(conf));
}
return ctx;
}
如此,便可。