Android 国际化(多语言)兼容8.0

作者: liliLearn | 来源:发表于2018-01-08 11:20 被阅读1315次

前言

Android中实现国际化相对来说还是简单的,因为Android有很独特的资源管理方式,我们可以很轻松的创建资源支持不同语言.

资源文件的的使用

android是在res/values目录下通过不同values文件夹的命名来匹配不同的资源

values-语言代码-r国家代码
例如:values-zh-rCN(中文)和values-en(英文)

先看效果,再扯淡

device-2018-01-10-172927~2.gif

一些帮助工具

国家_地区语言速查表:http://www.cnblogs.com/Mien/archive/2008/08/22/1273950.html

一键实现语言国际化插件
AndroidLocalizationer

API 25 (Android 7.1.1) 以下的方案

Resources resources = getContext().getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
Configuration config = resources.getConfiguration();
config.locale =  Locale.getDefault()
resources.updateConfiguration(config, dm);

通过以上代码我们就能通过更改Configuration配置来进行APP语言切换,但是
在API 25以后推荐我们使用,Context.createConfigurationContext(Configuration),Resources.updateConfiguration(config, dm)被弃用

image.png

新的系统的变更

Android 6.0

Screenshot_1515571286.png

Android 8.0

Screenshot_1515571209.png
根据2个截图来看在Android 6.0系统语言为单选,而在在Android 8.0 变成多选对应的Configuration API 也要求我们从configuration.locale = locale; 替换成configuration.configuration.setLocales(locales);
image.png
getLocales().get(0)为首选语言

开始兼容API 25 (Android 7.1.1)

API 25 (Android 7.1.1)的以后更改语言配置,我们需要重新替换Context,把Context替换成设置了指定语言的Context对象,我们可以采取以下方案。

1.重写一个ContextWrapper类 ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。

2.每个Activity都替换一次Context,这里我们可以把封装在BaseActivity中。

 @Override
    protected void attachBaseContext(Context newBase) {
        SharedPreferences preferences = newBase.getSharedPreferences("language", Context.MODE_PRIVATE);
        String selectedLanguage = preferences.getString("language", "");
        super.attachBaseContext(LanguageUtil.attachBaseContext(newBase, selectedLanguage));
    }

然后通过recreate重启Activity:

 activity.recreate();
 或
 finish();
 startActivity(new Intent(MainActivity.this,MainActivity.class));

来一个Util

public class LanguageConstants {
    // 简体中文
    public static final String SIMPLIFIED_CHINESE = "zh";
    // 英文
    public static final String ENGLISH = "en";
    // 繁体中文
    public static final String TRADITIONAL_CHINESE = "zh-hant";
    // 法语
    public static final String FRANCE = "fr";
    // 德语
    public static final String GERMAN = "de";
    // 意大利语
    public static final String ITALIAN = "it";
    //日语
    public static final String JAPAN = "ja";
}

public class SupportLanguageUtil {
    private static Map<String, Locale> mSupportLanguages = new HashMap<String, Locale>(7) {{
        put(LanguageConstants.ENGLISH, Locale.ENGLISH);
        put(LanguageConstants.SIMPLIFIED_CHINESE, Locale.SIMPLIFIED_CHINESE);
        put(LanguageConstants.TRADITIONAL_CHINESE, Locale.TRADITIONAL_CHINESE);
        put(LanguageConstants.FRANCE, Locale.FRANCE);
        put(LanguageConstants.GERMAN, Locale.GERMANY);
        put(LanguageConstants.HINDI, new Locale(LanguageConstants.HINDI, "IN"));
        put(LanguageConstants.ITALIAN, Locale.ITALY);
        put(LanguageConstants.JAPAN, Locale.JAPAN);
    }};

    /**
     * 是否支持此语言
     *
     * @param language language
     * @return true:支持 false:不支持
     */
    public static boolean isSupportLanguage(String language) {
        return mSupportLanguages.containsKey(language);
    }

    /**
     * 获取支持语言
     *
     * @param language language
     * @return 支持返回支持语言,不支持返回系统首选语言
     */
    @TargetApi(Build.VERSION_CODES.N)
    public static Locale getSupportLanguage(String language) {
        if (isSupportLanguage(language)) {
            return mSupportLanguages.get(language);
        }
        return getSystemPreferredLanguage();
    }

    /**
     * 获取系统首选语言
     *
     * @return Locale
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    public static Locale getSystemPreferredLanguage() {
        Locale locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            locale = LocaleList.getDefault().get(0);
        } else {
            locale = Locale.getDefault();
        }
        return locale;
    }
}
public class LanguageUtil {
    public static void applyLanguage(Context context, String newLanguage) {
        Resources resources = context.getResources();
        Configuration configuration = resources.getConfiguration();
        Locale locale = SupportLanguageUtil.getSupportLanguage(newLanguage);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // apply locale
            configuration.setLocale(locale);

        } else {
            // updateConfiguration
            configuration.locale = locale;
            DisplayMetrics dm = resources.getDisplayMetrics();
            resources.updateConfiguration(configuration, dm);
        }
    }

    public static Context attachBaseContext(Context context, String language) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return createConfigurationResources(context, language);
        } else {
            applyLanguage(context, language);
            return context;
        }
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context createConfigurationResources(Context context, String language) {
        Resources resources = context.getResources();
        Configuration configuration = resources.getConfiguration();
        Locale locale;
        if (TextUtils.isEmpty(language)) {//如果没有指定语言使用系统首选语言
            locale = SupportLanguageUtil.getSystemPreferredLanguage();
        } else {//指定了语言使用指定语言,没有则使用首选语言
            locale = SupportLanguageUtil.getSupportLanguage(language);
        }
        configuration.setLocale(locale);
        return context.createConfigurationContext(configuration);
    }
}

最后参考:

http://www.jcodecraeer.com/a/anzhuokaifa/2017/1017/8600.html
https://gunhansancar.com/change-language-programmatically-in-androi/

GitHub:https://github.com/liliLearn/switch_language_sample

相关文章

网友评论

  • Wait_eb1a:8.0的还是不行
    liliLearn:小米 8.0系统上亲测有效。
  • af49d120e89f:我这边是 切换语言后APP 直接重启, 测试在7.1.1 以下都可以成功的,但是在8.0不成功,本地资源文件得不到切换。
    Locale newLocale = new Locale(val[0], McDApplication.getContext().getCountry());
    Locale.setDefault(newLocale);

    Resources resources = McDApplication.getContext().getResources();
    Configuration config = new Configuration(resources.getConfiguration());
    config.setLocale(newLocale);//new APi

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    LocaleList localeList = new LocaleList(newLocale);
    LocaleList.setDefault(localeList);
    config.setLocales(localeList);
    McDApplication.getContext().createConfigurationContext(config);
    Locale.setDefault(newLocale);

    }
    resources.updateConfiguration(config, resources.getDisplayMetrics());
    liliLearn:直接跳转到小标题: 开始兼容API 25 (Android 7.1.1) 希望能帮到你!
    liliLearn:博客中很明确的介绍了7.1.1前使用的是resources.updateConfiguration方式 7.1.1后更改语言在Content中去设置了!
  • 熊猫的脑壳:实测有效, 谢谢
    liliLearn:@熊猫的脑壳 Configuration config = resources.getConfiguration(); 创建一个config 的副本 然后进行赋值 替换成 resources.updateConfiguration(newconfiguration, dm);
    熊猫的脑壳:@liliLearn 但这样会引起一个新的问题是: 这样设置后似乎Configuration的orientation 字段就失效了(还有其他的一些字段也是),比如我进应用的时候是竖屏 那么该值为1 等我应用横屏的时候我再拿这个值 他还是为1 ,按理说横屏的时候是2
    liliLearn:项目中模块抽取!还有8.0的区域设置还未兼容。

本文标题:Android 国际化(多语言)兼容8.0

本文链接:https://www.haomeiwen.com/subject/qzvvnxtx.html