Android 多語言切換,適配7.0以上(8.0)

最近公司項目也開始做國際版本,正好我來負責適配,剛開始以爲就是簡單的新增幾套詞條資源,更改一下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 就不普及了,不會用自行百度,

希望能幫到正在踩坑的同學

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章