害,亂糟糟,總要去梳理.
面對未知的一切,陌生感突突的.
甲方要求實現 App 國際化多語言,正好抽個時間弄了下,害,被自己蠢到死,特意記錄下.
如有不對,歡迎指正,一起交流~
效果演示
視訊錄製的不是太好,整體的效果出來了,大家見諒~
版本為別為: 6.0、8.0 以及 10.0
搞起來
簡單說下需要注意的:
- 國際化,多語言目錄建立,資源配置;
- Locale 資源獲取以及本地快取,快取的目的是為了下次重新開啟 App 依然是上次選擇的語言;
- Android 系統間不同的差異,例如 7.0 後不再是唯一預設語言,而是多種語言配置,具體差別如下所示:
好啦,直接上碼~
網上看到大家再討論這個 androidx 包下 appcompat 問題,這裡也把我使用的版本貼出來:
- implementation 'androidx.appcompat:appcompat:1.2.0'
一、建立對應的資原始檔
方式有兩種.如下:
- 方式一:
右鍵 「res」,選擇 「New」,「Android Resource File」:
按如下圖進行選擇配置語言表:
- 方式二:
Android Studio 左側選擇「Resource Manager」,隨後選擇小地圖 + 的標誌,最後在列表中選擇對應相容的國家即可.
隨後會為我們建立選擇的國家的 values 目錄以及 strings 檔案,如下所示:
好了,到現在,基本的語言目錄以及檔案都已經建立好了,剩下的就是會有專人負責提供對應的翻譯詞.
當然,我司一貫的原則是,自己動手,豐衣足食.
提供了部分常用的、不錯的線上翻譯地址,如下:
二、貼心附上過程中使用的 MMKV Utils
記得去引用 MMKV 依賴以及初始化,地址如下:
個人使用的版本如下:
- implementation 'com.tencent:mmkv:1.0.17'
/**
* @author:HLQ_Struggle
* @date:2020/4/13
* @desc:基礎資料快取
*/
class MMKVPro<T>(
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 英文模式下大寫
切換後效果如下:
目前使用的 TabLayout 版本如下:
- implementation 'com.google.android.material:material:1.2.1'
喏,設定個樣式就好:
<style name="TabLayoutTextStyle" parent="TextAppearance.Design.Tab">
<item name="android:textSize">@dimen/sp_18</item>
<item name="textAllCaps">false</item>
</style>
後續遇到再補充吧.