Android國際化(多語言)實現,支援8.0

JokAr發表於2018-04-07

前言

最近因為專案中使用了國際化,所以正好研究了下實現方法; 首先說下專案需求:

  • 可以隨著系統切換語言而切換語言,不支援的語言顯示預設
  • 使用者可以選擇語言,且不會隨著系統切換語言或者應用重啟而還原

雖然需求還是很簡單的,但是實現起來還是遇到了不少的麻煩,首先看下效果圖:

效果圖

老規矩

專案原始碼

實現思路

  • 在application 的 attachBaseContext設定當前設定的語言Local
  • 在application 的onConfigurationChanged(改變系統語言時會呼叫到)設定當前的語言Local
  • 在 Activity 的attachBaseContext設定當前設定的語言Local,所以一般這裡是建立BaseActivity來方便統一改變
  • 在 service 的attachBaseContext設定當前設定的語言Local

實現程式碼

有了思路實現起來就很清晰了,

  • 第一步肯定是建立對應語言的string.xml,在demo中我只實現了:中文簡體,中文繁體,和英文三個語言

    image.png

  • 因為這個我們要儲存使用者的選擇語言,所以這裡建立個 SharedPreferences的單例:

public class SPUtil {

    private final String SP_NAME = "language_setting";
    private final String TAG_LANGUAGE = "language_select";
    private static volatile SPUtil instance;

    private final SharedPreferences mSharedPreferences;

    public SPUtil(Context context) {
        mSharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
    }


    public void saveLanguage(int select) {
        SharedPreferences.Editor edit = mSharedPreferences.edit();
        edit.putInt(TAG_LANGUAGE, select);
        edit.commit();
    }

    public int getSelectLanguage() {
        return mSharedPreferences.getInt(TAG_LANGUAGE, 0);
    }

    public static SPUtil getInstance(Context context) {
        if (instance == null) {
            synchronized (SPUtil.class) {
                if (instance == null) {
                    instance = new SPUtil(context);
                }
            }
        }
        return instance;
    }
}
複製程式碼

建立管理語言的Util

  • 建立根據使用者設定獲取對應的 Local方法:
/**
     * 獲取選擇的語言設定
     *
     * @param context
     * @return
     */
    public static Locale getSetLanguageLocale(Context context) {

        switch (SPUtil.getInstance(context).getSelectLanguage()) {
            case 0:
                return getSystemLocale(context);
            case 1:
                return Locale.CHINA;
            case 2:
                return Locale.TAIWAN;
            case 3:
            default:
                return Locale.ENGLISH;
        }
    }

 /**
     * 獲取系統的locale
     *
     * @return Locale物件
     */
    public static Locale getSystemLocale(Context context) {
        Locale locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            locale = LocaleList.getDefault().get(0);
        } else {
            locale = Locale.getDefault();
        }
        return locale;
    }
複製程式碼
  • 建立改變Local方法
public static Context setLocal(Context context) {
        return updateResources(context, getSetLanguageLocale(context));
    }

    private static Context updateResources(Context context, Locale locale) {
        Locale.setDefault(locale);

        Resources res = context.getResources();
        Configuration config = new Configuration(res.getConfiguration());
        if (Build.VERSION.SDK_INT >= 17) {
            config.setLocale(locale);
            context = context.createConfigurationContext(config);
        } else {
            config.locale = locale;
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
        return context;
    }
複製程式碼
  • 在相應地方呼叫
#Application
 @Override
    protected void attachBaseContext(Context base) {
       super.attachBaseContext(LocalManageUtil.setLocal(base));
    }

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocalManageUtil.setLocal(context);
    }
複製程式碼
#BaseActivity
@Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(LocalManageUtil.setLocal(newBase));
    }
複製程式碼
#service
@Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocalManageUtil.setLocal(base));
    }
複製程式碼
  • 切換語言
private void selectLanguage(int select) {
        LocalManageUtil.saveSelectLanguage(this, select);
        //重啟APP到主頁面
        MainActivity.reStart(this);
    }
複製程式碼

搞定,

image.png

沒錯就是這麼簡單。

image.png

但是總有你想不到的事

我們都會在程式碼中呼叫context.getResource().getString()這句程式碼看起來沒什麼問題,但是你這個context要是用的是applicationContext那麼問題就來了。你會發現當你切換語言後用這樣方式設定的string沒有改變,所以我們需要改動我們的程式碼。 解決方法就是,在切換語言後把application的updateConfiguration也要更新了,方法如下:

 /**
     * 設定語言型別
     */
    public static void setApplicationLanguage(Context context) {
        Resources resources = context.getApplicationContext().getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        Configuration config = resources.getConfiguration();
        Locale locale = getSetLanguageLocale(context);
        config.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            LocaleList localeList = new LocaleList(locale);
            LocaleList.setDefault(localeList);
            config.setLocales(localeList);
            context.getApplicationContext().createConfigurationContext(config);
            Locale.setDefault(locale);
        }
        resources.updateConfiguration(config, dm);
    }
複製程式碼
  • 但是當你呼叫這個程式碼後你獲取到的系統選擇語言就是你設定的語言,這不準確呀,那怎麼辦呢?很簡單,我們把真實的系統語言儲存下來就行了。

  • 我們在SharedPreferences單例中新增系統Local變數:

    /**
     * 用來儲存系統語言
     */
    private Locale systemCurrentLocal = Locale.ENGLISH;

public int getSelectLanguage() {
        return mSharedPreferences.getInt(TAG_LANGUAGE, 0);
    }


    public Locale getSystemCurrentLocal() {
        return systemCurrentLocal;
    }
複製程式碼
  • 然後在 application的attachBaseContextonConfigurationChanged獲取真實的系統Local並儲存

  • 最後改變下我們原來獲取系統Local的方法:

    /**
     * 獲取系統的locale
     *
     * @return Locale物件
     */
    public static Locale getSystemLocale(Context context) {
        return SPUtil.getInstance(context).getSystemCurrentLocal();
    }
複製程式碼

這樣就真的完成了,具體的程式碼可見專案原始碼這裡, 若您覺得對您有幫助,歡迎點個start

相關文章