Android多语言/国际化灵活系统不畏惧NEW TASK

发表于2016-08-26
评论0 1.1k浏览
  我们知道,Android的国际化需要在values那里做折腾,多配置几个string文件,结合Resources和Configuration等。礼拜天闲来无事,折腾一个Demo出来。


  从上图可以看出,当我们在启动页切换语言的时候,实际上是打开了一次启动页。这样是为了“让一切重新开始”。这个后面会说明缘由。

一、从values文件夹说起。
  我们知道我们新建工程的时候会带有一个values文件夹,里面string.xml文件就是我们放硬编码索引的文件。
  现在,假设我们在res目录下新建多三个文件夹,跟values同级。分别如下
  values-zh
  values-zh-rCN
  values-zh-rTW
  其中values保持不变,后缀的zh表示中文(en则表示英文),后缀的rCN、rTW其中‘r’是一个标记,表示后面跟着的CN、TW是国家或地区标志。(后面我们会附上一份各个国家和地区的语言信息)
  所以以上三个资源文件夹表示所对应的语言环境分别为:
  中文
  中文-中国 (即中文简体)
  中文-台湾 (即中文繁体)
  这几个文件的作用以及采用策略
  默认情况下,Android会根据系统的语言地区设置,自动选择对应的资源。
  首先尝试语言地区全匹配,如果没有权匹配的资源包,则会尝试匹配语言,最后则会取默认的。
  比如如果Android系统的语言地区是中文简体,则首先会尝试从/values-zh-rCN中获取资源,如果没有此文件夹或者文件夹中没有响应的资源,则会尝试/values-zh,都获取不到的情况下即从/values中获取。(/values是必须存在的,否则不能通过编译)
  在上面的demo中,我们只分成了3种语言,分别是简体,繁体和英文。
  又由于我们默认的(values)是简体中文,所以本文中之另外建立了values-zh-rTW和values-en两个文件夹,分别表示繁体中文和英文。


二、代码
  怎么改变app采用的语言?











Resources resources = getContext().getResources();
 
DisplayMetrics dm = resources.getDisplayMetrics();
 
Configuration config = resources.getConfiguration();
 
// 应用用户选择语言
 
config.locale = Locale.ENGLISH;
 
resources.updateConfiguration(config, dm);
  本文用了 Locale 中的预设值
  Locale.ENGLISH
  Locale.TRADITIONAL_CHINESE
  Locale.SIMPLIFIED_CHINESE,
  分别表示英语,繁体中文,简体中文。
  注:跟随系统设置是 Locale.getDefault()
  我们上面的gif中,切换语言之后回到了启动页,这样是为了让所有页面都切换到正确语言,比如现在有ABC三个页面,我们依次顺序打开,如果只在C执行改变语言的代码,是没有办法让整个app的语言都进行切换的,所以我们才要回到“最开始的地方”,保证所有页面的遇到都得到切换。至于这点微博也是如此操作,至于微信,他是回到主页,个人估计是在Splash的一个遥望星球的页面做了处理。
  接下来看一下我们启动页MainActivity的代码


















































































































































































































public class MainActivity extends Activity {
 
private TextView mTvChooseLan;
 
private TextView mTvSetting;
 
private TextView mTvOther;
 
private TextView mTvNesTask;
 
@Override
 
protected void onCreate(Bundle savedInstanceState) {
 
super.onCreate(savedInstanceState);
 
setContentView(R.layout.activity_main);
 
mTvChooseLan = (TextView) findViewById(R.id.mTvChooseLan);
 
mTvSetting = (TextView) findViewById(R.id.mTvSetting);
 
mTvOther = (TextView) findViewById(R.id.mTvOther);
 
mTvNesTask = (TextView) findViewById(R.id.mTvNesTask);
 
mTvOther.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
startActivity(new Intent(MainActivity.this,OtherActivity.class));
 
}
 
});
 
mTvSetting.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
startActivity(new Intent(MainActivity.this,SettingActivity.class));
 
}
 
});
 
mTvNesTask.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
startActivity(new Intent(MainActivity.this,SingleInstanceActivity.class));
 
}
 
});
 
mTvChooseLan.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
initPopupWindow(mTvChooseLan);
 
}
 
});
 
}
 
// 选择语言的pop
 
PopupWindow popupWindow;
 
public  void initPopupWindow(View view) {
 
if (popupWindow == null) {
 
View popupView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_swich_language, null);
 
popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
 
initClick(popupView);
 
}
 
popupWindow.setOutsideTouchable(true);
 
popupWindow.setBackgroundDrawable(new BitmapDrawable());
 
popupWindow.setFocusable(true);
 
popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
 
popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
 
}
 
private void initClick(View popupView) {
 
TextView mTvSimpleChinese = (TextView) popupView.findViewById(R.id.mTvSimpleChinese);
 
TextView mTvTwChinese = (TextView) popupView.findViewById(R.id.mTvTwChinese);
 
TextView mTvEnglish = (TextView) popupView.findViewById(R.id.mTvEnglish);
 
mTvSimpleChinese.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
switchLanguage("zh_simple");
 
popupWindow.dismiss();
 
}
 
});
 
mTvTwChinese.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
switchLanguage("zh_tw");
 
popupWindow.dismiss();
 
}
 
});
 
mTvEnglish.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
switchLanguage("en");
 
popupWindow.dismiss();
 
}
 
});
 
}
 
/**
 
* 切换语言,这里参数之所以传String不传Local是为了Sp方便存值
 
* @param language
 
*/
 
private void switchLanguage(String language) {
 
//设置应用语言类型
 
Resources resources = getResources();
 
Configuration config = resources.getConfiguration();
 
DisplayMetrics dm = resources.getDisplayMetrics();
 
if (language.equals("zh_simple")) {
 
config.locale = Locale.SIMPLIFIED_CHINESE;
 
}else if(language.equals("zh_tw")){
 
config.locale = Locale.TRADITIONAL_CHINESE;
 
} else if(language.equals("en")){
 
config.locale = Locale.ENGLISH;
 
}else{
 
config.locale = Locale.getDefault();
 
}
 
resources.updateConfiguration(config, dm);
 
//保存设置语言的类型
 
CacheUtils.setString(MainActivity.this, AppConstant.LANGUAGE_RUN, language);
 
//更新语言后,destroy当前页面,重新绘制
 
finish();
 
Intent it = new Intent(MainActivity.this, MainActivity.class);
 
//清空任务栈确保当前打开activit为前台任务栈栈顶
 
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
 
startActivity(it);
 
}
 
}
  其中核心代码无非就是这段















































private void switchLanguage(String language) {
 
//设置应用语言类型
 
Resources resources = getResources();
 
Configuration config = resources.getConfiguration();
 
DisplayMetrics dm = resources.getDisplayMetrics();
 
if (language.equals("zh_simple")) {
 
config.locale = Locale.SIMPLIFIED_CHINESE;
 
}else if(language.equals("zh_tw")){
 
config.locale = Locale.TRADITIONAL_CHINESE;
 
} else if(language.equals("en")){
 
config.locale = Locale.ENGLISH;
 
}else{
 
config.locale = Locale.getDefault();
 
}
 
resources.updateConfiguration(config, dm);
 
//保存设置语言的类型
 
CacheUtils.setString(MainActivity.this, AppConstant.LANGUAGE_RUN, language);
 
//更新语言后,destroy当前页面,重新绘制
 
finish();
 
Intent it = new Intent(MainActivity.this, MainActivity.class);
 
//清空任务栈确保当前打开activit为前台任务栈栈顶
 
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
 
startActivity(it);
 
}
  点击Pop之后之后,根据传入值改变语言,并且我们将选择的语言存进了Sp,可以会根据需求在合适的地方取出并且设语言。最后重新打开启动页MainActivity,并且清空所有之前打开的页面。(加一层保证)
  通常来说如果是在登录页做改变语言的话这样就可以了。
  但是如果想在设置页面改变语言,我们就需要考虑多一个问题:
  1、我们普通的Activity都是存在于同一个任务栈的,语言改变都是改变同一个任务栈Activity。
  2、对于singleInstance这一启动模式自己有独立的任务栈。
  假设我们现在A是启动页,B是singleInstance启动模式,C是设置页。这时候我们A打开B,B在再开C,这时候BA在任务栈S1,B为S1的栈顶,C在任务栈S2里面,这个程序有S1和S2两个任务栈,并且S2为前台任务栈。











TaskRecord{3fb53a50 #52 A=com.amqr.mutilanguage U=0 sz=2}
 
Run #5: ActivityRecord{151fbb29 u0 com.amqr.mutilanguage/.SettingActivity t52}
 
TaskRecord{116c20b0 #53 A=com.amqr.mutilanguage U=0 sz=1}
 
Run #4: ActivityRecord{108ffc05 u0 com.amqr.mutilanguage/.SingleInstanceActivity t53}
 
TaskRecord{3fb53a50 #52 A=com.amqr.mutilanguage U=0 sz=2}
 
Run #3: ActivityRecord{375ad5f2 u0 com.amqr.mutilanguage/.MainActivity t52}
  这时候在C页面改变系统比语言,把中文切换英文,系统记住这个时候语言要改变,按照我们前面做的自然是S1成为栈顶,至此S1的所有Activity都会变成我们指定的语言。这点没问题,但是S2可不这么想,他是一个独立的任务栈啊。也就是说,你A开启B的时候,人家B记住的状态时中文,当你在C切为A指定切换成英文的时候,没错你的任务栈S1可以可以全部变成英文,但是人家S2记住的状态是S2你改变不了。这时会你从A打开B,会发B还是中文状态。
  所以当我们在标记为singleInstance的之后打开的页面改变语言的时候,我们可以可以直接杀掉当前 App 的进程,保证是“整个”程序重启。这样那些“S2”也会被干掉,整个程序的语言就一致了。
  杀死当前程序进程,可以采用两行代码



android.os.Process.killProcess(android.os.Process.myPid());
 
System.exit(0);
  根据上面的分析的,我们来看看我们的SettingActivity的代码:





























































































































































public class SettingActivity extends Activity{
 
private TextView mTvOtherPageSwitch;
 
@Override
 
protected void onCreate(Bundle savedInstanceState) {
 
super.onCreate(savedInstanceState);
 
setContentView(R.layout.activity_setting);
 
mTvOtherPageSwitch = (TextView) findViewById(R.id.mTvOtherPageSwitch);
 
mTvOtherPageSwitch.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
initPopupWindow(mTvOtherPageSwitch);
 
}
 
});
 
}
 
// 选择语言的pop
 
PopupWindow popupWindow;
 
public  void initPopupWindow(View view) {
 
if (popupWindow == null) {
 
View popupView = LayoutInflater.from(SettingActivity.this).inflate(R.layout.item_swich_language, null);
 
popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
 
initClick(popupView);
 
}
 
popupWindow.setOutsideTouchable(true);
 
popupWindow.setBackgroundDrawable(new BitmapDrawable());
 
popupWindow.setFocusable(true);
 
popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
 
popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
 
}
 
private void initClick(View popupView) {
 
TextView mTvSimpleChinese = (TextView) popupView.findViewById(R.id.mTvSimpleChinese);
 
TextView mTvTwChinese = (TextView) popupView.findViewById(R.id.mTvTwChinese);
 
TextView mTvEnglish = (TextView) popupView.findViewById(R.id.mTvEnglish);
 
mTvSimpleChinese.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
switchLanguage("zh_simple");
 
popupWindow.dismiss();
 
}
 
});
 
mTvTwChinese.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
switchLanguage("zh_tw");
 
popupWindow.dismiss();
 
}
 
});
 
mTvEnglish.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View v) {
 
switchLanguage("en");
 
popupWindow.dismiss();
 
}
 
});
 
}
 
private void switchLanguage(String language) {
 
//设置应用语言类型
 
Resources resources = getResources();
 
Configuration config = resources.getConfiguration();
 
DisplayMetrics dm = resources.getDisplayMetrics();
 
if (language.equals("zh_simple")) {
 
config.locale = Locale.SIMPLIFIED_CHINESE;
 
}else if(language.equals("zh_tw")){
 
config.locale = Locale.TRADITIONAL_CHINESE;
 
} else if(language.equals("en")){
 
config.locale = Locale.ENGLISH;
 
}else{
 
config.locale = Locale.getDefault();
 
}
 
resources.updateConfiguration(config, dm);
 
//保存设置语言的类型
 
CacheUtils.setString(SettingActivity.this, AppConstant.LANGUAGE_RUN, language);
 
//更新语言后,destroy当前页面,重新绘制
 
finish();
 
Intent it = new Intent(SettingActivity.this, MainActivity.class);
 
startActivity(it);
 
android.os.Process.killProcess(android.os.Process.myPid());
 
System.exit(0);
 
}
 
}
  主要的区别就是下面两行代码











//更新语言后,destroy当前页面,重新绘制
 
finish();
 
Intent it = new Intent(SettingActivity.this, MainActivity.class);
 
startActivity(it);
 
android.os.Process.killProcess(android.os.Process.myPid());
 
System.exit(0);
  这样关于启动页和app设置页面改变语言的问题应该算大致解决了。
  但是app替换安装或者改变手机系统语言的还是存在一些小问题的,语言不跟着换。
三、其他问题的解决
  由于前面我们改变的语言的时候已经选择结果缓存到了sp。现在这个sp终于有用了。
  关于替换安装的app的问题解决
  弄一个Application,















































public class MyApplication extends Application{
 
@Override
 
public void onCreate() {
 
super.onCreate();
 
String lan = CacheUtils.getString(getApplicationContext(), AppConstant.LANGUAGE_RUN, "def");
 
System.out.println("======之前选择的语言:  "+lan);
 
getLanguage(lan);
 
}
 
private void getLanguage(String lan){
 
Resources resources = getResources();
 
Configuration config = resources.getConfiguration();
 
DisplayMetrics dm = resources.getDisplayMetrics();
 
if (lan.equals("zh_simple")) {
 
config.locale = Locale.SIMPLIFIED_CHINESE;
 
}else if(lan.equals("zh_tw")){
 
config.locale = Locale.TRADITIONAL_CHINESE;
 
} else if(lan.equals("en")){
 
config.locale = Locale.ENGLISH;
 
}else{
 
config.locale = Locale.getDefault();
 
}
 
resources.updateConfiguration(config, dm);
 
}
 
}
  关于用户切换系统语言
  此法偏流氓
















































public class BaseActivity extends Activity {
 
@Override
 
protected void onCreate(Bundle savedInstanceState) {
 
super.onCreate(savedInstanceState);
 
System.out.println("=============== BaseActivity  onConfigurationChanged 执行");
 
String lan = CacheUtils.getString(getApplicationContext(), AppConstant.LANGUAGE_RUN, "def");
 
System.out.println("======之前选择的语言:  " + lan);
 
getLanguage(lan);
 
}
 
private void getLanguage(String lan) {
 
Resources resources = getResources();
 
Configuration config = resources.getConfiguration();
 
DisplayMetrics dm = resources.getDisplayMetrics();
 
if (lan.equals("zh_simple")) {
 
config.locale = Locale.SIMPLIFIED_CHINESE;
 
} else if (lan.equals("zh_tw")) {
 
config.locale = Locale.TRADITIONAL_CHINESE;
 
} else if (lan.equals("en")) {
 
config.locale = Locale.ENGLISH;
 
} else {
 
config.locale = Locale.getDefault();
 
}
 
resources.updateConfiguration(config, dm);
 
}
 
}

四、values对应的国家和地区
  输入简体字,点下面繁体字按钮进行在线转换在res目录下建立不同名称的values文件来调用不同的语言包
  Values文件汇总如下:
  中文(中国):values-zh-rCN中文(台湾):values-zh-rTW
  中文(香港):values-zh-rHK
  英语(美国):values-en-rUS
  英语(英国):values-en-rGB
  英文(澳大利亚):values-en-rAU
  英文(加拿大):values-en-rCA
  英文(爱尔兰):values-en-rIE
  英文(印度):values-en-rIN
  英文(新西兰):values-en-rNZ
  英文(新加坡):values-en-rSG
  英文(南非):values-en-rZA
  阿拉伯文(埃及):values-ar-rEG
  阿拉伯文(以色列):values-ar-rIL
  保加利亚文: values-bg-rBG
  加泰罗尼亚文:values-ca-rES
  捷克文:values-cs-rCZ
  丹麦文:values-da-rDK
  德文(奥地利):values-de-rAT
  德文(瑞士):values-de-rCH
  德文(德国):values-de-rDE
  德文(列支敦士登):values-de-rLI
  希腊文:values-el-rGR
  西班牙文(西班牙):values-es-rES
  西班牙文(美国):values-es-rUS
  芬兰文(芬兰):values-fi-rFI
  法文(比利时):values-fr-rBE
  法文(加拿大):values-fr-rCA
  法文(瑞士):values-fr-rCH
  法文(法国):values-fr-rFR
  希伯来文:values-iw-rIL
  印地文:values-hi-rIN
  克罗里亚文:values-hr-rHR
  匈牙利文:values-hu-rHU
  印度尼西亚文:values-in-rID
  意大利文(瑞士):values-it-rCH
  意大利文(意大利):values-it-rIT
  日文:values-ja-rJP
  韩文:values-ko-rKR
  立陶宛文:valueslt-rLT
  拉脱维亚文:values-lv-rLV
  挪威博克马尔文:values-nb-rNO
  荷兰文(比利时):values-nl-BE
  荷兰文(荷兰):values-nl-rNL
  波兰文:values-pl-rPL
  葡萄牙文(巴西):values-pt-rBR
  葡萄牙文(葡萄牙):values-pt-rPT
  罗马尼亚文:values-ro-rRO
  俄文:values-ru-rRU
  斯洛伐克文:values-sk-rSK
  斯洛文尼亚文:values-sl-rSI
  塞尔维亚文:values-sr-rRS
  瑞典文:values-sv-rSE
  泰文:values-th-rTH
  塔加洛语:values-tl-rPH
  土耳其文:values–r-rTR
  乌克兰文:values-uk-rUA
  越南文:values-vi-rVN

五、参考学习
  Android App 多语言切换
  Android的多语言实现
  Android多国语言文件夹命名方式

六、下载链接
  demo

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论