Maher Abuthraa
Maher Abuthraa

Reputation: 17813

Android Nougat 7.1 resets Locale after launching WebView

We got a weird behavior with Android N 7.1 ( API-25 ) That after Launching WebView, system enforces reseting Locale to device locale. That overrides used locale (for localization) on application. Easy way to reproduce that is to get a localization on app. and launch a WebView. Then you won't see localized content any more until you relaunching app again. That happens only on Android-7.1 (API-25)

Here is how I switch Locale which is working in all APIs:

 public void switchToCzLocale() {
        Locale mLocale = new Locale("cs","CZ");// it can be any other Locale
        Configuration config = getBaseContext().getResources()
                .getConfiguration();
        Locale.setDefault(mLocale);
        config.setLocale(mLocale);
        getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
    }

I have uploaded a sample to reproduce that issue with more details on:

https://github.com/mabuthraa/WebView-android7-issue

Please any idea if this behavior is a bug or probably bad implantation of changing locale.

Here is the link to issue ticket on Android group: Issue 218310: [developer preview] Creating a WebView resets Locale to user defaults

Upvotes: 18

Views: 3108

Answers (2)

Progamer
Progamer

Reputation: 188

This problem gave me some headache as well especially because Google thinks that it is not a bug on their side. However, this is definitely not a desired behavior in an app that the language gets changed just when opening a WebView.

Our solution does not depend on a hardcoded language and is adapted for Android 7+. First, add this code to your app build.gradle file:

android {
    defaultConfig {
        ...
        resConfigs rootProject.ext.available_languages
        buildConfigField "String[]", "AVAILABLE_LANGUAGES", "{\"${rootProject.ext.available_languages.join("\",\"")}\"}"
    }
}

And add the following code to your root build.gradle:

ext {
    available_languages = ["en", "de"]
}

These two code blocks define the list of available strings.xml files and puts the list in the BuildConfig.AVAILABLE_LANGUAGES field.

Now, we can define a BaseActivity, from which all activities should extend, with the following code:

abstract class BaseActivity : AppCompatActivity() {

    override fun attachBaseContext(newBase: Context) {
        val locale = getFirstAvailableLocale(newBase)
        val context = createContextWithLocale(locale, newBase)
        super.attachBaseContext(context)
    }

    private fun getFirstAvailableLocale(context: Context): Locale {
        val availableLanguages = BuildConfig.AVAILABLE_LANGUAGES // Gets list of available languages defined in the build.gradle file
        val locales = context.resources.configuration.locales
        for (i in 0..locales.size()) {
            if (locales[i].language in availableLanguages) {
                return locales[i]
            }
        }
        return locales[0]
    }

    @Suppress("DEPRECATION")
    private fun createContextWithLocale(locale: Locale, baseContext: Context): Context {
        val resources = baseContext.resources
        val configuration = resources.configuration
        val localeList = LocaleList(locale)
        LocaleList.setDefault(localeList)
        configuration.setLocales(localeList)
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
            baseContext.createConfigurationContext(configuration)
        } else {
            resources.updateConfiguration(configuration, resources.displayMetrics)
            baseContext
        }
    }
}

You can even extend this solution to support different regions but then you would have to split the strings from the resConfigs into the language and region as the Locale class does not recognize the language tag correctly (e.g. de-rDE needs to be defined in resConfigs but Locale would be de_DE)

Upvotes: 0

Maher Abuthraa
Maher Abuthraa

Reputation: 17813

Here is my workaround solution.

We resolved that issue by enforcing setting locale again after initializing webView and before loading content:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  MyApp.getApplication().switchToCzLocale();
}

For example in WebActivity:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        mWebView = (WebView) findViewById(R.id.webview);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
          MyApp.getApplication().switchToCzLocale();
        }
        mWebView.loadData(getString(R.string.web_content), "text/html", "charset=UTF-8");
    }

MyApp:

import android.app.Application;
import android.content.res.Configuration;

import java.util.Locale;


public class MyApp extends Application {
    private static MyApp sApplication;

    @Override
    public void onCreate() {
        super.onCreate();
        switchToCzLocale();
        sApplication = this;
    }

    public static MyApp getApplication() {
        return sApplication;
    }

    public void switchToCzLocale() {
        Locale mLocale = new Locale("cs","CZ");
        Configuration config = getBaseContext().getResources()
                .getConfiguration();
        Locale.setDefault(mLocale);
        config.setLocale(mLocale);
        getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
    }
}

I hope that may help,'.

Still Im looking for a better solution.

Upvotes: 17

Related Questions