Set Locale Programmatically

Set Locale programmatically

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

Android - Change application locale programmatically

A blog article, how to change the language on Android at runtime and don’t go mad, addressed the issue (along with others) and the author created a library called Lingver to solve the issues.

EDIT (3 Jun 2022): Lingver library has completely failed to address a few issues and appears to be inactive for some time. After a thorough investigation, I have came up with my own implementation: (You can copy the code below under the terms of either Apache-2.0 or GPL-3.0-or-later license)

LangUtils.java

public final class LangUtils {
public static final String LANG_AUTO = "auto";
public static final String LANG_DEFAULT = "en";

private static ArrayMap<String, Locale> sLocaleMap;

public static void init(@NonNull Application application) {
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
private final HashMap<ComponentName, Locale> mLastLocales = new HashMap<>();
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
mLastLocales.put(activity.getComponentName(), applyLocaleToActivity(activity));
}

@Override
public void onActivityStarted(@NonNull Activity activity) {
if (!Objects.equals(mLastLocales.get(activity.getComponentName()), getFromPreference(activity))) {
Log.d("LangUtils", "Locale changed in activity " + activity.getComponentName());
ActivityCompat.recreate(activity);
}
}

@Override
public void onActivityResumed(@NonNull Activity activity) {
}

@Override
public void onActivityPaused(@NonNull Activity activity) {
}

@Override
public void onActivityStopped(@NonNull Activity activity) {
}

@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}

@Override
public void onActivityDestroyed(@NonNull Activity activity) {
mLastLocales.remove(activity.getComponentName());
}
});
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
applyLocale(application);
}

@Override
public void onLowMemory() {
}
});
applyLocale(application);
}

public static void setAppLanguages(@NonNull Context context) {
if (sLocaleMap == null) sLocaleMap = new ArrayMap<>();
Resources res = context.getResources();
Configuration conf = res.getConfiguration();
// Assume that there is an array called language_key which contains all the supported language tags
String[] locales = context.getResources().getStringArray(R.array.languages_key);
Locale appDefaultLocale = Locale.forLanguageTag(LANG_DEFAULT);

for (String locale : locales) {
conf.setLocale(Locale.forLanguageTag(locale));
Context ctx = context.createConfigurationContext(conf);
String langTag = ctx.getString(R.string._lang_tag);

if (LANG_AUTO.equals(locale)) {
sLocaleMap.put(LANG_AUTO, null);
} else if (LANG_DEFAULT.equals(langTag)) {
sLocaleMap.put(LANG_DEFAULT, appDefaultLocale);
} else sLocaleMap.put(locale, ConfigurationCompat.getLocales(conf).get(0));
}
}

@NonNull
public static ArrayMap<String, Locale> getAppLanguages(@NonNull Context context) {
if (sLocaleMap == null) setAppLanguages(context);
return sLocaleMap;
}

@NonNull
public static Locale getFromPreference(@NonNull Context context) {
String language = // TODO: Fetch current language from the shared preferences
getAppLanguages(context);
Locale locale = sLocaleMap.get(language);
if (locale != null) {
return locale;
}
// Load from system configuration
Configuration conf = Resources.getSystem().getConfiguration();
//noinspection deprecation
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? conf.getLocales().get(0) : conf.locale;
}

public static Locale applyLocaleToActivity(Activity activity) {
Locale locale = applyLocale(activity);
// Update title
try {
ActivityInfo info = activity.getPackageManager().getActivityInfo(activity.getComponentName(), 0);
if (info.labelRes != 0) {
activity.setTitle(info.labelRes);
}
} catch (Exception e) {
e.printStackTrace();
}
// Update menu
activity.invalidateOptionsMenu();
return locale;
}

private static Locale applyLocale(Context context) {
return applyLocale(context, LangUtils.getFromPreference(context));
}

private static Locale applyLocale(@NonNull Context context, @NonNull Locale locale) {
updateResources(context, locale);
Context appContext = context.getApplicationContext();
if (appContext != context) {
updateResources(appContext, locale);
}
return locale;
}

private static void updateResources(@NonNull Context context, @NonNull Locale locale) {
Locale.setDefault(locale);

Resources res = context.getResources();
Configuration conf = res.getConfiguration();
//noinspection deprecation
Locale current = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? conf.getLocales().get(0) : conf.locale;

if (current == locale) {
return;
}

conf = new Configuration(conf);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setLocaleApi24(conf, locale);
} else {
conf.setLocale(locale);
}
//noinspection deprecation
res.updateConfiguration(conf, res.getDisplayMetrics());
}

@RequiresApi(Build.VERSION_CODES.N)
private static void setLocaleApi24(@NonNull Configuration config, @NonNull Locale locale) {
LocaleList defaultLocales = LocaleList.getDefault();
LinkedHashSet<Locale> locales = new LinkedHashSet<>(defaultLocales.size() + 1);
// Bring the target locale to the front of the list
// There's a hidden API, but it's not currently used here.
locales.add(locale);
for (int i = 0; i < defaultLocales.size(); ++i) {
locales.add(defaultLocales.get(i));
}
config.setLocales(new LocaleList(locales.toArray(new Locale[0])));
}
}

MyApplication.java

public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LangUtils.init(this);
}
}

In your preference where you are changing the language, you can simply restart the preferences activity like this:

ActivityCompat.recreate(activity);

Activites that use Android WebView
After loading the webview via Activity.findViewById() you can add the following line immediately:

// Fix locale issue due to WebView (https://issuetracker.google.com/issues/37113860)
LangUtils.applyLocaleToActivity(this);

Change app language programmatically in Android

It's possible. You can set the locale. However, I would not recommend that. We've tried it at early stages, it's basically fighting the system.

We have the same requirement for changing the language but decided to settle to the fact that UI should be same as phone UI. It was working via setting locale but was too buggy. And you have to set it every time you enter activity (each activity) from my experience. here is a code if you still need this (again, I don't recommend that)

Resources res = context.getResources();
// Change locale settings in the app.
DisplayMetrics dm = res.getDisplayMetrics();
android.content.res.Configuration conf = res.getConfiguration();
conf.setLocale(new Locale(language_code.toLowerCase())); // API 17+ only.
// Use conf.locale = new Locale(...) if targeting lower versions
res.updateConfiguration(conf, dm);

If you have language specific content - you can change that base on the setting.


update on 26th of march 2020

    public static void setLocale(Activity activity, String languageCode) {
Locale locale = new Locale(languageCode);
Locale.setDefault(locale);
Resources resources = activity.getResources();
Configuration config = resources.getConfiguration();
config.setLocale(locale);
resources.updateConfiguration(config, resources.getDisplayMetrics());
}
  • NOTES: Language code cannot got '-' & must be 2 small case letter only

Android: Setting locale programmatically in the new way alters context

After long investigation, I'll answer myself with what I have concluded is the optimal (and up to date) solution to set locale globally in the current way.

I have created a ContextWrapper class as follows:

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

import com.testmepracticetool.toeflsatactexamprep.helpers.TMLocale;

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 = TMLocale.getLocale(context);

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

This class will create a context with the specified language injected which will be later attached into current activity as context with the following method:

@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(MyContextWrapper.wrap(newBase));
}

This should be inserted on every activity but what I've ended up doing to make it even simpler and better is to create a BaseActivity class and every activity extends that.

This way we set the locale in the up to date and preferred way and it works, when you do a getString the correct language is picked up.

Hope my investigation helps anyone else having the same problem.

Xamarin.Android : change app language programmatically?

createConfigurationContext(configuration) is not enough, We need need to get the context that this method returns and then to set this context in the attachBaseContext method.

    protected override void AttachBaseContext(Context @base)
{
base.AttachBaseContext(updateBaseContextLocale(@base));
}

private Context updateBaseContextLocale(Context context)
{
var locale = new Java.Util.Locale("sp");
Java.Util.Locale.Default = locale;

if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.N)
{
Configuration configuration = context.Resources.Configuration;
configuration.SetLocale(locale);

return context.CreateConfigurationContext(configuration);
}
else
{
Resources resources = context.Resources;
Configuration configuration = resources.Configuration;
#pragma warning disable CS0618 // Type or member is obsolete
configuration.Locale = locale;
resources.UpdateConfiguration(configuration, resources.DisplayMetrics);
#pragma warning restore CS0618 // Type or member is obsolete

return context;
}
}

Get string in OnCreate method

   string title = GetString(Resource.String.TxtWelcome);

Refer to

https://stackoverflow.com/a/44571077/8187800



Related Topics



Leave a reply



Submit