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

本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。

前言:

我们知道,Android的国际化需要在values那里做折腾,多配置几个string文件,结合Resources和Configuration等。礼拜天闲来无事,折腾一个Demo出来。

先上图,再扯淡

多语言演示demo.gif

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

一、从values文件夹说起。

我们知道我们新建工程的时候会带有一个values文件夹,里面string.xml文件就是我们放硬编码索引的文件。

现在,假设我们在res目录下新建多三个文件夹,跟values同级。分别如下

  • values-zh

  • values-zh-rCN

  • values-zh-rTW

其中values保持不变,后缀的zh表示中文(en则表示英文),后缀的rCN、rTW其中‘r’是一个标记,表示后面跟着的CN、TW是国家或地区标志。(后面我们会附上一份各个国家和地区的语言信息)

所以以上三个资源文件夹表示所对应的语言环境分别为:

  • 中文

  • 中文-中国 (即中文简体)

  • 中文-台湾 (即中文繁体)

一、1 这几个文件的作用以及采用策略

默认情况下,Android会根据系统的语言地区设置,自动选择对应的资源。

首先尝试语言地区全匹配,如果没有权匹配的资源包,则会尝试匹配语言,最后则会取默认的。

比如如果Android系统的语言地区是中文简体,则首先会尝试从/values-zh-rCN中获取资源,如果没有此文件夹或者文件夹中没有响应的资源,则会尝试/values-zh,都获取不到的情况下即从/values中获取。(/values是必须存在的,否则不能通过编译)

在上面的demo中,我们只分成了3种语言,分别是简体,繁体和英文。

又由于我们默认的(values)是简体中文,所以本文中之另外建立了values-zh-rTW和values-en两个文件夹,分别表示繁体中文和英文。

Paste_Image.png

二、代码

怎么改变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终于有用了。

三、1、关于替换安装的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);

}

}

三、2、关于用户切换系统语言

此法偏流氓


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

本篇完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容