Android 应用内多语言切换
最近公司的 App 里需要用到多语言切换,简单来说,就是如果用户没有选择语言选项时,App 默认跟随系统语言,如果用户在 App 内进行了语言设置,那么就使用用户设置的语言。当然,你会发现,App 的语言切换也会导致加载的其它资源文件进行切换
上述内容大概可以分为以下几块:
1. 获取系统默认的语言和地区(**注意地区,后面会讲述这里的坑**)
2. 更改 App 的语言
## Android 应用资源国际化
在正式开始之前,先来讲解一下 Android 应用资源国际化的知识。对于资源文件的国际化,我们一般是在 Android `src/main/res/` 目录下,建立对应语言文件夹,格式一般为:`values-语言代号-地区代号`,默认的资源是不包含**语言代号和地区代号**的。一般情况下,应用资源是没有做任何适配的,所以不管如何切换语言和地区设置,应用显示的资源都不会发生任何改变。
配置选项包括**语言代号**和**地区代号**。表示中文和中国的配置选项是 zh-rCN(zh表示中文,CN表示中国)。表示英文和美国的配置选项是 en-rUS(en表示英文,US表示美国)。同一语言代号可以有多个地区代号,用 r 表示区分。
常见的国际化资源表示形式:
```
中文(中国):values-zh-rCN
中文(台湾):values-zh-rTW
中文(香港):values-zh-rHK
维吾尔文(中国):values-ug-rCN
英语(美国):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
```
## 获取系统默认的语言和地区
总的来说,获取语言和地区有三种方法:
1. 通过 Java 自带的接口来实现,即:
```java
Locale locale = Locale.getLocale();
String language = locale.getLanguage();
String country = locale.getCountry();
```
2. 通过 `Configuration` 来获取
```java
//方法1,该方法已废弃,如果在 API >= 17 的版本上使用 方法2
Locale locale = context.getResources().getConfiguration().locale;
//方法2,在 API >= 17 的版本上可以使用
Locale locale = context.getResources().getConfiguration().getLocales().get(0);
String language = locale.getLanguage();
String country = locale.getCountry();
```
其中, `context.getResources()` 也可以用 `Resources.getSystem()` 来代替,前者获取的是应用内部的语言和地区设置,后者获取的是系统的语言地区设置,默认情况下,前者跟随系统设置。
## 更改 App 的语言设置
通过上述分析,我们已经知道怎么获取系统和应用的语言地区设置了。接下来,我们来讲一下如何实现 Android App 的多语言切换。在前面我们已经使用到了 `Configuration` ,这个类保存了 Android 应用的所有设备信息,详见 [Configuration](https://developer.android.com/reference/android/content/res/Configuration.html)。要实现应用的多语言切换,我们所要做的就是更新 `Configuration` 中关于语言地区的属性。
```java
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
Configuration configuration = resources.getConfiguration();
//API >= 17 可以使用
configuration.setLocale(locale);
//该方法已经废弃,官方建议使用 Context.createConfigurationContext(Configuration)
resources.updateConfiguration(configuration, metrics);
```
资源目录结构大致如下:
```
│ └── res
│ ├── drawable
│ ├── drawable-xhdpi
│ │ └── icon_test.png
│ ├── drawable-zh-rCN-xhdpi//图标适配
│ │ └── icon_test.png
│ ├── layout
│ │ └── activity_main.xml
│ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-en-rWW
│ │ └── strings.xml
│ ├── values-ja-rJP
│ │ └── strings.xml
│ ├── values-ko-rKR
│ │ └── strings.xml
│ └── values-zh-rCN
│ └── strings.xml
```
### 重新加载资源
看到这里,你是不是觉得就结束了?不,当然不是,更新 `Configuration` 之后,如果不重启 Activity,应用的资源就不会被重新加载。
```java
Intent intent = new Intent(this, MainActivity.class);
//开始新的activity同时移除之前所有的activity
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
```
### 持久化存储设置
经过上述步骤,我们已经可以看到应用显示的资源发生了改变,但是当应用被杀掉重启后,之前所有的设置都已经失效,应用的语言地区又变成了系统默认的,这是因为我们对应用所做的变更只是保存在内存中,当应用被杀掉,在内存中的数据也随之被释放,再次启动应用的时候,应用读取的是系统的 `Configuration` ,语言地区也随之变成系统默认的。
当应用需要保存用户更改的操作,就需要对用户的选择进行持久化,并在应用重启的时候,从配置中读取并应用该配置。
```java
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//一般在 Application 的 onCreate() 方法中更新 Configuration
LanguageUtil.changeAppLanguage(this, Locale.SIMPLIFIED_CHINESE, true);
}
}
```
`LanguageUtil.java`
```java
/**
* 更改应用语言
*
* @param context
* @param locale 语言地区
* @param persistence 是否持久化
*/
public static void changeAppLanguage(Context context, Locale locale,
boolean persistence) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
Configuration configuration = resources.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(locale);
} else {
configuration.locale = locale;
}
resources.updateConfiguration(configuration, metrics);
if (persistence) {
saveLanguageSetting(context, locale);
}
}
private static void saveLanguageSetting(Context context, Locale locale) {
String name = context.getPackageName() + "_" + LANGUAGE;
SharedPreferences preferences =
context.getSharedPreferences(name, Context.MODE_PRIVATE);
preferences.edit().putString(LANGUAGE, locale.getLanguage()).apply();
preferences.edit().putString(COUNTRY, locale.getCountry()).apply();
}
```
这样,Android 应用内多语言切换基本完工。接下来,分享一下我在多语言切换过程中遇到的坑。
## 多语言切换中遇到的坑
1. 以静态变量的方式,在 Application 初始化时初始化网络请求错误提示语,然后再系统中切换语言后,网络请求错误提示语未更新。
解决办法:使用时直接通过 `getString()` 方法获取
2. App 多语言切换设置持久化后,在应用启动时, Application 的 `onCreate()` 中也进行了多语言切换。然后去系统设置中切换语言,App 也会随之跟随系统语言。
原因:在我们改变系统的语言时,应用的 `Configuration` 也随之跟随系统改变,而不是我们启动应用时的设置了
解决办法:监听 Activity 的生命周期,在 Activty 的 `onCreate()` 中判断应用当前的语言设置是否与用户设置值相同,否则强制更新应用语言设置。因为,当系统切换语言选项的时候,系统会重启 Activity,就如前文所说,我们需要重启 Activity 才能实现资源的重新加载一样。这里也有两种方案:
1. 创建一个基类 `BaseActivity` ,在其 `onCreate()` 方法中做处理
2. 使用 `ActivityLifecycleCallbacks` ,在其回调 `onActivityCreated()` 中做处理
对比一下,上述两种方案,第一种只能针对继承自 `BaseActivity` 的才有效,第二种则是监听所以 Activity 的生命周期。所以相对而言,第二种方案更好点。
```java
/**
* 判断是否与设定的语言相同.
*
* @param context
* @return
*/
public static boolean isSameWithSetting(Context context) {
Locale current = context.getResources().getConfiguration().locale;
return current.equals(getAppLocale(context));
}
```
```java
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
LanguageUtil.init(this);
//注册Activity生命周期监听回调
registerActivityLifecycleCallbacks(callbacks);
}
ActivityLifecycleCallbacks callbacks = new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//强制修改应用语言
if (!LanguageUtil.isSameWithSetting(activity)) {
LanguageUtil.changeAppLanguage(activity,
LanguageUtil.getAppLocale(activity));
}
}
//Activity 其它生命周期的回调
};
}
```
3. 对于在 AndroidManifest.xml 中配置 `launchMode` 为 `singleInstance` 的 Activity,使用
```java
Intent intent = new Intent(this, MainActivity.class);
//开始新的activity同时移除之前所有的activity
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
```
资源文件不更新。
原因:`launchMode` 为 `singleInstance` 的 Activity 与当前应用时不在同一个 Task 栈
解决方法:将 `launchMode` 改为其它模式或者杀掉应用重新启动
4. 资源文件夹为 `values-zh-rCN`时,将应用 Locale 设置为 `Locale.CHINESE` 时,找不到对应的资源文件。
原因:`values-zh-rCN` 对应的 Locale 为 `Locale.SIMPLIFIED_CHINESE`
解决办法:将 Locale 设置为 `Locale.SIMPLIFIED_CHINESE` 或者将资源文件改为 `values-zh`
这是踩得最惨的一个坑,浪费了大量时间,所以才会有开头 **Android 应用资源国际化** 那么一小节插曲。
代码已上传 Git,欢迎大家 [Star 和 Fork](https://github.com/MyLifeMyTravel/AndroidCore/tree/master/switchlanguage)。
Android 应用内多语言切换的更多相关文章
- Android app应用多语言切换功能实现
最近在做一个多语言切换的功能,类似于微信的语言切换,搜了下资料基本上都是以下这种: 1. 实现的效果 和微信类似,在设置界面打开切换语言的界面,选择语言后重启 HomeActivity,语言切换完成, ...
- iOS开发——iOS国际化 APP内语言切换
最近一个一直在迭代的老项目收到一份新的开发需求,项目需要做国际化适配,简体中文+英文.由于项目中采用了storyboard和纯代码两种布局方式,所以国际化也要同时实现.上网查了些资料,实现了更改系统语 ...
- Android 应用内切换语言
extends :http://bbs.51cto.com/thread-1075165-1.html,http://www.cnblogs.com/loulijun/p/3164746.html 1 ...
- Android原生多语言切换方案,兼容Android10
前言 一个应用若需要国际化,至少需要支持中文和英语这两种语言,而同时随着谷歌的系统的更新,安卓系统可以设置当前语言的首选语言.因此,本文立足于此,多语言的切换方案为:App固定的文字内容,跟随系统,中 ...
- 【转】Android 语言切换过程分析
最近在看一个bug,系统切换语言后,本来退到后台的音乐,会在通知栏上显示通知.为了解决这个bug,我学习了下android的语言切换流程,也参考了大量其他人的资料.(主要参考了http://blog. ...
- android 语言切换过程分析
android 语言切换过程分析 2014-02-27 18:13 1207人阅读 评论(0) 收藏 举报 语言切换android语言切换android改变语言 最近在看一个bug,系统切换语言后,本 ...
- 【Android 多语言切换简单实例分享】
一.Android多语言切换 Android应用的开发不可能仅仅针对某一个国家或者区域使用,各国间语言文化各不同样,因此一个优秀的APP必须支持多种语言,为了实现这个特性,Android给出了一个解决 ...
- vue 双语言切换中,data内翻译文字不正常切换的解决方案
背景 有这么一个登录页面,相关功能如下: 支持双语言,点击切换语言 表单内部有一个自定义的select,里面option的label.value都是的名字由外部提供:其中预设的option的label ...
- Android应用:横竖屏切换总结
眨眼间,已经到了2016你年春节前,离上一篇博客的时间已经有6个月多,回想起这半年的种种,不得不说,学习和工作实在是太忙了,或许这就是程序员的真实写照吧. 写博客之初,主要的目的还是为了把自己的学习痕 ...
随机推荐
- asp.net core源码飘香:从Hosting开始
知识点: 1.Kestrel服务器启动并处理Http请求的过程. 2.Startup的作用. 源码飘香: 总结: asp.net core将web开发拆分为多个独立的组件,大多以http中间件的形式添 ...
- SqlParameter参数类型为int32时候的传值陷阱
前2天在使用SqlParameter传递参数的时候遇到一个小坑,这里分享一下. SqlParameter para=new SqlParameter("@IsDeleted",0) ...
- es6 module + webpack
其实在之前本人就看了 es6 里面的一部分内容,当然是阮一峰大神的 ECMAScript 6 入门. 最近闲来无事又来看下,其中 Module 的语法 这章时候,用里面代码跑的时候,理所当然的报错 S ...
- add spring-boot-modules to maven project
spring boot 项目中 多modules parent 冲突 在IDEAJ 中,如果建立多多modules 项目,pom文件应该是这样: <groupId>cn.ifengkou& ...
- 【PAT_Basic日记】1004 成绩排名
至今仍然存在问题,第一个测试点不过 #include <stdio.h> #include <stdlib.h> #include <string.h> typed ...
- html 选择器之属性选择器
属性选择器的主要作用个人的理解就是对带有指定属性的元素设置css样式. 使用css3的属性选择器,可以指定元素的某个属性,也可以指定某个属性和这个属性所对应的值. css3的属性选择器主要包括下面几种 ...
- Java实现Android,iOS设备实时监控
Java实现Android设备实时监控 设计思路: 第一,启动一个实时截图线程,负责实时截取Android设备屏幕,保存到本地路径. 第二,在JSP页面,定义一个img对象,实时更换img对象的src ...
- h5 实现调用系统拍照或者选择照片并预览
这次又来分享个好东西! 调用手机相机拍照或者是调用手机相册选择照片,这个功能在 手机端页面 或者 webApp 应该是常用到的,就拿个人或会员资料录入那块来说就已经是经常会碰到的, 每当看到这块功能的 ...
- [Linux] PHP程序员玩转Linux系列-telnet轻松使用邮箱
1.PHP程序员玩转Linux系列-怎么安装使用CentOS 2.PHP程序员玩转Linux系列-lnmp环境的搭建 3.PHP程序员玩转Linux系列-搭建FTP代码开发环境 4.PHP程序员玩转L ...
- 数据可视化之MarkPoint
MarkPoint是什么效果?如上图,一闪一闪亮晶晶的效果,这是在Echarts中对应的效果.我最早看到的是腾讯的一个Flash的版本,显示当前QQ在线人数的全国分布效果,感觉效果很炫,当时也在想,怎 ...