77

我有一个多语言应用程序,主要语言是英语,次要语言是阿拉伯语。

文档中所述,

  • 我已android:supportsRtl="true"在清单中添加。
  • 我已将所有带有leftright属性的 xml 属性分别更改为startend
  • 我已经添加了阿拉伯语字符串strings-ar(对于其他资源也是如此)。

上述设置工作正常。更改为Localear-AE,阿拉伯语文本和资源会正确显示在我的活动中。

但是,每次我Activity使用 aWebView 和/或 a导航到 aWebViewClient时,区域设置、文本和布局方向都会突然恢复为设备默认值。

进一步提示:

  • 这只发生装有Android 7.0的 Nexus 6P 上。在 Android 6.0.1 及更低版本上一切正常。
  • 当我导航到Activity具有 aWebView和/或 a WebViewClient(并且我有多个)的位置时,才会发生区域设置的突然转变。它不会发生在任何其他活动上。

Android 7.0 支持多语言环境,允许用户设置多个默认语言环境。因此,如果我将主要语言环境设置为Locale.UK

在此处输入图像描述

然后在导航到 时WebView,区域设置从ar-AE 变为en-GB

Android 7.0 API 更改:

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. Android 4.2 原生 RTL 支持

2. 多语言支持 - 语言和区域设置

3. 警惕默认语言环境

4

13 回答 13

80

Ted Hopp 的回答设法解决了这个问题,但他没有解决为什么会发生这种情况的问题。

WebView原因是在 Android 7.0 中对类及其支持包所做的更改。

背景:

AndroidWebView是使用WebKit构建的。虽然它最初是 AOSP 的一部分,但从 KitKat 开始,它决定分拆WebView成一个名为Android System WebView的单独组件。它本质上是一个预装安卓设备的安卓系统应用程序。它会定期更新,就像其他系统应用程序(例如 Google Play Services 和 Play Store 应用程序)一样。您可以在已安装的系统应用程序列表中看到它:

安卓系统WebView

Android 7.0 变化

从 Android N 开始,Chrome 应用将用于渲染WebView第三方 Android 应用中的 any/all 。在具有 Android N 开箱即用的手机中,Android WebView System 应用程序根本不存在。在已收到 Android N OTA 更新的设备中,Android System WebView 被禁用:

WebView 已禁用

WebView 已禁用

此外,还引入了多语言环境支持,设备具有不止一种默认语言:

在此处输入图像描述

这对于具有多种语言的应用程序具有重要意义。如果您的应用程序有WebViews,那么这些是使用 Chrome 应用程序呈现的。因为 Chrome本身就是一个 Android 应用程序,在它自己的沙盒进程中运行,所以它不会绑定到您的应用程序设置的语言环境。相反,Chrome 将恢复到主要设备区域设置。例如,假设您的应用程序区域设置为ar-AE,而设备的主要区域设置为en-US。在这种情况下,Activity包含 a的语言环境WebView将从ar-AE变为en-US,并显示来自相应语言环境文件夹的字符串和资源。Activity您可能会在那些具有s 的 s上看到 LTR 和 RTL 字符串/资源的混搭WebView

解决方案:

这个问题的完整解决方案包括两个步骤:

第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您希望设置的默认值。例如,如果您希望将阿拉伯语/阿联酋设置为默认语言环境,则应通过new Locale("ar", "AE"). 或者,如果您希望设置默认语言环境(即Locale操作系统自动设置的语言环境),您应该通过Locale.US.

第2步:

此外,您需要添加以下代码行:

new WebView(this).destroy();

onCreate()您的Application班级中(如果您有的话),以及用户可能正在更改语言的其他任何地方。这将处理更改语言后应用程序重新启动时可能发生的各种边缘情况(您可能已经注意到其他语言中的字符串或在 Android 7.0++ 上更改语言后具有相反对齐方式的字符串)ActivitiesWebView

作为附录,Chrome 自定义标签现在是呈现应用内网页的首选方式。

参考:

1. Android 7.0 - 更改为WebView.

2. 了解WebView和Android安全补丁

3. 安卓网页视图

4. WebView:从“Powered by Chrome”到直接的 Chrome

5. 牛轧糖WebView

6. 安卓7.0牛轧糖

7. Android N 之谜,第 1 部分:Android 系统 WebView 现在只是“Chrome”?.

于 2016-11-18T11:07:12.120 回答
21

您的代码似乎在应用程序本身的配置中设置语言环境 ( 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()

于 2016-11-08T06:29:40.193 回答
5

在阅读了所有答案后,我发现每个答案都缺少一些东西,所以这是迄今为止对我有用的解决方案。由于 WebView 会覆盖 Activity 上下文和应用程序上下文的语言配置,因此您必须确保每次发生这种情况时都调用一个方法来重置这些更改。在我的情况下,我编写了以下课程,我的活动出现了这个问题(显示 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 覆盖之前保存初始配置对我有用,在我的特殊情况下,我发现这比发布的其他解决方案更容易实现。重要的是,在退出 WebView 后会调用 fix 方法,例如在按下返回和 onStop 时。如果 webView 显示在对话框中,您必须注意在关闭对话框后调用修复方法,主要是在 onResume 和/或 onCreate 中。如果 webView 直接在 Activity 的 onCreate 中加载,而不是在之后的新片段中,则还必须在 setContentView 之后直接调用修复,然后再设置 Activity 的标题等。如果 WebView 在 Activity 的片段中加载,请调用片段的 onViewCreated 中的活动和活动应该调用修复方法。并非所有活动都需要扩展上述类,如 aswer 中所述,这是一种矫枉过正的做法,而且没有必要。通过 Google Chrome 选项卡替换 WebView 或打开外部浏览器也无法解决此问题。

如果您真的需要您的资源配置来设置整个语言列表而不仅仅是一种语言,那么您需要将此解决方案与https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce中的解决方案合并 在我的情况下没有必要。

我也没有发现有必要调用 new WebView(this).destroy(); 如此处的答案所述。

于 2018-03-15T16:47:20.723 回答
4

这对于具有多种语言的应用程序具有重要意义。如果您的应用程序有 WebViews,那么这些是使用 Chrome 应用程序呈现的。因为 Chrome 本身就是一个 Android 应用程序,在它自己的沙盒进程中运行,所以它不会绑定到您的应用程序设置的语言环境。相反,Chrome 将恢复到主要设备区域设置。例如,假设您的应用程序区域设置为 ar-AE,而设备的主要区域设置为 en-US。在这种情况下,包含 WebView 的 Activity 的 locale 将从 ar-AE 更改为 en-US,并显示相应 locale 文件夹中的字符串和资源。您可能会在具有 WebView 的那些 Activity 上看到 LTR 和 RTL 字符串/资源的混杂。

解决方案:

这个问题的完整解决方案包括两个步骤:

第1步:

首先,在每个 Activity 中手动重置默认语言环境,或者至少在每个具有 WebView 的 Activity 中手动重置。

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());
    }

在所有活动的 onCreate() 方法中调用 setContentView(...) 之前调用上述方法。locale 参数应该是您希望设置的默认 Locale。例如,如果您希望将阿拉伯语/阿联酋设置为默认语言环境,则应传递 new Locale("ar", "AE")。或者如果你想设置默认的语言环境(即操作系统自动设置的语言环境),你应该通过Locale.US。

第2步:

此外,您需要添加以下代码行:

new WebView(this).destroy();

在您的 Application 类的 onCreate() 中(如果有的话),以及用户可能正在更改语言的其他任何地方。这将处理更改语言后应用程序重新启动时可能发生的各种边缘情况(您可能已经注意到其他语言的字符串或在更改具有 Android 7.0++ 上的 WebViews 的活动上的语言后具有相反对齐方式的字符串)。

作为附录,Chrome 自定义标签现在是呈现应用内网页的首选方式。

于 2020-01-07T06:21:18.377 回答
1

我想在这里再添加一个用例

当从 webview 活动中按下(即显示支付屏幕和用户按下返回按钮)时,前一个活动的 onCreate() 不会执行,因此该语言再次被重置。为了保持它没有错误,我们必须在基本 Activity中重置应用程序区域设置。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() 或至少在 webview 活动中调用上述方法。

编辑: 如果您正在处理片段,请确保在用户退出 webview 时调用此方法。

于 2017-08-24T09:54:44.103 回答
1

这里同样的问题。我有一个肮脏但简单的解决方案。

因为我观察到 Activity.onCreate(...) 函数中的语言环境仍然很好,并且在 Activity.onPostCreate(...) 函数中不再有效,所以我只是保存了语言环境并在 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);
    }
}

希望它会有所帮助。

于 2017-09-05T14:15:33.323 回答
1

我注意到这仅在您第一次在应用程序中使用网络视图时发生,但此后不会发生(即,如果从应用程序内部更改语言并且您再次打开网络视图或另一个网络视图,那么这次 webview 不会像第一次那样改回语言。因此,基于此,我所做的一个简单解决方案是我添加了一个 webview,其可见性已转到我在应用程序中的第一个活动(或主机活动,如果您使用带有片段的单个活动应用程序)然后我在调用 setContentView 之后将我的语言环境应用到该活动的 onCreate 方法中。所以在我的 activity.xml 中,我将拥有:

<WebView
        android:id="@+id/webview"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

在我的活动中:

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)//at this moment and because there is the webview in the xml, the language will be reverted to the device's language
    
    
            updateLocal(this ,"en")//this reset it back to whatever language you want to use(from shared preferences for example )
}

和 updateLocal 具有如下代码:

fun updateLocale(
            c: Context,
            languageToSwitchTo: String
        ): Context {
            val locale = Locale(languageToSwitchTo)
            Locale.setDefault(locale)
            var context = c
            val resources = context.resources

            val configuration: Configuration = resources.configuration
            configuration.setLayoutDirection(locale)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                val localeList = LocaleList(locale)
                LocaleList.setDefault(localeList)
                configuration.setLocales(localeList)
            } else {
                configuration.locale = locale

            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
                context = context.createConfigurationContext(configuration)
            }

            resources.updateConfiguration(configuration, resources.displayMetrics)

            return context
        }
于 2021-01-20T14:22:49.320 回答
0

上面的答案都没有帮助我,我设法在包含 Webview 的活动的onStop()方法中再次重置了应用程序区域设置

于 2017-02-22T14:23:59.830 回答
0

在 Android N 中,当你这样做时new WebView(),它会添加/system/app/WebViewGoogle/WebViewGoogle.apk到资源路径中。如果它还没有添加到路径中,它将导致 Resource 重新创建(仅在您第一次在应用程序中使用 WebView 时)。

因此,如果您想解决问题,请new WebView(applicationContext)Application#OnCreate()更改语言环境之前进行。

如果你懂中文,你可以阅读这个博客

于 2017-08-18T03:24:04.840 回答
0

如果您仅使用 WebView 来显示富文本(带有一些段落的文本或不同字体大小的粗斜体文本),那么您可以使用 TextView 和Html.fromHtml()代替。TextViews 与区域设置没有问题;-)

于 2017-09-18T14:04:34.867 回答
0

只需将 SEt 本地方法的参数从传递 BaseContext 更改为“this”或确切的活动,特别是在 android 7.0 及更早版本上

于 2019-10-03T09:03:29.407 回答
0

我在片段中面临同样的问题。通过在onDestroyView函数中再次设置语言来解决这个问题

@Override
    public void onDestroyView() {
        Utils.setLanguage(requireActivity());
        super.onDestroyView();
    }
于 2020-11-24T12:01:20.693 回答
0

我通过使用 lang 标签更改 HTML 的语言来解决发音问题。 HTML 语言辅助功能

于 2021-03-24T10:35:34.303 回答