27

I want to change the language of the app and this works fine until API 26.

For api > 25 I put Locale.setDefault(Locale.Category.DISPLAY, mynewlanglocale); before setContentView(R.layout.activity_main); but nothing changes.

The docs don't explain too much about this.

Janusz
  • 187,060
  • 113
  • 301
  • 369
Marius Razvan Varvarei
  • 1,331
  • 1
  • 17
  • 21

8 Answers8

36

I had the same problem: since Android 8.0+ some parts of my app did't change their language anymore. Updating of both application and activity context helps me. Here is an example of MainActivity function:

private void setApplicationLanguage(String newLanguage) {
    Resources activityRes = getResources();
    Configuration activityConf = activityRes.getConfiguration();
    Locale newLocale = new Locale(newLanguage);
    activityConf.setLocale(newLocale);
    activityRes.updateConfiguration(activityConf, activityRes.getDisplayMetrics());

    Resources applicationRes = getApplicationContext().getResources();
    Configuration applicationConf = applicationRes.getConfiguration();
    applicationConf.setLocale(newLocale);
    applicationRes.updateConfiguration(applicationConf, 
    applicationRes.getDisplayMetrics());
}
Skeptiker
  • 475
  • 4
  • 4
20

Yes in android Oreo localization is not working fine with updateconfiguration. But it is deprecated in android N itself. Instead of updateconfiguration use createconfiguration in each attachcontext. it is working fine for me. Try this...

In you activity add this..

@Override
protected void attachBaseContext(Context newBase) {
    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
        super.attachBaseContext(MyContextWrapper.wrap(newBase, "ta"));
    }
    else {
        super.attachBaseContext(newBase);
    }
}

In MyContextWrapper.java

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

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        configuration.setLocale(newLocale);
        LocaleList localeList = new LocaleList(newLocale);
        LocaleList.setDefault(localeList);
        configuration.setLocales(localeList);
        context = context.createConfigurationContext(configuration);

    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        configuration.setLocale(newLocale);
        context = context.createConfigurationContext(configuration);

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

    return new ContextWrapper(context);
}
Pablo Cegarra
  • 20,955
  • 12
  • 92
  • 110
  • Thanks for the answer. Why are you checking like this: if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {} What about lower version? How we change them? – sj_8 May 04 '18 at 10:21
  • I have added code for lower version too in else part. And in lower version updateconfiguration method is enough for the language change. – Rajalakshmi Arumugam May 14 '18 at 09:21
  • 1
    1. I need to add this on all my activities or just the MainActivity of my App? and 2. Inside of attachBaseContext, how can I retrieve the language from SharedPreferences? I tried but, the App crashes when I open it. –  Aug 10 '18 at 18:44
  • Can this be done on Application class? if yes, it would help to override attachBaseContext in all activities!! @RajalakshmiArumugam – arunrk Oct 03 '18 at 12:37
  • 3
    Not working for me on Oreo device s8. Can anyone help me? – Sakthivel Appavu Jan 10 '19 at 09:08
10

updateConfiguration is deprecated and you should use createConfigurationContext. I solved it this way:

    @Override
    protected void attachBaseContext(Context newBase) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Configuration config = newBase.getResources().getConfiguration();
            //Update your config with the Locale i. e. saved in SharedPreferences
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newBase);
            String language = prefs.getString(SP_KEY_LANGUAGE, "en_US");
            Locale.setDefault(locale);
            config.setLocale(new Locale(language));
            newBase = newBase.createConfigurationContext(config);
        }
        super.attachBaseContext(newBase);
    }
Luke
  • 2,539
  • 2
  • 23
  • 40
4

Updated For All android versions till Oreo

Create a class like this

public class LocaleUtils {

@Retention(RetentionPolicy.SOURCE)
@StringDef({ENGLISH, FRENCH, SPANISH})
public @interface LocaleDef {
    String[] SUPPORTED_LOCALES = {ENGLISH, FRENCH, SPANISH};
}

public static final String ENGLISH = "en";
public static final String FRENCH = "fr";
public static final String SPANISH = "es";


public static void initialize(Context context) {
    setLocale(context, ENGLISH);
}

public static void initialize(Context context, @LocaleDef String defaultLanguage) {
    setLocale(context, defaultLanguage);
}


public static boolean setLocale(Context context, @LocaleDef String language) {
    return updateResources(context, language);
}

private static boolean updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    context.createConfigurationContext(configuration);
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return true;
}

}

Now when you select the language from your app, Save the language code in Shared Preference like below

private static SharedPreferences getDefaultSharedPreference(Context context) {
    if (PreferenceManager.getDefaultSharedPreferences(Application.getInstance().getApplicationContext()) != null)
        return PreferenceManager.getDefaultSharedPreferences(Application.getInstance().getApplicationContext());
    else
        return null;
}

 public static void setSelectedLanguageId(String id){
    final SharedPreferences prefs = getDefaultSharedPreference(Application.getInstance().getApplicationContext());
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString("app_language_id", id);
    editor.apply();
}

public static String getSelectedLanguageId(){
    return getDefaultSharedPreference(Application.getInstance().getApplicationContext())
            .getString("app_language_id", "en");
}

These three functions should be written inside a Utiltiy class(your preference). Then when you select the app language from the app, call the setSelectedLanguageId() function and pass the language id as parameter.

This way you have saved the selected language in your app. Now in your application class write a function like this

public void initAppLanguage(Context context){
    LocaleUtils.initialize(context, PreferenceUtil.getSelectedLanguageId() );
}

Here the PreferenceUtil is my Utiltiy class. You should replace it with your utility class function.

You should also create a variable in your application class

private static Application applicationInstance;

and in your Application class's onCreate method, initialise applicationInstance to be the applications context like this

applicationInstance = this; 

Now write a getter function in your application class

public static synchronized Application getInstance() {
    return applicationInstance;
}

And now when you start your first activity, call this method in your activity's onCreate

Application.getInstance().initAppLanguage(this);

Remember that we are passing the activity's context to the initAppLanguage() function, not the application context. Passing the Application context won't make it work in Oreo(atleast for me).

So when you select the language try to restart your application completely. You can acheive this by

Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName());

startActivity(i);

Hope this helps you!

emilpmp
  • 1,716
  • 17
  • 32
  • great, it worked for me. Below line saved me- Remember that we are passing the activity's context to the initAppLanguage() function, not the application context. Passing the Application context won't make it work in Oreo(atleast for me). Thanks a lot. – Vishwas Singh Apr 19 '18 at 09:43
3

It is possible, however i would not recommend to set the language programatically

Android is designed so the System UI and your App have the same language, if you change it programmatically you would be fighting the system

Instead what you can do is enable multilanguage support by adding different strings.xml languages, this will change the language automatically

I reccommend reading through this Google Developers article:

Supporting Different Languages and Cultures

If you really need to change it programatically you can do the following

Locale locale = new Locale("en");
Locale.setDefault(locale);

Configuration config = context.getResources().getConfiguration();
config.setLocale(locale);
context.createConfigurationContext(config);
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());

On SDK >= 21, you need to call 'Resources.updateConfiguration()', otherwise resources will not be updated.

Hope it helps.

Andre Breton
  • 1,273
  • 1
  • 9
  • 19
1

Here is complete solution worked for kitkat, Lollipop, Marshmallow, Nougat and Oreo too. Just follow all below step.

First create a java class like below

import android.content.Context;
import android.content.res.Configuration;
import java.util.Locale;
public class LocaleUtils {
public static void updateConfig(Context mContext, String sLocale) {
    Locale locale = new Locale(sLocale);
    Locale.setDefault(locale);
    Configuration config = mContext.getResources().getConfiguration();
    config.locale = locale;
    mContext.getResources().updateConfiguration(config,
            mContext.getResources().getDisplayMetrics());
  }
}

Now add this snippet on Button click where you want to change locale

String lang="hi";//pass your language here
SharedPreferences preferences =  PreferenceManager.getDefaultSharedPreferences(mContext);
                        SharedPreferences.Editor editor = preferences.edit();
                        editor.clear();
                        editor.putString("lang", lang");
                        editor.putBoolean("langSelected", true);
                        editor.apply();
                        LocaleUtils.updateConfig(mContext,lang);
                        Intent intent = mContext.getIntent();
                        mContext.overridePendingTransition(0, 0);
                        mContext.finish();
                        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        mContext.overridePendingTransition(0, 0);
                        mContext.startActivity(intent);

Finally paste the below code in Splash Activity or in Launching Activity.

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    String lang = preferences.getString("lang", "");
    boolean langSelected = preferences.getBoolean("langSelected", false);
    SharedPreferences.Editor editor = preferences.edit();
    if (langSelected) {
        editor.clear();
        editor.putString("lang", lang);
        editor.putBoolean("langSelected", true);
        editor.apply();
        LocaleUtils.updateConfig(this,lang);
    } else {
        LocaleUtils.updateConfig(this, Locale.getDefault().getLanguage());
        editor.clear();
        editor.putString("lang", Locale.getDefault().getLanguage());
        editor.putBoolean("langSelected", false);
        editor.apply();
    }
Ness Tyagi
  • 2,008
  • 24
  • 18
0

You need to use getApplicationContext() instead of getContext()

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
0

After use all solution in all sources finally i found my issue. That makes me angry for 2 days.

Everyone knows that in Android Oreo (API 26) we must use createConfigurationContext, But My problem is using Country name with local.

Replace

en_US with en

ar_AE with ar

fa_IR with fa

And my problem solved.

Hope to help someone

Radesh
  • 13,084
  • 4
  • 51
  • 64