1. 程式人生 > >Android 入門——App國際化之動態切換多語言小結

Android 入門——App國際化之動態切換多語言小結

#引言
如果你的App是面向國際的或者是做手機ROM的,那麼對於Android國際化適配應該不會陌生,今天就好好地總結下。

一、Locale和Configuration 概述

Locale物件是用於表示特定的地理,政治或文化區域。 需要Locale執行其任務的操作稱為區域設定敏感,並使用Locale為使用者定製資訊。 例如顯示數字是區域設定敏感操作 - 應根據使用者原生國家,地區或文化的習慣和慣例格式化數字,再比如阿拉伯國家是從右往左佈局的,這些配置資訊都是由Locale決定的,當然也可以從Locale中獲取對應的配置資訊。而Configuration 封裝了影響應用程式檢索的資源的所有裝置配置資訊

包括使用者指定的配置選項(區域設定列表和縮放)以及裝置配置(例如輸入模式,螢幕大小和螢幕方向)等等資訊。通過使用Resources.getConfiguration()方法就可以從Resources獲取此物件。在Activity中還可以通過ContextThemeWrapper.getResources().getConfiguration()方法請求來獲取。

二、動態切換語言

如果你檢視過原始碼你會發現Application的第一個生命週期方法是attachBaseContext而不是onCreate方法,同樣的Activity從一定程度上來說也是先呼叫attachBaseContext方法拿到ContextWrapper上下文封裝類物件,然後根據對應的ContextWrapper進行初始化的,所以只要我們替換成我們自己的ContextWrapper就可以實現替換資源了,其實還有另另一種思路,全部使用程式碼進行動態設定。
##1、建立對應資原始檔夾
res資料夾上右鍵

——>New——>Android Resource Directory——>在Available qualifiers下拉列表處選擇Locale——>點選“>>”——>在Language中選擇要適配的語言——>再選擇指定的Region——>點選OK——>再在對應的values-xx檔案下——>New——>Values Resource File
在這裡插入圖片描述

2、自定義用於資源適配的ContextWrapper

package com.crazymo.changelanguage;

import android.content.Context;
import android.content.ContextWrapper; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.LocaleList; import android.text.TextUtils; import java.util.Locale; /** * Auther: Crazy.Mo on 2018/4/18 14:58 * Summary: */ public class LanContextWrapper extends ContextWrapper { public static final String LANG_HK = "hk"; public static final String LANG_CN = "cn"; public static final String LANG_EN = "en"; public LanContextWrapper(Context ctx) { super(ctx); } public static ContextWrapper wrap(Context context) { Locale newLocale; SharedPreferences sharedPreferences=context.getSharedPreferences(MainActivity.SP_NAME, MODE_PRIVATE); String lanTag=sharedPreferences.getString(MainActivity.LANGUAGE, "def"); switch (lanTag) { case "def"://沒有手東設定過對應的語言則預設讀取手機系統的語言 String locale = Locale.getDefault().getLanguage(); String langFlag=""; if (TextUtils.isEmpty(locale)) { langFlag=LANG_CN; newLocale = Locale.CHINESE; } else if (locale.contains("en")) { langFlag=LANG_EN; newLocale = Locale.ENGLISH; }else if (locale.startsWith("zh")) { String region=Locale.getDefault().getDisplayCountry(); if(region.equals("香港特別行政區")||region.equals("臺灣")){ langFlag=LANG_HK; newLocale=Locale.TRADITIONAL_CHINESE; }else{ langFlag=LANG_CN; newLocale=Locale.SIMPLIFIED_CHINESE; } } else { newLocale = Locale.CHINESE; } sharedPreferences.edit().putString(MainActivity.LANGUAGE, langFlag).apply(); break; case LANG_EN://設定為英語 newLocale = Locale.ENGLISH; break; case LANG_HK://設定為繁體 newLocale = Locale.TRADITIONAL_CHINESE; break; case LANG_CN: newLocale=Locale.SIMPLIFIED_CHINESE; break; default://預設為漢語 newLocale = Locale.SIMPLIFIED_CHINESE; break; } context = getLanContext(context, newLocale); return new ContextWrapper(context); } /** * 初始化Context * @param context * @param pNewLocale * @return */ private static Context getLanContext(Context context, Locale pNewLocale) { Resources res = context.getApplicationContext().getResources();//1、獲取Resources Configuration configuration = res.getConfiguration();//2、獲取Configuration //3、設定Locale並初始化Context if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { configuration.setLocale(pNewLocale); LocaleList localeList = new LocaleList(pNewLocale); LocaleList.setDefault(localeList); configuration.setLocales(localeList); context = context.createConfigurationContext(configuration); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { configuration.setLocale(pNewLocale); context = context.createConfigurationContext(configuration); } return context; } /** * 獲取手機設定的語言國家 * @param context * @return */ public static String getCountry(Context context) { String country; Resources resources = context.getApplicationContext().getResources(); //在7.0以上和7.0一下獲取國家的方式有點不一樣 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { country = resources.getConfiguration().getLocales().get(0).getCountry(); } else { country = resources.getConfiguration().locale.getCountry(); } return country; } }

3、在Activity中進行動態切換

package com.crazymo.changelanguage;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    public static final String SP_NAME = "name";
    public static final String LANGUAGE = "language";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 設定修改語言
     *
     * @param newBase
     */
    @Override
    protected void attachBaseContext(Context newBase) {
        Context context = LanContextWrapper.wrap(newBase);
        super.attachBaseContext(context);
    }

    public void changeChinese(View view) {
        SharedPreferences sharedPreferences=getSharedPreferences(MainActivity.SP_NAME, MODE_PRIVATE);
        sharedPreferences.edit().putString(MainActivity.LANGUAGE, LanContextWrapper.LANG_CN).apply();
        rebot();
    }

    public void changeEnglish(View view) {
        SharedPreferences sharedPreferences=getSharedPreferences(MainActivity.SP_NAME, MODE_PRIVATE);
        sharedPreferences.edit().putString(MainActivity.LANGUAGE, LanContextWrapper.LANG_EN).apply();
        rebot();
    }

    public void changeRChinese(View view) {
        SharedPreferences sharedPreferences=getSharedPreferences(MainActivity.SP_NAME, MODE_PRIVATE);
        sharedPreferences.edit().putString(MainActivity.LANGUAGE, LanContextWrapper.LANG_HK).apply();
        rebot();
    }

    private void rebot() {
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) {
            Intent intent = new Intent(this, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            overridePendingTransition(R.anim.anim_change_lang_enter, R.anim.anim_change_lang_exit);
            finish();
        }else{
            recreate();
        }
    }

    public void toOther(View view) {
        String country=LanContextWrapper.getCountry(this);
        startActivity(new Intent(this,Main2Activity.class));
    }
}

#附錄、國際化資源表示形式
Android 應用資源國際化的知識。對於資原始檔的國際化,我們一般是在 Android src/main/res/ 目錄下,建立對應語言資料夾,格式一般為:values-語言代號-地區代號,預設的資源是不包含語言代號和地區代號的。一般情況下,應用資源是沒有做任何適配的,所以不管如何切換語言和地區設定,應用顯示的資源都不會發生任何改變。配置選項包括語言代號和地區代號。表示中文和中國的配置選項是 zh-rCN(zh表示中文,CN表示中國)。表示英文和美國的配置選項是 en-rUS(en表示英文,US表示美國)。同一語言代號可以有多個地區代號,用 r 表示區分。

國家/區域 對應values資料夾
中文(中國) values-zh-rCN
中文(臺灣) values-zh-rTW
中文(香港) values-zh-rHK
維吾爾文(中國) values-ug-rCN
英語(美國) values-en-rUS
英語(英國) values-en-rGB
英文(澳大利亞) values-en-rAU
英文(加拿大) values-en-rCA
英文(愛爾蘭) values-en-rIE
英文(印度) values-en-rIN
英文(紐西蘭) values-en-rNZ
英文(新加坡) values-en-rSG
英文(南非) values-en-rZA
阿拉伯文(埃及) values-ar-rEG
阿拉伯文(以色列) values-ar-rIL
保加利亞文: values-bg-rBG
加泰羅尼亞文 values-ca-rES
捷克文 values-cs-rCZ
丹麥文 values-da-rDK
德文(奧地利) values-de-rAT
德文(瑞士) values-de-rCH
德文(德國) values-de-rDE
德文(列支敦斯登) values-de-rLI
希臘文 values-el-rGR
西班牙文(西班牙) values-es-rES
西班牙文(美國) values-es-rUS
芬蘭文(芬蘭) values-fi-rFI
法文(比利時) values-fr-rBE
法文(加拿大) values-fr-rCA
法文(瑞士) values-fr-rCH
法文(法國) values-fr-rFR
希伯來文 values-iw-rIL
印地文 values-hi-rIN
克羅里亞文 values-hr-rHR
匈牙利文 values-hu-rHU
印度尼西亞文 values-in-rID
義大利文(瑞士) values-it-rCH
義大利文(義大利) values-it-rIT
日文 values-ja-rJP
韓文 values-ko-rKR
立陶宛文 valueslt-rLT
拉脫維亞文 values-lv-rLV
挪威博克馬爾文 values-nb-rNO
荷蘭文(比利時) values-nl-BE
荷蘭文(荷蘭) values-nl-rNL
波蘭文 values-pl-rPL
葡萄牙文(巴西) values-pt-rBR
葡萄牙文(葡萄牙) values-pt-rPT
羅馬尼亞文 values-ro-rRO
俄文 values-ru-rRU
斯洛伐克文 values-sk-rSK
斯洛維尼亞文 values-sl-rSI
塞爾維亞文 values-sr-rRS
瑞典文 values-sv-rSE
泰文 values-th-rTH
塔加洛語 values-tl-rPH
土耳其文 values–r-rTR
烏克蘭文 values-uk-rUA
越南文 values-vi-rVN