最近在做App内切换语言的功能,不光需要切换成指定的语言,还提供跟随系统选项,并且app内全局生效,踩了很多坑,于是做了今天的总结;
参考:
https://juliensalvi.medium.com/demystifying-locale-on-android-95450adf5aec
场景:
假设需要实现app内切换语言需求, 当前我们app内已经内置了三种语言, 中文(zh),英文(en),阿拉伯(ar); 设置页面有切换语言选项, 选择有 中文,英文,阿拉伯文,跟随系统;
踩过的坑:
1. 打开WebView后回到主界面,会造成部分不生效
2. 设置App语言之后,部分不生效,比如动态代码中生效,xml中定义的字符串不生效
3. 三方sdk中的activity不生效
4. 切换系统语言后,会影响app的部分语言显示
最终实现方案:
1. 实现点击切换语言逻辑
/**
* 点击切换语言
* languageCode = en/zh/ar
* SAVED_BASE_LANGUAGE 为定义的SharePreference的key
**/
btn_change?.setOnClickListener{
//保存当前设置的语言标识
PrefUtils.putString(SAVED_BASE_LANGUAGE, languageCode)
//切换app内语言
LanguageManager.changeLanguageGlobal()
//业务调整, 这里是可选,里面可以做一些语言切换后的逻辑,比如某些接口要重新刷新等等;
onLanguageChanged()
//重启app
restartApp()
}
//切换app内语言
fun changeLanguageGlobal() {
//获取app内所有activity,service,Application
var contextList = arrayListOf<Context>().apply {
addAll(AppUtil.activeActivitys)
addAll(AppUtil.activeServices)
add(AppUtil.getAppContext())
}
if (contextList.isEmpty())) return
//重置WebView语言设置
WebView(contextList[0]).destroy()
//设置语言
val language = PrefUtils.getString(SAVED_BASE_LANGUAGE)
val locale = if (!language.isStrictEmpty()) Locale(language) else getSystemLocale()
list.forEach {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val resource = context.resources
val configuration = resource.configuration
configuration.setLocale(local)
configuration.setLocales(LocaleList(local))
val displayMetrics = resource.displayMetrics
resource.updateConfiguration(configuration,displayMetrics)
context.createConfigurationContext(configuration)
} else {
Locale.setDefault(local)
val resources = context.resources
val displayMetrics = resources.displayMetrics
val configuration = resources.configuration
// 获取当前系统语言,默认设置跟随系统
configuration.setLocale(local)
resources.updateConfiguration(configuration, displayMetrics)
}
}
}
/**
* 获取系统当前设置的语言
*/
fun getSystemLocale(): Locale {
var currentLocale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val locales = Resources.getSystem().configuration.locales
if (locales.isEmpty) {
Locale.getDefault()
} else {
locales[0]
}
} else {
Resources.getSystem().configuration.locale
}
return currentLocale
}
AppUtil这里保存了所有的activity和service以及application的context,都可以在对应的生命周期onCreate中添加到列表,并在onDestroy时移除
//重启App
fun restartApp() {
activity?.let {
val intent = Intent(context, it.javaClass)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
}
此种重启activity方式会回到主界面, 如果不想跳页面可以使用 ActivityCompat.recreate(requireActivity()), 但是部分场景不生效
2. App重启后设置
Application启动
override fun onCreate() {
LanguageManager.changeLanguageGlobal()
}
//这里必须,因为如果设置了跟随系统, 当系统语言改变,但未重启app时,这里就会起作用
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
LanguageManager.changeLanguageGlobal()
}
Activity启动
override fun onCreate() {
super.onCreate()
LanguageManager.changeLanguageGlobal()
}
override fun onResume() {
super.onResume()
LanguageManager.changeLanguageGlobal()
}
通过实践,基本可实现全部场景的语言切换功能