Android 多语言支持

添加多国语言文件

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

在这里插入图片描述

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

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

语言英语名称中文名称国家码国标
LanguageEnglish NameChinese NameCodeGBICSJBKKL
العربيةArabic(Israel)阿拉伯语(以色列)ar_ILYYYYY
Arabic(Egypt)阿拉伯语(埃及)ar_EGGD_MNGD_MNGD_MNGD_MNGD_MN
中文Chinese Simplified中文简体zh_CNYYYYY
中文Chinese Tradition中文繁体zh_TWYYYYY
中文Chinese中文(香港)zh_HKNNPPY
NederlandsDutch (Netherlands)荷兰语nl_NLYYYYY
NederlandsDutch (Netherlands)荷兰语(比利时)nl_BEGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglishEnglish(United States)英语(美国)en_USYYYY
EnglishEnglish(Australia)英语(澳大利亚)en_AUGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(Canada)英语(加拿大)en_CAGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(India)英语(印度)en_INGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(Ireland)英语(爱尔兰)en_IEGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(New Zealand)英语(新西兰)en_NZGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(Singapore)英语(新加波)en_SGGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(South Africa)英语(南非)en_ZAGD_MNGD_MNGD_MNGD_MNGD_MN
EnglishEnglish(United Kingdom)英语(英国)en_GBGD_MNGD_MNGD_MNYY
FrançaisFrench法语fr_FRYYYYY
FrançaisFrench法语(比利时)fr_BEGD_MNGD_MNGD_MNGD_MNGD_MN
FrançaisFrench法语(加拿大)fr_CAGD_MNGD_MNGD_MNGD_MNGD_MN
FrançaisFrench法语(瑞士)fr_CHGD_MNGD_MNGD_MNGD_MNGD_MN
DeutschGerman德语de_DEYYYYY
DeutschGerman德语(列支敦斯登)de_LIGD_MNGD_MNGD_MNGD_MNGD_MN
DeutschGerman德语(奥地利)de_ATGD_MNGD_MNGD_MNYY
DeutschGerman德语(瑞士)de_CHGD_MNGD_MNGD_MNGD_MNGD_MN
ItalianoItalian意大利语it_ITYYYYY
ItalianoItalian意大利语(瑞士)it_CHGD_MNGD_MNGD_MNGD_MNGD_MN
ProtuguêsPortuguese葡萄牙语(巴西)Pt_BRYYYYY
ProtuguêsPortuguese葡萄牙语Pt_PTYYYYY
EspañolSpanish西班牙语es_ESYYYYY
EspañolSpanish西班牙语(美国)es_USGD_MNGD_MNPYY
বাংলাBengali孟加拉语bn_BDYNNNY
বাংলাBengali孟加拉语(印度)bn_INYYYYY
hrvatskiCroatian克罗地亚语hr_HRGD_MNGD_MNPYY
češtinaCzech捷克语cs_CZYYYYY
DanskDanish丹麦语da_DKGD_MNGD_MNPYY
ελληνικάGreek希腊语el_GRGD_MNYYYY
עבריתHebrew希伯来语(以色列)he_ILGD_MNGD_MNGD_MNGD_MNGD_MN
עבריתHebrew希伯来语(以色列)iw_ILGD_MNGD_MNYYY
हिंदीHindi印度语hi_INYYYYY
MagyarHungarian匈牙利语hu_HUGD_MNYYYY
Indonesian印度尼西亚语in_IDYYYYY
日本語の言語Japanese日语ja_JPGD_MNGD_MNPPY
한국의Korean韩语(朝鲜语)ko_KRGD_MNGD_MNYYY
Bahasa MelayuMalay马来语ms_MYYYYYY
فارسیPerisan波斯语fa_IRYYYYY
PolskiPolish波兰语Pl_PLGD_MNGD_MNPYY
românaRomanian罗马尼亚语ro_ROGD_MNYYYY
РусскийRussian俄罗斯语ru_RUYYYYY
српскиSerbian塞尔维亚语sr_RSGD_MNGD_MNPPY
SvenskaSwedish瑞典语sv_SEGD_MNGD_MNPYY
ไทยThai泰语th_THYYYYY
TürkçeTurkey土耳其语tr_TRYYYYY
اردوUrdu乌尔都语ur_PKYYYYY
tiếng việtVietnamese越南语vi_VNYYYYY
cataláCatalan加泰隆语(西班牙)ca_ESGD_MNGD_MNPPY
latviešuLatviesu拉脱维亚语lv_LVGD_MNGD_MNPYY
LietuviųLithuanian立陶宛语lt_LTGD_MNGD_MNPYY
Norsk bokmalNorwegian挪威语Nb_NOGD_MNGD_MNPYY
Slovenčinaslovencina斯洛伐克语sk_SKGD_MNGD_MNPYY
SlovenščinaSlovenian斯洛文尼亚语sl_SIGD_MNGD_MNPPY
българскиbulgarian保加利亚语bg_BGGD_MNGD_MNPYY
українськаUkrainian乌克兰语uk_UAGD_MNGD_MNPYY
TagalogFilipino菲律宾语tl_PHGD_MNYYYY
SuomiFinnish芬兰语fi_FIGD_MNGD_MNPYY
AfrikaansAfrikaans南非语af_ZANGD_MNGD_MNGD_MNGD_MN
RumantschRomansh罗曼什语(瑞士)rm_CHGD_MNGD_MNGD_MNGD_MNGD_MN
ဗမာBurmese(Zawgyi)缅甸语(民间)my_ZGNNPPY
ဗမာBurmese(Paduak)缅甸语(官方)my_MMNNYYY
ខ្មែរKhmer柬埔寨语km_KHNNYYY
አማርኛAmharic阿姆哈拉语(埃塞俄比亚)am_ETNGD_MNGD_MNGD_MNGD_MN
беларускаяBelarusian白俄罗斯语be_BYNGD_MNGD_MNGD_MNGD_MN
eestiEstonian爱沙尼亚语et_EENGD_MNPPY
KiswahiliSwahili斯瓦希里语(坦桑尼亚)sw_TZNGD_MNGD_MNGD_MNGD_MN
isiZuluZulu祖鲁语(南非)zu_ZANGD_MNGD_MNGD_MNGD_MN
azərbaycancaAzerbaijani阿塞拜疆语az_AZNNNGD_MNGD_MN
ՀայերէնArmenian亚美尼亚语(亚美尼亚)hy_AMNNPPY
ქართულიGeorgian格鲁吉亚语(格鲁吉亚)ka_GENNNGD_MNGD_MN
ລາວLaotian老挝语(老挝)lo_LANNNGD_MNGD_MN
МонголMongolian蒙古语mn_MNNNNGD_MNGD_MN
नेपालीNepali尼泊尔语ne_NPNNNGD_MNGD_MN
қазақ тіліKazakh哈萨克语kk_KZNNPPY
GalegoGalician加利西亚语gl-rESNNNNGD_MN
íslenskaIcelandic冰岛语is-rISNNNNGD_MN
ಕನ್ನಡKannada坎纳达语kn-rINNNNNGD_MN
кыргыз тили; قىرعىز تىلىKyrgyz吉尔吉斯语ky-rKGNNNNGD_MN
മലയാളംMalayalam马拉亚拉姆语ml-rINNNNNGD_MN
मराठीMarathi马拉提语/马拉地语mr-rINNNNNGD_MN
தமிழ்Tamil泰米尔语ta-rINNNNNGD_MN
македонски јазикMacedonian马其顿语mk-rMKNNNNGD_MN
తెలుగుTelugu泰卢固语te-rINNNNNGD_MN
Ўзбек тилиUzbek乌兹别克语uz-rUZNNNNGD_MN
EuskaraBasque巴斯克语eu-rESNNNNGD_MN
සිංහලSinhala僧加罗语(斯里兰卡)si_LKNNNNN

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

分别创建如下文件
在这里插入图片描述

实现方案:

  • 通过数组存储字符串的ID
    arrays.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="language_ids">
        <item>@string/language_default</item>
        <item>@string/language_zh</item>
        <item>@string/language_en</item>
        <item>@string/language_es</item>
    </array>
</resources>
  • 切换语言的工具类(安卓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)

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

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值