以编程方式设置语言环境

我的应用程序支持3种(很快就是4种)语言。由于有几个地区非常相似,我想让用户在我的应用程序中选择更改地区,例如,意大利人可能更喜欢西班牙语而不是英语。

用户是否有办法在应用程序可用的区域设置中进行选择,然后更改所使用的区域设置?我不认为为每个 Activity 设置 locale 是一个问题,因为它是在基类中执行的一个简单任务。

179078 次浏览

希望这个能帮上忙(在简历中) :

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

编辑: 2022年10月21日

从 Android 13开始,您现在可以使用方法 setApplicationLocales(appLocale)AppCompatDelegate设置您的区域设置,并且它具有向后兼容性。

空气污染指数33或以上:

这个过程非常简单,你可以这样做:

val appLocale: LocaleListCompat =
LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)

在使用这个命令时,你不再需要像文档中提到的那样在活动中调用 recreate():

请注意,调用 setApplicationLocales ()将重新创建您的 Activity,除非您的应用程序自己处理区域设置更改。

空气污染指数低于33

如果您的目标是 空气污染指数低于33,那么您还需要在清单中添加一个服务,以便处理区域存储:

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

这只适用于 AndroidX.appcompat: appcompat: 1.6.0-alpha01的 AndroidX

您可以找到文档 给你

————古老的答案————

对于仍在寻找这个答案的人来说,由于 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 更改了区域设置,那么您将需要重新启动它以使更改生效。

编辑: 2018年5月11日

从@CookieMonster 的文章中可以看出,在更高的 API 版本中保持区域设置的变化可能会有问题。如果是这样,将以下代码添加到 Base Activity (BaseActivity 扩展了 AppCompatActivity/其他活动) ,以便在每个活动创建时更新上下文语言环境:

@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_MR1)
private Context updateResourcesLocale(Context context, Locale locale) {
Configuration configuration = new 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;
}

如果使用这种方法,请不要忘记在使用 setLocale(locale)设置区域设置时将语言保存为 SharedPreferences

编辑2020年4月7日

您可能在 Android6和 Android7中遇到问题,这是由于处理夜间模式时 androidx 库中的问题造成的。为此,您还需要在基本活动中覆盖 applyOverrideConfiguration,并在创建新的区域设置时更新配置的区域设置。

示例代码:

@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
// update overrideConfiguration with your locale
setLocale(overrideConfiguration) // you will need to implement this
}
super.applyOverrideConfiguration(overrideConfiguration);
}

简单明了

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

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

只需使用这个 helper 方法来强制使用特定的 locale。

2017年8月22日。 最好用 这种方法

我在用设备以编程方式设置语言环境时遇到了问题 有 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方法中设置此上下文。

使用以下方法添加一个助手类:

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.setLocale(locale);
activity.getResources().updateConfiguration(config,
activity.getResources().getDisplayMetrics());
}


}
}

并在你的创业活动中调用它,比如 MainActivity.java:

public void onCreate(Bundle savedInstanceState) {
...
LanguageHelper.setAppLocale("fa", this);
...
}

由于目前解决这个问题的方法没有完整的答案,我试图给出一个完整的解决方案的说明。请评论是否有东西丢失或可以做得更好。

一般资料

首先,有一些图书馆想要解决这个问题,但是它们看起来都过时了,或者缺少了一些特性:

此外,我认为编写一个库可能不是解决这个问题的好/容易的方法,因为要做的事情不多,而且必须做的是更改现有代码,而不是使用完全解耦的代码。 因此,我编写了以下应该完整的说明。

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

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

我对它进行了一些更改,将持久存储所选区域设置的部分解耦(正如下面建议的那样,可能需要单独进行解耦)。

解决方案

解决办法包括以下两个步骤:

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

步骤1: 更改区域设置

使用基于 Gunhansancar 的本地助手的类 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;
}
}

创建如下 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: 让应用程序使用自定义语言环境

现在将每个 Activity 设置为使用自定义区域设置集。实现这一点的最简单方法是使用以下代码(其中重要代码位于 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 以更新其字符串

关于这个解决方案的注释

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

    • 这可以通过在每个活动的 onCreate()方法中简单地有一个 setTitle(R.string.mytitle)来实现。
  • 它允许用户选择系统默认语言环境,以及应用程序的默认语言环境(在本例中可以命名为“ English”)。

  • 到目前为止,只支持语言代码,不支持区域(国家)和变体代码(如 fr-rCA)。为了支持完整的区域设置规范,可以使用类似于 Android 语言库中的解析器(它支持区域但不支持变体代码)。

    • 如果有人发现或编写了一个好的解析器,请添加注释,以便我可以将其包括在解决方案中。
 /**
* 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();
}

适用于 API16至 API28 只要把这个方法放在某个地方:

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;

使用以下方法在所有活动中插入此代码:

    @Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(localeUpdateResources(base, "<-- language code -->"));
}

或者在需要新上下文的片段、适配器等上调用 localeUpdateResources。

图片来源: 雅罗斯拉夫 · 别列赞斯基

有一个超级简单的方法。

在 BaseActivity 中,Activity 或片段重写 attachBaseContext

 override fun attachBaseContext(context: Context) {
super.attachBaseContext(context.changeLocale("tr"))
}

分机

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

对于那些尝试了一切但是不工作的人来说。请检查,如果你设置 darkmodeAppCompatDelegate.setDefaultNightMode和系统是不黑暗的,那么 Configuration.setLocale将不工作以上的 安卓7.0

在你的每个活动中添加这个代码来解决这个问题:

override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
if (overrideConfiguration != null) {
val uiMode = overrideConfiguration.uiMode
overrideConfiguration.setTo(baseContext.resources.configuration)
overrideConfiguration.uiMode = uiMode
}
super.applyOverrideConfiguration(overrideConfiguration)
}

我发现 androidx.appcompat:appcompat:1.1.0错误也可以通过简单地调用 applyOverrideConfiguration()中的 getResources()来修复

@Override public void
applyOverrideConfiguration(Configuration cfgOverride)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// add this to fix androidx.appcompat:appcompat 1.1.0 bug
// which happens on Android 6.x ~ 7.x
getResources();
}


super.applyOverrideConfiguration(cfgOverride);
}

到2020年,语言管理将变得更加容易! 你所要做的就是:

  1. 呼叫 Activity.applyOverrideConfiguration
  2. 呼叫 Locale.setDefault

您必须从 活动构造函数活动构造函数调用它们,因为您只能调用 applyOverrideConfiguration一次,而且系统很早就会调用它。

而且要小心应用程序包,当使用应用程序包时,Google 会自动按语言资源分割你的 APK。查看新的 API 和解决方案 给你


我创建了一个 helper 类来帮助您。在我的实现中,G.app是应用程序上下文。另外,我需要从应用程序上下文访问资源,所以我使用 Res类,这是可选的,但我也提供了它的代码。

用法

public BaseActivity(){
LanguageUtility.init(this);
}


public void changeLanguage(Local local){
// you must recreat your activity after you call this
LanguageUtillity.setDefaultLanguage(local, this);
}

源代码

public class LanguageUtility {


private static Configuration configuration;


public static void setDefaultLanguage(Locale locale, Context context) {
Locale.setDefault(locale);


context.getSharedPreferences("LocaleSettings", Context.MODE_PRIVATE)
.edit()
.putString("language", locale.getLanguage())
.putString("country", locale.getCountry())
.putString("variant", locale.getVariant())
.apply();


configuration = createConfiguration(context);
Res.updateContext();
}


/**
* Used to update your app context in case you cache it.
*/
public static Context createConfigurationContext(Context context) {
return context.createConfigurationContext(getConfiguration(context));
}


public static void init(Activity activity) {
activity.applyOverrideConfiguration(LanguageUtility.getConfiguration(G.app));
// you can't access sharedPrefferences from activity constructor
// with activity context, so I used the app context.
Locale.setDefault(getLocale(G.app));
}


@NotNull
private static Configuration getConfiguration(Context context) {
if (configuration == null) {
configuration = createConfiguration(context);
}
return configuration;
}


@NotNull
private static Configuration createConfiguration(Context context) {
Locale locale = getLocale(context);
Configuration configuration = new Configuration();
configuration.setLocale(locale);
LanguageUtility.configuration = configuration;
return configuration;
}


@NotNull
private static Locale getLocale(Context context) {
Locale aDefault = Locale.getDefault();
SharedPreferences preferences =
context.getSharedPreferences("LocaleSettings", Context.MODE_PRIVATE);
String language = preferences.getString("language", aDefault.getLanguage());
String country = preferences.getString("country", aDefault.getCountry());
String variant = preferences.getString("variant", aDefault.getVariant());
return new Locale(language, country, variant);
}
}

一个可选的 Res 类。

public class Res {


@SuppressLint("StaticFieldLeak")
public static Context appLocalContext = LanguageUtility.createConfigurationContext(G.app);


public static void updateContext() {
appLocalContext = LanguageUtility.createConfigurationContext(G.app);
}


public static String getString(@StringRes int id, Object... formatArgs) {
return appLocalContext.getResources().getString(id, formatArgs);
}


public static int getColor(@ColorRes int id) {
return G.app.getColor(id);
}
}

在 BaseActivity-> onCreate ()和 BaseFragment-> OnCreateView ()上调用此方法

测试 API 22,23,24,25,26,27,28,29... 最新版本

fun Context.updateLang() {
val resources = resources
val config = Configuration(resources.configuration)
config.setLocale(PreferenceManager(this).getAppLanguage()) // language from preference
val dm = resources.displayMetrics
createConfigurationContext(config)
resources.updateConfiguration(config, dm)
}

Android 13引入了 每个应用程序的语言首选项,极大地简化了处理应用内语言环境变化的过程。

免责声明: 在撰写本文时,Android 13目前在“开发者预览版”中,这里提到的部分内容是 alpha 版(因此,可能会有变化)。

从文档中可以看出,有几种新的方法来处理这些变化,为了兼容性,最好的方法似乎是通过 AppCompatDelegate:


val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)


(方法可从 androidx.appcompat:appcompat:1.6.0-alpha01获得)

这将负责将地区设置为 appLocale,并根据需要重新启动任何活动。 此外,正如在 < a href = “ https://developer.android.com/reference/androidx/appcompat/app/AppCompatGenerate # setApplicationLocales (androidx.core.os.LocaleListCompat)”rel = “ nofollow noReferrer”> setApplicationLocales docs 上提到的

在 API 级别33及以上,此 API 将自动处理存储

这意味着,为了让自定义应用程序内语言环境工作,需要做的仅仅是向用户提供一个语言选择器,并在执行选择时调用 setApplicationLocales一次。

那么 API < 33呢?

Developer.android.com 上的文档确实提到,开发人员可以要求将这个值自动存储在旧的 API 级别上

通过在他们的 AndroidManifest 中添加一个特殊的 meta 数据条目,类似于:

但是在编写本文时,并没有提到所需的实际元数据(也许在文档生成中出了问题)。

相反,它在 AppCompatGenerate在此发表的源代码中可见,它看起来如下所示

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


将以上内容添加到您的清单中应该可以使此解决方案适用于所有 API 级别

有一种新的方法让用户选择应用程序的默认语言,因为 Appcompat 1.6.0-alpha04和更高。假设你有一个按钮,当用户点击它时,它会将应用程序的语言改为 意大利菜:

binding.button.setOnClickListener {
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("it")
AppCompatDelegate.setApplicationLocales(appLocale)
}

此外,要添加对旧设备的支持(< API 级别32) ,请在 AndroidManifest.xml文件的 <application>标记中添加以下 service:

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

确保有最新的 AppCompat依赖项:


implementation 'androidx.appcompat:appcompat:1.6.0-alpha05'

参考资料: https://developer.android.com/about/versions/13/features/app-languages