最近公司項目也開始做國際版本,正好我來負責適配,剛開始以爲就是簡單的新增幾套詞條資源,更改一下app的語言環境,
然而測試的時候發現,問題太多了,,,,,
百度 Google搜了很多,借鑑了很多適配的方案,目前爲止,總算是穩定了很多,記錄一下:
1.首先是首次安裝獲取 手機系統語言,並且給 app 環境也保存個 語言環境,之後 app 的語言就和 手機系統的 沒關係了
2.之後每次通過按鈕切換語言,保存本地,並且更新頁面
3.每次切換語言的時候,通知服務器,用戶修改了語言環境,接下來的操作,就是新的語言了
4.我們項目中做了幾處地方的修改:(這點不重要)
主動通知服務器 用戶更改過後的語言環境,
修改語言之後 公共請求頭攜帶新語言參數(首次安裝是按手機系統環境走的)
設置頁面 切換語言之後,重建頁面,更新語言,並且頁面仍保留在當前頁面;
正文:
寫的很混亂,湊合着理解吧,儘量寫的清楚點。。
更新一下:適配8.0
8.0網搜一下 ,語言的初始化和之前的不一樣,需要返回個context。
上新一下utils的最新代碼;
資源文件 :簡寫的values 文件夾,如果寫成 values-zh-rCN等,5.0 、6.0的手機 有時候會切換失敗 語言混亂,debug 模式下,發現引用的資源文件是 默認文件夾下的,改成簡寫的 就顯示正常了。
context引用:util 方法 建議就傳當前activity的 context 就可以了。 一個是全局工具類對context的引用,
代碼中 對string資源文件的引用,使用 context.getResources().getString()或者getResources().getString() 或者getString()
避免使用全局緩存的context,如果使用 初始化時 application 傳給某個全局類的context 可能會出現語言混亂。
哦,這裏推薦一篇大佬寫的文章,建議看一下了解的深一點
https://mp.weixin.qq.com/s/A27dvFV3glX26Ur0WsdJ9g
測試中發現,通過 下面 getAppLocale()獲取到的local,7.0以上版本 華爲P20,一加 等 偶爾 出現混亂,錘子 堅果pro 從始至終就沒出現過混亂,但是 7.0 以下的 努比亞 小米 三星 大概率出現切換失敗,特別是 activity 嵌套 fragment 特別頻繁,個別頁面切換OK。
/**
* 獲取系統local
* @return
*/
public static Locale getSystemLocale() {
Locale locale = Resources.getSystem().getConfiguration().locale;
Log.d(TAG ,"getLanguage : " + Locale.getDefault().getLanguage());
return locale;
}/**
* 獲取locale
* @return Locale對象
*/
public static Locale getAppLocale() {
Locale locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
locale = LocaleList.getDefault().get(0);
} else {
locale = Locale.getDefault();
}
Log.i(TAG, locale.getLanguage());return locale;
}
/**
* 設置語言 ,網上大部分都是用的這個方法更新的 本地語言,
*/
public static void setApplicationLanguage(Context context) {Resources resources = context.getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
Configuration config = resources.getConfiguration();
Locale locale = getSetLanguageLocale(context);//獲取sp裏面保存的語言
config.locale = locale;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
config.setLocales(localeList);
context.createConfigurationContext(config);
Locale.setDefault(locale);
}
resources.updateConfiguration(config, dm);
}//我是用的
//context.getResources()和Resources.getSystem() 好像不是一個東西, toSring(),值是不一樣的
/*
* 設置語言類型
*/
private static void setApplicationLanguage(Context context) {// Resources resources = context.getResources();
// DisplayMetrics dm = context.getResources().getDisplayMetrics();
Locale locale = getSetLanguageLocale(context);//獲取sp裏面保存的語言
// 更新 app 環境
Configuration conf = context.getResources().getConfiguration();
conf.locale = locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
conf.setLocales(localeList);
context.createConfigurationContext(conf);
Locale.setDefault(locale);
}
context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());// 更新系統 環境
Configuration config = Resources.getSystem().getConfiguration();
config.locale = locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
config.setLocales(localeList);
context.createConfigurationContext(config);
conf.setLocales(localeList);
context.createConfigurationContext(conf);
Locale.setDefault(locale);
}
Resources.getSystem().updateConfiguration(config, context.getResources().getDisplayMetrics());
}
完整的代碼:
//工具類 public class LocalManageUtil { /** * 獲取選擇的語言設置 * * @param context * @return */ private static Locale getSetLanguageLocale(Context context) { switch (SPManager.getInstance(context).getInt(CoreConst.LOCALE_LANGUAGE)) { case 0://跟隨系統 return getSystemLocale(); case 1://英語 return Locale.ENGLISH; case 2://漢語 return Locale.CHINESE; case 3://日本語 return Locale.JAPANESE; default://默認 漢語 return Locale.CHINESE; } } /** * 設置 本地語言 * * @param context * @param select */ public static void saveSelectLanguage(Context context, int select) { SPManager.getInstance().putInt(CoreConst.LOCALE_LANGUAGE, select); setApplicationLanguage(context); } /** * 初始化語言 方法 * * @param context */ public static Context setLocal(Context context) { return setApplicationLanguage(context); } /** * 設置語言類型 */ public static Context setApplicationLanguage(Context context) { Resources resources = context.getResources(); DisplayMetrics dm = resources.getDisplayMetrics(); Configuration config = resources.getConfiguration(); Locale locale = getSetLanguageLocale(context);//獲取sp裏面保存的語言 MLog.d("SNN", "修改的 語言 : " + locale.getLanguage()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { LocaleList localeList = new LocaleList(locale); LocaleList.setDefault(localeList); config.setLocales(localeList); Locale.setDefault(locale); // context.createConfigurationContext(config); return context.createConfigurationContext(config); } else { config.locale = locale; } resources.updateConfiguration(config, dm); return context; } /** * 設置語言類型 * 測試 是否能解決 部分手機 語言亂碼的 問題 */ // private static void setApplicationLanguage(Context context) { // //// Resources resources = context.getResources(); //// DisplayMetrics dm = context.getResources().getDisplayMetrics(); // Locale locale = getSetLanguageLocale(context);//獲取sp裏面保存的語言 // // // // 更新 app 環境 // Configuration conf = context.getResources().getConfiguration(); // conf.locale = locale; // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // LocaleList localeList = new LocaleList(locale); // LocaleList.setDefault(localeList); // conf.setLocales(localeList); // context.createConfigurationContext(conf); // Locale.setDefault(locale); // } // context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics()); // // // 更新系統 環境 // Configuration config = Resources.getSystem().getConfiguration(); // config.locale = locale; // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // LocaleList localeList = new LocaleList(locale); // LocaleList.setDefault(localeList); // config.setLocales(localeList); // context.createConfigurationContext(config); // Locale.setDefault(locale); // } // Resources.getSystem().updateConfiguration(config, context.getResources().getDisplayMetrics()); // } /** * 獲取App的locale * * @return Locale對象 */ public static Locale getAppLocale() { Locale locale; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { locale = LocaleList.getDefault().get(0); } else { locale = Locale.getDefault(); } MLog.i(CoreConst.ANSEN, locale.getLanguage()); MLog.d("SNN", " 版本 獲取 getLanguage : " + locale.getLanguage()); return locale; } /** * 獲取系統local * * @return */ public static Locale getSystemLocale() { Locale locale = Resources.getSystem().getConfiguration().locale; MLog.d("SNN", "系統獲取 :getLanguage : " + Locale.getDefault().getLanguage()); return locale; } /** * 獲取本地保存的語言 * * @param context * @return */ public static String getLocalSaveLanguage(Context context) { Locale locale = getSetLanguageLocale(context); String language = locale.getLanguage(); if (language.equals("zh")) { language = "zh-CN"; } else if (language.equals("en")) { language = "en"; } else if (language.equals("ja")) { language = "ja"; } return language; } }
================================================================================
用法就是,在application 重寫attachBaseContext() 調用初始化方法
8.0 在 attachBaseContext調用setLocal()返回個 context ,就可以了。
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocalManageUtil.setLocal(base));
// 應用啓動時 修改 語言
//LocalManageUtil.setLocal(base);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 當切換橫豎屏 重置語言
LocalManageUtil.setLocal(getApplicationContext());
}
在baseactivity 重寫attachBaseContext
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocalManageUtil.setLocal(base));
// 應用啓動時 修改 語言
// LocalManageUtil.setLocal(base);
}
如果是第三方的庫,,我這邊是 引入的module,所以可以在module 裏增加相應的資源文件,並且在主activity重寫
attachBaseContext() 方法。
依賴的庫,就不知道了,。。
最後就是切換語言之後頁面重建的問題,
我看微信的是 修改之後 重建頁面 會重新回到設置界面,猜測是記錄了頁面路徑(應該不會這麼low)
我是通過 廣播實現的,eventbus
選擇語言後-->保存本地新的語言-->發送廣播-->跳到mainactivity -->關閉當前頁面。mainactivity 收到廣播之後-->再跳到剛纔的設置頁面 ,就是下面的 步驟:
// 設置頁面的 方法,選擇 語言調用,判斷如果是本次選擇的 和之前保存的是同一種語言,就沒必要做切換& 重建頁面了。
// SettingActivity :
private void toSetLanguage(int localType, String value) {
if (SPManager.getInstance().getInt(CoreConst.LOCALE_LANGUAGE) == localType) {
return; }
// MLog.i(“snn”,"更新語言成功:"+localType);LocalManageUtil.saveSelectLanguage(this, localType);// 修改本地保存的語言
presenter.updateCommonField(this);// 更改請求頭信息,
goTo(MainActivity.class);// 跳轉到首頁 (分裝的 方法,其實是 跳轉到main,並關閉當前頁面) EventBus.getDefault().post(EventBusConstans.LANGUAGE_CHANGE);// 發送廣播
}
MainActivity 收到廣播之後-->跳到之前的 設置頁面 -->並且 調用recreate() 重建main頁面,
MainActivity 是 singleTask 啓動方式,啓動方式不理解的,,,,百度,,
if (flag == EventBusConstans.LANGUAGE_CHANGE) {
goTo(SettingActivity.class);
recreate();
}
EventBus 就不普及了,不會用自行百度,希望能幫到正在踩坑的同學