web-dev-qa-db-fra.com

Android - La langue de WebView change brusquement sur Android 7.0 et supérieur)

J'ai une application multilingue avec la langue principale, l'anglais et la langue secondaire, l'arabe.

Comme décrit dans la documentation ,

  • J'ai ajouté Android:supportsRtl="true" Dans le manifeste.
  • J'ai changé toutes les propriétés xml avec les attributs left et right en start et end respectivement.
  • J'ai ajouté des chaînes de langue arabe dans strings-ar (Et de même pour d'autres ressources).

La configuration ci-dessus fonctionne correctement. Après avoir changé le Locale en ar-AE, Le texte et les ressources arabes sont correctement affichés dans mes activités.

Cependant, chaque fois que je navigue vers un Activity avec un WebView et/ou un WebViewClient, les paramètres régionaux, le texte et la direction de la mise en page reviennent brusquement à la valeur par défaut de l'appareil.

Autres conseils:

  • Cela se produit seulement sur un Nexus 6P avec Android 7.0 . Tout fonctionne correctement sur Android 6.0.1 et inférieur.
  • Le changement brusque de paramètres régionaux se produit seulement lorsque je navigue vers un Activity qui a un WebView et/ou un WebViewClient (et j'en ai plusieurs). Il ne se produit sur aucune des autres activités.

Android 7.0 prend en charge plusieurs paramètres régionaux, permettant à l'utilisateur de définir plusieurs paramètres régionaux par défaut. Donc, si je règle les paramètres régionaux principaux sur Locale.UK:

enter image description here

Ensuite, lorsque vous accédez à WebView, les paramètres régionaux passent de ar-AE À en-GB.

Modifications apportées à l'API Android 7.0:

Comme indiqué dans la liste des changements d'API , de nouvelles méthodes relatives aux paramètres régionaux ont été ajoutées aux classes suivantes dans l'API 24:

Locale:

Configuration:

Cependant, je crée mon application avec l'API 23 et n'utilise aucune de ces nouvelles méthodes.

De plus ...

  • Le problème se produit également sur l'émulateur Nexus 6P.

  • Pour obtenir les paramètres régionaux par défaut, j'utilise Locale.getDefault() .

  • Pour définir les paramètres régionaux par défaut, j'utilise le code suivant:

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

Quelqu'un a-t-il déjà rencontré ce problème? Quelle en est la raison et comment résoudre ce problème?

Références:

1. Prise en charge RTL native dans Android 4.2 .

2. Support multilingue - Langue et paramètres régionaux =.

3. Méfiez-vous des paramètres régionaux par défaut =.

54
Y.S

réponse de Ted Hopp a réussi à résoudre le problème, mais il n'a pas abordé la question de pourquoi cela se produit.

La raison en est les modifications apportées à la classe WebView et son package de support dans Android 7.0.

Contexte:

WebView d'Android est construit en utilisant WebKit . Alors qu'il faisait à l'origine partie d'AOSP, à partir de KitKat, il a été décidé de dériver WebView dans un composant distinct appelé Android System WebView . Il s'agit essentiellement d'une application système Android qui est préinstallée avec les appareils Android. Il est régulièrement mis à jour, tout comme d'autres applications système telles que les services Google Play et l'application Play Store. Vous pouvez le voir dans votre liste d'applications système installées:

Android System WebView

Modifications d'Android 7.0 :

À partir de Android N, l'application Chrome sera utilisée pour afficher tout/tous WebView s dans des applications tierces Android. Dans les téléphones qui ont Android N prêt à l'emploi, l'application système WebView Android n'est pas du tout présente. Sur les appareils qui ont reçu une mise à jour OTA vers Android N, la Android System WebView est désactivée:

WebView disabled

et

WebView disabled

De plus, une prise en charge multi-locale a été introduite, avec des appareils ayant plus d'une langue par défaut:

enter image description here

Cela a une conséquence importante pour les applications qui ont plusieurs langues. Si votre application possède WebView, ceux-ci sont rendus à l'aide de l'application Chrome. Comme Chrome est une application Android en elle-même , exécutée dans son propre processus en bac à sable, elle ne sera pas liée aux paramètres régionaux définis par votre application. À la place, Chrome reviendra aux paramètres régionaux du périphérique principal. Par exemple, supposons que les paramètres régionaux de votre application soient définis sur ar-AE, Tandis que les paramètres régionaux principaux de l'appareil sont en-US. Dans ce cas, les paramètres régionaux de Activity contenant un WebView passeront de ar-AE À en-US, Et les chaînes et ressources des dossiers de paramètres régionaux correspondants seront affiché. Vous pouvez voir un méli-mélo de chaînes/ressources LTR et RTL sur les Activity qui ont WebView.

La solution:

La solution complète à ce problème comprend deux étapes:

ÉTAPE 1:

Tout d'abord, réinitialisez manuellement les paramètres régionaux par défaut dans tous les Activity ou au moins tous les Activity qui ont un 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());
}

Appelez la méthode ci-dessus avant d'appeler setContentView(...) dans la méthode onCreate() de toutes vos activités. Le paramètre locale doit être la valeur par défaut Locale que vous souhaitez définir. Par exemple, si vous souhaitez définir l'arabe/les Émirats arabes unis comme paramètres régionaux par défaut, vous devez passer new Locale("ar", "AE"). Ou si vous souhaitez définir les paramètres régionaux par défaut (c'est-à-dire le Locale qui est automatiquement défini par le système d'exploitation), vous devez passer Locale.US.

ÉTAPE 2:

En outre, vous devez ajouter la ligne de code suivante:

new WebView(this).destroy();

dans la onCreate() de votre classe Application (si vous en avez une), et partout où l'utilisateur peut changer la langue. Cela prendra en charge toutes sortes de cas Edge qui peuvent se produire au redémarrage de l'application après avoir changé la langue (vous avez peut-être remarqué des chaînes dans d'autres langues ou avec l'alignement opposé après avoir changé la langue sur Activities qui ont WebView s sur Android 7.0 ++).

En complément, onglets personnalisés Chrome sont désormais le moyen préféré de rendre les pages Web dans l'application.

Références:

1. Android 7.0 - modifications pour WebView .

2. Comprendre WebView et Android correctifs de sécurité .

3.. WebView pour Android .

4. WebView: de "Powered by Chrome" à directement Chrome .

5. Nougat WebView .

6. Android 7.0 Nougat .

7. Android N Mysteries, partie 1: Android System WebView est juste "Chrome" maintenant? .

57
Y.S

Votre code semble définir les paramètres régionaux dans la configuration de l'application elle-même (MyApplication.getInstance()). Cependant, vous devez mettre à jour la configuration du contexte d'activité avant de gonfler la vue de contenu de l'activité. J'ai trouvé que modifier le contexte de l'application n'était pas suffisant (et, en fait, ce n'est même pas nécessaire). Si je ne mets pas à jour chaque contexte d'activité, le comportement est incohérent entre les activités.

La façon dont j'aborde ceci est de sous-classe AppCompatActivity (ou Activity, si vous n'utilisez pas la bibliothèque de compatibilité), puis dérivez toutes mes classes d'activité de cette sous-classe. Voici une version simplifiée de mon 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;
        }
    }
}

Ensuite, je m'assure d'appeler super.onCreate(savedInstanceState) dans la méthode onCreate() de chaque sous-classe avant en appelant toutes les méthodes (telles que setContentView()) qui utilisent le contexte .

20
Ted Hopp

Après avoir lu toutes les réponses, j'ai découvert qu'il y avait quelque chose qui manquait dans chacune, alors voici la solution qui a fonctionné pour moi jusqu'à présent. Étant donné que WebView remplace la configuration linguistique du contexte de l'activité et du contexte de l'application, vous devez vous assurer que chaque fois que cela se produit, vous appelez une méthode qui réinitialise ces modifications. Dans mon cas, j'ai écrit au cours suivant que mes activités qui présentent ce problème s'étendent (celles montrant une 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);
        }

    }
}
}

L'idée publiée par @Tobliug pour enregistrer la configuration initiale avant que WebView ne l'emporte, cela a fonctionné pour moi, dans mon cas particulier, j'ai trouvé que c'était plus facile à implémenter que les autres solutions publiées. Il est important que la méthode fix soit appelée après la sortie de WebView, par ex. en appuyant en arrière et sur onStop. Si la vue Web est affichée dans une boîte de dialogue, vous devez veiller à ce que la méthode de correction soit appelée après la fermeture de la boîte de dialogue, principalement dans onResume et/ou onCreate. Et si le WebView est directement chargé dans onCreate de l'activité et non par la suite dans un nouveau fragment, le correctif doit également être appelé directement après setContentView avant que le titre de l'activité ne soit défini, etc. Si le WebView est chargé dans un fragment de l'activité, appelez l'activité dans onViewCreated du fragment et l'activité doit appeler la méthode fix. Toutes les activités n'ont pas besoin d'étendre la classe ci-dessus comme indiqué dans une réponse, c'est une surpuissance et pas nécessaire. Ce problème n'est pas non plus résolu en remplaçant WebView par Google Chrome Tabs ou en ouvrant un navigateur externe.

Si vous avez vraiment besoin de la configuration de vos ressources pour avoir la liste complète des langues définies et pas seulement une, alors vous devrez fusionner cette solution avec celle de https://Gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce Dans mon cas, ce n'était pas nécessaire.

Je n'ai pas non plus jugé nécessaire d'appeler new WebView (this) .destroy (); comme indiqué dans une réponse ici.

4
David

Même problème ici. J'ai une solution sale, mais simple.

Parce que j'observe que les paramètres régionaux sont toujours bons dans la fonction Activity.onCreate (...) et qu'ils ne sont plus valides dans la fonction Activity.onPostCreate (...), je sauvegarde simplement les paramètres régionaux et les force à la fin de onPostCreate (...) une fonction.

Et c'est parti :

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 - la fonction de modification des paramètres régionaux:

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

J'espère que cela vous aidera.

1
Tobliug

Aucune des réponses ci-dessus ne m'a aidé, j'ai réussi à réinitialiser à nouveau les paramètres régionaux de l'application à l'intérieur onStop () méthode de l'activité contenant la vue Web

0
MohammadReza

Si vous utilisez WebView uniquement pour afficher du texte riche (texte avec quelques paragraphes ou texte en gras et en italique dans différentes tailles de police), vous pouvez utiliser TextView et Html.fromHtml () à la place. TextViews n'a aucun problème avec les paramètres régionaux ;-)

0
Ben

Je veux ajouter un autre cas d'utilisation ici:

Lorsque vous appuyez en arrière à partir de l'activité de visualisation Web (c'est-à-dire en montrant l'écran de paiement et le bouton de retour de l'utilisateur), onCreate () de l'activité précédente ne s'exécute pas, de sorte que la langue a été réinitialisée à nouveau. Pour le garder sans bug, nous devons réinitialiser les paramètres régionaux de l'application dans onResume() de l'activité de base.

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

Appelez la méthode ci-dessus dans onResume () de l'activité de base ou au moins dans l'activité de visualisation Web.

Edit: Si vous avez affaire à des fragments, assurez-vous que cette méthode est appelée lorsque l'utilisateur quitte Webview.

0
Akhilesh Dhar Dubey