Смена языка приложения android API 23-25

Внедряю в приложение функция смены языка через настройки. На API 26+ все работает без проблем, но если ниже 26 то реакции нет. Язык не меняется.

LocaleHelper.class

public class LocaleHelper {
private static final String SELECTED_LANGUAGE = "language";

public static Context onAttach(Context context) {
    String lang = getLanguage(context);
    return setLocale(context, lang);
}

public static String getLanguage(Context context) {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    return preferences.getString(SELECTED_LANGUAGE, Locale.getDefault().getLanguage());
}

public static Context setLocale(Context context, String language) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResources(context, language);
    }
    return updateResourcesLegacy(context, language);
}

@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    Resources resources = context.getResources();

    Configuration configuration = resources.getConfiguration();
    configuration.setLocale(locale);
    configuration.setLayoutDirection(locale);

    return context.createConfigurationContext(configuration);
}

private static Context updateResourcesLegacy(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    Resources resources = context.getResources();

    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    configuration.setLayoutDirection(locale);

    resources.updateConfiguration(configuration, resources.getDisplayMetrics());

    return context;
}

В MainActivity и MainApplication добавил:

    @Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(LocaleHelper.onAttach(base));
}

Активити при выборе языка перезапускаю. После перезапуска функция updateResources из LocaleHelper выполняется. Видимо проблема именно в ней, но не могу понять почему. Рабочих примеров не нашол.

Подскажите пожалуйста в чем проблема.


Ответы (1 шт):

Автор решения: Vlad Orlinskas

Смена языка на лету интересная задача, есть моменты. Например для перевода заголовков экрана, нужно будет перезапусть приложение. Показываю пример кода, который работает на API 19+

object LocaleUtil {

    fun setLocaleFromSettings(baseContext: Context): Context {
        val locale = Settings(baseContext).valueLanguage.locale

        return if (locale == Language.DEFAULT.locale) {
            baseContext
        } else {
            Locale.setDefault(locale)

            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
                updateResourcesLocale(baseContext, locale)
            } else {
                updateResourcesLocaleLegacy(baseContext, locale)
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.N)
    private fun updateResourcesLocale(context: Context, locale: Locale): Context {
        val configuration = Configuration(context.resources.configuration)
        configuration.setLocale(locale)
        return context.createConfigurationContext(configuration)
    }

    @Suppress("DEPRECATION")
    private fun updateResourcesLocaleLegacy(context: Context, locale: Locale): Context {
        val configuration = Configuration(context.resources.configuration)
        val displayMetrics = context.resources.displayMetrics
        configuration.setLocale(locale)
        context.resources.updateConfiguration(configuration, displayMetrics)
        return context
    }
}

Вот как я перезапускаю приложение для окончательного перевода

    private fun triggerRebirth() {
        val intent = Intent(baseContext, MainActivity::class.java)
        intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
        startActivity(intent)
        finish()
        Runtime.getRuntime().exit(0)
    }

Enum в котором удобно хранить языки

@Keep
@Suppress("ConstantLocale")
enum class Language(val languageNameResId: Int, val locale: Locale) : SettingsEnum {
    DEFAULT(R.string.locale_default, Locale.ENGLISH) {
        override fun getResId(): Int {
            return languageNameResId
        }
    },
    RUSSIAN(R.string.locale_russian, Locale("ru", "RU")) {
        override fun getResId(): Int {
            return languageNameResId
        }
    },
    UKRAINIAN(R.string.locale_ukrainian, Locale("uk", "UA")) {
        override fun getResId(): Int {
            return languageNameResId
        }
    };
}

Это prefs в котором хранится текущая выбраная локаль

Settings(baseContext).valueLanguage.locale

Прошу заметить, что данное решение позволяет сохранить автоматический выбор языка системой.

Так же я переопределил два метода в application классе

override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(
            base?.let {
                LocaleUtil.setLocaleFromSettings(it)
            }
        )
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        LocaleUtil.setLocaleFromSettings(baseContext)
    }
→ Ссылка