Android 国际化之多语言适配小记

害,乱糟糟,总要去梳理.
面对未知的一切,陌生感突突的.
甲方要求实现 App 国际化多语言,正好抽个时间弄了下,害,被自己蠢到死,特意记录下.
如有不对,欢迎指正,一起交流~
效果演示
视频录制的不是太好,整体的效果出来了,大家见谅~

版本为别为: 6.0、8.0 以及 10.0
搞起来
简单说下需要注意的:

  • 国际化,多语言目录创建,资源配置;
  • Locale 资源获取以及本地缓存,缓存的目的是为了下次重新打开 App 依然是上次选择的语言;
  • Android 系统间不同的差异,例如 7.0 后不再是唯一默认语言,而是多种语言配置,具体差别如下所示:
Android 国际化之多语言适配小记
文章图片

好啦,直接上码~
网上看到大家再讨论这个 androidx 包下 appcompat 问题,这里也把我使用的版本贴出来:
  • implementation 'androidx.appcompat:appcompat:1.2.0'
一、创建对应的资源文件 方式有两种.如下:
  • 方式一:
右键 「res」,选择 「New」,「Android Resource File」:
Android 国际化之多语言适配小记
文章图片

按如下图进行选择配置语言表:
Android 国际化之多语言适配小记
文章图片

  • 方式二:
Android Studio 左侧选择「Resource Manager」,随后选择小地图 + 的标志,最后在列表中选择对应兼容的国家即可.
Android 国际化之多语言适配小记
文章图片

随后会为我们创建选择的国家的 values 目录以及 strings 文件,如下所示:
Android 国际化之多语言适配小记
文章图片

好了,到现在,基本的语言目录以及文件都已经创建好了,剩下的就是会有专人负责提供对应的翻译词.
当然,我司一贯的原则是,自己动手,丰衣足食.
提供了部分常用的、不错的在线翻译地址,如下:
  • https://www.deepl.com/translator
  • https://translate.google.cn/
二、贴心附上过程中使用的 MMKV Utils 记得去引用 MMKV 依赖以及初始化,地址如下:
  • https://github.com/Tencent/MMKV
个人使用的版本如下:
  • implementation 'com.tencent:mmkv:1.0.17'
/** * @author:HLQ_Struggle * @date:2020/4/13 * @desc:基础数据缓存 */class MMKVPro( private val mmkv: MMKV, private val key: String, private val defValue: T ) {operator fun getValue(thisRef: Any?, property: KProperty<*>): T { // 本地加密存储并支持多进程访问 return mmkv.run { when (defValue) { is String -> getString(key, defValue) is Boolean -> getBoolean(key, defValue) is Long -> getLong(key, defValue) is Int -> getInt(key, defValue) is Float -> getFloat(key, defValue) else -> Unit } } as T }operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { return mmkv.run { when (value) { is String -> putString(key, value) is Boolean -> putBoolean(key, value) is Long -> putLong(key, value) is Int -> putInt(key, value) is Float -> putFloat(key, value) else -> Unit } } }}/** * 移除 key */ fun removeKey(key: String) { MMKV.mmkvWithID(F_APP_CACHE, MMKV.MULTI_PROCESS_MODE, K_ENCRYPT).run { remove(key) } }

三、准备多语言 utils
/** * @author HLQ_Struggle * @date 2021/02/26 * @desc *//** * Activity 更新语言资源 */ fun getAttachBaseContext(context: Context): Context { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return setAppLanguageApi24(context) } else { setAppLanguage(context) } return context }/** * 设置应用语言 */ @Suppress("DEPRECATION") fun setAppLanguage(context: Context) { val resources = context.resources val displayMetrics = resources.displayMetrics val configuration = resources.configuration // 获取当前系统语言,默认设置跟随系统 val locale = getAppLocale() 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) private fun setAppLanguageApi24(context: Context): Context { val locale = getAppLocale() val resource = context.resources val configuration = resource.configuration configuration.setLocale(locale) configuration.setLocales(LocaleList(locale)) return context.createConfigurationContext(configuration) }/** * 获取 App 当前语言 */ private fun getAppLocale() = when (LocalDataStorage().multilingual) { 0 -> { // 跟随系统 getSystemLocale() } 1 -> { // 中文 Locale.CHINA } 2 -> { // 英文 Locale.ENGLISH } else -> Locale.ENGLISH }/** * 获取当前系统语言,如未包含则默认英文 */ private fun getSystemLocale(): Locale { val systemLocale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { LocaleList.getDefault()[0] } else { Locale.getDefault() } return when (systemLocale.language) { Locale.CHINA.language -> { Locale.CHINA } Locale.ENGLISH.language -> { Locale.ENGLISH } else -> { Locale.ENGLISH } } }

四、在选择多语言页面进行处理 当然这里我的思路是,本地缓存语言列表索引,然后后续根据 id 直接获取对应的语言即可.
点击确认时,进行缓存当前选择的
override fun onClick(v: View?) { when (v?.id) { R.id.tvDone -> { // 更新选择状态 LocalDataStorage().multilingual = mAfterPosition setAppLanguage(this) reStartActivity() } } }private fun reStartActivity() { val intent = Intent(mSelfActivity, MainActivity::class.java) intent.flags = FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK startActivity(intent) // 取消其专场动画 overridePendingTransition(0, 0) }

五、Application 中 Configuration 处理
override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // ... setAppLanguage(this) }

六、BaseActivity 处理 由于需要重建 Activity 去处理对应资源,所以这里个人是把它放在 BaseActivity 中去处理:
override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(newBase?.let { getAttachBaseContext(it) }) }

七、优化项,资源文件更新 大家千万记得更新这个,如果做过 Apk 大小优化,八成都会限制 resConfigs 内容,避免打包时多处一些无用内容增加 Apk 大小.
大家千万记得更新这个,如果做过 Apk 大小优化,八成都会限制 resConfigs 内容,避免打包时多处一些无用内容增加 Apk 大小.
大家千万记得更新这个,如果做过 Apk 大小优化,八成都会限制 resConfigs 内容,避免打包时多处一些无用内容增加 Apk 大小.
我就是写完之后,怎么也不出效果,后来一看,好家伙,限制只有中文.当时的尴尬、无奈...
resConfigs "zh-rCN", "en"

好了,到此结束,当然,Android 不得不面对的多机型适配...
这里后续遇到在更新把~
多语言遇到的一些问题
1. 布局问题 这个的确让人蛮头疼的,尤其对于我们基建不完整的情况,能做的只能说是保证大部分的效果,尽量使用短称英文或者非中文.
同时这个也提醒我,如何在开发的过程中尽可能兼容后续呢?
可能也是经验把,慢慢努力.
2.TabLayout 英文模式下大写 【Android 国际化之多语言适配小记】切换后效果如下:
Android 国际化之多语言适配小记
文章图片

目前使用的 TabLayout 版本如下:
  • implementation 'com.google.android.material:material:1.2.1'
喏,设置个样式就好:
@dimen/sp_18 false

后续遇到再补充吧.
参考资料
  • 本地化您的应用
  • Unicode 和国际化支持
  • 语言和语言区域解析概览
  • Android(国际化)多语言的实现和切换
  • Android多语言切换(兼容安卓9、10)

    推荐阅读