最近公司项目也开始做国际版本,正好我来负责适配,刚开始以为就是简单的新增几套词条资源,更改一下app的语言环境,
然而测试的时候发现,问题太多了,,,,,
百度 Google搜了很多,借鉴了很多适配的方案,目前为止,总算是稳定了很多,记录一下:
1.首先是首次安装获取 手机系统语言,并且给 app 环境也保存个 语言环境,之后 app 的语言就和 手机系统的 没关系了
2.之后每次通过按钮切换语言,保存本地,并且更新页面
3.每次切换语言的时候,通知服务器,用户修改了语言环境,接下来的操作,就是新的语言了
4.我们项目中做了几处地方的修改:(这点不重要)
主动通知服务器 用户更改过后的语言环境,
修改语言之后 公共请求头携带新语言参数(首次安装是按手机系统环境走的)
设置页面 切换语言之后,重建页面,更新语言,并且页面仍保留在当前页面;
写的很混乱,凑合着理解吧,尽量写的清楚点。。
更新一下:适配8.0
8.0网搜一下 ,语言的初始化和之前的不一样,需要返回个context。
上新一下utils的最新代码;
资源文件 :简写的values 文件夹,如果写成 values-zh-rCN等,5.0 、6.0的手机 有时候会切换失败 语言混乱,debug 模式下,发现引用的资源文件是 默认文件夹下的,改成简写的 就显示正常了。
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 就不普及了,不会用自行百度,希望能帮到正在踩坑的同学