Android N change language programmatically
Asked Answered
C

11

68

I found really weird bug that is reproduced only on Android N devices.

In tour of my app there is a possibility to change language. Here is the code that changes it.

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

This code works great in activity of my tour ( with recreate() call) but in all next activities all String resources are wrong. Screen rotation fixes it. What can i do with this problem? Should i change locale for Android N differently or it's just system bug?

P.S. Here's what i found. At first start of MainActivity (which is after my tour) Locale.getDefault() is correct but resources are wrong. But in other activities it gives me wrong Locale and wrong resources from this locale. After rotation screen (or perhaps some other configuration change) Locale.getDefault() is correct.

Cursory answered 26/9, 2016 at 14:39 Comment(1)
I filed a bug to Android team and here the answer: code.google.com/p/android/issues/detail?id=225679Chiaki
C
117

Ok. Finally i managed to find a solution.

First you should know that in 25 API Resources.updateConfiguration(...) is deprecated. So instead you can do something like this:

1) You need to create your own ContextWrapper that will override all configuration params in baseContext. For example this is mine ContextWrapper that changes Locale correctly. Pay attention on context.createConfigurationContext(configuration) method.

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) Here's what you should do in your 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);
}

Note:

Remember to recreate your activity if you want to change Locale in your App somewhere. You can override any configuration you want using this solution.

Cursory answered 28/11, 2016 at 16:29 Comment(17)
where do you get the persisted locale object from inside attacheBaseContext?Congratulant
Well when user choose locale in your app you should store it somewhere (in my app we have enum for it). For example in SharedPreferences. You can read it in onCreate() method of your Application and then pass it there. For example Locale locale = ((YourAppClass)newBase).getLocale(); newBase in attachBaseContext method is u're Application class so u can cast it. Or you can use Dagger2 here somehow.Cursory
At some point I feel like it's better to just suppress the deprecation. As far as I can tell, using the deprecated method doesn't cause problems, and this is a heck of a lot of complexity.Saloma
In my case it caused a lot of problems on Android N devices. After chaning the language some string resources can be wrong (from system main locale). Also i had problems with LTR as my application supports arabic locale.Cursory
First time I change app language everything works nice (EN -> ES), but when I try to change language back (ES -> EN) it doesn't work. Language stays changed (ES). Only when I kill the app and start it again language is correct (EN). Any idea why?Asymptomatic
Is this bug exists on android versions < N? Also try to look at u're default locale and configuration at runtime when you change locale from EN to ES and from ES to EN. May be you'll find a difference.Cursory
@Cursory mentioned problem is only on Android > N.Asymptomatic
It's can't start Receiver.java.lang.ClassCastException: cannot be cast to android.app.ContextImandroid.content.ContextWrapperplMutter
@Asymptomatic Got same problem with Android 7.1. Try setting the locale back in the configuration using setLocale() on the newly created context after calling context = context.createConfigurationContext(config);. That did it for me.Mongeau
For some weird reasons, animations are not loaded from resources properly and it's loaded based on the device locale (not the one I set).Hillhouse
do u have any idea how to implement this functionality with calligraphy library because this library also uses the same method "attachBaseContext" and we cannot extend CalligraphyContextWrapper it contain private constructor? please adviceRosewood
@Cursory I dont want to restart my app becasue app is doing some task like recording screen . so without restarting app is there any solution for Android 7.0Calysta
@Cursory If we have a custom view in the Activity, it no longer receives call to onConfigurationChanged(). Adding attachBaseContext() causes this. Any way to fix it instead of passing event from Activity to view in this case?Panne
this solution will only change the basecontext, but the application context will not be changed. the locale will be the same as the one saved in the context.Abscind
after creating new context with createConfigurationContext, this new context returns wrong screen height. any thoughs?Muley
Note that you don't need to do "new ContextWrapper(...)", you can just return what createConfigurationContext() has returned it's already a wrapped Context.Siddons
for changing Configuration don't forget make new configuration Configuration config = new Configuration(context.getResources().getConfiguration()); this solves problem in android NSordid
L
25

Inspired by various codes (i.e: our Stackoverflow team (shout out people)), I had produced a much simpler version. The ContextWrapper extension is unnecessary.

First let's say you have 2 buttons for 2 languages, EN and KH. In the onClick for the buttons save the language code into SharedPreferences, then call the activity recreate() method.

Example:

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

Then create a static method that returns ContextWrapper, perhaps in a Utils class (coz that's what I did, lul).

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

Finally, load the language code from SharedPreferences in ALL ACTIVITY'S attachBaseContext(Context newBase) method.

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

BONUS: To save palm sweat on keyboard, I created a LangSupportBaseActivity class that extends the Activity and use the last chunk of code there. And I have all other activities extends LangSupportBaseActivity.

Example:

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
}
Laruelarum answered 25/10, 2017 at 17:55 Comment(4)
Thank you very much for this. I had to bypass the if (!lang_code.equals("") && !sysLocale.getLanguage().equals(lang_code)) check because after a change (by the user in the settings for example) sometimes sysLocale.getLanguage() was having the wanted language value but not setting explicitly the locale was resulting in the language not changing.Hynes
'&& !sysLocale.getLanguage().equals(lang_code)' this condition in the if part should be removed. Because this sometimes gives false value.Osbert
@AyushGupta That part of the condition check is to prevent unnecessary language switch. For example, the app language is already "en" and the user accidentally pressed the "en" language button.Laruelarum
@Laruelarum Yup i fully agree to your saying, but dont know why when i change language then also default english language is shown in some activities. It looks like system returns wrong locale sometimes. May be its an OS bug( MIUI ). When i removed this condition in if statement, it worked or the condition is executed in if statementOsbert
P
18

Since Android 7.0+ some parts of my app didn't change their language anymore. Even with the new methods proposed above. Updating of both application and activity context helped me. Here is a Kotlin example of Activity subclass overrides:

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

Note: updateConfiguration is deprecated but anyway, createConfigurationContext for each Activity, left some strings unchanged.

Pandemonium answered 29/9, 2019 at 12:25 Comment(1)
That's the only way that worked for me, thank you. The ContextWrapper method only worked partially. Tested on API 24 and 33.Ferreous
G
3

Changing locale programatically in Android app is quite a pain. I have spent lot of time to find working solution, that currently works in production.

You need to override context in every Activity but also in your Application class, otherwise you will end up with mixed languages in ui.

So here is mine solution which works up to API 29:

Subclass your MainApplication class from:

abstract class LocalApplication : Application() {

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

Also every Activity from:

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

Add LocaleExt.kt with next extension functions:

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
    }

Add to your res/values/arrays.xml your supported languages in array:

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

Here is key points:

  • Use config.setLayoutDirection(toLocale); to change layout direction when you use RTL locales like Arabic, Persian, etc.
  • "sys" in the code is a value that means "inherit system default language".
  • Here "langPref" is a key of preference where you put user current language.
  • There is no need to recreate the context if it already uses needed locale.
  • There is no need for ContextWraper as posted here, just set new context returned from createConfigurationContext as baseContext
  • This is very important! When you call createConfigurationContext you should pass configuration crated from scratch and only with Locale property set. There shouldn't be any other property set to this configuration. Because if we set some other properties for this config (orientation for example), we override that property forever, and our context no longer change this orientation property even if we rotate the screen.
  • It is not enough only to recreate activity when user selects a different language, because applicationContext will remain with old locale and it could provide unexpected behaviour. So listen to preference change and restart whole application task instead:

fun Context.recreateTask() {
    this.packageManager
        .getLaunchIntentForPackage(context.packageName)
        ?.let { intent ->
            val restartIntent = Intent.makeRestartActivityTask(intent.component)
            this.startActivity(restartIntent)
            Runtime.getRuntime().exit(0)
         }
}
Geneticist answered 6/4, 2020 at 14:18 Comment(1)
Can you please describe your QA process? How long did you tested? in how many phones? How long in production?.. . BTW 1. the LocalActivity had error, i assumed you meant it like LocalApplication?! 2. I would also write an extra key-point that toolbar titles should be set manually. Anyway, thx.Dissymmetry
N
2

The above answers set me on the right track but left a couple of issues

  1. On android 7 and 9 I could happily change to any language other than the app default. When I changed back to the app default language it showed the last language selected - not surprising as this has overridden the default (although interestingly this wasn't an issue on Android 8!).
  2. For RTL languages it didn't update the layouts to RTL

To fix the first item I stored the default locale on app start.

Note If your default language is set to "en" then locales of "enGB" or "enUS" both need to match the default locale (unless you provide seperate localisations for them). Similarly in the example below if the user's phone locale is arEG (Arabic Egypt) then the defLanguage needs to be "ar" not "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;
}

To fix the RTL issues I extended AppCompatActivity as per Fragments comments in this answer

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);
    }
  }
}
Necessitarianism answered 22/9, 2018 at 18:57 Comment(0)
D
2

This is my code and it works! Please let me know, if there are problems:

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);
}
Drivein answered 6/6, 2020 at 7:50 Comment(0)
R
1

UPDATE SEP 2020

For Latest Androidx Appcombat Stable version 1.2.0, remove all the work arounds for 1.1.0 and add this

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

Add your locale logics into function wrap(you can add the ContextWrapper in Above accepted Answer). This class must be inside the androidx.appcompat.app package because the only existing AppCompatDelegate constructor is package private

Then inside your base activity class you remove all your 1.1.0 workarounds and simply add this

private var baseContextWrappingDelegate: AppCompatDelegate? = null

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

configuration changes might break locale changes. To fix that

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

That's it. You are good to go with latest 1.2.0 appCombat

Roband answered 17/9, 2020 at 8:26 Comment(0)
R
1

This works for me, I'm using 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)
        }
Ruthenium answered 15/1, 2021 at 14:52 Comment(0)
B
0

UPDATE NOV 2020

Hi everyone, I just want to share my experience. Couple of days ago, I started to get reports about an issue in Android N devices that language is not changing from the setting of my app. I searched a lot and after trying multiple changes in my code, I came to know that their was no issue in my code and issue was causing due to androidx constraint Layout gradle dependency version 2.0.0 and after downgrading it to 1.1.3, language issue was resolved. I got my issue resolved using this version of ConstraintLayout library.

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
Bowne answered 24/11, 2020 at 5:55 Comment(0)
L
0

In my case, the Xamarin.Android:

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

and use in all activity:

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

And work in android 10,11,12 I have not checked below.

Lorislorita answered 3/1, 2022 at 7:47 Comment(0)
E
0

I tried many approaches, the answer mentioned in this stackoverflow answer worked best for me.

Summary:

  1. override attachBaseContext method in all activities and application class too.
  2. Create a LocaleHelper class to set Locale
  3. Use SharedPreferences to save the user preference for language.
Envenom answered 9/1 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.