前言
最近接手一個Android項目,需要實現對維吾爾族語的支持。雖然做了這么久的android開發,只做過多語言支持,但做應用內部多語言支持還是第一次,而且還是對維吾爾語的支持。所以,又是一次面向搜索引擎編程。
面向搜索編程
如果我們搜索“android 多語言切換”,我相信得到大部分的答案是千篇一律的,連文章都長的一樣:
Locale locale = new Locale("zh");
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
以上代碼應該說沒什么大問題,畢竟確實可以工作,可以實現需求。但是,作為一個強迫癥患者,我實際受不了有2處劃線的地方,也就是說上面代碼中有2處被廢棄了(沒錯,API 25被廢棄的):
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
看到有2處被廢棄了,必須進去看看啊。
/**
* Current user preference for the locale, corresponding to
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
* resource qualifier.
*
* @deprecated Do not set or read this directly. Use {@link #getLocales()} and
* {@link #setLocales(LocaleList)}. If only the primary locale is needed,
* <code>getLocales().get(0)</code> is now the preferred accessor.
*/
@Deprecated public Locale locale;
很顯然我們找到了替代方法:
config.setLocales(LocaleList);
config.setLocale(locale);
很好,沒問題。繼續查看下一個:
/**
* Store the newly updated configuration.
*
* @deprecated See {@link android.content.Context#createConfigurationContext(Configuration)}.
*/
@Deprecated
public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
updateConfiguration(config, metrics, null);
}
很簡單嘛,代替方法也找到了哦。于是就這么寫了下去:
Locale locale = new Locale("ug");
final Resources res = getResources();
final Configuration config = res.getConfiguration();
config.setLocale(locale); // getLocale() should return a Locale
createConfigurationContext(config);
完全不好用!怎么寫就是不好用。再仔細一看,文檔上說createConfigurationContext(config)
會返回一個新的Context,然而對新Context如何處理只字未提。又是一番面向搜索編程,終于找到了正確姿勢:
@Override
protected void attachBaseContext(Context newBase) {
Locale locale = new Locale("ug");
final Resources res = newBase.getResources();
final Configuration config = res.getConfiguration();
config.setLocale(locale); // getLocale() should return a Locale
final Context newContext = newBase.createConfigurationContext(config);
super.attachBaseContext(newContext);
}
我們的Activity應該重寫這個方法。然后當Activity被創建的時候,新的Context將被應用。需要注意的,如果你想讓當前Activity生效,你需要調用recreate()
。
當然啦,我們也可以寫個工具類:
public class ConfigurationWrapper {
private ConfigurationWrapper() {
}
public static Context wrapConfiguration(@NonNull final Context context, @NonNull final Configuration config) {
return context.createConfigurationContext(config);
}
public static Context wrapLocale(@NonNull final Context context,@NonNull final Locale locale) {
final Resources res = context.getResources();
final Configuration config = res.getConfiguration();
config.setLocale(locale);
return wrapConfiguration(context, config);
}
}
然后你就可以這樣使用。如果你想更改configuration,就可以用wrapConfiguration
。
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(ConfigurationWrapper.wrapLocale(newContext, getLocale()));
}
關于Locale
實現多語言切換用到了Locale。Locale里很多常見國家和地區以及語言,如果我們做常見的語言,可以直接調用系統的,比如Locale.CHINESE
。但是這次做的維吾爾語的適配,略我坑一下。
首先,維吾爾語的英文名字叫Uyghur。Locale里并沒有,也可能是我找的不對。
其次,Uyghur是阿拉伯系的。嗯,沒錯,有個詞叫RTL,會出現神奇的東西。
最后,AndroidStudio的values里面是有Uyghur選項的。并且有相對應選項,其中就包括中國,我猜這就是維吾爾語。所以就建了一個,全名是:values-ug-rCN
。
問題是,我最開始直接這樣寫的:
Locale locale = new Locale("ug-rCN");
好吧,是我太天真,不好用。機智的我看了看Locale的構造方法。后來猜了一個結論
ug代表語種,rCN代表國家和地區。比如說同樣都是中文zh,但分了很多國家和地區,如中國,臺灣,香港以及新家坡等
所以下面的寫法才是正確姿勢:
Locale locale = new Locale("ug", Locale.CHINA.getCountry());