Android 多语言支持

添加多国语言文件

下面以添加 西班牙语 为例,首先在 res 右键 “new”,选择 “Values resource file”。

Android 多语言支持_第1张图片

左侧选中 “Locale”,点击 “ >> ”,然后 “Chosen qualifiers” 面板中就会出现 “Locale” 这栏
Android 多语言支持_第2张图片
西班牙语是 “es”,在 “Language” 面板中最开始是没有搜索框的,直接聚焦后输入,搜索框就会自动出现了。然后在右边的 “Specific Region Only” 面版中 选择 “Any Region”。
Android 多语言支持_第3张图片

多语言表(转自 https://blog.csdn.net/u010586698/article/details/56673379)

语言 英语名称 中文名称 国家码 国标
Language English Name Chinese Name Code GB ICS JB KK L
العربية Arabic(Israel) 阿拉伯语(以色列) ar_IL Y Y Y Y Y
Arabic(Egypt) 阿拉伯语(埃及) ar_EG GD_MN GD_MN GD_MN GD_MN GD_MN
中文 Chinese Simplified 中文简体 zh_CN Y Y Y Y Y
中文 Chinese Tradition 中文繁体 zh_TW Y Y Y Y Y
中文 Chinese 中文(香港) zh_HK N N P P Y
Nederlands Dutch (Netherlands) 荷兰语 nl_NL Y Y Y Y Y
Nederlands Dutch (Netherlands) 荷兰语(比利时) nl_BE GD_MN GD_MN GD_MN GD_MN GD_MN
English English English(United States) 英语(美国) en_US Y Y Y Y
English English(Australia) 英语(澳大利亚) en_AU GD_MN GD_MN GD_MN GD_MN GD_MN
English English(Canada) 英语(加拿大) en_CA GD_MN GD_MN GD_MN GD_MN GD_MN
English English(India) 英语(印度) en_IN GD_MN GD_MN GD_MN GD_MN GD_MN
English English(Ireland) 英语(爱尔兰) en_IE GD_MN GD_MN GD_MN GD_MN GD_MN
English English(New Zealand) 英语(新西兰) en_NZ GD_MN GD_MN GD_MN GD_MN GD_MN
English English(Singapore) 英语(新加波) en_SG GD_MN GD_MN GD_MN GD_MN GD_MN
English English(South Africa) 英语(南非) en_ZA GD_MN GD_MN GD_MN GD_MN GD_MN
English English(United Kingdom) 英语(英国) en_GB GD_MN GD_MN GD_MN Y Y
Français French 法语 fr_FR Y Y Y Y Y
Français French 法语(比利时) fr_BE GD_MN GD_MN GD_MN GD_MN GD_MN
Français French 法语(加拿大) fr_CA GD_MN GD_MN GD_MN GD_MN GD_MN
Français French 法语(瑞士) fr_CH GD_MN GD_MN GD_MN GD_MN GD_MN
Deutsch German 德语 de_DE Y Y Y Y Y
Deutsch German 德语(列支敦斯登) de_LI GD_MN GD_MN GD_MN GD_MN GD_MN
Deutsch German 德语(奥地利) de_AT GD_MN GD_MN GD_MN Y Y
Deutsch German 德语(瑞士) de_CH GD_MN GD_MN GD_MN GD_MN GD_MN
Italiano Italian 意大利语 it_IT Y Y Y Y Y
Italiano Italian 意大利语(瑞士) it_CH GD_MN GD_MN GD_MN GD_MN GD_MN
Protuguês Portuguese 葡萄牙语(巴西) Pt_BR Y Y Y Y Y
Protuguês Portuguese 葡萄牙语 Pt_PT Y Y Y Y Y
Español Spanish 西班牙语 es_ES Y Y Y Y Y
Español Spanish 西班牙语(美国) es_US GD_MN GD_MN P Y Y
বাংলা Bengali 孟加拉语 bn_BD Y N N N Y
বাংলা Bengali 孟加拉语(印度) bn_IN Y Y Y Y Y
hrvatski Croatian 克罗地亚语 hr_HR GD_MN GD_MN P Y Y
čeština Czech 捷克语 cs_CZ Y Y Y Y Y
Dansk Danish 丹麦语 da_DK GD_MN GD_MN P Y Y
ελληνικά Greek 希腊语 el_GR GD_MN Y Y Y Y
עברית Hebrew 希伯来语(以色列) he_IL GD_MN GD_MN GD_MN GD_MN GD_MN
עברית Hebrew 希伯来语(以色列) iw_IL GD_MN GD_MN Y Y Y
हिंदी Hindi 印度语 hi_IN Y Y Y Y Y
Magyar Hungarian 匈牙利语 hu_HU GD_MN Y Y Y Y
Indonesian 印度尼西亚语 in_ID Y Y Y Y Y
日本語の言語 Japanese 日语 ja_JP GD_MN GD_MN P P Y
한국의 Korean 韩语(朝鲜语) ko_KR GD_MN GD_MN Y Y Y
Bahasa Melayu Malay 马来语 ms_MY Y Y Y Y Y
فارسی Perisan 波斯语 fa_IR Y Y Y Y Y
Polski Polish 波兰语 Pl_PL GD_MN GD_MN P Y Y
româna Romanian 罗马尼亚语 ro_RO GD_MN Y Y Y Y
Русский Russian 俄罗斯语 ru_RU Y Y Y Y Y
српски Serbian 塞尔维亚语 sr_RS GD_MN GD_MN P P Y
Svenska Swedish 瑞典语 sv_SE GD_MN GD_MN P Y Y
ไทย Thai 泰语 th_TH Y Y Y Y Y
Türkçe Turkey 土耳其语 tr_TR Y Y Y Y Y
اردو Urdu 乌尔都语 ur_PK Y Y Y Y Y
tiếng việt Vietnamese 越南语 vi_VN Y Y Y Y Y
catalá Catalan 加泰隆语(西班牙) ca_ES GD_MN GD_MN P P Y
latviešu Latviesu 拉脱维亚语 lv_LV GD_MN GD_MN P Y Y
Lietuvių Lithuanian 立陶宛语 lt_LT GD_MN GD_MN P Y Y
Norsk bokmal Norwegian 挪威语 Nb_NO GD_MN GD_MN P Y Y
Slovenčina slovencina 斯洛伐克语 sk_SK GD_MN GD_MN P Y Y
Slovenščina Slovenian 斯洛文尼亚语 sl_SI GD_MN GD_MN P P Y
български bulgarian 保加利亚语 bg_BG GD_MN GD_MN P Y Y
українська Ukrainian 乌克兰语 uk_UA GD_MN GD_MN P Y Y
Tagalog Filipino 菲律宾语 tl_PH GD_MN Y Y Y Y
Suomi Finnish 芬兰语 fi_FI GD_MN GD_MN P Y Y
Afrikaans Afrikaans 南非语 af_ZA N GD_MN GD_MN GD_MN GD_MN
Rumantsch Romansh 罗曼什语(瑞士) rm_CH GD_MN GD_MN GD_MN GD_MN GD_MN
ဗမာ Burmese(Zawgyi) 缅甸语(民间) my_ZG N N P P Y
ဗမာ Burmese(Paduak) 缅甸语(官方) my_MM N N Y Y Y
ខ្មែរ Khmer 柬埔寨语 km_KH N N Y Y Y
አማርኛ Amharic 阿姆哈拉语(埃塞俄比亚) am_ET N GD_MN GD_MN GD_MN GD_MN
беларуская Belarusian 白俄罗斯语 be_BY N GD_MN GD_MN GD_MN GD_MN
eesti Estonian 爱沙尼亚语 et_EE N GD_MN P P Y
Kiswahili Swahili 斯瓦希里语(坦桑尼亚) sw_TZ N GD_MN GD_MN GD_MN GD_MN
isiZulu Zulu 祖鲁语(南非) zu_ZA N GD_MN GD_MN GD_MN GD_MN
azərbaycanca Azerbaijani 阿塞拜疆语 az_AZ N N N GD_MN GD_MN
Հայերէն Armenian 亚美尼亚语(亚美尼亚) hy_AM N N P P Y
ქართული Georgian 格鲁吉亚语(格鲁吉亚) ka_GE N N N GD_MN GD_MN
ລາວ Laotian 老挝语(老挝) lo_LA N N N GD_MN GD_MN
Монгол Mongolian 蒙古语 mn_MN N N N GD_MN GD_MN
नेपाली Nepali 尼泊尔语 ne_NP N N N GD_MN GD_MN
қазақ тілі Kazakh 哈萨克语 kk_KZ N N P P Y
Galego Galician 加利西亚语 gl-rES N N N N GD_MN
íslenska Icelandic 冰岛语 is-rIS N N N N GD_MN
ಕನ್ನಡ Kannada 坎纳达语 kn-rIN N N N N GD_MN
кыргыз тили; قىرعىز تىلى Kyrgyz 吉尔吉斯语 ky-rKG N N N N GD_MN
മലയാളം Malayalam 马拉亚拉姆语 ml-rIN N N N N GD_MN
मराठी Marathi 马拉提语/马拉地语 mr-rIN N N N N GD_MN
தமிழ் Tamil 泰米尔语 ta-rIN N N N N GD_MN
македонски јазик Macedonian 马其顿语 mk-rMK N N N N GD_MN
తెలుగు Telugu 泰卢固语 te-rIN N N N N GD_MN
Ўзбек тили Uzbek 乌兹别克语 uz-rUZ N N N N GD_MN
Euskara Basque 巴斯克语 eu-rES N N N N GD_MN
සිංහල Sinhala 僧加罗语(斯里兰卡) si_LK N N N N N

下面我做一个支持默认、中文、英文、西班牙语的实例

分别创建如下文件
Android 多语言支持_第4张图片

实现方案:

  • 通过数组存储字符串的ID
    arrays.xml


    
        @string/language_default
        @string/language_zh
        @string/language_en
        @string/language_es
    

  • 切换语言的工具类(安卓7为分界,有两种不同的处理方案)
    LanguageUtil.java
public class LanguageUtil {
    private static final String SP_NAME = "LanguageSP";
    private static final String LANGUAGE_INDEX = "LanguageIndex";

    private static LanguageUtil instance;
    private Context context;
    private int[] languageIds;

    public static LanguageUtil getInstance(Context context) {
        if (instance == null) {
            instance = new LanguageUtil(context);
        } else {
            instance.context  = context;
        }
        return instance;
    }

    private LanguageUtil(Context context) {
        this.context = context;
        final Resources resources = context.getResources();
        TypedArray ta = resources.obtainTypedArray(R.array.language_ids);

        languageIds = new int[ta.length()];
        for (byte i = 0; i < ta.length(); i++) {
            languageIds[i] = ta.getResourceId(i, 0);
        }
    }

    /**
     * 获取语言的字符串数组
     */
    private String[] getLanguages() {
        final String[] languages = new String[languageIds.length];
        for (byte i = 0; i < languageIds.length; i++) {
            languages[i] = context.getString(languageIds[i]);
        }
        return languages;
    }

    /**
     * 根据索引拿到国际化区域类
     */
    private Locale getLocale(int index) {
        Locale locale;
        switch(languageIds[index]) {
            case R.string.language_en:
                locale = Locale.ENGLISH;
                break;
            case R.string.language_es:
                locale = new Locale("es");
                break;
            case R.string.language_zh:
                locale = Locale.CHINA;
                break;
            default:
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    locale = LocaleList.getDefault().get(0);
                } else {
                    locale = Locale.getDefault();
                }
                break;
        }
        return locale;
    }

    /**
     * 在 API 24(Android 7.0)后需要在重启后替换 Context 才能切换语言
     * ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象
     */
    private Context attachBaseContext(int index) {
        Resources resources = context.getResources();
        Configuration configuration = resources.getConfiguration();
        Locale locale = getLocale(index);
        configuration.setLocale(locale);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale);
            context = context.createConfigurationContext(configuration);
        } else {
            DisplayMetrics dm = resources.getDisplayMetrics();
            resources.updateConfiguration(configuration, dm);
        }
        return context;
    }

    /**
     * 重启 Activity
     */
    private void recreate(final Activity activity) {
//        activity.runOnUiThread(new Runnable() {
//            @Override
//            public void run() {
//重启 Activity(这个方法一定要在主线程中被调用)
//注意:recreate 这个方法有的问题,有时候会闪屏,就是一个界面跳两次出来
//recreate之后不能finish,会崩溃
//                activity.recreate();
//            }
//        });

        //重启 Activity
        Intent intent = new Intent(context, activity.getClass());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        activity.startActivity(intent);
        activity.finish();
    }

    private void saveIndexToPreference(int index) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor et = sp.edit();
        et.putInt(LANGUAGE_INDEX, index);
        et.commit();
    }

    private int getIndexFromPreference() {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        return sp.getInt(LANGUAGE_INDEX, 0);
    }

    public void changeLanguage(int index, Activity activity) {
        saveIndexToPreference(index);
        attachBaseContext(index);
        recreate(activity);
    }
    
    /**
     * 每次Activity启动时都会去寻找默认的语言配置,所以必须在分配Context之前进行预处理,修改成你所需要的配置。
     * 这个预处理方法就是 attachBaseContent
     */
    public Context restoreLanguage() {
        int index = getIndexFromPreference();
        return attachBaseContext(index);
    }
}

  • 所有Activity都必须继承一个基类,在基类的 attachBaseContext 方法中切换语言
    BaseInternationalActivity.java
public abstract class BaseInternationalActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context newBase) {
        newBase= LanguageUtil.getInstance(newBase).restoreLanguage();
        super.attachBaseContext(newBase);
    }
}

注意事项

上面的方法只会更新 activity 的 context,对于 application 和 service 的 context 并不会被改变。
虽然 application 和 service 也有 attachBaseContext,只会在初次打开时被替换。
而且这里只用了 activity.recreate() 影响的只会是当前的 Activity, 其它的 activity 启动的时候还是使用默认的 context。

所以如果要用到 service 里面的 context,我的做法是 比如在 bind 的时候重新设置它的context

attachBaseContext (转自 https://www.jianshu.com/p/94e0f9ab3f1d)

Android 多语言支持_第5张图片
Context类本身是一个纯abstract类,它有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。

你可能感兴趣的:(Android)