Search code examples
androidsharedpreferenceslocale

Locale always get reset after onCreate


I try to setting locale in app, first run it works. But, after onCreate() (Rotate) will get reset to default language and I can't change language in app after reset.

I use SharePreference to save locale value

PrefUtils.java to get value from SharePreference

public class PrefUtil {
    private static final String PREF_SETTINGS_LANGUAGE = "pref_settings_language";
    public static String getLocale(Context context){
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

        String loadLanguage = sharedPreferences.getString(PREF_SETTINGS_LANGUAGE, "");
        Log.d("LoadLanguage", loadLanguage);
        return loadLanguage;
    }
}

MainActivity.java to set locale, I put in onCreate and onResume

private void appLocale(String localeCode){
        Locale locale = new Locale(localeCode);
        Locale.setDefault(locale);
        Configuration configuration = getBaseContext().getResources().getConfiguration();
        configuration.locale = locale;
        getBaseContext().getResources().updateConfiguration(configuration, getBaseContext().getResources().getDisplayMetrics());
    }

Solution

  • In activity class, you should override attachBaseContext(base : Context).

    Create an abstract class BaseActivity and extends it on MainActivity.

    By doing this you don't have to write onAttachBaseContext() in every activity class but should extend BaseActivity instead of AppCompatActivity.

    class BaseActivity : AppCompatActivity {
    
        override fun attachBaseContext(base: Context) {
            super.attachBaseContext(LocaleHelper.onAttach(base))
        }
    }
    

    To retain language setting you should also override attachBaseContext(base:Context) in Application class.

    class App : Application() {
    
       override fun attachBaseContext(base: Context) {
            super.attachBaseContext(LocaleHelper.onAttach(base, LocaleHelper.LANG_EN))
        }
    }
    

    If you need a helper class of localization, here is the one. The code is in Kotlin, but I think you will understand.

    object LocaleHelper {
    
        private const val SELECTED_LANGUAGE = "Locale.Helper.Selected.Language"
    
        const val LANG_EN = "en"
        const val LANG_ES = "es"
    
        val languageMap = hashMapOf(
            LANG_EN to "English",
            LANG_ES to "Spanish"
        )
    
        fun onAttach(context: Context): Context {
            val lang = getPersistedData(context, Locale.getDefault().language)
            return setLocale(context, lang)
        }
    
        fun onAttach(context: Context, defaultLanguage: String): Context {
            val lang = getPersistedData(context, defaultLanguage)
            return setLocale(context, lang)
        }
    
        fun getLanguage(context: Context): String? {
            return getPersistedData(context, Locale.getDefault().language)
        }
    
        fun setLocale(context: Context, language: String?): Context {
            persist(context, language)
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                updateResources(context, language)
            } else {
                updateResourcesLegacy(context, language)
            }
    
        }
    
        private fun getPersistedData(context: Context, defaultLanguage: String): String? {
            val preferences = PreferenceManager.getDefaultSharedPreferences(context)
            return preferences.getString(SELECTED_LANGUAGE, defaultLanguage)
        }
    
        private fun persist(context: Context, language: String?) {
            val preferences = PreferenceManager.getDefaultSharedPreferences(context)
            val editor = preferences.edit()
    
            editor.putString(SELECTED_LANGUAGE, language)
            editor.apply()
        }
    
        @TargetApi(Build.VERSION_CODES.N)
        private fun updateResources(context: Context, language: String?): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
    
            val configuration = context.resources.configuration
            configuration.setLocale(locale)
            configuration.setLayoutDirection(locale)
    
            return context.createConfigurationContext(configuration)
        }
    
        // used for api level < 24
        @Suppress("DEPRECATION")
        private fun updateResourcesLegacy(context: Context, language: String?): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
            val resources = context.resources
            val configuration = resources.configuration
            configuration.locale = locale
            resources.updateConfiguration(configuration, resources.displayMetrics)
            return context
        }
    }