Search code examples
androidkotlinlocalizationlocaleandroid-6.0-marshmallow

Changing application language (localization) wont apply on api 23 android


I am facing some problem with localization in the android application.

Function for changing language work properly when i test it on api level 24 and above, but when I change language on emulator api level 23, it just wont apply local changes. I read about this on many forums and i have tried almost every provided solution but it just wont work.

Important thing is that animation of closing popup menu, which is listPreference, is slightly weird than for example closing animation on Android Nougat, so it already smells like some irregularity.

Important note is that i run application on my physical device which is api level 23 or Marshmallow and the problem is still there.

In the following sections, i have provided some code which is important for this case, on the other hand i omitted some files like strings.xml and preferences.xml which is not necessary for this case. So main focus here is enabling possibility to change application languge. If any of you got a clue, what might be the problem i would ask him to share with us.


Helper class for setting Locale

package com.metropolitan.hangouter;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Build;

import java.util.Locale;

public class MyContextWrapper extends ContextWrapper {

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

@SuppressWarnings("deprecation")
public static ContextWrapper wrap(Context context, String language) {
    Configuration config = context.getResources().getConfiguration();
    Locale sysLocale = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        sysLocale = getSystemLocale(config);
    } else {
        sysLocale = getSystemLocaleLegacy(config);
    }
    if (!language.equals("") && !sysLocale.getLanguage().equals(language)) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            setSystemLocale(config, locale);
        } else {
            setSystemLocaleLegacy(config, 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 MyContextWrapper(context);
}


@SuppressWarnings("deprecation")
public static Locale getSystemLocaleLegacy(Configuration config) {
    return config.locale;
}

@TargetApi(Build.VERSION_CODES.N)
public static Locale getSystemLocale(Configuration config) {
    return config.getLocales().get(0);
}

@SuppressWarnings("deprecation")
public static void setSystemLocaleLegacy(Configuration config, Locale locale) {
    config.locale = locale;
}

@TargetApi(Build.VERSION_CODES.N)
public static void setSystemLocale(Configuration config, Locale locale) {
    config.setLocale(locale);
}
}

Then in my main activity among other methods, i call this one which is crucial for us.

override fun attachBaseContext(newBase: Context) {
    preferences = PreferenceManager.getDefaultSharedPreferences(newBase)
    vred = preferences.getString("languageKey", "no selection")!!
    val locale = Locale(vred)
    super.attachBaseContext(MyContextWrapper.wrap(newBase, locale.language))
}

This method is also called in the SettingsActivity which has the same functionality and that is attaching local configuration to baseContext, of course i need to refresh the activity because of changes that been made and i am handling that event.

So this method is calling at the right place and at the right time. In other words, i am refreshing activities every time onPreferenceChange. My application is written in kotlin but this MyContextWrapper class is in java. So that's it for now, if someone need some more info, i will be very glad to provide it.


Solution

  • This is probably happening because of the following:

    Starting in Android 7.0 (API level 24), Android provides enhanced support for multilingual users, allowing them to select multiple locales in settings. Android provides this capability by greatly expanding the number of locales supported and changing the way the system resolves resources.

    You can read more about this here

    If you have your resources named xx_XX, try removing XX, this has worked for me on older versions. Otherwise specify fully qualified locale.