Как применить изменения после смены языка?

Я использую пользовательские настройки для своих предпочтений. Одним из них является ListPreference для выбора языка приложения. До сих пор я мог реализовать настройки локали, но чтобы применить изменения, я перезапускаю приложение. Проблема в том, что это не работает для всех версий Android. Вот что я сделал:

import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentActivity
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat

class SettingsActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_settings)
        val toolbar: Toolbar = findViewById(R.id.toolbar_settings)
        toolbar.apply {
            setNavigationIcon(R.drawable.ic_arrow)
            setNavigationOnClickListener { finish() }
            title = getString(R.string.settings)
            setTitleTextColor(Color.WHITE)
        }
        supportFragmentManager.beginTransaction().replace(R.id.content, SettingsFragment())
            .commit()
    }

    class SettingsFragment : PreferenceFragmentCompat() {

        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
            addPreferencesFromResource(R.xml.preferences)

            val lang: ListPreference = findPreference("Language")!!
            val language = when (LanguageSettings().getLang()) {
                "en" -> getString(R.string.en_lang)
                "de" -> getString(R.string.de_lang)
                else -> getString(R.string.ru_lang)
            }

            lang.summary = "${getString(R.string.current_language)} $language"
            val temp = lang.value
            lang.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
                val dialogBuilder =
                    activity?.let { AlertDialog.Builder(it, R.style.AlertDialogLight) }
                dialogBuilder!!.setMessage(getString(R.string.restart_txt))
                    .setPositiveButton(getString(R.string.restart)) { _, _ ->
                        activity?.let { LanguageSettings().setLocale(it, newValue.toString()) }
                        restartApp()
                    }
                    .setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
                        lang.value = temp
                        dialog.cancel()
                    }
                dialogBuilder.create().apply {
                    show()
                }
                true
            }
        }

        private fun restartApp() {
            val intent = activity!!.packageManager
                .getLaunchIntentForPackage(activity!!.packageName)
            intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            activity!!.finish()
            startActivity(intent)
        }
    }

    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LanguageSettings().onAttach(newBase))
    }
}

Ниже Lollipop после перезапуска изменения применяются, что означает, что язык приложения изменяется после перезапуска. Также это работает для Android 10. Но это не работает для Lollipop и Marshmallow (пока проверено), но что только работает, язык меняется только для SettingsActivity, другие действия остаются без изменений. Возможно, между Marshmallow и Android X есть и другие версии, для которых это тоже работает.

Но, тем не менее, я хочу, чтобы это работало для всех версий, мое приложение поддерживает SDK от 16 до последней версии.

Спасибо заранее. Если я был не так точен и вам нужна дополнительная информация, пожалуйста, дайте мне знать, я постараюсь предоставить больше информации. Между тем, пожалуйста, простите мой несовершенный английский.

Мой LanguageSettings.kt класс:

import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.util.Log
import androidx.preference.PreferenceManager
import java.util.*

@Suppress("DEPRECATION")
class LanguageSettings {

    fun onAttach(context: Context): Context {
        return setLocale(context, getPersistedData(context, getLang()!!)!!)
    }

    fun onAttach(context: Context, defaultLanguage: String): Context {
        return setLocale(context, getPersistedData(context, defaultLanguage)!!)
    }

    fun getLang(): String? {
        return when (Locale.getDefault().displayLanguage) {
            "English" -> "en"
            "Deutsch" -> "de"
            "русский" -> "ru"
            else -> "en"
        }
    }

    fun setLocale(context: Context, language: String): Context {
        persist(context, language)
        val configuration: Configuration
        val resources = context.resources
        val locale =
            Locale(language)
        Locale.setDefault(locale)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration = context.resources.configuration
            configuration.setLocale(locale)
            configuration.setLayoutDirection(locale)
            return context.createConfigurationContext(configuration)
        }
        configuration = resources.configuration
        configuration.locale = locale
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
            configuration.setLayoutDirection(locale)
        resources.updateConfiguration(configuration, resources.displayMetrics)
        return context
    }

    private fun getPersistedData(context: Context, defaultLanguage: String): String? {
        return PreferenceManager
            .getDefaultSharedPreferences(context)
            .getString("Language", defaultLanguage)
    }

    private fun persist(context: Context, language: String) {
        PreferenceManager
            .getDefaultSharedPreferences(context)
            .edit()
            .putString("Language", language)
            .apply()
    }
}

Проблема: это работает для Android 4.2-4.4 и выше 7, но не для Android 5.0/5.1/6 (и может быть для некоторых версий также не работает, пока не проверял для всех версий, только для вышеупомянутых версий) .

И этот мой класс MyApp.kt, который расширяет Application, назначается в файле манифеста (android:name=".MyApp"):

class MyApp : Application() {

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(LanguageSettings().onAttach(base, "en"))
    }
}

person Mark Delphi    schedule 20.12.2019    source источник


Ответы (1)


Я нашел решение после тестирования множества разных случаев:

private fun setLocale(context: Context, language: String): Context {
        val configuration: Configuration
        val resources = context.resources
        val locale = when (language) {
            "de" -> Locale("de", "DE")
            "ru" -> Locale("ru", "RU")
            "en" -> Locale("en", "US")
            else -> Locale("en", "US")
        }

        Locale.setDefault(locale)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration = context.resources.configuration
            configuration.setLocale(locale)
            configuration.setLayoutDirection(locale)
            return context.createConfigurationContext(configuration)
        }
        configuration = resources.configuration
        configuration.locale = locale
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
            configuration.setLayoutDirection(locale)
        resources.updateConfiguration(configuration, resources.displayMetrics)
        return context
    } 

Это работает для меня правильно в Котлине.

person Mark Delphi    schedule 27.12.2019
comment
Еще одна вещь: resources.xxXXXX необходимо использовать, чтобы разрешить изменение строковых значений между языками. - person Mark Delphi; 05.02.2020