Android使用AndroidUtilCode实现多语言
TimeFine 人气:0一、项目中配置多语言
多语言的实现是通过AndroidUtilCode实现的,表示感谢!
项目里面有4种语言:中文,英文,德文,俄文。文件夹如下:
配置多语言的思路是:
1、判断是否为国内版本,如果为国内版本则设置为简体中文
2、 如果为国外版本,获取用户之前设置的App语言,如果用户之前有设置App语言,则设置为之前用户设置的语言;如果用户之前没有设置App语言则获取手机系统的语言。
3、判断当前手机系统的语言是否App有做语言适配,如果有适配则设置成跟手机系统一样的语言,如果没有适配则设置为英文。
二、具体实现
1、初始化PropertiesUtil和MMKV,具体代码请参考上篇博客
2、在BaseApplication中设置语言
abstract class BaseApplication : Application() { abstract fun init() override fun onCreate() { super.onCreate() init() PropertiesUtil.init(this) MMKV.initialize(this) MMKVUtil.setUserId(1000L) //设置App语言 setAppLanguage() } /** * 判断是否为国内版本,如果为国内版本则设置为简体中文 * 如果为国外版本,获取用户之前设置的App语言, * 如果用户之前有设置App语言,则设置为之前用户设置的语言 * 如果用户之前没有设置App语言则获取手机系统的语言 * 判断手机系统的语言是否App有做语言适配,如果有适配则设置成跟手机系统一样的语言 * 如果App没有对当前系统语言做适配则设置为英文 */ private fun setAppLanguage() { if (PropertiesUtil.isCN()) { //国内版本 LanguageUtils.applyLanguage(Locale.SIMPLIFIED_CHINESE, false) } else { MMKVUtil.getLanguage().also { if (it.isNotEmpty()) { setLanguageAndBackCountry(it) } else { //获取系统语言 LanguageUtils.getSystemLanguage().country.also { country -> setLanguageAndBackCountry(country).also { value -> //保存设置的语言 MMKVUtil.setLanguage(value) } } } } } } private fun setLanguageAndBackCountry(it: String): String { return when (it) { LanguageType.CN.name -> { LanguageUtils.applyLanguage(Locale.SIMPLIFIED_CHINESE, false) it } LanguageType.US.name -> { LanguageUtils.applyLanguage(Locale.ENGLISH, false) it } LanguageType.DE.name -> { LanguageUtils.applyLanguage(Locale.GERMANY, false) it } LanguageType.RU.name -> { LanguageUtils.applyLanguage(Locale("ru"), false) it } else -> { LanguageUtils.applyLanguage(Locale.ENGLISH, false) LanguageType.US.name } } } }
3、切换语言
比如设置为德文,按钮触发:
MMKVUtil.setLanguage(LanguageType.DE.name) LanguageUtils.applyLanguage(Locale.GERMANY, false) //true:重启App false:不重启App
4、注意gradle配置resConfigs
不要限制为只有中文,比如:resConfigs "zh-rCN", "en"
三、AndroidX和多进程存在的问题
1、多进程读取Configuration
时发现其他进程与主进程获取的Configuration
值不一致,导致主进程切换语言后其他语言并没有切换成功。
2、AndroidX切换失败的问题,具体可以看下这篇博客【踩坑记录】多语言切换在Androidx失效
解决办法:重写Activity的attachBaseContext
方法,修改Context
/** * 多语言的切换类, 解决多进程切换语言失败的问题以及AndroidX多语言切换失效的问题 * 解决由于 WebView 初始化会修改 Activity 语种配置,间接导致 Activity 语种会被还原,所以需要你手动重写 WebView 对这个问题进行修复 */ object MultiLanguageUtil { fun getAttachBaseContext(context: Context): Context { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { configAppcompatLanguage(setAppLanguageApi24(context)) } else { setAppLanguage(context) configAppcompatLanguage(context) } } /** * 设置应用语言 */ @Suppress("DEPRECATION") private fun setAppLanguage(context: Context) { val resources = context.resources val displayMetrics = resources.displayMetrics val configuration = resources.configuration // 获取当前系统语言,默认设置跟随系统 val locale = getLocale() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { configuration.setLocale(locale) } else { configuration.locale = locale } resources.updateConfiguration(configuration, displayMetrics) } /** * 兼容 7.0 及以上 */ @TargetApi(Build.VERSION_CODES.N) fun setAppLanguageApi24(context: Context): Context { val locale = getLocale() val resource = context.resources val configuration = resource.configuration configuration.setLocale(locale) configuration.setLocales(LocaleList(locale)) return context.createConfigurationContext(configuration) } private fun configAppcompatLanguage(context: Context): Context { val configuration = context.resources.configuration //兼容appcompat 1.2.0后切换语言失效问题 return object : ContextThemeWrapper(context, R.style.Base_Theme_AppCompat_Empty) { override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) { overrideConfiguration?.setTo(configuration) super.applyOverrideConfiguration(overrideConfiguration) } } } private fun getLocale(): Locale { return when (CacheUtil.getInt(GlobalConstants.LANGUAGE_KEY, true)) { 0 -> { Locale.SIMPLIFIED_CHINESE } 1 -> { Locale.ENGLISH } 2 -> { Locale.GERMANY } 3 -> { Locale("ru") } else -> Locale.ENGLISH } } /** * 解决WebView多语言失效的问题 */ fun updateLanguage(context: Context) { val resources = context.resources val config = resources.configuration val settingLanguage = getLocale().language val systemLanguage = config.locales[0].language if (settingLanguage != systemLanguage) { setLocale(config, Locale(settingLanguage)) resources.updateConfiguration(config, resources.displayMetrics) } } private fun setLocale(config: Configuration, locale: Locale?) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val localeList = LocaleList(locale) config.setLocales(localeList) } else { config.setLocale(locale) } } else { config.locale = locale } } }
获取appContext
lateinit var appContext: Application //BaseApplication中调用方法获取Application的上下文 fun BaseApplication.getContext(application: BaseApplication) { appContext = application }
四、WebView导致的语言重置的问题
由于 WebView 初始化会修改 Activity 语种配置,间接导致 Activity 语种会被还原,所以需要你手动重写 WebView 对这个问题进行修复,如下:
/** * 由于 WebView 初始化会修改 Activity 语种配置,间接导致 Activity 语种会被还原回去,所以需要你手动重写 WebView 对这个问题进行修复 */ class LanguagesWebView( context: Context, @Nullable attrs: AttributeSet?, defStyleAttr: Int ) : WebView(context, attrs, defStyleAttr) { constructor(context: Context) : this(context, null) {} constructor(context: Context, @Nullable attrs: AttributeSet?) : this( context, attrs, 0 ) init { //修复 WebView 初始化时会修改Activity 语种配置的问题 MultiLanguageUtil.updateLanguage(context) } }
项目中用这个WebView即可。这个问题在华为手机鸿蒙系统上会出现。
五、枚举类的多语言实现
枚举类型是线程安全的,并且只会装载一次,这就导致下面的写法导致枚举的err
值在切换语言后不会发生变化。
enum class Error( var code: Int, var err: String) { /** * 未知错误 */ UNKNOWN(1000,appContext.getString(R.string.error_1000)), /** * 解析错误 */ PARSE_ERROR(1001, appContext.getString(R.string.error_1001)), /** * 网络错误 */ NETWORK_ERROR(1002, appContext.getString(R.string.error_1002)), /** * 证书出错 */ SSL_ERROR(1004, appContext.getString(R.string.error_1004)), /** * 连接超时 */ TIMEOUT_ERROR(1006, appContext.getString(R.string.error_1002)); fun getValue(): String { return err } fun getKey(): Int { return code } }
那么如果做枚举类的多语言适配呢? 代码如下:
enum class Error(private val code: Int, private val err: Int) { /** * 未知错误 */ UNKNOWN(1000, R.string.error_1000), /** * 解析错误 */ PARSE_ERROR(1001, R.string.error_1001), /** * 网络错误 */ NETWORK_ERROR(1002, R.string.error_1002), /** * 证书出错 */ SSL_ERROR(1004, R.string.error_1004), /** * 连接超时 */ TIMEOUT_ERROR(1006, R.string.error_1002); fun getValue(): String { return appContext.getString(err) } fun getKey(): Int { return code } }
因为字符串的id是固定的不会发生变化,所以即使枚举类只会装载一次也不会有影响,通过getValue
就能取到正确语言的字符串。
参考
加载全部内容