Android N如何通过编程更改语言

66

我发现一个非常奇怪的错误,只在Android N设备上重现。

在我的应用程序中,有更改语言的可能性。这是更改它的代码。

 public void update(Locale locale) {

    Locale.setDefault(locale);

    Configuration configuration = res.getConfiguration();

    if (BuildUtils.isAtLeast24Api()) {
        LocaleList localeList = new LocaleList(locale);

        LocaleList.setDefault(localeList);
        configuration.setLocales(localeList);
        configuration.setLocale(locale);

    } else if (BuildUtils.isAtLeast17Api()){
        configuration.setLocale(locale);

    } else {
        configuration.locale = locale;
    }

    res.updateConfiguration(configuration, res.getDisplayMetrics());
}

这段代码在我的旅游活动中运行良好(使用recreate()调用),但在所有接下来的活动中,所有字符串资源都是错误的。屏幕旋转可以解决它。我该怎么处理这个问题?我应该为Android N更改语言环境,还是这只是系统错误?

P.S. 这是我发现的情况。在MainActivity的第一次启动(也就是在我的旅行之后)中,Locale.getDefault()是正确的,但是资源是错误的。但在其他活动中,它给我错误的Locale和来自此Locale的错误资源。在旋转屏幕(或者可能是某些其他配置更改)之后,Locale.getDefault()就是正确的。


6
我已向Android团队提交了一个漏洞报告,以下是他们的回复:https://code.google.com/p/android/issues/detail?id=225679 - Metwalli
非常感谢!这个答案真的帮了我很多。 - Kuva
10个回答

117

好的。最终我成功地找到了解决方案。

首先,您应该知道在25个API中,Resources.updateConfiguration(...)已被弃用。因此,您可以像这样进行操作:

1)您需要创建自己的ContextWrapper,该ContextWrapper将覆盖baseContext中的所有配置参数。例如,这是我的ContextWrapper,可以正确更改区域设置。请注意context.createConfigurationContext(configuration)方法。

public class ContextWrapper extends android.content.ContextWrapper {

    public ContextWrapper(Context base) {
        super(base);
    }

    public static ContextWrapper wrap(Context context, Locale newLocale) {
        Resources res = context.getResources();
        Configuration configuration = res.getConfiguration();

        if (BuildUtils.isAtLeast24Api()) {
            configuration.setLocale(newLocale);

            LocaleList localeList = new LocaleList(newLocale);
            LocaleList.setDefault(localeList);
            configuration.setLocales(localeList);

            context = context.createConfigurationContext(configuration);

        } else if (BuildUtils.isAtLeast17Api()) {
            configuration.setLocale(newLocale);
            context = context.createConfigurationContext(configuration);

        } else {
            configuration.locale = newLocale;
            res.updateConfiguration(configuration, res.getDisplayMetrics());
        }

        return new ContextWrapper(context);
    }
}

2) 这是在您的BaseActivity中应该执行的操作:

@Override
protected void attachBaseContext(Context newBase) {

    Locale newLocale;
    // .. create or get your new Locale object here.

    Context context = ContextWrapper.wrap(newBase, newLocale);
    super.attachBaseContext(context);
}

注意:

如果您想在应用程序的某个地方更改区域设置,请记得重新创建您的活动。您可以使用此解决方案覆盖任何配置。


4
在attacheBaseContext中,您如何获取持久化的本地化对象? - muilpp
6
有时候,我觉得最好还是压制这个废弃的方法。据我所知,使用这个被弃用的方法并不会引起问题,而且这样做会增加很多复杂性。 - CorayThan
5
在我的情况下,这在 Android N 设备上引起了很多问题。更改语言后,某些字符串资源可能不正确(来自系统主区域设置)。另外,由于我的应用程序支持阿拉伯语环境,所以我遇到了从左到右的问题。 - Kuva
5
第一次更改应用程序语言时一切都很好(从英语到西班牙语),但当我尝试将语言改回去(从西班牙语到英语)时就无法成功。语言保持更改后的状态(西班牙语)。只有当我关闭并重新启动应用程序时,语言才会正确更改为英语。有任何想法是为什么吗? - elementstyle
3
不,目前每次更改应用程序语言我都需要通过编程重新启动应用程序 :( - elementstyle
显示剩余19条评论

25

受多种代码启发(例如:我们的Stackoverflow团队(向大家喊话)),我已经制作出了一个更简单的版本。扩展 ContextWrapper 是不必要的。

首先,假设你有两个按钮用于两种语言,英语和高棉语。在按钮的onClick事件中将语言代码保存到 SharedPreferences 中,然后调用活动的 recreate() 方法。

示例:

@Override
public void onClick(View v) {
    switch(v.getId()) {
        case R.id.btn_lang_en:
            //save "en" to SharedPref here
            break;
        case R.id.btn_lang_kh:
            //save "kh" to SharedPref here
            break;

        default:
        break;
    }
    getActivity().recreate();
}

然后创建一个静态方法,返回ContextWrapper,可以在Utils类中实现(因为这就是我做的,哈哈)。

public static ContextWrapper changeLang(Context context, String lang_code){
    Locale sysLocale;

    Resources rs = context.getResources();
    Configuration config = rs.getConfiguration();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        sysLocale = config.getLocales().get(0);
    } else {
        sysLocale = config.locale;
    }
    if (!lang_code.equals("") && !sysLocale.getLanguage().equals(lang_code)) {
        Locale locale = new Locale(lang_code);
        Locale.setDefault(locale);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            config.setLocale(locale);
        } else {
            config.locale = locale;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            context = context.createConfigurationContext(config);
        } else {
            context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
        }
    }

    return new ContextWrapper(context);
}

最后,在所有活动attachBaseContext(Context newBase)方法中,从SharedPreferences加载语言代码。

@Override
protected void attachBaseContext(Context newBase) {
    String lang_code = "en"; //load it from SharedPref
    Context context = Utils.changeLang(newBase, lang_code);
    super.attachBaseContext(context);
}

福利:为了避免手汗淋漓地敲键盘,我创建了一个LangSupportBaseActivity类,它继承自Activity并使用那里的最后一段代码。而我让所有其他活动都扩展LangSupportBaseActivity

示例:

public class LangSupportBaseActivity extends Activity{
    ...blab blab blab so on and so forth lines of neccessary code

    @Override
    protected void attachBaseContext(Context newBase) {
        String lang_code = "en"; //load it from SharedPref
        Context context = Utils.changeLang(newBase, lang_code);
        super.attachBaseContext(context);
    }
}

public class HomeActivity extends LangSupportBaseActivity{
    ...blab blab blab
}

2
非常感谢您。我不得不绕过 if (!lang_code.equals("") && !sysLocale.getLanguage().equals(lang_code)) 检查,因为在更改后(例如用户在设置中更改),有时 sysLocale.getLanguage() 具有所需的语言值,但未明确设置区域设置会导致语言未更改。 - Stéphane
2
应该移除 if 部分中的 '&& !sysLocale.getLanguage().equals(lang_code)' 条件,因为它有时会给出错误的值。 - Ayush P Gupta
1
@thyzz,我完全同意你的说法,但不知道为什么在更改语言后,某些活动仍显示默认的英语语言。看起来系统有时会返回错误的区域设置。可能是操作系统的一个漏洞(MIUI)。当我在if语句中删除了这个条件时,它就可以工作了,或者该条件是否在if语句中执行。 - Ayush P Gupta
@AyushGupta 如果它可用,那就好了:D... 至少现在没问题,直到下一个 bug 出现,我们会修复更多的问题,干杯,兄弟。 - thyzz
@thyzz,你的回答帮了我很多。我几乎因为在我的应用程序中设置区域而发疯。再次感谢你的回答。 - Ayush P Gupta
显示剩余4条评论

17

自从Android 7.0+以来,我的应用程序的某些部分再也没有改变过它们的语言。即使使用了上面提出的新方法。更新应用程序和活动上下文均有所帮助。这里是Activity子类覆盖的Kotlin示例:

private fun setApplicationLanguage(newLanguage: String) {
    val activityRes = resources
    val activityConf = activityRes.configuration
    val newLocale = Locale(newLanguage)
    activityConf.setLocale(newLocale)
    activityRes.updateConfiguration(activityConf, activityRes.displayMetrics)

    val applicationRes = applicationContext.resources
    val applicationConf = applicationRes.configuration
    applicationConf.setLocale(newLocale)
    applicationRes.updateConfiguration(applicationConf,
            applicationRes.displayMetrics)
}

override fun attachBaseContext(newBase: Context?) {
    super.attachBaseContext(newBase)

    setApplicationLanguage("fa");
}

注意:updateConfiguration已被弃用,但是为每个Activity创建createConfigurationContext,在此过程中保留一些字符串未更改。


那是唯一对我有效的方法,谢谢。ContextWrapper方法只部分有效。在API 24和33上进行了测试。 - undefined

3

在Android应用程序中以编程方式更改语言环境相当麻烦。我花了很多时间寻找可在生产环境中正常工作的解决方案。

您需要在每个ActivityApplication类中覆盖上下文,否则您将会在ui中出现混合语言。

因此,以下是适用于API 29的解决方案:

从以下类别中派生您的MainApplication类:

abstract class LocalApplication : Application() {

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(
            base.toLangIfDiff(
                PreferenceManager
                    .getDefaultSharedPreferences(base)
                    .getString("langPref", "sys")!!
             )
        )
    }
}

此外,从每个Activity开始:
abstract class LocalActivity : AppCompatActivity() {

    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(            
            PreferenceManager
                .getDefaultSharedPreferences(base)
                    .getString("langPref", "sys")!!
        )
    }

    override fun applyOverrideConfiguration(overrideConfiguration: Configuration) {
        super.applyOverrideConfiguration(baseContext.resources.configuration)
    }
}

添加LocaleExt.kt文件,并具有以下扩展函数:
const val SYSTEM_LANG = "sys"
const val ZH_LANG = "zh"
const val SIMPLIFIED_CHINESE_SUFFIX = "rCN"


private fun Context.isAppLangDiff(prefLang: String): Boolean {
    val appConfig: Configuration = this.resources.configuration
    val sysConfig: Configuration = Resources.getSystem().configuration

    val appLang: String = appConfig.localeCompat.language
    val sysLang: String = sysConfig.localeCompat.language

    return if (SYSTEM_LANG == prefLang) {
        appLang != sysLang
    } else {
        appLang != prefLang
                || ZH_LANG == prefLang
    }
}

fun Context.toLangIfDiff(lang: String): Context =
    if (this.isAppLangDiff(lang)) {
        this.toLang(lang)
    } else {
        this
    }

@Suppress("DEPRECATION")
fun Context.toLang(toLang: String): Context {
    val config = Configuration()

    val toLocale = langToLocale(toLang)

    Locale.setDefault(toLocale)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        config.setLocale(toLocale)

        val localeList = LocaleList(toLocale)
        LocaleList.setDefault(localeList)
        config.setLocales(localeList)
    } else {
        config.locale = toLocale
    }

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        config.setLayoutDirection(toLocale)
        this.createConfigurationContext(config)
    } else {
        this.resources.updateConfiguration(config, this.resources.displayMetrics)
        this
    }
}

/**
 * @param toLang - two character representation of language, could be "sys" - which represents system's locale
 */
fun langToLocale(toLang: String): Locale =
    when {
        toLang == SYSTEM_LANG ->
            Resources.getSystem().configuration.localeCompat

        toLang.contains(ZH_LANG) -> when {
            toLang.contains(SIMPLIFIED_CHINESE_SUFFIX) ->
                Locale.SIMPLIFIED_CHINESE
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ->
                Locale(ZH_LANG, "Hant")
            else ->
                Locale.TRADITIONAL_CHINESE
        }

        else -> Locale(toLang)
    }

@Suppress("DEPRECATION")
private val Configuration.localeCompat: Locale
    get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        this.locales.get(0)
    } else {
        this.locale
    }

res/values/arrays.xml文件中添加你支持的语言数组:
<string-array name="lang_values" translatable="false">
    <item>sys</item> <!-- System default -->
    <item>ar</item>
    <item>de</item>
    <item>en</item>
    <item>es</item>
    <item>fa</item>
    ...
    <item>zh</item> <!-- Traditional Chinese -->
    <item>zh-rCN</item> <!-- Simplified Chinese -->
</string-array>

以下是要点:
  • 当您使用RTL语言环境(如阿拉伯语,波斯语等)时,请使用config.setLayoutDirection(toLocale);更改布局方向。
  • 代码中的"sys"是一个值,表示"继承系统默认语言"。
  • 这里的"langPref"是偏好设置的键,您可以在其中放置用户当前使用的语言。
  • 如果上下文已经使用所需的区域设置,则无需重新创建上下文。
  • 无需像此处发布的那样使用ContextWraper,只需将从createConfigurationContext返回的新上下文设置为baseContext即可。
  • 这非常重要!当您调用createConfigurationContext时,应传递完全从头开始创建的配置,并且仅设置了Locale属性。不应设置此配置的任何其他属性。因为如果我们为此配置设置其他某些属性(例如方向),则我们永久覆盖该属性,我们的上下文不再更改此方向属性,即使我们旋转屏幕也是如此。
  • 仅重新创建活动不足以在用户选择不同语言时完成操作,因为applicationContext将保留旧的语言环境,并可能提供意外的行为。因此,请监听偏好设置更改并重新启动整个应用程序任务:

fun Context.recreateTask() {
    this.packageManager
        .getLaunchIntentForPackage(context.packageName)
        ?.let { intent ->
            val restartIntent = Intent.makeRestartActivityTask(intent.component)
            this.startActivity(restartIntent)
            Runtime.getRuntime().exit(0)
         }
}

请问您能描述一下您的QA流程吗?测试了多久?在几部手机上测试过?在生产环境中测试了多长时间?另外,顺便提一下:1. LocalActivity 出现了错误,我猜您是指 LocalApplication 吧?!2. 我还会额外写一个要点,那就是工具栏标题需要手动设置。无论如何,谢谢。 - epic

2
上述答案让我找到了正确的方向,但还有几个问题:
  1. 在安卓7和9上,我可以愉快地更改为除应用程序默认语言以外的任何语言。当我更改回应用程序默认语言时,它显示了最后选择的语言 - 这并不奇怪,因为这已经覆盖了默认值(有趣的是,在安卓8上这不是问题!)。
  2. 对于从右到左书写的语言,它没有更新布局为从右到左。
为了解决第一个问题,我在应用程序启动时存储了默认语言环境。 注意:如果您的默认语言设置为“en”,则“enGB”或“enUS”的语言环境都需要与默认语言环境匹配(除非您为它们提供单独的本地化)。同样,在下面的示例中,如果用户手机的语言环境是arEG(阿拉伯语埃及),则defLanguage需要是“ar”,而不是“arEG”。
private Locale defLocale = Locale.getDefault();
private Locale locale = Locale.getDefault();
public static myApplication myApp;
public static Resources res;
private static String defLanguage = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry();
private static sLanguage = "en";
private static final Set<String> SUPPORTEDLANGUAGES = new HashSet<>(Arrays.asList(new String[]{"en", "ar", "arEG"})); 

@Override
protected void attachBaseContext(Context base) {
  if (myApp == null) myApp = this;
  if (base == null) super.attachBaseContext(this);
  else super.attachBaseContext(setLocale(base));
}

@Override
public void onCreate() {
  myApp = this;

  if (!SUPPORTEDLANGUAGES.contains(test)) {
    // The default locale (eg enUS) is not in the supported list - lets see if the language is
    if (SUPPORTEDLANGUAGES.contains(defLanguage.substring(0,2))) {
      defLanguage = defLanguage.substring(0,2);
    }
  }
}

private static void setLanguage(String sLang) {
  Configuration baseCfg = myApp.getBaseContext().getResources().getConfiguration();
  if ( sLang.length() > 2 ) {
    String s[] = sLang.split("_");
    myApp.locale = new Locale(s[0],s[1]);
    sLanguage = s[0] + s[1];
  }
  else {
    myApp.locale = new Locale(sLang);
    sLanguage = sLang;
  }
}

public static Context setLocale(Context ctx) {
  Locale.setDefault(myApp.locale);
  Resources tempRes = ctx.getResources();
  Configuration config = tempRes.getConfiguration();

  if (Build.VERSION.SDK_INT >= 24) {
    // If changing to the app default language, set locale to the default locale
    if (sLanguage.equals(myApp.defLanguage)) {
      config.setLocale(myApp.defLocale);
      // restored the default locale as well
      Locale.setDefault(myApp.defLocale);
    }
    else config.setLocale(myApp.locale);

    ctx = ctx.createConfigurationContext(config);

    // update the resources object to point to the current localisation
    res = ctx.getResources();
  } else {
    config.locale = myApp.locale;
    tempRes.updateConfiguration(config, tempRes.getDisplayMetrics());
  }

  return ctx;
}

为了解决RTL问题,我按照Fragments评论中的建议扩展了AppCompatActivity。答案
public class myCompatActivity extends AppCompatActivity {
  @Override
  protected void attachBaseContext(Context base) {
    super.attachBaseContext(myApplication.setLocale(base));
  }

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (Build.VERSION.SDK_INT >= 17) {
      getWindow().getDecorView().setLayoutDirection(myApplication.isRTL() ?
              View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
    }
  }
}

2
这是我的代码,它可以正常工作!如果有问题,请告诉我:
protected void attachBaseContext(Context newBase) {
    String lang = "en"; // your language or load from SharedPref
    Locale locale = new Locale(lang);
    Configuration config = new Configuration(newBase.getResources().getConfiguration());
    Locale.setDefault(locale);
    config.setLocale(locale);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        newBase = newBase.createConfigurationContext(config);
    } else {
        newBase.getResources().updateConfiguration(config, newBase.getResources().getDisplayMetrics());
    }
    super.attachBaseContext(newBase);
}

1
这对我有效,我正在使用androidx.appcompat:appcompat:1.2.0
 override fun attachBaseContext(newBase: Context?) {
            val sp = PreferenceManager.getDefaultSharedPreferences(newBase)
            val locale = when(sp.getString("app_language", "")) {
                "en" -> { Locale("en") }
                "hu" -> { Locale("hu") }
                else -> {
                    if (Build.VERSION.SDK_INT >= 24) {
                        Resources.getSystem().configuration.locales.get(0);
                    }
                    else {
                        Resources.getSystem().configuration.locale
                    }
                }
            }
            if(newBase != null) {
                Locale.setDefault(locale)
                newBase.resources.configuration.setLocale(locale)
                applyOverrideConfiguration(newBase.resources.configuration)
            }
            super.attachBaseContext(newBase)
        }

1

2020年9月更新:

针对最新的Androidx Appcombat稳定版本1.2.0,移除所有针对1.1.0的解决方法并添加以下内容。

package androidx.appcompat.app

import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.util.AttributeSet
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar

class BaseContextWrappingDelegate(private val superDelegate: 
AppCompatDelegate) : AppCompatDelegate() {

override fun getSupportActionBar() = superDelegate.supportActionBar

override fun setSupportActionBar(toolbar: Toolbar?) = superDelegate.setSupportActionBar(toolbar)

override fun getMenuInflater(): MenuInflater? = superDelegate.menuInflater

override fun onCreate(savedInstanceState: Bundle?) {
    superDelegate.onCreate(savedInstanceState)
    removeActivityDelegate(superDelegate)
    addActiveDelegate(this)
}

override fun onPostCreate(savedInstanceState: Bundle?) = superDelegate.onPostCreate(savedInstanceState)

override fun onConfigurationChanged(newConfig: Configuration?) = superDelegate.onConfigurationChanged(newConfig)

override fun onStart() = superDelegate.onStart()

override fun onStop() = superDelegate.onStop()

override fun onPostResume() = superDelegate.onPostResume()

override fun setTheme(themeResId: Int) = superDelegate.setTheme(themeResId)

override fun <T : View?> findViewById(id: Int) = superDelegate.findViewById<T>(id)

override fun setContentView(v: View?) = superDelegate.setContentView(v)

override fun setContentView(resId: Int) = superDelegate.setContentView(resId)

override fun setContentView(v: View?, lp: ViewGroup.LayoutParams?) = superDelegate.setContentView(v, lp)

override fun addContentView(v: View?, lp: ViewGroup.LayoutParams?) = superDelegate.addContentView(v, lp)

override fun attachBaseContext2(context: Context) = wrap(superDelegate.attachBaseContext2(super.attachBaseContext2(context)))

override fun setTitle(title: CharSequence?) = superDelegate.setTitle(title)

override fun invalidateOptionsMenu() = superDelegate.invalidateOptionsMenu()

override fun onDestroy() {
    superDelegate.onDestroy()
    removeActivityDelegate(this)
}

override fun getDrawerToggleDelegate() = superDelegate.drawerToggleDelegate

override fun requestWindowFeature(featureId: Int) = superDelegate.requestWindowFeature(featureId)

override fun hasWindowFeature(featureId: Int) = superDelegate.hasWindowFeature(featureId)

override fun startSupportActionMode(callback: ActionMode.Callback) = superDelegate.startSupportActionMode(callback)

override fun installViewFactory() = superDelegate.installViewFactory()

override fun createView(parent: View?, name: String?, context: Context, attrs: AttributeSet): View? = superDelegate.createView(parent, name, context, attrs)

override fun setHandleNativeActionModesEnabled(enabled: Boolean) {
    superDelegate.isHandleNativeActionModesEnabled = enabled
}

override fun isHandleNativeActionModesEnabled() = superDelegate.isHandleNativeActionModesEnabled

override fun onSaveInstanceState(outState: Bundle?) = superDelegate.onSaveInstanceState(outState)

override fun applyDayNight() = superDelegate.applyDayNight()

override fun setLocalNightMode(mode: Int) {
    superDelegate.localNightMode = mode
}

override fun getLocalNightMode() = superDelegate.localNightMode

private fun wrap(context: Context): Context {
    TODO("your wrapping implementation here")
}
}

将您的本地化逻辑添加到wrap函数中(您可以在上面接受的答案中添加ContextWrapper)。此类必须位于androidx.appcompat.app包中,因为唯一存在的AppCompatDelegate构造函数是包私有的。
然后,在您的基础活动类中,删除所有1.1.0的解决方法,只需添加此代码。
private var baseContextWrappingDelegate: AppCompatDelegate? = null

override fun getDelegate() = baseContextWrappingDelegate ?: 
BaseContextWrappingDelegate(super.getDelegate()).apply {
baseContextWrappingDelegate = this
}

配置更改可能会破坏语言环境更改。为了解决这个问题

override fun createConfigurationContext(overrideConfiguration: Configuration) 
: Context {
val context = super.createConfigurationContext(overrideConfiguration)
TODO("your wrapping implementation here")
}

就这样,您可以使用最新的1.2.0 appCombat。


0

2020年11月更新

大家好,我想分享一下我的经验。几天前,我开始收到有关Android N设备中语言无法从我的应用程序设置更改的问题的报告。我进行了大量搜索,并尝试了多次更改我的代码后,我发现我的代码没有问题,问题是由于androidx约束布局gradle依赖版本2.0.0引起的,将其降级为1.1.3后,语言问题得到解决。 我使用这个版本的ConstraintLayout库解决了我的问题。

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

0
在我的情况下,Xamarin.Android:
创建ContextWrapper:
 public class LanguageContextWrapper : Android.Content.ContextWrapper
 {
   public LanguageContextWrapper(Context @base) : base(@base)
   {
    
   }

   public static ContextWrapper Wrap(Context context, string newLocale)
   {
     Locale.Default = new Locale(newLocale);
     Configuration config = new Configuration();
     config.SetLocale(Locale.Default);
     context = context.CreateConfigurationContext(config);

     return new ContextWrapper(context);
    }
 }

并在所有活动中使用:

protected override void AttachBaseContext(Context newBase)
{                      
    Context context = LanguageContextWrapper.Wrap(newBase, "en"); //need use short name of locale language
    
    base.AttachBaseContext(context);          
}

并且可以在Android 10、11、12上工作,我还没有测试过更低版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接