以编程方式设置区域设置
我的应用程序支持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一些特征:
- https://github.com/delight-im/Android-Languages
- 过时(见问题 )
- 在UI中select一种语言时,它总是显示所有语言(在图书馆中硬编码),而不仅仅是存在翻译的语言
- https://github.com/akexorcist/Android-LocalizationActivity
- 看起来相当复杂,可能会使用类似的方法,如下所示
此外,我认为编写一个库可能不是一个很好的/简单的方法来解决这个问题,因为没有太多的工作要做,而是要改变现有的代码,而不是使用完全解耦的东西。 因此,我编写了下列应该完整的说明。
我的解决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());