Set Locale programmatically

194.2k views Asked by At

My app supports 3 (soon 4) languages. Since several locales are quite similar I'd like to give the user the option to change locale in my application, for instance an Italian person might prefer Spanish over English.

Is there a way for the user to select among the locales that are available for the application and then change what locale is used? I don't see it as a problem to set locale for each Activity since it is a simple task to perform in a base class.

17

There are 17 answers

30
Ricardo On BEST ANSWER

EDIT 21st OCTOBER 2022

Starting from Android 13 you can now set your locale from the AppCompatDelegate with the method setApplicationLocales(appLocale) and it has backwards compatibility.

For API 33 and above:

The process is quite simple and you can achieve it as follow:

val appLocale: LocaleListCompat = 
LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)

While using this you should no longer need to call recreate() on the activity as the docs mention:

Note that calling setApplicationLocales() recreates your Activity, unless your app handles locale configuration changes by itself.

API lower than 33

If you are targetting an API lower than 33 you will need to also add a service to your manifest in order to handle locale storage:

<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
    android:enabled="false"
    android:exported="false">
    <meta-data
      android:name="autoStoreLocales"
      android:value="true" />
  </service>

This is only available for AndroidX from androidx.appcompat:appcompat:1.6.0-alpha01

You can find the documentation here

-------------- Old Answers ------------

For people still looking for this answer, since configuration.locale was deprecated from API 24, you can now use:

configuration.setLocale(locale);

Take in consideration that the minSkdVersion for this method is API 17.

Full example code:

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

Don't forget that, if you change the locale with a running Activity, you will need to restart it for the changes to take effect.

EDIT 11th MAY 2018

As from @CookieMonster's post, you might have problems keeping the locale change in higher API versions. If so, add the following code to your Base Activity (BaseActivity extends AppCompatActivity / other activities) so that you update the context locale on every Activity creation:

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

If you use this, don't forget to save the language to SharedPreferences when you set the locale with setLocale(locale)

EDIT 7th APRIL 2020

You might be experiencing issues in Android 6 and 7, and this happens due to an issue in the androidx libraries while handling the night mode. For this you will also need to override applyOverrideConfiguration in your base activity and update the configuration's locale in case a fresh new locale one is created.

Sample code:

@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);
} 
13
Rubycon On

Hope this help(in onResume):

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

As of 2020 language management become easy! All you have to do is:

  1. Call to Activity.applyOverrideConfiguration
  2. And call to Locale.setDefault

You must call those from the activity constructor since you can call to applyOverrideConfiguration only once, and the system calls it pretty early.

And watch out from app-bundles, Google will split your APK by language resources automatically when using app-bundles. Check out the new API and the workaround here.


I created a helper class to help you with it. In my implementation G.app is the application context. Also, I need to access resources from the app context so I use the Res class for it, this one is optional, but I provide its code as well.

Usage

public BaseActivity(){
    LanguageUtility.init(this);
}

public void changeLanguage(Local local){
    // you must recreat your activity after you call this
    LanguageUtillity.setDefaultLanguage(local, this);
}

Source code

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

An optional Res class.

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);
    }
}
0
Arul On

Call this method on BaseActivity -> onCreate() and BaseFragment -> OnCreateView()

Tested on API 22, 23, 24, 25, 26, 27, 28, 29...uptodate version

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)
}
3
Marino On

Android 13 introduces Per-app language preferences which significantly simplifies handling in-app Locale changes.

Disclaimer: Android 13 is currently in "Developer Preview" at the time of writing and part of what mentioned here is in alpha (thus, subject to change).

As visible in the docs there are couple of new ways to handle such changes, for compatibility the best one seem to be through AppCompatDelegate:


val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)

(Method is available from androidx.appcompat:appcompat:1.6.0-alpha01 )

This will take care of setting the locale to appLocale and restart any activities as needed. Moreover, as mentioned on setApplicationLocales docs

On API level 33 and above, this API will handle storage automatically

Meaning that what needs to be done for a custom in-app locale to work is just to offer the user a language picker and call setApplicationLocales once when a choice is performed.

What about API < 33?

The docs on developer.android.com do mention that devs can require to store this value automatically on older API levels

..by adding a special metaData entry in their AndroidManifest, similar to :

but at the time of writing this, the actual metadata that is needed isn't mentioned (maybe something went wrong in the doc generation).

It was instead visible in the source code for AppCompatDelegate published here and it looks as follows

   <service
       android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
       android:enabled="false"
       android:exported="false">
       <meta-data
           android:name="autoStoreLocales"
           android:value="true" />
   </service>

Adding the above to your Manifest should make this solution work for all API levels

0
Yeahia2508 On

For those who tried everything but not not working. Please check that if you set darkmode with AppCompatDelegate.setDefaultNightMode and the system is not dark, then Configuration.setLocale will not work above Andorid 7.0.

Add this code in your every activity to solve this issue:

override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
  if (overrideConfiguration != null) {
    val uiMode = overrideConfiguration.uiMode
    overrideConfiguration.setTo(baseContext.resources.configuration)
    overrideConfiguration.uiMode = uiMode
  }
  super.applyOverrideConfiguration(overrideConfiguration)
}
0
Mohammad Zaer On

Add a helper class with the following method:

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.setLocale(locale);
            activity.getResources().updateConfiguration(config,
                    activity.getResources().getDisplayMetrics());
        }

    }
}

And call it in your startup activity, like MainActivity.java:

public void onCreate(Bundle savedInstanceState) {
    ...
    LanguageHelper.setAppLocale("fa", this);
    ...
}
0
Burak Dizlek On

There is a super simple way.

in BaseActivity, Activity or Fragment override attachBaseContext

 override fun attachBaseContext(context: Context) {
    super.attachBaseContext(context.changeLocale("tr"))
}

extension

fun Context.changeLocale(language:String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)
    val config = this.resources.configuration
    config.setLocale(locale)
    return createConfigurationContext(config)
}
0
Zbigniew Mazur On
 /**
 * 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
    }
}
0
Xurshid Raxmatov On

Put this code in your activity

 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();
    }
1
Sam Lu On

I found the androidx.appcompat:appcompat:1.1.0 bug can also be fixed by simply calling getResources() in applyOverrideConfiguration()

@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);
}
0
AudioBubble On

There's a new way to let users select app's default language since Appcompat 1.6.0-alpha04 and later. Lets say you have a button that should change the app's language to Italian when the user clicks it:

binding.button.setOnClickListener {
    val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("it")
    AppCompatDelegate.setApplicationLocales(appLocale)
}

Also, to add support for older devices (< API level 32) add the following service inside the <application> tag of your AndroidManifest.xml file:

<service
  android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
  android:enabled="false"
  android:exported="false">
  <meta-data
    android:name="autoStoreLocales"
    android:value="true" />
</service>

Make sure to have the latest AppCompat dependency:


implementation 'androidx.appcompat:appcompat:1.6.0-alpha05'

Reference: https://developer.android.com/about/versions/13/features/app-languages

2
Makvin On

simple and easy

Locale locale = new Locale("en", "US");
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = locale;
res.updateConfiguration(conf, dm);

where "en" is language code and "US" is country code.

0
localhost On
@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);
    }
}

Just use this helper method to force specific locale.

UDPATE 22 AUG 2017. Better use this approach.

8
user905686 On

As no answer is complete for the current way to solve this problem, I try to give instructions for a complete solution. Please comment if something is missing or could be done better.

General information

First, there exist some libraries that want to solve the problem but they all seem outdated or are missing some features:

Further I think writing a library might not be a good/easy way to solve this problem because there is not very much to do, and what has to be done is rather changing existing code than using something completely decoupled. Therefore I composed the following instructions that should be complete.

My solution is mainly based on https://github.com/gunhansancar/ChangeLanguageExample (as already linked to by localhost). It is the best code I found to orientate at. Some remarks:

  • As necessary, it provides different implementations to change locale for Android N (and above) and below
  • It uses a method updateViews() in each Activity to manually update all strings after changing locale (using the usual getString(id)) which is not necessary in the approach shown below
  • It only supports languages and not complete locales (which also include region (country) and variant codes)

I changed it a bit, decoupling the part which persists the chosen locale (as one might want to do that separately, as suggested below).

Solution

The solution consists of the following two steps:

  • Permanently change the locale to be used by the app
  • Make the app use the custom locale set, without restarting

Step 1: Change the locale

Use the class LocaleHelper, based on gunhansancar's LocaleHelper:

  • Add a ListPreference in a PreferenceFragment with the available languages (has to be maintained when languages should be added later)
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;
    }
}

Create a SettingsFragment like the following:

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

Create a resource locales.xml listing all locales with available translations in the following way (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>

In your PreferenceScreen you can use the following section to let the user select the available languages:

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

which uses the following strings from 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>

Step 2: Make the app use the custom locale

Now setup each Activity to use the custom locale set. The easiest way to accomplish this is to have a common base class for all activities with the following code (where the important code is in attachBaseContext(Context base) and 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();
        }
    }
}

What it does is

  • Override attachBaseContext(Context base) to use the locale previously persisted with LocaleHelper
  • Detect a change of the locale and recreate the Activity to update its strings

Notes on this solution

  • Recreating an Activity does not update the title of the ActionBar (as already observed here: https://github.com/gunhansancar/ChangeLanguageExample/issues/1).

    • This can be achieved by simply having a setTitle(R.string.mytitle) in the onCreate() method of each activity.
  • It lets the user chose the system default locale, as well as the default locale of the app (which can be named, in this case "English").

  • Only language codes, no region (country) and variant codes (like fr-rCA) are supported so far. To support full locale specifications, a parser similar to that in the Android-Languages library can be used (which supports region but no variant codes).

    • If someone finds or has written a good parser, add a comment so I can include it in the solution.
6
CookieMonster On

I had a problem with setting locale programmatically with devices that has Android OS N and higher. For me the solution was writing this code in my base activity:

(if you don't have a base activity then you should make these changes in all of your activities)

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

note that here it is not enough to call

createConfigurationContext(configuration)

you also need to get the context that this method returns and then to set this context in the attachBaseContext method.

0
nnyerges On

Valid for API16 to API28 Just place this method some where:

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;

Insert this code in all your activitys using:

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(localeUpdateResources(base, "<-- language code -->"));
    }

or call localeUpdateResources on fragments, adapters, etc. where you need the new context.

Credits: Yaroslav Berezanskyi