Search code examples
androidandroid-layoutlocalizationandroid-configchanges

Android Layout Direction is not updating on run-time language change


I'm working on a application which supports two different languages -

  1. English
  2. Arabic

The user can switch to any language at any time & based on the selection I'm changing the app language.

Mentioned below are the code snippet -

public static void setLocale(String languageCode, Context ctx) {

    Locale locale = new Locale(languageCode);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
        updateResourcesLocale(ctx, locale);
    } else {
        updateResourcesLocaleLegacy(ctx, locale);
    }
}


private static void updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    context.getApplicationContext().createConfigurationContext(configuration);
}

private static void updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    configuration.setLayoutDirection(locale);
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
}

Also in every activities of the application I've overridden attachBaseContext method. I've used below logic in it -

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(ContextWrapper.onAttach(base, LanguageUtil.getSelectedLanguageFromSharedPreferences(base)));
}

This is the Utility class -

public class ContextWrapper {

private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";

    public static Context onAttach(Context context, String defaultLanguage) {
    persist(context, defaultLanguage);
    String lang = getPersistedData(context, defaultLanguage);
    return setLocale(context, lang);
}

   private static Context setLocale(Context context, String language) {
    persist(context, language);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResources(context, language);
    }

    return updateResourcesLegacy(context, language);
}

private static String getPersistedData(Context context, String defaultLanguage) {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
}

private static void persist(Context context, String language) {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = preferences.edit();

    editor.putString(SELECTED_LANGUAGE, language);
    editor.apply();
}

@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    configuration.setLayoutDirection(locale);

    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private static Context updateResourcesLegacy(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    Resources resources = context.getResources();

    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;

    resources.updateConfiguration(configuration, resources.getDisplayMetrics());

    return context;
}

The issue is - if I switched to Arabic language, the layout direction is getting changed to RTL & this is expected. Then I'm switching my language to English again. However this time, the layout direction is not updating. It's still RTL, but it should be LTR. At this point if I change language to Arabic, layout direction goes to LTR which is again wrong. I've added below property in the application tag of manifest file -

android:supportsRtl="true"

Also, I've replaced all left alignments by start & right alignments by end.

While debugging it's observed that layout direction is getting correctly saved. However it's not reflecting in the UI.

My minSdkVersion 19 & targetSdkVersion 27

I'm stuck in this & not able to fix this issue. Is there anything that I'm missing here?


Solution

  • Finally After lot's of trial & error, I've fixed this issue by adding below code in all activities onCreate method before setContentView is called.

         if (BuildConfig.FLAVOR_country.equals("saudi") && "ar".equals(LanguageUtil.getSelectedLanguageFromSharedPreferences(this))) {
            getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
         } else {
            getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
        }