Android - язык WebView резко меняется на Android 7.0 и выше
У меня есть многоязычное приложение с основным языком на английском и арабском языках.
Как описано в документации,
- Я добавил
android:supportsRtl="true"
в манифест.
- Я изменил все свойства xml с атрибутами
left
и right
на start
и end
соответственно.
- Я добавил строки арабского языка в
strings-ar
(и аналогично для других ресурсов).
Вышеуказанная настройка работает правильно. После изменения Locale
на ar-AE
, арабский текст и ресурсы отображаются правильно в моих действиях.
Однако каждый раз, когда я перехожу к Activity
с WebView
и/или a WebViewClient
, язык локали, текста и макета внезапно вернитесь к устройству по умолчанию.
Дополнительные советы:
- Это происходит только на Nexus 6P с Android 7.0. Все работает правильно на Android 6.0.1 и ниже.
- Резкий сдвиг в локали происходит только, когда я перехожу к
Activity
, который имеет WebView
и/или a WebViewClient
(и у меня их несколько). Это не происходит ни в одной из других Деяний.
Android 7.0 поддерживает многоязычность, позволяя пользователю устанавливать более одного стандарта по умолчанию. Поэтому, если я устанавливаю основной язык в Locale.UK
:
![введите описание изображения здесь]()
Затем при переходе к WebView
, локаль изменяется с ar-AE
до en-GB
.
Изменения API Android 7.0:
Как указано в списке изменений API, новые методы, относящиеся к языку, были добавлены к следующим классам в API 24:
Locale
:
Configuration
:
Однако я создаю свое приложение с API 23, и я не использую ни один из эти новые методы.
Кроме того...
-
Проблема возникает и на эмуляторе Nexus 6P.
-
Чтобы получить стандартную локаль, я использую Locale.getDefault()
.
-
Чтобы установить языковой стандарт по умолчанию, я использую следующий код:
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());
}
Кто-нибудь сталкивался с этой проблемой раньше? В чем причина этого и как мне это решить?
Литература:
1. Встроенная поддержка RTL в Android 4.2.
2. Многоязыковая поддержка - язык и локаль.
3. Будьте осторожны с языковым стандартом по умолчанию.
Ответы
Ответ 1
Ответ теда Хоппа удалось решить проблему, но он не ответил на вопрос , почему это происходит.
Причина - изменения, внесенные в класс WebView
и его пакет поддержки в Android 7.0.
Справочная информация:
Android WebView
построен с использованием WebKit. Хотя изначально он был частью AOSP, начиная с KitKat было принято решение выделить WebView
в отдельный компонент, который называется Android System WebView. По сути, это системное приложение Android, которое предустановлено на устройствах Android. Он периодически обновляется, как и другие системные приложения, такие как Google Play Services и приложение Play Store. Вы можете увидеть его в списке установленных системных приложений:
![Android System WebView]()
Изменения в Android 7.0:
Начиная с Android N, приложение Chrome будет использоваться для рендеринга любых/всех WebView
в сторонних приложениях Android. В телефонах с установленной ОС Android N приложение Android WebView System отсутствует вообще. На устройствах, которые получили OTA-обновление до Android N, веб-представление Android System отключено:
![WebView disabled]()
и
![WebView disabled]()
Кроме того, была введена поддержка нескольких локалей, когда устройства имеют более одного языка по умолчанию:
![enter image description here]()
Это имеет важное значение для приложений с несколькими языками. Если в вашем приложении WebView
, то они отображаются с помощью приложения Chrome. Поскольку Chrome сам по себе является приложением Android и работает в своем изолированном изолированном процессе, он не будет привязан к локали, установленной вашим приложением. Вместо этого Chrome вернется к основной локали устройства. Например, скажем, языковой стандарт вашего приложения установлен на ar-AE
, а основным языковым стандартом устройства является en-US
. В этом случае локаль Activity
, содержащая WebView
, изменится с ar-AE
на en-US
, и будут отображены строки и ресурсы из соответствующих папок локали. Вы можете увидеть мешанину строк/ресурсов LTR и RTL на тех Activity
, которые имеют WebView
s.
Решение:
Полное решение этой проблемы состоит из двух шагов:
ШАГ 1:
Сначала сбросьте локаль по умолчанию вручную в каждом Activity
или, по крайней мере, в каждом Activity
, который имеет 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());
}
Вызовите указанный выше метод перед вызовом setContentView(...)
в методе onCreate()
всех ваших действий. Параметр locale
должен быть значением по умолчанию Locale
, которое вы хотите установить. Например, если вы хотите установить арабский язык /UAE в качестве локали по умолчанию, вы должны передать new Locale("ar", "AE")
. Или, если вы хотите установить язык по умолчанию (то есть Locale
, который автоматически устанавливается операционной системой), вы должны передать Locale.US
.
ШАГ 2:
Кроме того, вам нужно добавить следующую строку кода:
new WebView(this).destroy();
в onCreate()
вашего класса Application
(если он у вас есть) и везде, где пользователь может менять язык. Это позаботится обо всех видах крайних случаев, которые могут возникнуть при перезапуске приложения после изменения языка (вы могли заметить строки на других языках или с противоположным выравниванием после изменения языка на Activities
, которые имеют WebView
на Android 7. 0++).
В качестве дополнения пользовательские вкладки Chrome теперь являются предпочтительным способом отображения веб-страниц в приложении.
Ссылки:
1. Android7.0 - изменения для WebView
.
2. Пониманиеисправлений безопасности для WebView и Android.
3. WebViewдля Android.
4. WebView:от "Работает на Chrome" до "Chrome".
5. NougatWebView.
6. Android7.0 Nougat.
7. ТайныAndroid N, часть 1: Android System WebView теперь просто "Chrome"?.
Ответ 2
Кажется, что ваш код устанавливает локаль в конфигурации самого приложения (MyApplication.getInstance()
). Тем не менее вам необходимо обновить конфигурацию контекста активности до раздувания представления содержимого активности. Я обнаружил, что изменить контекст приложения недостаточно (и, как оказалось, даже не нужно). Если я не обновляю каждый контекст действия, то поведение несовместимо между действиями.
То, как я подхожу к этому, относится к подклассу AppCompatActivity
(или Activity
, если не используется библиотека совместимости), а затем выводить все мои классы активности из этого подкласса. Вот упрощенная версия моего кода:
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;
}
}
}
Затем я обязательно вызову super.onCreate(savedInstanceState)
в каждом подклассе onCreate()
перед вызовом любых методов (например, setContentView()
), которые используют контекст.
Ответ 3
Прочитав все ответы, я обнаружил, что в каждом из них чего-то не хватает, поэтому вот решение, которое до сих пор работало для меня. Поскольку WebView переопределяет языковую конфигурацию контекста активности и контекста приложения, вы должны убедиться, что каждый раз, когда это происходит, вы вызываете метод, который сбрасывает эти изменения обратно. В моем случае я написал следующий класс, что мои действия, которые представляют эту проблему, расширяются (те, которые показывают 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);
}
}
}
}
Идея, опубликованная @Tobliug для сохранения первоначальной конфигурации до того, как WebView переопределит ее, сработала для меня, в моем конкретном случае я обнаружил, что это легче реализовать, чем другие опубликованные решения.
Важно то, что метод fix вызывается после выхода из WebView, например, при нажатии назад и в onStop.
Если webView отображается в диалоге, вы должны позаботиться о том, чтобы метод fix вызывался после закрытия диалогового окна, в основном в onResume и/или onCreate. И если веб-представление загружается непосредственно в onCreate действия, а не после этого в новом фрагменте, исправление также должно вызываться непосредственно после setContentView до установки заголовка действия и т.д. Если веб-представление загружается внутри фрагмента в действии, вызовите активность в onViewCreated фрагмента и активность должны вызывать метод fix.
Не все виды деятельности должны расширяться вышеупомянутым классом, как отмечается в ответе, что это излишнее и не нужно.
Эта проблема также не решена путем замены WebView на вкладки Google Chrome или открытия внешнего браузера.
Если вам действительно нужно настроить свои ресурсы для настройки всего списка языков, а не только одного, то вам нужно объединить это решение с тем, что находится на https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce
В моем случае это было не нужно.
Я также не нашел необходимости вызывать новый WebView (this).destroy(); как отмечено в ответе здесь.
Ответ 4
Такая же проблема. У меня грязное, но простое решение.
Поскольку я замечаю, что локаль по-прежнему хороша в функции Activity.onCreate(...) и не более достоверна в функции Activity.onPostCreate(...), я просто сохраняю Locale и заставляю ее в конце функции onPostCreate (...).
Здесь мы идем:
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);
}
Бонус - функция языкового изменения:
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);
}
}
Надеюсь, что это поможет.
Ответ 5
Ни один из ответов выше не помог мне, мне удалось reset локализовать приложение снова внутри onStop() метода активности, содержащей Webview
Ответ 6
Я хочу добавить еще один пример использования:
При возврате из операции веб-просмотра (то есть при отображении экрана оплаты и нажатии пользователем кнопки "назад") onCreate() предыдущей операции не выполняется, так что язык снова был сброшен. Чтобы избежать ошибок, мы должны сбросить локаль приложения в onResume()
базовой активности.
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());
}
Вызовите вышеуказанный метод в onResume() базовой активности или, по крайней мере, в веб-просмотре.
Изменить:
Если вы имеете дело с фрагментами, убедитесь, что этот метод вызывается при выходе пользователя из веб-просмотра.
Ответ 7
Если вы используете WebView только для отображения расширенного текста (текст с некоторыми абзацами или жирный и курсивный текст с разными размерами шрифтов), вы можете использовать TextView и Html.fromHtml(). TextViews не имеет проблем с настройками локали; -)
Ответ 8
Просто измените параметр для SEt Local Method с передачи BaseContext на "this" или "Точное действие", особенно на Android 7.0 и старше
Ответ 9
В Android N, когда вы выполняете new WebView()
, он добавит /system/app/WebViewGoogle/WebViewGoogle.apk
в путь к ресурсу, и если он не добавил к пути, это приведет к восстановлению ресурса.
Итак, если вы хотите решить вопрос, просто сделайте new WebView(application)
в приложении до того, как вы измените локальный.
Если вы знаете китайский, вы можете прочитать этот блог.