以编程方式设置区域设置

我的应用程序支持3(即将4)语言。 由于几个语言环境非常相似,我想让用户select在我的应用程序中更改语言环境,例如,意大利人可能更喜欢西class牙语而不喜欢英语。

有没有一种方法让用户select可用于应用程序的语言环境,然后更改使用的语言环境? 我不认为这是为每个Activity设置语言环境的问题,因为这是一个在基类中执行的简单任务。

对于仍然在寻找这个答案的人来说,由于configuration.locale已被弃用,现在可以使用API​​ 24:

 configuration.setLocale(locale); 

考虑到这种方法的minSkdVersion是API 17。

API 17之前的完整示例代码:

 Resources resources = getResources(); Configuration configuration = resources.getConfiguration(); DisplayMetrics displayMetrics = resources.getDisplayMetrics(); configuration.setLocale(locale); resources.updateConfiguration(configuration,displayMetrics); 

编辑

从API 25 updateConfiguration(configuration,displayMetrics)已被弃用,所以您应该使用createConfigurationContext(configuration) , 这里的文档

API 17或更高版本的完整示例代码:

 Resources resources = getResources(); Configuration configuration = resources.getConfiguration(); configuration.setLocale(locale); getApplicationContext().createConfigurationContext(configuration); 

编辑2

完整的示例代码来处理这两种情况:

 @SuppressWarnings("deprecation") private void setLocale(Locale locale){ 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); getApplicationContext().createConfigurationContext(configuration); } else{ configuration.locale=locale; resources.updateConfiguration(configuration,displayMetrics); } } 

希望这个帮助(在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 2017年8月22日。更好地使用这种方法

我在使用Android OS N或更高版本的设备编程设置区域设置时遇到了问题。 对我来说,解决scheme是在我的基本活动中编写这个代码:

(如果你没有基本的活动,那么你应该在所有的活动中做出这些改变)

 @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方法中设置此上下文。

简单和容易

 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”是国家代码。

用下面的方法添加一个辅助类:

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

由于目前解决这个问题的方法没有完整的答案,所以我试图给出一个完整的解决scheme的说明。 请留下意见,如有遗漏或可以做得更好。

一般信息

首先,有一些图书馆想要解决这个问题,但都显得过时或缺less一些特征:

此外,我认为编写一个库可能不是一个很好的/简单的方法来解决这个问题,因为没有太多的工作要做,而是要改变现有的代码,而不是使用完全解耦的东西。 因此,我编写了下列应该完整的说明。

我的解决scheme主要基于https://github.com/gunhansancar/ChangeLanguageExample (已经由本地主机链接)。 这是我find的最好的代码。 一些评论:

  • 根据需要,它提供了不同的实现来更改Android N(及更高版本)和更低版本的区域设置
  • 它在每个Activity中使用updateViews()方法来更改区域设置(使用通常的getString(id) )之后手动更新所有string,这在下面显示的方法中不是必需的
  • 它只支持语言和不完整的语言环境(也包括地区(国家)和变体代码)

我改变了一点,将持续所选语言环境的部分分开(正如下面build议的那样,可能需要单独执行)。

该解决scheme由以下两个步骤组成:

  • 永久更改应用程序使用的区域设置
  • 使应用程序使用自定义区域设置,而无需重新启动

步骤1:更改语言环境

根据gunhansancar的LocaleHelper使用LocaleHelper类 :

  • PreferenceFragment添加一个ListPreference和可用的语言(稍后添加语言时必须保留)
 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; } } 

像下面这样创build一个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); } } 

创build资源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您可以使用以下部分让用户select可用的语言:

 <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:

 <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 basebefo)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保持的区域设置
  • 检测语言环境的更改并重新创build“活动”以更新其string

注意这个解决scheme

  • 重新创build一个Activity不会更新ActionBar的标题(正如已经在这里观察到的: https : //github.com/gunhansancar/ChangeLanguageExample/issues/1 )。

    • 这可以通过简单地在每个活动的onCreate()方法中使用setTitle(R.string.mytitle)来实现。
  • 它可以让用户select系统默认的语言环境,以及应用程序的默认语言环境(可以命名,在这种情况下,“英语”)。

  • 目前仅支持语言代码,无地区(国家)和变体代码(如fr-rCA )。 为了支持完整的语言环境规范,可以使用类似于Android语言库中的语法分析器(其支持区域但不包含变体代码)。

    • 如果有人发现或编写了一个好的parsing器,请添加一个注释,以便将其包含在解决scheme中。

像下面添加新的值文件夹 在这里输入图像说明

russion的ru值,uzbek的uz值。 你必须设置你的语言。 在你的actvitiy类中添加这个代码

  String languageToLoad = "uz"; // your language Locale locale = new Locale(languageToLoad); Locale.setDefault(locale); Configuration config = new Configuration(); config.locale = locale; getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());