Harm, mess, always sort out.

Facing the unknown, the sense of strangeness was sudden.

Party A requested the realization of App internationalization and multilingualism, and it took a time to get it.

If there is something wrong, please correct me and communicate together~

Effect demonstration

The video recording is not very good, the overall effect is out, everyone forgive me~

The version is: 6.0, 8.0 and 10.0

Get it up

Simply talk about the things that need to be noted:

  • Internationalization, multilingual catalog creation, resource configuration;
  • Locale resource acquisition and local caching, the purpose of caching is to reopen the App next time and still be the last selected language;
  • The differences between Android systems, for example, after 7.0, it is no longer the only default language, but multiple language configurations. The specific differences are as follows:

Okay, just upload the code~

I saw on the Internet that everyone discusses the appcompat issue under the androidx package. Here I also post the version I use:

  • implementation 'androidx.appcompat:appcompat:1.2.0'

One, create the corresponding resource file

There are two ways. As follows:

  • method one:

Right-click ``res'', select ``New'', ``Android Resource File'':

Select the configuration language table as shown below:

  • Way two:

Select ``Resource Manager'' on the left side of Android Studio, then select the small map + logo, and finally select the corresponding compatible country in the list.

Then the values directory and strings file of the selected country will be created for us, as shown below:

Well, up to now, the basic language directory and files have been created, and the rest is that someone will be responsible for providing the corresponding translation words.

Of course, our company's consistent principle is to do it yourself and get enough food and clothing.

Some commonly used and good online translation addresses are provided, as follows:

2. Intimately attach the MMKV Utils used in the process

Remember to refer to MMKV dependencies and initialization, the address is as follows:

The personal version is as follows:

  • 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)
    }
}

Three, prepare multilingual 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
        }
    }
}

Four, select the multi-language page for processing

Of course, my idea here is to cache the language list index locally, and then directly obtain the corresponding language according to the id.

When you click confirm, cache the currently selected

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)
}

Five, Configuration processing in Application

override fun onConfigurationChanged(newConfig: Configuration?) {
    super.onConfigurationChanged(newConfig)
    // ...
    setAppLanguage(this)
}

Six, BaseActivity processing

Since the Activity needs to be rebuilt to process the corresponding resources, I personally put it in BaseActivity to handle it:

override fun attachBaseContext(newBase: Context?) {
    super.attachBaseContext(newBase?.let { getAttachBaseContext(it) })
}

Seven, optimization items, resource file update

Everyone, please remember to update this. If you have done Apk size optimization, 80% will limit the content of resConfigs, avoiding multiple useless content during packaging to increase the Apk size.
Everyone, please remember to update this. If you have done Apk size optimization, 80% will limit the content of resConfigs, avoiding multiple useless content during packaging to increase the Apk size.
Everyone, please remember to update this. If you have done Apk size optimization, 80% will limit the content of resConfigs, avoiding multiple useless content during packaging to increase the Apk size.

After I finished writing, it didn't work. Later, I saw, good guy, only Chinese was restricted. At that time, I was embarrassed and helpless...

resConfigs "zh-rCN", "en"

Well, that's the end, of course, Android has to face the multi-model adaptation...

The follow-up encounter here is in the update~

Some problems encountered in multiple languages

1. Layout issues

This is really a headache, especially for our incomplete infrastructure. What we can do is to ensure most of the effects, and try to use short English or non-Chinese.

At the same time, this also reminds me, how to be as compatible as possible in the development process?

It may also be experience, and work slowly.

2.TabLayout uppercase in English mode

The effect after switching is as follows:

The currently used TabLayout version is as follows:

  • implementation 'com.google.android.material:material:1.2.1'

Here, just set a style:

<style name="TabLayoutTextStyle" parent="TextAppearance.Design.Tab">
    <item name="android:textSize">@dimen/sp_18</item>
    <item name="textAllCaps">false</item>
</style>

Add it later when you encounter it.

Reference


贺biubiu
148 声望751 粉丝

Just do it.