Android 多语言切换,适配7.0以上(8.0)

最近公司项目也开始做国际版本,正好我来负责适配,刚开始以为就是简单的新增几套词条资源,更改一下app的语言环境,

然而测试的时候发现,问题太多了,,,,,

百度 Google搜了很多,借鉴了很多适配的方案,目前为止,总算是稳定了很多,记录一下:

1.首先是首次安装获取 手机系统语言,并且给 app 环境也保存个 语言环境,之后 app 的语言就和 手机系统的 没关系了

2.之后每次通过按钮切换语言,保存本地,并且更新页面

3.每次切换语言的时候,通知服务器,用户修改了语言环境,接下来的操作,就是新的语言了

4.我们项目中做了几处地方的修改:(这点不重要)

       主动通知服务器 用户更改过后的语言环境,

        修改语言之后 公共请求头携带新语言参数(首次安装是按手机系统环境走的)

     设置页面 切换语言之后,重建页面,更新语言,并且页面仍保留在当前页面;

正文:

写的很混乱,凑合着理解吧,尽量写的清楚点。。

更新一下:适配8.0

8.0网搜一下 ,语言的初始化和之前的不一样,需要返回个context。

上新一下utils的最新代码;

资源文件 :简写的values 文件夹,如果写成 values-zh-rCN等,5.0 、6.0的手机 有时候会切换失败 语言混乱,debug 模式下,发现引用的资源文件是 默认文件夹下的,改成简写的 就显示正常了。

Android 多语言切换,适配7.0以上(8.0)_第1张图片

context引用:util 方法  建议就传当前activity的 context 就可以了。   一个是全局工具类对context的引用,

代码中 对string资源文件的引用,使用 context.getResources().getString()或者getResources().getString() 或者getString()

避免使用全局缓存的context,如果使用 初始化时 application 传给某个全局类的context 可能会出现语言混乱。

哦,这里推荐一篇大佬写的文章,建议看一下了解的深一点

https://mp.weixin.qq.com/s/A27dvFV3glX26Ur0WsdJ9g

测试中发现,通过 下面 getAppLocale()获取到的local,7.0以上版本  华为P20,一加 等 偶尔 出现混乱,锤子 坚果pro 从始至终就没出现过混乱,但是 7.0 以下的 努比亚  小米 三星 大概率出现切换失败,特别是 activity  嵌套 fragment 特别频繁,个别页面切换OK。

/**
 * 获取系统local
 * @return
 */
public static Locale getSystemLocale() {
    Locale locale = Resources.getSystem().getConfiguration().locale;
    Log.d(TAG ,"getLanguage : " + Locale.getDefault().getLanguage());
    return locale;
}

/**
 * 获取locale
 * @return Locale对象
 */
public static Locale getAppLocale() {
    Locale locale;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        locale = LocaleList.getDefault().get(0);
    } else {
        locale = Locale.getDefault();
    }
    Log.i(TAG, locale.getLanguage());

    return locale;
}


/**
 * 设置语言 ,网上大部分都是用的这个方法更新的 本地语言,
 */
public static void setApplicationLanguage(Context context) {

    Resources resources = context.getResources();
    DisplayMetrics dm = resources.getDisplayMetrics();
    Configuration config = resources.getConfiguration();
    Locale locale = getSetLanguageLocale(context);//获取sp里面保存的语言
    config.locale = locale;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        LocaleList localeList = new LocaleList(locale);
        LocaleList.setDefault(localeList);
        config.setLocales(localeList);
        context.createConfigurationContext(config);
        Locale.setDefault(locale);
    }
    resources.updateConfiguration(config, dm);
}

//我是用的 
//context.getResources()和Resources.getSystem() 好像不是一个东西, toSring(),值是不一样的
    /*
    * 设置语言类型
    */
    private static void setApplicationLanguage(Context context) {

//        Resources resources = context.getResources();
//        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        Locale locale = getSetLanguageLocale(context);//获取sp里面保存的语言


        // 更新 app 环境
        Configuration conf = context.getResources().getConfiguration();
        conf.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            LocaleList localeList = new LocaleList(locale);
            LocaleList.setDefault(localeList);
            conf.setLocales(localeList);
            context.createConfigurationContext(conf);
            Locale.setDefault(locale);
        }
        context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());

        // 更新系统 环境
        Configuration config = Resources.getSystem().getConfiguration();
        config.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            LocaleList localeList = new LocaleList(locale);
            LocaleList.setDefault(localeList);
            config.setLocales(localeList);
            context.createConfigurationContext(config);
            conf.setLocales(localeList);
            context.createConfigurationContext(conf);
            Locale.setDefault(locale);
        }
        Resources.getSystem().updateConfiguration(config, context.getResources().getDisplayMetrics());
    }
 

完整的代码:



//工具类
public class LocalManageUtil {

    /**
     * 获取选择的语言设置
     *
     * @param context
     * @return
     */
    private static Locale getSetLanguageLocale(Context context) {
        switch (SPManager.getInstance(context).getInt(CoreConst.LOCALE_LANGUAGE)) {
            case 0://跟随系统
                return getSystemLocale();
            case 1://英语
                return Locale.ENGLISH;
            case 2://汉语
                return Locale.CHINESE;
            case 3://日本语
                return Locale.JAPANESE;
            default://默认 汉语
                return Locale.CHINESE;
        }
    }

    /**
     * 设置 本地语言
     *
     * @param context
     * @param select
     */
    public static void saveSelectLanguage(Context context, int select) {
        SPManager.getInstance().putInt(CoreConst.LOCALE_LANGUAGE, select);
        setApplicationLanguage(context);
    }


    /**
     * 初始化语言 方法
     *
     * @param context
     */
    public static Context setLocal(Context context) {
        return setApplicationLanguage(context);
    }

    /**
     * 设置语言类型
     */
    public static Context setApplicationLanguage(Context context) {

        Resources resources = context.getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        Configuration config = resources.getConfiguration();
        Locale locale = getSetLanguageLocale(context);//获取sp里面保存的语言
        MLog.d("SNN", "修改的 语言 : " + locale.getLanguage());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            LocaleList localeList = new LocaleList(locale);
            LocaleList.setDefault(localeList);
            config.setLocales(localeList);
            Locale.setDefault(locale);
//            context.createConfigurationContext(config);
            return context.createConfigurationContext(config);
        } else {
            config.locale = locale;
        }
        resources.updateConfiguration(config, dm);
        return context;
    }


    /**
     * 设置语言类型
     * 测试 是否能解决 部分手机 语言乱码的 问题
     */
//    private static void setApplicationLanguage(Context context) {
//
        Resources resources = context.getResources();
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
//        Locale locale = getSetLanguageLocale(context);//获取sp里面保存的语言
//
//
//        // 更新 app 环境
//        Configuration conf = context.getResources().getConfiguration();
//        conf.locale = locale;
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//            LocaleList localeList = new LocaleList(locale);
//            LocaleList.setDefault(localeList);
//            conf.setLocales(localeList);
//            context.createConfigurationContext(conf);
//            Locale.setDefault(locale);
//        }
//        context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());
//
//        // 更新系统 环境
//        Configuration config = Resources.getSystem().getConfiguration();
//        config.locale = locale;
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//            LocaleList localeList = new LocaleList(locale);
//            LocaleList.setDefault(localeList);
//            config.setLocales(localeList);
//            context.createConfigurationContext(config);
//            Locale.setDefault(locale);
//        }
//        Resources.getSystem().updateConfiguration(config, context.getResources().getDisplayMetrics());
//    }

    /**
     * 获取App的locale
     *
     * @return Locale对象
     */
    public static Locale getAppLocale() {
        Locale locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            locale = LocaleList.getDefault().get(0);
        } else {
            locale = Locale.getDefault();
        }
        MLog.i(CoreConst.ANSEN, locale.getLanguage());
        MLog.d("SNN", " 版本 获取 getLanguage : " + locale.getLanguage());
        return locale;
    }

    /**
     * 获取系统local
     *
     * @return
     */
    public static Locale getSystemLocale() {
        Locale locale = Resources.getSystem().getConfiguration().locale;
        MLog.d("SNN", "系统获取  :getLanguage : " +            Locale.getDefault().getLanguage());
        return locale;
    }

    /**
     * 获取本地保存的语言
     *
     * @param context
     * @return
     */
    public static String getLocalSaveLanguage(Context context) {
        Locale locale = getSetLanguageLocale(context);
        String language = locale.getLanguage();
        if (language.equals("zh")) {
            language = "zh-CN";
        } else if (language.equals("en")) {
            language = "en";
        } else if (language.equals("ja")) {
            language = "ja";
        }
        return language;
    }
}

 ================================================================================
 

用法就是,在application 重写attachBaseContext()  调用初始化方法

8.0  在 attachBaseContext调用setLocal()返回个 context ,就可以了。
@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(LocalManageUtil.setLocal(base));
    // 应用启动时 修改 语言
    //LocalManageUtil.setLocal(base);
}
 
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // 当切换横竖屏 重置语言
    LocalManageUtil.setLocal(getApplicationContext());
}
 
 
 在baseactivity  重写attachBaseContext
@Override 
protected void attachBaseContext(Context base) { 
    super.attachBaseContext(LocalManageUtil.setLocal(base));  
    // 应用启动时 修改 语言 
   // LocalManageUtil.setLocal(base);
 }
 
 
如果是第三方的库,,我这边是 引入的module,所以可以在module 里增加相应的资源文件,并且在主activity重写
attachBaseContext() 方法。
 
依赖的库,就不知道了,。。
 

 
 
 
 
 
最后就是切换语言之后页面重建的问题,
我看微信的是 修改之后 重建页面 会重新回到设置界面,猜测是记录了页面路径(应该不会这么low)
我是通过 广播实现的,eventbus
选择语言后-->保存本地新的语言-->发送广播-->跳到mainactivity -->关闭当前页面。mainactivity 收到广播之后-->再跳到刚才的设置页面 ,就是下面的 步骤:
 
// 设置页面的 方法,选择 语言调用,判断如果是本次选择的 和之前保存的是同一种语言,就没必要做切换& 重建页面了。
// SettingActivity :
private void toSetLanguage(int localType, String value) {
    if (SPManager.getInstance().getInt(CoreConst.LOCALE_LANGUAGE) == localType) {             
             return; }
// MLog.i(“snn”,"更新语言成功:"+localType);

   LocalManageUtil.saveSelectLanguage(this, localType);// 修改本地保存的语言
    presenter.updateCommonField(this);// 更改请求头信息,
    goTo(MainActivity.class);// 跳转到首页 (分装的 方法,其实是 跳转到main,并关闭当前页面)              EventBus.getDefault().post(EventBusConstans.LANGUAGE_CHANGE);// 发送广播
}
 
MainActivity 收到广播之后-->跳到之前的 设置页面 -->并且 调用recreate() 重建main页面,
MainActivity 是 singleTask 启动方式,启动方式不理解的,,,,百度,,
 
if (flag == EventBusConstans.LANGUAGE_CHANGE) {
        goTo(SettingActivity.class);
        recreate();
}
 
EventBus 就不普及了,不会用自行百度,

希望能帮到正在踩坑的同学

你可能感兴趣的:(Android)