it-swarm-ru.tech

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

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

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

99
Roland

Для людей, которые все еще ищут этот ответ, поскольку 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)

66
Ricardo

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

Locale locale = new Locale("ru");
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
      getBaseContext().getResources().getDisplayMetrics());
169
Rubycon
@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 года Лучше использовать этот подход.

13
localhost

У меня была проблема с программной настройкой языкового стандарта для устройств С ОС 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.

13
CookieMonster

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

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

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

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

Мое решение в основном основано на 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();
        }
    }
}

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

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

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

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

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

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

    • Если кто-то найдет или написал хороший анализатор, добавьте комментарий, чтобы я мог включить его в решение.
9
user905686

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

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);
    ...
}
4
Hadid Graphics

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

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» - код страны. 

1
Makvin
 /**
 * 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
    }
}
1
Zbigniew Mazur

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

 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();
    }
0
Xurshid Raxmatov