Установить языковой стандарт программно
мое приложение поддерживает 3 (скоро 4) языках. Поскольку несколько локалей очень похожи, я хотел бы дать пользователю возможность изменить локаль в моем приложении, например, итальянский человек может предпочесть испанский язык английскому.
есть ли способ для пользователя, чтобы выбрать один из языков, которые доступны для приложения, а затем изменить то, что используется локаль? Я не вижу проблемы в том, чтобы установить локаль для каждого действия, так как это простая задача для выполнения в базе класс.
9 ответов:
для людей, которые все еще ищут этот ответ, так как
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); } }не забывайте, что если вы измените язык с выполняющуюся операцию, вам нужно будет перезапустить его, чтобы изменения вступили в силу.
редактировать 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());
@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. Лучше использовать этот подход.
у меня была проблема с установкой локали программно с устройствами это имеет Android OS 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/delight-im/Android-Languages
- устаревшие (см. вопросы)
- при выборе языка в пользовательском интерфейсе он всегда показывает все языки (жестко закодированные в библиотеке), а не только те, где существуют переводы
- https://github.com/akexorcist/Android-LocalizationActivity
- кажется довольно сложным и может использовать подобный подход, как показано ниже
кроме того, я думаю, что написание библиотеки не может быть хорошим/простым способом решить эту проблему, потому что не очень много нужно сделать, и то, что нужно сделать, скорее меняет существующий код, чем использует что-то полностью развязанное. Поэтому я составил следующие инструкции, которые должны быть полными.
мое решение в основном основано наhttps://github.com/gunhansancar/ChangeLanguageExample (как уже связано с localhost). Это лучший код,который я нашел, чтобы сориентироваться. Некоторые замечания:
- при необходимости он предоставляет различные реализации для изменения локали для Android N (и выше) и ниже
- он использует метод
updateViews()в каждом действии вручную обновить все строки после изменения локали (используя обычныйgetString(id)) который не является необходимым в подходе, показанном ниже- он поддерживает только языки и не полные локали (которые также включают регион (страну) и коды вариантов)
я немного изменил его, отделив часть, которая сохраняет выбранную локаль (как можно было бы сделать это отдельно, как предлагается ниже).
решение
решение состоит из следующих двух шагов:
- постоянно изменять языковой стандарт, который будет использоваться приложением
- сделать приложение использовать пользовательский набор языковых стандартов, без перезапуск
Шаг 1: измените локаль
использовать класс
LocaleHelperна основе gunhansancar по LocaleHelper:
- добавить
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- обнаружьте изменение локали и воссоздайте действие, чтобы обновить его строки
примечания к этому решению
при повторном создании действия не обновляется заголовок панели действий (как уже было замечено здесь:https://github.com/gunhansancar/ChangeLanguageExample/issues/1).
- это может быть достигнуто просто имея
setTitle(R.string.mytitle)наonCreate()метод каждого вида деятельности.это позволяет пользователю выбрать системную локаль по умолчанию, а также локаль по умолчанию приложения (который может быть назван, в данном случае "английский").
только языковые коды, без региона (страны) и варианта коды (например,
fr-rCA) поддерживаются до сих пор. Для поддержки полной спецификации локали, парсер похож на то, что в Android-библиотека языков можно использовать (который поддерживает регион, но не коды вариантов).
- если кто-то находит или написал хороший парсер, добавьте комментарий, чтобы я мог включить его в решение.
добавить вспомогательный класс с помощью следующего метода:
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); ... }
просто и легко
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 " - код страны.
/** * 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 } }
поместите этот код в свою активность
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(); }
Comments