Установите Locale программно

Мое приложение поддерживает 3 (скоро 4) языка. Поскольку несколько локалей очень похожи, я бы хотел дать пользователю возможность изменить локаль в моем приложении, например, итальянец может предпочесть испанский, а не английский.

Есть ли способ для пользователя выбрать один из языковых стандартов, доступных для приложения, а затем изменить используемый языковой стандарт? Я не вижу проблемы в настройке локали для каждого действия, поскольку это простая задача для базового класса.

18 ответов

Решение

Для людей, которые все еще ищут этот ответ, так как configuration.locale устарела, теперь вы можете использовать из API 24:

configuration.setLocale(locale);

Примите во внимание, что minSkdVersion для этого метода - API 17.

Полный пример кода:

@SuppressWarnings("deprecation")
private void setLocale(Locale locale){
    SharedPrefUtils.saveLocale(locale); // optional - Helper method to save the selected language to SharedPreferences in case you might need to attach to activity context (you will need to code this)
    Resources resources = getResources();
    Configuration configuration = resources.getConfiguration();
    DisplayMetrics displayMetrics = resources.getDisplayMetrics();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
        configuration.setLocale(locale);
    } else{
        configuration.locale=locale;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
        getApplicationContext().createConfigurationContext(configuration);
    } else {
        resources.updateConfiguration(configuration,displayMetrics);
    }
}

Не забывайте, что если вы меняете локаль с запущенным Activity, вам нужно будет перезапустить его, чтобы изменения вступили в силу.

РЕДАКТИРОВАТЬ 11 мая 2018

Начиная с поста @CookieMonster, у вас могут возникнуть проблемы с сохранением изменения локали в более высоких версиях API. Если это так, добавьте следующий код в базовую активность, чтобы обновлять контекстную локаль при каждом создании действия:

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

private Context updateBaseContextLocale(Context context) {
    String language = SharedPrefUtils.getSavedLanguage(); // Helper method to get saved language from SharedPreferences
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResourcesLocale(context, locale);
    }

    return updateResourcesLocaleLegacy(context, locale);
}

@TargetApi(Build.VERSION_CODES.N)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private Context updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return context;
}

Если вы используете это, не забудьте сохранить язык в SharedPreferences, когда вы устанавливаете локаль с setLocate(locale)

Надеюсь, что это поможет (в onResume):

Locale locale = new Locale("ru");
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
      getBaseContext().getResources().getDisplayMetrics());

У меня была проблема с программной настройкой локали на устройствах с ОС Android N и выше. Для меня решением было написать этот код в моей основной деятельности:

(если у вас нет базовой активности, вы должны внести эти изменения во все ваши действия)

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

private Context updateBaseContextLocale(Context context) {
    String language = SharedPref.getInstance().getSavedLanguage();
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResourcesLocale(context, locale);
    }

    return updateResourcesLocaleLegacy(context, locale);
}

@TargetApi(Build.VERSION_CODES.N)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private Context updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return context;
}

обратите внимание, что здесь недостаточно позвонить

createConfigurationContext(configuration)

вам также нужно получить контекст, который возвращает этот метод, а затем установить этот контекст в attachBaseContext метод.

Поскольку пока нет полного ответа на текущий способ решения этой проблемы, я пытаюсь дать инструкции для полного решения. Пожалуйста, прокомментируйте, если что-то отсутствует или может быть сделано лучше.

Основная информация

Во-первых, существуют библиотеки, которые хотят решить проблему, но все они кажутся устаревшими или не имеют некоторых функций:

Кроме того, я думаю, что написание библиотеки не может быть хорошим / легким способом решения этой проблемы, потому что не так уж много нужно сделать, и что нужно сделать, это скорее изменить существующий код, чем использовать что-то полностью отделенное. Поэтому я составил следующие инструкции, которые должны быть завершены.

Мое решение в основном основано на https://github.com/gunhansancar/ChangeLanguageExample (как уже связано с localhost). Это лучший код для поиска. Некоторые замечания:

  • При необходимости, он предоставляет различные реализации для изменения локали для Android N (и выше) и ниже
  • Он использует метод updateViews() в каждом действии вручную обновлять все строки после изменения локали (используя обычные getString(id)) что не является необходимым в подходе, показанном ниже
  • Поддерживаются только языки, а не полные локали (которые также включают код региона (страны) и варианта)

Я немного его изменил, отделив часть, которая сохраняет выбранную локаль (как можно было бы сделать это отдельно, как предложено ниже).

Решение

Решение состоит из следующих двух шагов:

  • Навсегда изменить локаль, которая будет использоваться приложением
  • Заставьте приложение использовать пользовательский набор языковых настроек без перезапуска

Шаг 1: Измените локаль

Используйте класс LocaleHelper, основанный на LocaleHelper Gunhansancar:

  • Добавить ListPreference в PreferenceFragment с доступными языками (необходимо сохранить, когда языки должны быть добавлены позже)
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;

import java.util.Locale;

import mypackage.SettingsFragment;

/**
 * Manages setting of the app's locale.
 */
public class LocaleHelper {

    public static Context onAttach(Context context) {
        String locale = getPersistedLocale(context);
        return setLocale(context, locale);
    }

    public static String getPersistedLocale(Context context) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SettingsFragment.KEY_PREF_LANGUAGE, "");
    }

    /**
     * Set the app's locale to the one specified by the given String.
     *
     * @param context
     * @param localeSpec a locale specification as used for Android resources (NOTE: does not
     *                   support country and variant codes so far); the special string "system" sets
     *                   the locale to the locale specified in system settings
     * @return
     */
    public static Context setLocale(Context context, String localeSpec) {
        Locale locale;
        if (localeSpec.equals("system")) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                locale = Resources.getSystem().getConfiguration().getLocales().get(0);
            } else {
                //noinspection deprecation
                locale = Resources.getSystem().getConfiguration().locale;
            }
        } else {
            locale = new Locale(localeSpec);
        }
        Locale.setDefault(locale);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return updateResources(context, locale);
        } else {
            return updateResourcesLegacy(context, locale);
        }
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, Locale locale) {
        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);

        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, Locale locale) {
        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }

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

        return context;
    }
}

Создать SettingsFragment как следующее:

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import mypackage.LocaleHelper;
import mypackage.R;

/**
 * Fragment containing the app's main settings.
 */
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_LANGUAGE = "pref_key_language";

    public SettingsFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_settings, container, false);
        return view;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        switch (key) {
            case KEY_PREF_LANGUAGE:
                LocaleHelper.setLocale(getContext(), PreferenceManager.getDefaultSharedPreferences(getContext()).getString(key, ""));
                getActivity().recreate(); // necessary here because this Activity is currently running and thus a recreate() in onResume() would be too late
                break;
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        // documentation requires that a reference to the listener is kept as long as it may be called, which is the case as it can only be called from this Fragment
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
}

Создать ресурс locales.xml перечисление всех локалей с доступными переводами следующим образом ( список кодов локали):

<!-- Lists available locales used for setting the locale manually.
     For now only language codes (locale codes without country and variant) are supported.
     Has to be in sync with "settings_language_values" in strings.xml (the entries must correspond).
  -->
<resources>
    <string name="system_locale" translatable="false">system</string>
    <string name="default_locale" translatable="false"></string>
    <string-array name="locales">
        <item>@string/system_locale</item> <!-- system setting -->
        <item>@string/default_locale</item> <!-- default locale -->
        <item>de</item>
    </string-array>
</resources>

В вашем PreferenceScreen Вы можете использовать следующий раздел, чтобы позволить пользователю выбирать доступные языки:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/preferences_category_general">
        <ListPreference
            android:key="pref_key_language"
            android:title="@string/preferences_language"
            android:dialogTitle="@string/preferences_language"
            android:entries="@array/settings_language_values"
            android:entryValues="@array/locales"
            android:defaultValue="@string/system_locale"
            android:summary="%s">
        </ListPreference>
    </PreferenceCategory>
</PreferenceScreen>

который использует следующие строки из strings.xml:

<string name="preferences_category_general">General</string>
<string name="preferences_language">Language</string>
<!-- NOTE: Has to correspond to array "locales" in locales.xml (elements in same orderwith) -->
<string-array name="settings_language_values">
    <item>Default (System setting)</item>
    <item>English</item>
    <item>German</item>
</string-array>

Шаг 2. Заставьте приложение использовать пользовательский язык

Теперь настройте каждое действие, чтобы использовать пользовательский набор языковых стандартов. Самый простой способ сделать это - иметь общий базовый класс для всех действий со следующим кодом (где важен код attachBaseContext(Context base) а также onResume()):

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import mypackage.LocaleHelper;
import mypackage.R;

/**
 * {@link AppCompatActivity} with main menu in the action bar. Automatically recreates
 * the activity when the locale has changed.
 */
public class MenuAppCompatActivity extends AppCompatActivity {
    private String initialLocale;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initialLocale = LocaleHelper.getPersistedLocale(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_settings:
                Intent intent = new Intent(this, SettingsActivity.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

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

    @Override
    protected void onResume() {
        super.onResume();
        if (initialLocale != null && !initialLocale.equals(LocaleHelper.getPersistedLocale(this))) {
            recreate();
        }
    }
}

Что это делает

  • Override attachBaseContext(Context base) использовать локаль, ранее сохраненную с LocaleHelper
  • Обнаружьте изменение языкового стандарта и заново создайте Activity, чтобы обновить его строки

Примечания к этому решению

  • Воссоздание Действия не обновляет заголовок ActionBar (как уже наблюдалось здесь: https://github.com/gunhansancar/ChangeLanguageExample/issues/1).

    • Это может быть достигнуто простым setTitle(R.string.mytitle) в onCreate() метод каждой деятельности.
  • Это позволяет пользователю выбрать системный языковой стандарт по умолчанию, а также языковой стандарт приложения по умолчанию (который может быть назван, в данном случае "английский").

  • Только коды языков, без региона (страны) и кодов вариантов (например, fr-rCA) поддерживаются до сих пор. Для поддержки полных спецификаций локали можно использовать синтаксический анализатор, подобный таковому в библиотеке Android-Languages (который поддерживает регион, но без кодов вариантов).

    • Если кто-то найдет или написал хороший анализатор, добавьте комментарий, чтобы я мог включить его в решение.
@SuppressWarnings("deprecation")
public static void forceLocale(Context context, String localeCode) {
    String localeCodeLowerCase = localeCode.toLowerCase();

    Resources resources = context.getApplicationContext().getResources();
    Configuration overrideConfiguration = resources.getConfiguration();
    Locale overrideLocale = new Locale(localeCodeLowerCase);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        overrideConfiguration.setLocale(overrideLocale);
    } else {
        overrideConfiguration.locale = overrideLocale;
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        context.getApplicationContext().createConfigurationContext(overrideConfiguration);
    } else {
        resources.updateConfiguration(overrideConfiguration, null);
    }
}

Просто используйте этот вспомогательный метод для принудительного определения конкретной локали.

UDPATE 22 августа 2017 года. Лучше использовать этот подход.

Есть супер простой способ.

в BaseActivity, Activity или Fragment переопределить attachBaseContext

 override fun attachBaseContext(context: Context) {
    super.attachBaseContext(context.changeLocale("tr"))
}

расширение

fun Context.changeLocale(language:String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)
    val config = this.resources.configuration
    config.setLocale(locale)
    return createConfigurationContext(config)
}

Добавьте вспомогательный класс с помощью следующего метода:

public class LanguageHelper {
    public static final void setAppLocale(String language, Activity activity) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Resources resources = activity.getResources();
            Configuration configuration = resources.getConfiguration();
            configuration.setLocale(new Locale(language));
            activity.getApplicationContext().createConfigurationContext(configuration);
        } else {
            Locale locale = new Locale(language);
            Locale.setDefault(locale);
            Configuration config = activity.getResources().getConfiguration();
            config.locale = locale;
            activity.getResources().updateConfiguration(config,
                    activity.getResources().getDisplayMetrics());
        }

    }
}

И назовите это в своей деятельности запуска, как MainActivity.java:

public void onCreate(Bundle savedInstanceState) {
    ...
    LanguageHelper.setAppLocale("fa", this);
    ...
}

Действительно для API16 - API28 Просто разместите этот метод где-нибудь:

    Context newContext = context;

        Locale locale = new Locale(languageCode);
        Locale.setDefault(locale);

        Resources resources = context.getResources();
        Configuration config = new Configuration(resources.getConfiguration());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {

        config.setLocale(locale);
                newContext = context.createConfigurationContext(config);

        } else {

        config.locale = locale;
                resources.updateConfiguration(config, resources.getDisplayMetrics());
        }

    return newContext;
}

Вставьте этот код во все свои действия, используя:

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(localeUpdateResources(base, "<-- language code -->"));
    }

или вызовите localeUpdateResources для фрагментов, адаптеров и т. д., где вам нужен новый контекст.

Кредиты: Ярослав Березанский

Я нашел androidx.appcompat:appcompat:1.1.0 ошибку также можно исправить, просто позвонив getResources() в applyOverrideConfiguration()

@Override public void
applyOverrideConfiguration(Configuration cfgOverride)
{
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
      Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    // add this to fix androidx.appcompat:appcompat 1.1.0 bug
    // which happens on Android 6.x ~ 7.x
    getResources();
  }

  super.applyOverrideConfiguration(cfgOverride);
}

Просто и легко

Locale locale = new Locale("en", "US");
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = locale;
res.updateConfiguration(conf, dm);

где "en" - это код языка, а "US" - код страны.

В Android 13 представлены языковые настройки для каждого приложения, которые значительно упрощают обработку изменений локали в приложении.

Отказ от ответственности: Android 13 в настоящее время находится в «Предварительной версии для разработчиков» на момент написания, и часть упомянутого здесь находится в альфа-версии (таким образом, может быть изменена).

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

      
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)

(Метод доступен с androidx.appcompat:appcompat:1.6.0-alpha01)

Это позаботится о настройке локали на appLocaleи перезапустите любые действия по мере необходимости. Более того, как упоминалось в документах setApplicationLocales

На уровне API 33 и выше этот API будет автоматически обрабатывать хранилище.

Это означает, что для работы пользовательской локали в приложении нужно просто предложить пользователю средство выбора языка и вызвать setApplicationLocalesодин раз при выполнении выбора.

А как насчет API < 33?

В документах на developer.android.com упоминается, что разработчики могут требовать автоматического сохранения этого значения на более старых уровнях API.

.. добавив специальную запись метаданных в свой AndroidManifest, например:

но на момент написания этой статьи фактические необходимые метаданные не упоминались (возможно, что-то пошло не так при создании документа).

Вместо этого он был виден в исходном коде AppCompatDelegate , опубликованном здесь, и выглядит следующим образом .

         <service
       android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
       android:enabled="false"
       android:exported="false">
       <meta-data
           android:name="autoStoreLocales"
           android:value="true" />
   </service>

Добавление вышеуказанного в ваш манифест должно заставить это решение работать для всех уровней API.

Существует новый способ позволить пользователям выбирать язык приложения по умолчанию, начиная с Appcompat 1.6.0-alpha04 и более поздних версий. Допустим, у вас есть кнопка, которая должна менять язык приложения на итальянский , когда пользователь нажимает на нее:

      binding.button.setOnClickListener {
    val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("it")
    AppCompatDelegate.setApplicationLocales(appLocale)
}

Кроме того, чтобы добавить поддержку старых устройств (<уровень API 32), добавьте следующееserviceвнутри<application>тег вашегоAndroidManifest.xmlфайл:

      <service
  android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
  android:enabled="false"
  android:exported="false">
  <meta-data
    android:name="autoStoreLocales"
    android:value="true" />
</service>

Убедитесь, что у вас есть последняяAppCompatзависимость:

      
implementation 'androidx.appcompat:appcompat:1.6.0-alpha05'

Ссылка: https://developer.android.com/about/versions/13/features/app-languages

 /**
 * Requests the system to update the list of system locales.
 * Note that the system looks halted for a while during the Locale migration,
 * so the caller need to take care of it.
 */
public static void updateLocales(LocaleList locales) {
    try {
        final IActivityManager am = ActivityManager.getService();
        final Configuration config = am.getConfiguration();

        config.setLocales(locales);
        config.userSetLocale = true;

        am.updatePersistentConfiguration(config);
    } catch (RemoteException e) {
        // Intentionally left blank
    }
}

С 2020 года управлять языком стало проще! Все, что вам нужно сделать, это:

  1. Позвонить Activity.applyOverrideConfiguration
  2. И позвонить Locale.setDefault

Вы должны вызывать их из конструктора активности, так как вы можете вызывать applyOverrideConfiguration только один раз, и система вызывает это довольно рано.

И будьте осторожны с наборами приложений, Google автоматически разделит ваш APK по языковым ресурсам при использовании наборов приложений. Ознакомьтесь с новым API и обходным путем здесь.


Я создал вспомогательный класс, чтобы помочь вам с этим. В моей реализации G.appконтекст приложения. Кроме того, мне нужно получить доступ к ресурсам из контекста приложения, поэтому я использую Res class для него, это необязательно, но я также предоставляю его код.

Применение

public BaseActivity(){
    LanguageUtility.init(this);
}

public void changeLanguage(Local local){
    // you must recreat your activity after you call this
    LanguageUtillity.setDefaultLanguage(local, this);
}

Исходный код

public class LanguageUtility {

    private static Configuration configuration;

    public static void setDefaultLanguage(Locale locale, Context context) {
        Locale.setDefault(locale);

        context.getSharedPreferences("LocaleSettings", Context.MODE_PRIVATE)
                .edit()
                .putString("language", locale.getLanguage())
                .putString("country", locale.getCountry())
                .putString("variant", locale.getVariant())
                .apply();

        configuration = createConfiguration(context);
        Res.updateContext();
    }

    /**
     * Used to update your app context in case you cache it.
     */
    public static Context createConfigurationContext(Context context) {
        return context.createConfigurationContext(getConfiguration(context));
    }

    public static void init(Activity activity) {
        activity.applyOverrideConfiguration(LanguageUtility.getConfiguration(G.app));
        // you can't access sharedPrefferences from activity constructor 
        // with activity context, so I used the app context.
        Locale.setDefault(getLocale(G.app));
    }

    @NotNull
    private static Configuration getConfiguration(Context context) {
        if (configuration == null) {
            configuration = createConfiguration(context);
        }
        return configuration;
    }

    @NotNull
    private static Configuration createConfiguration(Context context) {
        Locale locale = getLocale(context);
        Configuration configuration = new Configuration();
        configuration.setLocale(locale);
        LanguageUtility.configuration = configuration;
        return configuration;
    }

    @NotNull
    private static Locale getLocale(Context context) {
        Locale aDefault = Locale.getDefault();
        SharedPreferences preferences =
                context.getSharedPreferences("LocaleSettings", Context.MODE_PRIVATE);
        String language = preferences.getString("language", aDefault.getLanguage());
        String country = preferences.getString("country", aDefault.getCountry());
        String variant = preferences.getString("variant", aDefault.getVariant());
        return new Locale(language, country, variant);
    }
}

Необязательный класс Res.

public class Res {

    @SuppressLint("StaticFieldLeak")
    public static Context appLocalContext = LanguageUtility.createConfigurationContext(G.app);

    public static void updateContext() {
        appLocalContext = LanguageUtility.createConfigurationContext(G.app);
    }

    public static String getString(@StringRes int id, Object... formatArgs) {
        return appLocalContext.getResources().getString(id, formatArgs);
    }

    public static int getColor(@ColorRes int id) {
        return G.app.getColor(id);
    }
}

Вызовите этот метод в BaseActivity -> onCreate () и BaseFragment -> OnCreateView ()

Протестировано на API 22, 23, 24, 25, 26, 27, 28, 29... в последней версии

      fun Context.updateLang() {
    val resources = resources
    val config = Configuration(resources.configuration)
    config.setLocale(PreferenceManager(this).getAppLanguage()) // language from preference
    val dm = resources.displayMetrics
    createConfigurationContext(config)
    resources.updateConfiguration(config, dm)
}

Для тех, кто все пробовал, но не работает. Убедитесь, что если вы установилиdarkmode с AppCompatDelegate.setDefaultNightMode а система не тёмная, то Configuration.setLocaleне будет работать выше Andorid 7.0.

Добавьте этот код в каждое действие, чтобы решить эту проблему:

override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
  if (overrideConfiguration != null) {
    val uiMode = overrideConfiguration.uiMode
    overrideConfiguration.setTo(baseContext.resources.configuration)
    overrideConfiguration.uiMode = uiMode
  }
  super.applyOverrideConfiguration(overrideConfiguration)
}
@SuppressWarnings("deprecation")
private void setLocale(Locale locale){
    SharedPrefUtils.saveLocale(locale); // optional - Helper method to save the selected language to SharedPreferences in case you might need to attach to activity context (you will need to code this)
    Resources resources = getResources();
    Configuration configuration = resources.getConfiguration();
    DisplayMetrics displayMetrics = resources.getDisplayMetrics();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
        configuration.setLocale(locale);
    } else{
        configuration.locale=locale;
    }
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N){
        getApplicationContext().createConfigurationContext(configuration);
    } else {
        resources.updateConfiguration(configuration,displayMetrics);
    }
}

Поместите этот код в вашу деятельность

 if (id==R.id.uz)
    {
        LocaleHelper.setLocale(MainActivity.this, mLanguageCode);

        //It is required to recreate the activity to reflect the change in UI.
        recreate();
        return true;
    }
    if (id == R.id.ru) {

        LocaleHelper.setLocale(MainActivity.this, mLanguageCode);

        //It is required to recreate the activity to reflect the change in UI.
        recreate();
    }
Другие вопросы по тегам