Android - WebView language changes dramatically on Android 7.0 and higher

I have a multilingual application with the main language in English and Arabic.

As described in the documentation ,

  • I added android:supportsRtl="true" to the manifest.
  • I changed all xml properties with left and right attributes to start and end respectively.
  • I added Arabic lines to strings-ar (and similarly for other resources).

The above setting is working correctly. After changing Locale to ar-AE , the Arabic text and resources display correctly in my actions.

However, every time I go to Activity with WebView and / or a WebViewClient , the locale, text, and layout language suddenly revert to the default device.

Additional tips:

  • This only happens on Nexus 6P with Android 7.0 . Everything works correctly on Android 6.0.1 and below.
  • A sharp shift in the locale only happens when I switch to an Activity that has a WebView and / or a WebViewClient (and I have several of them). This does not occur in any of the other Acts.

Android 7.0 supports multilingualism, allowing the user to set more than one standard by default. Therefore, if I install the main language in Locale.UK :

enter image description here

Then, when navigating to WebView , the locale changes from ar-AE to en-GB .

Android 7.0 API changes:

As indicated in the API change list , new language-related methods have been added to the following classes in API 24:

Locale :

Configuration :

However, I am building my application with API 23 and I am not using any of these new methods.

Besides...

  • The problem also arises on the Nexus 6P emulator.

  • To get the standard locale, I use Locale.getDefault() .

  • To set the default locale, I use the following code:

     public static void setLocale(Locale locale){ Locale.setDefault(locale); Configuration config = new Configuration(); config.setLocale(locale); Context context = MyApplication.getInstance(); context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); } 

Has anyone encountered this problem before? What is the reason for this and how do I solve this?

Literature:

1. Built-in RTL support in Android 4.2 .

2. Multilingual support - language and locale .

3. Be careful with the default locale .

+56
android android-7.0-nougat android-webview locale nexus-6p android-chrome
Nov 03 '16 at 9:57
source share
9 answers

Ted Hopp's answer managed to solve the problem, but he did not answer the question of why this is happening.

The reason is the changes made to the WebView class and its support package in Android 7.0.

Reference Information:

Android WebView built using WebKit . Although it was originally part of AOSP, starting with KitKat it was decided to separate WebView into a separate component called Android System WebView . In fact, this is an Android system application that is preinstalled on Android devices. It is periodically updated, as well as other system applications, such as Google Play Services and the Play Store application. You can see it in the list of installed system applications:

Android System WebView

Changes in Android 7.0 :

Starting with Android N, the Chrome app will be used to render any / all WebView in third-party Android apps. In phones with Android N installed, the Android WebView System application is not available at all. On devices that have received an OTA update to Android N, the Android System web view is disabled:

WebView disabled

and

WebView disabled

In addition, support for multiple locales was introduced when devices have more than one default language:

enter image description here

This is important for multi-language applications. If your application has WebView , then they are displayed using the Chrome application. Since Chrome itself is an Android application and runs in its own isolated sandbox, it will not be bound to the locale installed by your application. Instead, Chrome will revert to the deviceโ€™s main locale. For example, let's say your application locale is set to ar-AE , and the deviceโ€™s locale is en-US . In this case, the Activity locale containing the WebView will change from ar-AE to en-US , and the rows and resources from the corresponding locale folders will be displayed. You can see the mix of LTR and RTL lines / resources on those WebView that have WebView s.

Decision:

A complete solution to this problem consists of two steps:

STEP 1:

First reset the default locale manually in each Activity or at least in every Activity that has a WebView .

 public static void setLocale(Locale locale){ Context context = MyApplication.getInstance(); Resources resources = context.getResources(); Configuration configuration = resources.getConfiguration(); Locale.setDefault(locale); configuration.setLocale(locale); if (Build.VERSION.SDK_INT >= 25) { context = context.getApplicationContext().createConfigurationContext(configuration); context = context.createConfigurationContext(configuration); } context.getResources().updateConfiguration(configuration, resources.getDisplayMetrics()); } 

Call the above method before calling setContentView(...) in the onCreate() method of all your actions. The locale parameter must be the default Locale that you want to set. For example, if you want to set Arabic / UAE as the default locale, you must pass new Locale("ar", "AE") . Or, if you want to set the default language (that is, Locale , which is automatically installed by the operating system), you must pass Locale.US .

STEP 2:

In addition, you need to add the following line of code:

 new WebView(this).destroy(); 

in onCreate() your Application class (if you have one) and wherever the user can change the language. This will take care of all kinds of extreme cases that may occur when you restart the application after changing the language (you might notice lines in other languages โ€‹โ€‹or with the opposite alignment after changing the language to Activities that have WebView on Android 7. 0 ++).

As a complement , Chrome custom tabs are now the preferred way to display web pages in the app.

References:

1. Android7.0 - changes for WebView .

2. Understanding security fixes for WebView and Android .

3. WebView for Android .

4. WebView: from "Powered by Chrome" to "Chrome . "

5. NougatWebView .

6. Android7.0 Nougat .

7. Secrets Android N, part 1: Android System WebView is now just "Chrome"? .

+59
Nov 18 '16 at 11:07
source share

It seems that your code sets the locale in the configuration of the application itself ( MyApplication.getInstance() ). However, you need to update the activity context configuration before inflating the presentation of the activity contents. I found that changing the context of the application is not enough (and, as it turns out, is not even necessary). If I do not update every action context, then the behavior is incompatible between actions.

The way I approach this applies to the AppCompatActivity subclass (or Activity , if the compatibility library is not used), and then derive all my activity classes from this subclass. Here is a simplified version of my code:

 public class LocaleSensitiveActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { Locale locale = ... // the locale to use for this activity fixupLocale(this, locale); super.onCreate(savedInstanceState); ... } static void fixupLocale(Context ctx, Locale newLocale) { final Resources res = ctx.getResources(); final Configuration config = res.getConfiguration(); final Locale curLocale = getLocale(config); if (!curLocale.equals(newLocale)) { Locale.setDefault(newLocale); final Configuration conf = new Configuration(config); conf.setLocale(newLocale); res.updateConfiguration(conf, res.getDisplayMetrics()); } } private static Locale getLocale(Configuration config) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return config.getLocales().get(0); } else { //noinspection deprecation return config.locale; } } } 

Then I will definitely call super.onCreate(savedInstanceState) in each subclass of onCreate() before calling any methods (like setContentView() ) that use the context.

+20
Nov 08 '16 at 6:29
source share

After reading all the answers, I found that something was missing in each of them, so here is a solution that has worked for me so far. Because WebView overrides the language configuration of the activity context and application context, you must ensure that each time you do this, you invoke a method that discards these changes back. In my case, I wrote the following class that my actions that represent this problem are expanding (those that show WebView):

 public class WebViewFixAppCompatActivity extends AppCompatActivity { private Locale mBackedUpLocale = null; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { mBackedUpLocale = getApplicationContext().getResources().getConfiguration().getLocales().get(0); } } @Override protected void onStop() { super.onStop(); fixLocale(); } @Override public void onBackPressed() { fixLocale(); super.onBackPressed(); } /** * The locale configuration of the activity context and the global application context gets overridden with the first language the app supports. */ public void fixLocale() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Resources resources = getResources(); final Configuration config = resources.getConfiguration(); if (null != mBackedUpLocale && !config.getLocales().get(0).equals(mBackedUpLocale)) { Locale.setDefault(mBackedUpLocale); final Configuration newConfig = new Configuration(config); newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry())); resources.updateConfiguration(newConfig, null); } // Also this must be overridden, otherwise for example when opening a dialog the title could have one language and the content other, because // different contexts are used to get the resources. Resources appResources = getApplicationContext().getResources(); final Configuration appConfig = appResources.getConfiguration(); if (null != mBackedUpLocale && !appConfig.getLocales().get(0).equals(mBackedUpLocale)) { Locale.setDefault(mBackedUpLocale); final Configuration newConfig = new Configuration(appConfig); newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry())); appResources.updateConfiguration(newConfig, null); } } } } 

The idea posted by @Tobliug to save the original configuration before WebView overrides it worked for me, in my specific case, I found it easier to implement than other published solutions. The important thing is that the fix method is called after exiting the WebView, for example, by pressing back and onStop. If the webView is displayed in a dialog, you should ensure that the fix method is called after the dialog is closed, mainly in onResume and / or onCreate. And if the web view is loaded directly into the onCreate of the action, and not after that in a new fragment, the fix should also be called immediately after setContentView before setting the action header, etc. If the web view is loading inside the fragment in action, call the activity on the onViewCreated fragment and the activity should call the fix method. Not all activities should be expanded by the aforementioned class, as noted in the answer that this is unnecessary and not necessary. This problem is also not resolved by replacing WebView with Google Chrome tabs or opening an external browser.

If you really need to configure your resources to configure the entire list of languages, and not just one, then you need to combine this solution with what is located at https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce In my case, this was not necessary .

I also did not find the need to call the new WebView (this) .destroy (); as noted in the answer here.

+4
Mar 15 '18 at 16:47
source share

Same problem. I have a dirty but simple solution.

Since I notice that the locale is still good in the Activity.onCreate (...) function and no more reliable in the Activity.onPostCreate (...) function, I just save the Locale and force it at the end of the onPostCreate (... )

Here we go:

 private Locale backedUpLocale = null; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); backedUpLocale = getApplicationContext().getResources().getConfiguration().locale; } @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); changeLocale(backedUpLocale); } 

Bonus - language change function:

 public void changeLocale(final Locale locale) { final Configuration config = res.getConfiguration(); if(null != locale && !config.locale.equals(locale)) { Locale.setDefault(locale); final Configuration newConfig = new Configuration(config); if(PlatformVersion.isAtLeastJellyBeanMR1()) { newConfig.setLocale(new Locale(locale.getLanguage())); } else { newConfig.locale = new Locale(locale.getLanguage()); } res.updateConfiguration(newConfig, null); } } 

Hope this helps.

+1
Sep 05 '17 at 14:15
source share

None of the answers above helped me, I managed to reset localize the application again inside the onStop () of the activity method containing Webview

0
Feb 22 '17 at 14:23
source share

I want to add another usage example:

When you return from the web browsing operation (that is, when the payment screen is displayed and the user presses the back button) onCreate () of the previous operation is not performed, so the language has been reset again. To avoid errors, we must reset the application locale in onResume() base activity.

 private static void updateResources(Context context, String language) { Locale locale = new Locale(language); Locale.setDefault(locale); Configuration config = new Configuration(); config.setLocale(locale); config.setLayoutDirection(locale); context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics()); } 

Call the above method in onResume () of the underlying activity, or at least in the webview.

Edit: If you are dealing with fragments, make sure that this method is called when the user exits the web view.

0
Aug 24 '17 at 9:54 on
source share

If you use WebView only to display extended text (text with some paragraphs or bold and italic text with different font sizes), you can use TextView and Html.fromHtml () . TextViews has no problems with locale settings; -)

0
Sep 18 '17 at 2:04 on
source share

Just change the parameter for the SEt Local Method from passing the BaseContext to "this" or "Exact action", especially on Android 7.0 and later

0
Oct 03 '19 at 9:03
source share

In Android N, when you execute new WebView() , it will add /system/app/WebViewGoogle/WebViewGoogle.apk to the resource path, and if it has not added to the path, it will restore the resource.

So, if you want to resolve the issue, just make a new WebView(application) in the application before you change the local one.

If you know Chinese, you can read this blog .

-one
Aug 18 '17 at 3:24 on
source share



All Articles