77

第一言語が英語で第二言語がアラビア語の多言語アプリがあります。

ドキュメントに記載されているように、

  • android:supportsRtl="true"マニフェストに追加しました。
  • leftおよびright属性を持つすべての xml プロパティをそれぞれ および に変更しstartましendた。
  • アラビア語の文字列を追加しましたstrings-ar(他のリソースについても同様です)。

上記のセットアップは正常に機能します。を に変更するLocalear-AE、アクティビティにアラビア語のテキストとリソースが正しく表示されます。

ただし、および/またはActivityを使用してに移動するたびに、ロケール、テキスト、およびレイアウトの方向が突然デバイスのデフォルトに戻ります。WebViewWebViewClient

さらなるヒント:

  • これは、 Android 7.0を搭載した Nexus 6P でのみ発生します。Android 6.0.1 以下ではすべて正常に動作します。
  • ロケールの突然のシフトは、および/またはを含む に移動した場合にのみ発生します(そして、いくつかあります)。他のアクティビティでは発生しません。ActivityWebViewWebViewClient

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 の回答は問題を解決することに成功しましたが、なぜこれが発生するのかという問題には対処しませんでした。

その理由は、WebViewAndroid 7.0 でクラスとそのサポート パッケージに加えられた変更です。

バックグラウンド:

AndroidはWebKitWebViewを使用して構築されています。元々は AOSP の一部でしたが、KitKat 以降、 Android System WebViewと呼ばれる別のコンポーネントにスピンオフすることが決定されました。これは基本的に、Android デバイスにプリインストールされている Android システム アプリです。Google Play サービスや Play ストア アプリなどの他のシステム アプリと同様に、定期的に更新されます。インストールされているシステム アプリのリストで確認できます。WebView

Android システムの WebView

Android 7.0 の変更点:

Android N 以降では、Chrome アプリを使用してWebView、サードパーティの Android アプリですべての をレンダリングします。すぐに使用できる Android N を搭載した携帯電話には、Android WebView System アプリはまったく存在しません。Android N への OTA アップデートを受信したデバイスでは、Android システム WebView が無効になっています。

WebView 無効

WebView 無効

さらに、マルチロケール サポートが導入され、複数のデフォルト言語を持つデバイスが使用されます。

ここに画像の説明を入力

これは、複数の言語を持つアプリにとって重要な結果をもたらします。アプリに がある場合、WebViewそれらは Chrome アプリを使用してレンダリングされます。Chrome はそれ自体が Android アプリであり、独自のサンドボックス プロセスで実行されるため、アプリで設定されたロケールにバインドされません。代わりに、Chrome はプライマリ デバイス ロケールに戻ります。たとえば、アプリのロケールが に設定されてar-AEいて、デバイスのプライマリ ロケールが であるとしますen-US。この場合、Activityを含むのロケールが からにWebView変わり、対応するロケール フォルダの文字列とリソースが表示されます。が含まれる に、LTR と RTL の文字列/リソースが混在しているのが見られる場合があります。ar-AEen-USActivityWebView

ソリューション:

この問題の完全な解決策は、次の 2 つの手順で構成されます。

ステップ1:

最初に、すべてのActivity、または少なくとも.ActivityWebView

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()ます。たとえば、アラビア語/アラブ首長国連邦をデフォルトのロケールとして設定したい場合は、 を渡す必要があります。または、デフォルトのロケール (つまり、オペレーティング システムによって自動的に設定されるロケール) を設定する場合は、 .localeLocalenew Locale("ar", "AE")LocaleLocale.US

ステップ2:

さらに、次のコード行を追加する必要があります。

new WebView(this).destroy();

onCreate()あなたのクラスApplication(もしあれば)の中で、そしてユーザーが言語を変更しているかもしれない他の場所で。これにより、言語を変更した後にアプリを再起動したときに発生する可能性のあるあらゆる種類のエッジ ケースが処理されます ( Android 7.0++ で言語Activitiesを変更した後に、他の言語の文字列または反対の配置で文字列が表示される場合があります)。WebView

補足として、Chrome カスタム タブは現在、アプリ内 Web ページをレンダリングするための推奨される方法です。

参考文献:

1. Android 7.0 - の変更WebView

2. WebView と Android のセキュリティ パッチを理解する

3. Android 用の WebView

4. WebView: 「Powered by Chrome」からそのまま Chrome へ

5. ヌガー WebView

6. Android 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 はアクティビティのコンテキストとアプリケーション コンテキストの言語構成をオーバーライドするため、これが発生するたびに、これらの変更を元に戻すメソッドを呼び出すようにする必要があります。私の場合、この問題を提示するアクティビティが拡張される次のクラスを作成しました(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 メソッドが呼び出されることです。ダイアログに webView が表示されている場合は、ダイアログを閉じた後に fix メソッドが呼び出されるように注意する必要があります。ほとんどの場合、onResume や onCreate で行われます。また、webView がアクティビティの onCreate に直接読み込まれ、後で新しいフラグメントに読み込まれない場合は、setContentView の直後にアクティビティのタイトルを設定する前に、修正を直接呼び出す必要があります。たとえば、WebView がアクティビティのフラグメント内に読み込まれる場合は、フラグメントの onViewCreated 内のアクティビティと、アクティビティが fix メソッドを呼び出す必要があります。すべてのアクティビティで上記のクラスを拡張する必要があるわけではありません。これはやり過ぎであり、必要ではありません。この問題は、WebView を Google Chrome タブに置き換えたり、外部ブラウザを開いたりしても解決されません。

1つだけでなく、言語のリスト全体を設定するためにリソース構成が本当に必要な場合は、このソリューションをhttps://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ceのソリューションとマージする必要が あります必要はありません。

new WebView(this).destroy(); を呼び出す必要もありませんでした。こちらの回答に記載されているとおりです。

于 2018-03-15T16:47:20.723 に答える