【问题标题】:Set Locale programmatically以编程方式设置语言环境
【发布时间】:2011-06-26 12:40:13
【问题描述】:

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

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

【问题讨论】:

  • 如果您以后需要一种方法来恢复默认语言环境,或者如果您需要包含语言列表的语言首选项,并且如果您想更方便地更改语言环境,这可能会有所帮助:@ 987654321@

标签: android locale user-defined


【解决方案1】:

希望这个帮助(在 onResume 中):

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

【讨论】:

  • 所以这必须为每个活动设置?
  • 1.必须使用 getBaseContext() 还是应该更好地使用应用程序上下文? 2.这个代码应该在每个活动中调用吗?谢谢。
  • 我将此代码放入我的启动器 Activity 的 onCreate() 中(其他任何地方都没有),并惊喜地发现该语言环境适用于整个应用程序。这是针对 4.3 的应用程序,minSDK 为 14 (ICS)。
  • 无需创建新的配置对象。您可以使用当前配置并对其进行更新:getResources().getConfiguration()
  • 不要使用 new Configuration();,它会改变 textAppearance、fontSize
【解决方案2】:

对于仍在寻找此答案的人,由于 API 24 已弃用 configuration.locale,您现在可以使用:

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 / 其他 Activity),以便您在每次创建 Activity 时更新上下文区域设置:

@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 日编辑

您可能在 Android 6 和 7 中遇到问题,这是由于处理夜间模式时 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);
} 

【讨论】:

  • 这适用于活动,但有没有办法更新应用程序上下文?
  • 在将androidx.appcompat:appcompat: 版本从1.0.2 更改为1.1.0 后无法在android 7 上运行,但在android 9 上运行。
  • 我和1.1.0 androidx 遇到同样的问题
  • 对我来说同样的问题。在我更改为 androidx.appcompat:appcompat:1.1.0' lib
  • appcompat:1.1.0 的问题可以用appcompat:1.2.0-alpha02 和代码Set&lt;Locale&gt; set = new LinkedHashSet&lt;&gt;(); // bring the target locale to the front of the list set.add(locale); LocaleList all = LocaleList.getDefault(); for (int i = 0; i &lt; all.size(); i++) { // append other locales supported by the user set.add(all.get(i)); } Locale[] locales = set.toArray(new Locale[0]); configuration.setLocales(new LocaleList(locales));@TargetApi(Build.VERSION_CODES.N) updateResourcesLocale() 内修复
【解决方案3】:

简单易行

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

【讨论】:

  • 正如我的帖子中所述,conf.locale=locale; 已被弃用,并且也是 updateConfiguration
  • 非常简单,不太复杂:)
【解决方案4】:
@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 日。 最好使用 this approach

【讨论】:

    【解决方案5】:

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

    【讨论】:

    • 这是最简单有效的解决方案!这应该是公认的答案。
    • 此代码在 7 以上的 android 上非常有效,但在 N 以下的版本中不起作用。你有什么解决办法吗?
    • 不确定,因为它对我有用。你想把你的实现发给我,让我看看吗?
    • 在 Android N 下的版本中不起作用,因为 resources.updateConfiguration 必须在 onCreate() 而不是 attachBaseContext() 中调用
    • @Chandler 是对的。对于 Android 6-,调用父/基本活动的onCreate 中的updateBaseContextLocale 方法。
    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

      由于当前解决此问题的方法没有完整的答案,因此我尝试提供完整解决方案的说明。如有遗漏或可以做得更好,请发表评论。

      一般信息

      首先,存在一些想要解决问题的库,但它们似乎都已过时或缺少某些功能:

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

      我的解决方案主要基于https://github.com/gunhansancar/ChangeLanguageExample(已由localhost 链接)。这是我发现的最好的代码。一些备注:

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

      我对其进行了一些更改,将保留所选语言环境的部分解耦(因为有人可能希望单独执行此操作,如下所示)。

      解决方案

      解决方案包括以下两个步骤:

      • 永久更改应用使用的语言环境
      • 让应用使用自定义区域设置,无需重新启动

      第 1 步:更改语言环境

      使用类LocaleHelper,基于gunhansancar's 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,以下列方式列出所有具有可用翻译的语言环境 (list of locale codes):

      <!-- 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) 来实现。
      • 它允许用户选择系统默认语言环境,以及应用程序的默认语言环境(可以命名,在本例中为“英语”)。

      • 目前仅支持语言代码,不支持地区(国家)和变体代码(如fr-rCA)。要支持完整的语言环境规范,可以使用类似于Android-Languages library 中的解析器(支持区域但不支持变体代码)。

        • 如果有人发现或编写了一个好的解析器,请添加评论,以便我可以将其包含在解决方案中。

      【讨论】:

      • 优秀但噩梦之王
      • 见鬼,我的应用程序已经太复杂了,这种方法将来维护起来将是一场噩梦。
      • @Josh 你能再解释一下吗?实际上,您使用的每个 Activity 基类只需添加几行代码。我看到可能无法对所有活动使用相同的基类,但更大的项目应该能够与一些活动相处。面向方面的编程在这里可能会有所帮助,但组合(将代码从 attachBaseContext(Context base)onResume() 移动到单独的类)可以解决问题。然后你所要做的就是在每个活动基类中声明一个对象并委托这两个调用。
      • 如果用户改变了locale,那么之前所有活动页面的locale是否也可以改变?
      • 这是这个问题的最佳答案。谢谢兄弟,可以了
      【解决方案8】:
       /**
       * 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
          }
      }
      

      【讨论】:

        【解决方案9】:

        将此代码放入您的活动中

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

        【讨论】:

          【解决方案10】:

          适用于 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。

          致谢:雅罗斯拉夫·别列赞斯基

          【讨论】:

            【解决方案11】:

            有一个超级简单的方法。

            在 BaseActivity、Activity 或 Fragment 中覆盖 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)
            }
            

            【讨论】:

              【解决方案12】:

              对于那些尝试了一切但没有成功的人。请检查如果您将darkmode 设置为AppCompatDelegate.setDefaultNightMode 并且系统不黑,那么Configuration.setLocaleAndorid 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)
              }
              

              【讨论】:

                【解决方案13】:

                我发现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);
                }
                

                【讨论】:

                • 你是一个救生员兄弟
                【解决方案14】:

                从 2020 年开始,语言管理变得简单!您所要做的就是:

                1. 致电Activity.applyOverrideConfiguration
                2. 并致电Locale.setDefault

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

                请注意 app-bundles,Google 会在使用 app-bundles 时自动按语言资源拆分您的 APK。查看新 API 和解决方法here


                我创建了一个帮助类来帮助你。在我的实现中,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);
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  在 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)
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2012-12-31
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-10-03
                    • 2012-09-14
                    • 2011-06-29
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-09-09
                    相关资源
                    最近更新 更多