Android 多语言处理
由于公司最近在扩展海外市场,所以新项目不可避免的要用到多语言。多语言处理也就是针对用户选择的语言环境来切换并使用不同的string资源。
这次项目的具体的业务是当用户首次进入App,默认获取系统语言,如果在语言列表内,则使用该语言,否则,默认使用美式英语。
UI界面类似下面,选择不同语言后,当退出页面后才去切换语言环境:
一、配置不同环境的资源文件
在res目录下新建不同语言环境的资源文件,选择Locale,并选择对应语言的国家地区:
二、配置不同语言环境的Locale对象
这里我用一个SparseArray来存储对应的Locale对象,方便后面获取。
private val localeLanguageArray = SparseArray<Locale>()
init {
localeLanguageArray.put(SELECT_SIMPLIFIED_CHINESE, Locale("zh", "CN")) // yyyy-M-d
localeLanguageArray.put(SELECT_TRADITIONAL_CHINESE, Locale("zh", "HK")) // yyyy'年'M'月'd'日'
localeLanguageArray.put(SELECT_ENGLISH, Locale("en", "US")) // MMM d, yyyy
localeLanguageArray.put(SELECT_HINDI, Locale("hi", "IN")) // d MMM, yyyy
localeLanguageArray.put(SELECT_INDONESIAN, Locale("in", "ID")) // dd MMM yy
localeLanguageArray.put(SELECT_MALAY, Locale("ms", "MY")) // dd MMMM yyyy
localeLanguageArray.put(SELECT_FILIPINO, Locale("tl", "PH")) // MMM d, yyyy
localeLanguageArray.put(SELECT_THAI, Locale("th", "TH")) // d MMM yyyy
localeLanguageArray.put(SELECT_VIETNAMESE, Locale("vi", "VN")) // dd-MM-yyyy
}
三、在Application中配置当前的语言环境
首先在attachBaseContext保存当前默认选中的语言环境:
override fun attachBaseContext(base: Context?) {
base?.let {
mContext = base // 这里赋值是由于设置语言内部使用了sp
LanguageUtil.saveSystemCurrentLanguage() // 保存系统选择语言
super.attachBaseContext(LanguageUtil.setLocal(base))
} ?: super.attachBaseContext(base)
}
LanguageUtil:
fun saveSystemCurrentLanguage() {
val locale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList.getDefault().get(0)
} else {
Locale.getDefault()
}
PrefsUtil.setSystemCurrentLocal(locale) // 这里保存系统Locale是为了获取当前用户地区信息
val selectLanguage = PrefsUtil.getSelectLanguage()
if (selectLanguage != -1) { // 已经设置过默认语言,则不再设置
return
}
// 遍历当前支持的语言列表
for (index in 0 until localeLanguageArray.size()) {
if (locale.country == localeLanguageArray[index].country) { // 不能直接判断Locale对象(不准确),所以通过地区信息来从语言列表中来查是否有对应的Locale对象
PrefsUtil.saveSelectLanguage(index) // 保存当前设置的语言
break
}
}
}
这里主要是将当前选择的语言环境的int值存入到sp中,用于后面获取并设置,然后在onConfigurationChanged中保存并设置当前语言环境:
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
// 保存系统选择语言
LanguageUtil.onConfigurationChanged(applicationContext)
}
LanguageUtil:
fun onConfigurationChanged(context: Context) {
saveSystemCurrentLanguage()
setLocal(context)
setApplicationLanguage(context)
}
/**
* 设置语言
*/
fun setLocal(context: Context?): Context? {
return context?.let {
updateResources(it, getSelectLanguageLocale())
}
}
/**
* update
*/
@SuppressLint("ObsoleteSdkInt")
private fun updateResources(context: Context, locale: Locale): Context {
var mContext = context
Locale.setDefault(locale)
val res = mContext.resources
val config = Configuration(res.configuration)
if (Build.VERSION.SDK_INT >= 17) {
config.setLocale(locale)
mContext = mContext.createConfigurationContext(config)
} else {
config.locale = locale
res.updateConfiguration(config, res.displayMetrics)
}
return mContext
}
/**
* 设置语言类型
*/
private fun setApplicationLanguage(context: Context) {
val resources = context.applicationContext.resources
val dm = resources.displayMetrics
val config = resources.configuration
val locale = getSelectLanguageLocale()
config.locale = locale
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(locale)
LocaleList.setDefault(localeList)
config.locales = localeList
context.applicationContext.createConfigurationContext(config)
Locale.setDefault(locale)
}
resources.updateConfiguration(config, dm)
}
这里贴出LanguageUtil的完成代码:
object LanguageUtil {
private val localeLanguageArray = SparseArray<Locale>()
init {
localeLanguageArray.put(SELECT_SIMPLIFIED_CHINESE, Locale("zh", "CN")) // yyyy-M-d
localeLanguageArray.put(SELECT_TRADITIONAL_CHINESE, Locale("zh", "HK")) // yyyy'年'M'月'd'日'
localeLanguageArray.put(SELECT_ENGLISH, Locale("en", "US")) // MMM d, yyyy
localeLanguageArray.put(SELECT_HINDI, Locale("hi", "IN")) // d MMM, yyyy
localeLanguageArray.put(SELECT_INDONESIAN, Locale("in", "ID")) // dd MMM yy
localeLanguageArray.put(SELECT_MALAY, Locale("ms", "MY")) // dd MMMM yyyy
localeLanguageArray.put(SELECT_FILIPINO, Locale("tl", "PH")) // MMM d, yyyy
localeLanguageArray.put(SELECT_THAI, Locale("th", "TH")) // d MMM yyyy
localeLanguageArray.put(SELECT_VIETNAMESE, Locale("vi", "VN")) // dd-MM-yyyy
}
/**
* 设置语言
*/
fun setLocal(context: Context?): Context? {
return context?.let {
updateResources(it, getSelectLanguageLocale())
}
}
/**
* update
*/
@SuppressLint("ObsoleteSdkInt")
private fun updateResources(context: Context, locale: Locale): Context {
var mContext = context
Locale.setDefault(locale)
val res = mContext.resources
val config = Configuration(res.configuration)
if (Build.VERSION.SDK_INT >= 17) {
config.setLocale(locale)
mContext = mContext.createConfigurationContext(config)
} else {
config.locale = locale
res.updateConfiguration(config, res.displayMetrics)
}
return mContext
}
/**
* 获取选择的语言设置
*/
fun getSelectLanguageLocale(): Locale {
return localeLanguageArray.get(PrefsUtil.getSelectLanguage(), Locale("en", "US"))
}
/**
* 获取系统的locale
*
* @return Locale对象
*/
private fun getSystemLocale(): Locale {
return PrefsUtil.getSystemCurrentLocal()
}
/**
* 保存系统选择语言
*/
fun saveSystemCurrentLanguage() {
val locale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList.getDefault().get(0)
} else {
Locale.getDefault()
}
PrefsUtil.setSystemCurrentLocal(locale)
val selectLanguage = PrefsUtil.getSelectLanguage()
if (selectLanguage != -1) { // 已经设置过默认语言,则不再设置
return
}
// 遍历
for (index in 0 until localeLanguageArray.size()) {
if (locale.country == localeLanguageArray[index].country) {
PrefsUtil.saveSelectLanguage(index) // 保存当前设置的语言
break
}
}
}
/**
* 保存当前选择语言
*/
fun saveSelectLanguage(context: Context, select: Int) {
PrefsUtil.saveSelectLanguage(select)
setApplicationLanguage(context)
}
fun onConfigurationChanged(context: Context) {
saveSystemCurrentLanguage()
setLocal(context)
setApplicationLanguage(context)
}
/**
* 设置语言类型
*/
private fun setApplicationLanguage(context: Context) {
val resources = context.applicationContext.resources
val dm = resources.displayMetrics
val config = resources.configuration
val locale = getSelectLanguageLocale()
config.locale = locale
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(locale)
LocaleList.setDefault(localeList)
config.locales = localeList
context.applicationContext.createConfigurationContext(config)
Locale.setDefault(locale)
}
resources.updateConfiguration(config, dm)
}
}
四、在BaseActivity的attachBaseContext中设置当前语言环境
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(LanguageUtil.setLocal(newBase))
}
到这里,多语言的配置就基本完成了,当切换语言环境时,对当前任务栈内的Activity执行recreate就ok了。