Android N Change Language Programmatically

Android N change language programmatically

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.

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

Change language programmatically (Android N 7.0 - API 24)

Create a new class extends ContextWrapper

public class MyContextWrapper extends ContextWrapper {
public MyContextWrapper(Context base) {
super(base);
}

@TargetApi(Build.VERSION_CODES.N)
public static ContextWrapper wrap(Context context, Locale newLocale) {
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();

if (VersionUtils.isAfter24()) {
configuration.setLocale(newLocale);

LocaleList localeList = new LocaleList(newLocale);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);

context = context.createConfigurationContext(configuration);

} else if (VersionUtils.isAfter17()) {
configuration.setLocale(newLocale);
context = context.createConfigurationContext(configuration);

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

return new ContextWrapper(context);
}
}

override Activity's attachBaseContext method

@Override
protected void attachBaseContext(Context newBase) {
Locale languageType = LanguageUtil.getLanguageType(mContext);
super.attachBaseContext(MyContextWrapper.wrap(newBase, languageType));
}

finish the activity and start it again,new locale will become effective.

demo:https://github.com/fanturbo/MultiLanguageDemo



Related Topics



Leave a reply



Submit