Reputation: 17813
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
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
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