Изменить язык приложения программно в Android

Можно ли программным образом изменить язык приложения, все еще используя ресурсы Android?

Если нет, можно ли запросить ресурс на определенном языке?

Я хотел бы позволить пользователю изменить язык приложения из приложения.

46 ответов

Решение

Возможно. Вы можете установить локаль. Однако я бы не рекомендовал это. Мы пробовали это на ранних стадиях, это в основном борьба с системой.

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

Resources res = context.getResources();
// Change locale settings in the app.
DisplayMetrics dm = res.getDisplayMetrics();
android.content.res.Configuration conf = res.getConfiguration();
conf.setLocale(new Locale(language_code.toLowerCase())); // API 17+ only.
// Use conf.locale = new Locale(...) if targeting lower versions
res.updateConfiguration(conf, dm);

Если у вас есть контент для конкретного языка - вы можете изменить эту базу в зависимости от настроек.

Это действительно работа:

fa = персидский, en = английский

Введите свой код языка в languageToLoad переменная:

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;

public class Main extends Activity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    String languageToLoad  = "fa"; // your language
    Locale locale = new Locale(languageToLoad); 
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.locale = locale;
    getBaseContext().getResources().updateConfiguration(config, 
      getBaseContext().getResources().getDisplayMetrics());
    this.setContentView(R.layout.main);
  }
}

Вы можете найти пример здесь

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

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

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

Это недокументированный API и, следовательно, не должен использоваться для приложений рынка / конечного пользователя!

В любом случае вот решение, которое я нашел:

  Locale locale = new Locale(targetLocaleAsString);

  Class amnClass = Class.forName("android.app.ActivityManagerNative");
  Object amn = null;
  Configuration config = null;

  // amn = ActivityManagerNative.getDefault();
  Method methodGetDefault = amnClass.getMethod("getDefault");
  methodGetDefault.setAccessible(true);
  amn = methodGetDefault.invoke(amnClass);

  // config = amn.getConfiguration();
  Method methodGetConfiguration = amnClass.getMethod("getConfiguration");
  methodGetConfiguration.setAccessible(true);
  config = (Configuration) methodGetConfiguration.invoke(amn);

  // config.userSetLocale = true;
  Class configClass = config.getClass();
  Field f = configClass.getField("userSetLocale");
  f.setBoolean(config, true);

  // set the locale to the new value
  config.locale = locale;

  // amn.updateConfiguration(config);
  Method methodUpdateConfiguration = amnClass.getMethod("updateConfiguration", Configuration.class);
  methodUpdateConfiguration.setAccessible(true);
  methodUpdateConfiguration.invoke(amn, config);

Если вы хотите сохранить язык, измененный во всем приложении, вам нужно сделать две вещи.

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

public class BaseActivity extends AppCompatActivity {

    private Locale mCurrentLocale;

    @Override
    protected void onStart() {
        super.onStart();

        mCurrentLocale = getResources().getConfiguration().locale;
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Locale locale = getLocale(this);

        if (!locale.equals(mCurrentLocale)) {

            mCurrentLocale = locale;
            recreate();
        }
    }

    public static Locale getLocale(Context context){
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

        String lang = sharedPreferences.getString("language", "en");
        switch (lang) {
            case "English":
                lang = "en";
                break;
            case "Spanish":
                lang = "es";
                break;
        }
        return new Locale(lang);
    }
}

Обратите внимание, что я сохраняю новый язык в sharedPreference.

Во-вторых, создайте расширение Application следующим образом:

    public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        setLocale();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setLocale();
    }

    private void setLocale() {

        final Resources resources = getResources();
        final Configuration configuration = resources.getConfiguration();
        final Locale locale = getLocale(this);
        if (!configuration.locale.equals(locale)) {
            configuration.setLocale(locale);
            resources.updateConfiguration(configuration, null);
        }
    }
}

Обратите внимание, что getLocale() это то же самое, что и выше.

Это все! Я надеюсь, что это может кому-то помочь.

По этой статье. Вам нужно будет скачать LocaleHelper.java упоминается в этой статье.

  1. Создайте MyApplication класс, который будет расширяться Application
  2. Override attachBaseContext() обновить язык.
  3. Зарегистрируйте этот класс в манифесте.

    public class MyApplication extends Application {
       @Override
       protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base, "en"));
       }
    }
    
    <application
         android:name="com.package.MyApplication"
         .../>
    
  4. Создайте BaseActivity и переопределить onAttach() обновить язык. Требуется для Android 6+

    public class BaseActivity extends Activity {
      @Override
      protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base));
      }
    }
    
  5. Сделать все действия в вашем приложении начинается с BaseActivity,

    public class LocaleHelper {
    
    private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";
    
    public static Context onAttach(Context context) {
        String lang = getPersistedData(context, Locale.getDefault().getLanguage());
        return setLocale(context, lang);
    }
    
    public static Context onAttach(Context context, String defaultLanguage) {
        String lang = getPersistedData(context, defaultLanguage);
        return setLocale(context, lang);
    }
    
    public static String getLanguage(Context context) {
        return getPersistedData(context, Locale.getDefault().getLanguage());
    }
    
    public static Context setLocale(Context context, String language) {
        persist(context, language);
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return updateResources(context, language);
        }
    
        return updateResourcesLegacy(context, language);
    }
    
    private static String getPersistedData(Context context, String defaultLanguage) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
    }
    
    private static void persist(Context context, String language) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = preferences.edit();
    
        editor.putString(SELECTED_LANGUAGE, language);
        editor.apply();
    }
    
    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);
    
        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);
    
        return context.createConfigurationContext(configuration);
    }
    
    @SuppressWarnings("deprecation")
    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;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }
    
        resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    
        return context;
    }
    }
    

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

В то время как другие ответы хорошо работают с "де", например

String lang = "de";
Locale locale = new Locale(lang); 
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config, 
    getBaseContext().getResources().getDisplayMetrics());

Выше не будет работать, например, с "fr_BE" локаль, так что будет использовать values-fr-rBE папка или подобное.

Требуется следующее небольшое изменение для работы с "fr_BE"

String lang = "fr";

//create a string for country
String country = "BE";
//use constructor with country
Locale locale = new Locale(lang, country);

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

Создать класс Расширяется Application и создать статический метод. Затем вы можете вызвать этот метод во всех действиях до setContentView(),

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
}

public static void setLocaleFa (Context context){
    Locale locale = new Locale("fa"); 
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.locale = locale;
    context.getApplicationContext().getResources().updateConfiguration(config, null);
}

public static void setLocaleEn (Context context){
    Locale locale = new Locale("en_US"); 
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.locale = locale;
    context.getApplicationContext().getResources().updateConfiguration(config, null);
}

}

Использование в деятельности:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyApp.setLocaleFa(MainActivity.this);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
}

Я перешел на немецкий язык для запуска самого приложения.

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

мой код:

Configuration config ; // variable declaration in globally

// this part is given inside onCreate Method starting and before setContentView()

public void onCreate(Bundle icic) 
{
    super.onCreate(icic);
    config = new Configuration(getResources().getConfiguration());
    config.locale = Locale.GERMAN ;
    getResources().updateConfiguration(config,getResources().getDisplayMetrics());

    setContentView(R.layout.newdesign);
}

Я знаю, что уже поздно отвечать, но я нашел эту статью здесь. Что очень хорошо объясняет весь процесс и дает вам хорошо структурированный код.

Класс Locale Helper:

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;

/**
 * This class is used to change your application locale and persist this change for the next time
 * that your app is going to be used.
 * <p/>
 * You can also change the locale of your application on the fly by using the setLocale method.
 * <p/>
 * Created by gunhansancar on 07/10/15.
 */
public class LocaleHelper {

    private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";

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

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

    public static String getLanguage(Context context) {
        return getPersistedData(context, Locale.getDefault().getLanguage());
    }

    public static Context setLocale(Context context, String language) {
        persist(context, language);

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

        return updateResourcesLegacy(context, language);
    }

    private static String getPersistedData(Context context, String defaultLanguage) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
    }

    private static void persist(Context context, String language) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = preferences.edit();

        editor.putString(SELECTED_LANGUAGE, language);
        editor.apply();
    }

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

        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);

        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    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;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }

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

        return context;
    }
}

Вам нужно переопределить attachBaseContext и вызвать LocaleHelper.onAttach() для инициализации настроек локали в вашем приложении.

import android.app.Application;
import android.content.Context;

import com.gunhansancar.changelanguageexample.helper.LocaleHelper;

public class MainApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base, "en"));
    }
}

Все, что вам нужно сделать, это добавить

LocaleHelper.onCreate(this, "en");

везде, где вы хотите изменить локаль.

Время для обновления.

Прежде всего, устаревший список с API, в котором он устарел:

  • configuration.locale (API 17)
  • updateConfiguration(configuration, displaymetrics) (API 17)

То, на что не ответил ни один вопрос в последнее время, - использование нового метода.

createConfigurationContext - это новый метод для updateConfiguration.

Некоторые использовали его как этот:

Configuration overrideConfiguration = ctx.getResources().getConfiguration();
Locale locale = new Locale("en_US");
overrideConfiguration.setLocale(locale);
createConfigurationContext(overrideConfiguration);

... но это не работает. Зачем? Метод возвращает контекст, который затем используется для обработки переводов Strings.xml и других локализованных ресурсов (изображений, макетов и т. Д.).

Правильное использование так:

Configuration overrideConfiguration = ctx.getResources().getConfiguration();
Locale locale = new Locale("en_US");
overrideConfiguration.setLocale(locale);
//the configuration can be used for other stuff as well
Context context  = createConfigurationContext(overrideConfiguration);
Resources resources = context.getResources();

Если вы просто скопировали это в свою IDE, вы можете увидеть предупреждение о том, что API требует, чтобы вы настроили таргетинг на API 17 или выше. Это можно обойти, поместив его в метод и добавив аннотацию @TargetApi(17)

Но ждать. А как насчет старых API?

Вам нужно создать другой метод, используя updateConfiguration без аннотации TargetApi.

Resources res = YourApplication.getInstance().getResources();
// Change locale settings in the app.
DisplayMetrics dm = res.getDisplayMetrics();
android.content.res.Configuration conf = res.getConfiguration();
conf.locale = new Locale("th");
res.updateConfiguration(conf, dm);

Вам не нужно возвращать контекст здесь.

Теперь управлять ими может быть сложно. В API 17+ вам нужен созданный контекст (или ресурсы из созданного контекста), чтобы получить соответствующие ресурсы на основе локализации. Как вы справляетесь с этим?

Ну, вот как я это делаю:

/**
 * Full locale list: https://stackru.com/questions/7973023/what-is-the-list-of-supported-languages-locales-on-android
 * @param lang language code (e.g. en_US)
 * @return the context
 * PLEASE READ: This method can be changed for usage outside an Activity. Simply add a COntext to the arguments
 */
public Context setLanguage(String lang/*, Context c*/){
    Context c = AndroidLauncher.this;//remove if the context argument is passed. This is a utility line, can be removed totally by replacing calls to c with the activity (if argument Context isn't passed)
    int API = Build.VERSION.SDK_INT;
    if(API >= 17){
        return setLanguage17(lang, c);
    }else{
        return setLanguageLegacy(lang, c);
    }
}

/**
 * Set language for API 17
 * @param lang
 * @param c
 * @return
 */
@TargetApi(17)
public Context setLanguage17(String lang, Context c){
    Configuration overrideConfiguration = c.getResources().getConfiguration();
    Locale locale = new Locale(lang);
    Locale.setDefault(locale);
    overrideConfiguration.setLocale(locale);
    //the configuration can be used for other stuff as well
    Context context  = createConfigurationContext(overrideConfiguration);//"local variable is redundant" if the below line is uncommented, it is needed
    //Resources resources = context.getResources();//If you want to pass the resources instead of a Context, uncomment this line and put it somewhere useful
    return context;
}

public Context setLanguageLegacy(String lang, Context c){
    Resources res = c.getResources();
    // Change locale settings in the app.
    DisplayMetrics dm = res.getDisplayMetrics();//Utility line
    android.content.res.Configuration conf = res.getConfiguration();

    conf.locale = new Locale(lang);//setLocale requires API 17+ - just like createConfigurationContext
    Locale.setDefault(conf.locale);
    res.updateConfiguration(conf, dm);

    //Using this method you don't need to modify the Context itself. Setting it at the start of the app is enough. As you
    //target both API's though, you want to return the context as you have no clue what is called. Now you can use the Context
    //supplied for both things
    return c;
}

Этот код работает, имея один метод, который выполняет вызовы соответствующего метода, в зависимости от того, какой API. Это то, что я сделал с множеством различных устаревших вызовов (в том числе Html.fromHtml). У вас есть один метод, который принимает необходимые аргументы, который затем разделяет его на один из двух (или трех или более) методов и возвращает соответствующий результат на основе уровня API. Он гибкий, так как вам не нужно проверять несколько раз, метод "entry" сделает это за вас. Метод входа здесь setLanguage

ПОЖАЛУЙСТА, ПРОЧИТАЙТЕ ЭТО ПЕРЕД ИСПОЛЬЗОВАНИЕМ ЭТОГО

Вам нужно использовать контекст, возвращаемый при получении ресурсов. Зачем? Я видел другие ответы, которые используют createConfigurationContext и не используют возвращаемый контекст. Чтобы заставить его работать так, необходимо вызвать updateConfiguration. Что не рекомендуется. Используйте контекст, возвращаемый методом, чтобы получить ресурсы.

Пример использования:

Конструктор или где-то подобное:

ctx = getLanguage(lang);//lang is loaded or generated. How you get the String lang is not something this answer handles (nor will handle in the future)

И затем, где бы вы ни хотели получить ресурсы, вы делаете:

String fromResources = ctx.getString(R.string.helloworld);

Использование любого другого контекста (теоретически) сломает это.

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


И, наконец, использовать recreate() на активность, чтобы обновить содержимое. Ярлык, чтобы не создавать намерение обновить.

Ответ Маюри правильный, но он будет работать только на Api 33 или выше. Вот пошаговое обратно совместимое решение:

Шаг 1: создайте locales_config.xml в папке res/xml.

      //res/xml/locales_config.xml
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Add your required languages -->
    <locale android:name="hi" />
    <locale android:name="en" />
</locale-config>

Шаг 2. Добавьте localeConfig в манифест в разделе «Приложение».

      <manifest>
<application
    android:localeConfig="@xml/locales_config">
</application>

Шаг 3: Добавьте эту службу в манифест

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

Шаг 4: укажите те же языки, используя свойство resConfigs в файле build.gradle вашего приложения на уровне модуля:

        android {
  defaultConfig {
      ...
      resConfigs "hi","en"
  }
  }

(требуется appCompat версии 1.6.0 или выше)

      implementation 'androidx.appcompat:appcompat:1.6.0'

Шаг 5. Теперь вы можете использовать приведенный ниже код для изменения языка приложения (проверено на Android 9,10,12 и 13)

        LocaleListCompat appLocale = LocaleListCompat.forLanguageTags("hi"); //Give user selected language code 
  AppCompatDelegate.setApplicationLocales(appLocale);

Для Android 7.0 Nougat (и ниже) следуйте этой статье:

Изменить язык программно в Android

Старый ответ
Это включает поддержку RTL/LTR:

public static void changeLocale(Context context, Locale locale) {
    Configuration conf = context.getResources().getConfiguration();
    conf.locale = locale;
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
       conf.setLayoutDirection(conf.locale);
    }

    context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());
}

Если ты пишешь

android:configChanges="locale"

В каждом действии нет необходимости устанавливать его каждый раз, когда вы вводите Activity,

Вы можете попросить пользователя выбрать язык на первом экране и сохранить его в SharedPreferences

      SharedPreferences.Editor editor = getSharedPreferences("uinfo", MODE_PRIVATE).edit();
editor.putString("lang", "si");
editor.apply();
    
recreate();

Тогда вы можете принять это в каждом Activityв вашем приложении. Здесь я установил английский и сингальский языки.

      @Override
protected void attachBaseContext(Context base) {
    SharedPreferences prefs = base.getSharedPreferences("uinfo", MODE_PRIVATE);
    String restoredText = prefs.getString("lang", "No name defined");

    if (restoredText.equals("si")){
        super.attachBaseContext(LocaleHelper.localeUpdateResources(base, "si"));
    }else{
        super.attachBaseContext(LocaleHelper.localeUpdateResources(base, "en"));
    }
}

А это твоя localUpdateResourcesметод. Поместите это в LocalHelper класс

      public class LocaleHelper {
    public static Context localeUpdateResources(Context context, String languageCode) {

        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;

    }
}

Resources.updateConfiguration() устарел, и я решил эту проблему, не создавая никаких настраиваемых ContextWrapper.

Сначала я создал функцию расширения

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

Тогда в деятельности attachBaseContext метод, просто заменив контекст новым.

      override fun attachBaseContext(newBase: Context) {
  super.attachBaseContext(ContextWrapper(newBase.setAppLocale("bn")))
}

Единственное решение, которое полностью работает для меня, - это сочетание кода Алексея Волового с механизмом перезапуска приложения:

void restartApplication() {
    Intent i = new Intent(MainTabActivity.context, MagicAppRestart.class);
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    MainTabActivity.context.startActivity(i);
}


/** This activity shows nothing; instead, it restarts the android process */
public class MagicAppRestart extends Activity {
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        finish();
    }

    protected void onResume() {
        super.onResume();
        startActivityForResult(new Intent(this, MainTabActivity.class), 0);         
    }
}

Я столкнулся с той же проблемой. На GitHub я обнаружил библиотеку Android-LocalizationActivity.

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

LocalizationActivity расширяет AppCompatActivity, поэтому вы также можете использовать его при использовании фрагментов.

public class MainActivity extends LocalizationActivity implements View.OnClickListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);

        findViewById(R.id.btn_th).setOnClickListener(this);
        findViewById(R.id.btn_en).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.btn_en) {
            setLanguage("en");
        } else if (id == R.id.btn_th) {
            setLanguage("th");
        }
    }
}

Поддержка языковых настроек для каждого приложения была только что добавлена ​​в API 33 (Android 13, Tiramisu в настоящее время находится в предварительной версии для разработчиков).

Чтобы изменить локаль приложения, просто вызовите setApplicationLocales из LocaleManager :

      // Set app locale to pt-BR (Portuguese, Brazil)
getSystemService(LocaleManager::class.java)
    .applicationLocales = LocaleList(Locale.forLanguageTag("pt-BR"))

Подробнее см. на странице https://developer.android.com/about/versions/13/features/app-languages#api-impl .

Для поддержки арабского /RTL

  1. Вы должны обновить свои языковые настройки с помощью - attachBaseContext()
  2. Для версии Android N и выше вы должны использовать createConfigurationContext() и updateConfiguration() - иначе макет RTL не работает должным образом.

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

    public Context updateBaseContextLocale(Context context) {
        String language = SharedPreference.getInstance().getValue(context, "lan");//it return "en", "ar" like this
        if (language == null || language.isEmpty()) {
            //when first time enter into app (get the device language and set it
            language = Locale.getDefault().getLanguage();
            if (language.equals("ar")) {
                SharedPreference.getInstance().save(mContext, "lan", "ar");
            }
        }
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            updateResourcesLocale(context, locale);
            return  updateResourcesLocaleLegacy(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;
    }

Ни одно из перечисленных здесь решений мне не помогло.

Язык не переключался на android >= 7.0, если AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)

Этот LocaleUtils отлично работает:https://gist.github.com/GigigoGreenLabs/7d555c762ba2d3a810fe

LocaleUtils

public class LocaleUtils {

public static final String LAN_SPANISH      = "es";
public static final String LAN_PORTUGUESE   = "pt";
public static final String LAN_ENGLISH      = "en";

private static Locale sLocale;

public static void setLocale(Locale locale) {
    sLocale = locale;
    if(sLocale != null) {
        Locale.setDefault(sLocale);
    }
}

public static void updateConfig(ContextThemeWrapper wrapper) {
    if(sLocale != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        Configuration configuration = new Configuration();
        configuration.setLocale(sLocale);
        wrapper.applyOverrideConfiguration(configuration);
    }
}

public static void updateConfig(Application app, Configuration configuration) {
    if(sLocale != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
        //Wrapping the configuration to avoid Activity endless loop
        Configuration config = new Configuration(configuration);
        config.locale = sLocale;
        Resources res = app.getBaseContext().getResources();
        res.updateConfiguration(config, res.getDisplayMetrics());
    }
}
}

Добавил этот код в приложение

public class App extends Application {
public void onCreate(){
    super.onCreate();

    LocaleUtils.setLocale(new Locale("iw"));
    LocaleUtils.updateConfig(this, getBaseContext().getResources().getConfiguration());
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    LocaleUtils.updateConfig(this, newConfig);
}
}

Код в действии

public class BaseActivity extends AppCompatActivity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}
Locale locale = new Locale("en");
Locale.setDefault(locale);

Configuration config = context.getResources().getConfiguration();
config.setLocale(locale);
context.createConfigurationContext(config);

Важное обновление:

context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());

Обратите внимание, что в SDK >= 21 вам необходимо вызвать 'Resources.updateConfiguration ()', иначе ресурсы не будут обновлены.

Эта функция официально запущена Google для Android 13 (а также имеет обратную поддержку). Теперь Android позволяет выбирать язык для каждого приложения.

Официальная документация здесь - https://developer.android.com/guide/topics/resources/app-languages

Чтобы установить предпочтительный язык пользователя, вы должны попросить пользователя выбрать языковой стандарт в средстве выбора языка, а затем установить это значение в системе:

      // 1. Inside an activity, in-app language picker gets an input locale "xx-YY"
// 2. App calls the API to set its locale
mContext.getSystemService(LocaleManager.class
    ).setApplicationLocales(newLocaleList(Locale.forLanguageTag("xx-YY")));
// 3. The system updates the locale and restarts the app, including any configuration updates
// 4. The app is now displayed in "xx-YY" language

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

      
// 1. App calls the API to get the preferred locale
LocaleList currentAppLocales =
    mContext.getSystemService(LocaleManager.class).getApplicationLocales();
// 2. App uses the returned LocaleList to display languages to the user

Сначала создайте multi string.xml для разных языков; затем использовать этот блок кода в onCreate() метод:

super.onCreate(savedInstanceState);
String languageToLoad  = "fr"; // change your language here
Locale locale = new Locale(languageToLoad); 
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config, 
  getBaseContext().getResources().getDisplayMetrics());
this.setContentView(R.layout.main);

Вот код, который работает для меня:

public class  MainActivity extends AppCompatActivity {
public static String storeLang;

@Override
protected void onCreate(Bundle savedInstanceState) {

    SharedPreferences shp = PreferenceManager.getDefaultSharedPreferences(this);
    storeLang = shp.getString(getString(R.string.key_lang), "");

    // Create a new Locale object
    Locale locale = new Locale(storeLang);

    // Create a new configuration object
    Configuration config = new Configuration();
    // Set the locale of the new configuration
    config.locale = locale;
    // Update the configuration of the Accplication context
    getResources().updateConfiguration(
            config,
            getResources().getDisplayMetrics()
    );

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

Источник: здесь

Localeconfiguration должны быть установлены в каждом activity перед настройкой контента - this.setContentView(R.layout.main);

/*change language at Run-time*/
//use method like that:
//setLocale("en");
 public void setLocale(String lang) { 
  myLocale = new Locale(lang);         
  Resources res = getResources();         
  DisplayMetrics dm = res.getDisplayMetrics();         
  Configuration conf = res.getConfiguration();         
  conf.locale = myLocale;         
  res.updateConfiguration(conf, dm);         
  Intent refresh = new Intent(this, AndroidLocalize.class);         
  startActivity(refresh); 
 }

Обратите внимание, что это решение с использованием updateConfiguration больше не будет работать с выпуском Android M через несколько недель. Новый способ сделать это теперь использует applyOverrideConfiguration метод из ContextThemeWrapper см. API документ

Вы можете найти мое полное решение здесь, так как я сам столкнулся с проблемой: /questions/3640292/force-locale-dlya-aromata-android-s-pomoschyu-resconfig/3640305#3640305

Ответ Алексея Волового работает только для меня, если он используется в методе действия onCreate.

Ответ, который работает во всех методах, находится в другом потоке

Изменить язык программно в Android

Вот адаптация кода



    Resources standardResources = getBaseContext().getResources();

    AssetManager assets = standardResources.getAssets();

    DisplayMetrics metrics = standardResources.getDisplayMetrics();

    Configuration config = new Configuration(standardResources.getConfiguration());

    config.locale = new Locale(languageToLoad);

    Resources defaultResources = new Resources(assets, metrics, config);

Надеюсь, что это поможет.

Я наконец понял, как настроить его для работы на обеих =N версиях Android.

Расширьте AppCompatActivity своим собственным абстрактным классом, например:

abstract class MLAppCompatActivity : AppCompatActivity() {
  override fun attachBaseContext(newBase: Context?) {
    super.attachBaseContext(LocaleHelper.wrap(newBase))
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        LocaleHelper.wrap(this)
    }
  }
}

attachBaseContext вызывается в версиях Android >=N, и в этом случае активность будет использовать правильный контекст. В Android

class TermsActivity : MLAppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_terms)
  }
}

И, наконец, LocaleHelper выглядит так:

import android.annotation.TargetApi;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;

import com.at_zone.constants.SharedPreferencesKeys;

import java.util.Locale;

public class LocaleHelper extends ContextWrapper {

    public LocaleHelper(Context base) {
        super(base);
    }

    public static Context wrap(Context context) {
        SharedPreferences sharedPreferences = context.getSharedPreferences(
                SharedPreferencesKeys.SHARED_PREFERENCES, Context.MODE_PRIVATE
        );
        String language = sharedPreferences.getString(SharedPreferencesKeys.CURRENT_LANGUAGE, "default");
        if (!language.equals("default")) {
            Configuration config = context.getResources().getConfiguration();
            if (!language.equals("")) {
                Locale locale = new Locale(language);
                Locale.setDefault(locale);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    setSystemLocale(config, locale);
                } else {
                    setSystemLocaleLegacy(context, config, locale);
                }
                config.setLayoutDirection(locale);
                context = context.createConfigurationContext(config);
            }
            return new LocaleHelper(context);
        }
        return context;
    }

    public static String getSystemLanguage(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return getSystemLocale(context).getLanguage().toLowerCase();
        } else {
            return getSystemLocaleLegacy(context).getLanguage().toLowerCase();
        }
    }

    public static Locale getSystemLocaleLegacy(Context context) {
        Configuration config = context.getResources().getConfiguration();
        return config.locale;
    }

    @TargetApi(Build.VERSION_CODES.N)
    public static Locale getSystemLocale(Context context) {
        return context.getResources().getConfiguration().getLocales().get(0);
    }

    public static void setSystemLocaleLegacy(Context context, Configuration config, Locale locale) {
        config.locale = locale;
        Resources res = context.getResources();
        DisplayMetrics dm = res.getDisplayMetrics();
        res.updateConfiguration(config, dm);
    }

    @TargetApi(Build.VERSION_CODES.N)
    public static void setSystemLocale(Configuration config, Locale locale) {
        config.setLocale(locale);
    }

}

/questions/40369137/izmenit-yazyik-prilozheniya-programmno-v-android/40369171#40369171

Это нормально, но не разделяйте updateResources на разные версии, просто используйте решение ниже (kotlin). Ключ находится в "Конфигурации (context.resources.configuration)", он делает глубокую копию.

100% решение для API 21+. На нижние не тестировал, но должно работать.

private fun updateResources(context: Context, language: String): Context {
    return Configuration(context.resources.configuration).run {
        Locale.setDefault(Locale(language).also { locale ->
            setLocale(locale)
        }).let {
            context.createConfigurationContext(this)
        }
    }
}
Другие вопросы по тегам