76 votes

Android - Le langage WebView change brusquement sur Android 7.0 et supérieur

J'ai une application multilingue dont la langue principale est l'anglais et la langue secondaire l'arabe.

Comme décrit dans le documentation ,

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

La configuration ci-dessus fonctionne correctement. Après avoir modifié le Locale à ar-AE Le texte et les ressources en arabe s'affichent correctement dans mes activités.

Cependant, chaque fois que je navigue vers un Activity avec un WebView et/ou un WebViewClient la locale, le texte et la direction de la mise en page reviennent brusquement à la valeur par défaut du périphérique.

D'autres indices :

  • Cela se produit uniquement sur un Nexus 6P avec Android 7.0 . Tout fonctionne correctement sur Android 6.0.1 et inférieur.
  • Le changement brusque de lieu se produit uniquement 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 les paramètres régionaux multiples, ce qui permet à l'utilisateur de définir plusieurs paramètres régionaux par défaut. Ainsi, si je définis la locale principale à Locale.UK :

enter image description here

Ensuite, en naviguant vers le WebView la locale passe de ar-AE à en-GB .

Modifications de l'API d'Android 7.0 :

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

Locale :

Configuration :

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

En outre...

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

  • Pour obtenir la locale par défaut, j'utilise Locale.getDefault() .

  • Pour définir la locale 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 puis-je le résoudre ?

Références :

1. Support RTL natif dans Android 4.2 .

2. Support multilingue - Langue et localité .

3. Méfiez-vous de la locale par défaut .

1 votes

honnêtement, ça ressemble à un bug de leur part. Avez-vous vérifié le bug track d'Android ?

0 votes

Pouvez-vous afficher le onCreate (ou autre code pertinent) de l'activité qui gonfle ou instancie le webview ?

0 votes

Monsieur, j'ai un problème similaire dans mon application et cette méthode ne fonctionne pas, pouvez-vous s'il vous plaît voir ma question et voir ce qui peut être le problème ? stackoverflow.com/questions/42105938/

79voto

cerebro Points 1075

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

La raison en est les changements apportés à la WebView et son paquet de support dans Android 7.0.

Le contexte :

Android WebView est construit en utilisant WebKit . Alors qu'il faisait à l'origine partie de l'AOSP, à partir de KitKat, il a été décidé de le séparer de l'AOSP. WebView dans un composant séparé appelé Système Android WebView . Il s'agit essentiellement d'une application système Android qui est préinstallée sur les appareils Android. Elle est périodiquement mise à jour, tout comme les autres applications système telles que Google Play Services et l'application Play Store. Vous pouvez la voir dans votre liste d'applications système installées :

Android System WebView

Changements dans Android 7.0 :

À partir d'Android N, l'application Chrome sera utilisée pour rendre tout ou partie des données. WebView dans les applications Android tierces. Sur les téléphones équipés d'Android N prêt à l'emploi, l'application Android WebView System n'est pas du tout présente. Dans les appareils qui ont reçu une mise à jour OTA vers Android N, l'application Android System WebView est désactivée :

WebView disabled

et

WebView disabled

De plus, la prise en charge multi-locale a été introduite, les appareils disposant de 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 a WebView puis ils sont rendus à l'aide de l'application Chrome. Comme Chrome est une application Android en soi s'exécutant dans son propre processus sandbox, il ne sera pas lié à la locale définie par votre application. Au lieu de cela, Chrome reviendra à la locale principale de l'appareil. Par exemple, si la locale de votre application est définie comme suit ar-AE tandis que la locale primaire de l'appareil est en-US . Dans ce cas, la locale de l Activity contenant un WebView passera de ar-AE à en-US et les chaînes et ressources des dossiers de la locale correspondante seront affichées. Vous pouvez voir un mélange de chaînes/ressources LTR et RTL sur ces dossiers. Activity qui ont WebView s.

La solution :

La solution complète de ce problème se compose de deux étapes :

ÉTAPE 1 :

D'abord, réinitialisez manuellement la locale par défaut dans chaque Activity ou au moins chaque Activity qui a 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 le onCreate() de toutes vos activités. Le site locale doit être le paramètre par défaut Locale que vous souhaitez régler. Par exemple, si vous souhaitez définir l'arabe/les Émirats arabes unis comme paramètres régionaux par défaut, vous devez passer le paramètre suivant new Locale("ar", "AE") . Ou si vous souhaitez définir la locale par défaut (c'est-à-dire l'option Locale qui est automatiquement définie par le système d'exploitation), vous devriez passer Locale.US .

ÉTAPE 2 :

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

new WebView(this).destroy();

dans le onCreate() de votre Application (si vous en avez une), et partout ailleurs où l'utilisateur peut changer la langue. Cela permettra de résoudre toutes sortes de problèmes qui peuvent survenir au redémarrage de l'application après un changement de langue (vous avez peut-être remarqué des chaînes dans d'autres langues ou avec l'alignement opposé après un changement de langue dans la classe Activities qui ont WebView sur Android 7.0++).

En guise d'addendum, Onglets personnalisés de Chrome sont désormais le moyen privilégié de rendre les pages web in-app.

Références :

1. Android 7.0 - changements pour WebView .

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

3. WebView pour Android .

4. WebView : De "Powered by Chrome" à Chrome tout court .

5. WebView Nougat .

6. Android 7.0 Nougat .

7. Mystères d'Android N, 1ère partie : Le WebView du système Android n'est plus que "Chrome" ? .

0 votes

Y a-t-il un problème ouvert dans Android concernant ce comportement bogué ? Il semble que ce soit un bogue dans le composant système/webView qui devrait être corrigé...

0 votes

@TalKanel : Cela semble provenir de la différence dans la façon dont les pages Web sont maintenant rendues in-app. Je ne suis pas sûr qu'il s'agisse d'un bug, mais plutôt d'une conséquence d'un nouveau processus d'arrière-plan.

0 votes

Étant donné qu'il n'y a pas de solution possible pour contourner cette "conséquence", je dirais que c'est un bug : Vous ne pouvez pas utiliser webView + plusieurs locales, quelque chose qui devrait être trivial dans Android 7. La solution que vous avez proposée ne prend pas en compte toutes les sortes de cas limites. Honnêtement, je me bats en ce moment même pour trouver une solution sans bug à ce problème.

21voto

Ted Hopp Points 122617

Votre code semble définir la locale dans la configuration de l'application elle-même ( MyApplication.getInstance() ). Cependant, vous devez mettre à jour la configuration du contexte de l'activité avant de gonfler la vue du contenu de l'activité. J'ai constaté que la modification du contexte de l'application n'est pas suffisante (et, en fin de compte, 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 cette question est de sous-classer AppCompatActivity (ou Activity (si je n'utilise pas la bibliothèque de compatibilité), puis je fais dériver 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 chaque sous-classe onCreate() méthode avant l'appel de toute méthode (telle que setContentView() ) qui utilisent le contexte.

0 votes

Je pense que vous devez changer la signature de la méthode fixupLocale en boolean au lieu de void.

0 votes

@MaherAbuthraa - Oups, oui. C'était un artefact de paraphraser mon propre code. Je me suis juste débarrassé du return puisque la valeur de retour n'est pas utilisée dans le code affiché.

0 votes

Ni la configuration du contexte de l'application ni celle du contexte de l'activité ne suffisent si vous ouvrez le webView dans votre activité. Consultez stackoverflow.com/a/9475663/3270968 , gunhansancar.com/changer-de-langue-programmatiquement-en-Android

5voto

David Points 525

Après avoir lu toutes les réponses, j'ai constaté qu'il manquait quelque chose dans chacune d'entre elles. Voici donc la solution qui a fonctionné pour moi jusqu'à présent. Puisque le WebView remplace la configuration de la langue du contexte de l'activité et du contexte de l'application, vous devez vous assurer qu'à chaque fois que cela se produit, vous appelez une méthode qui réinitialise ces changements. Dans mon cas, j'ai écrit la classe suivante que mes activités qui présentent ce problème étendent (celles qui affichent 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 postée par @Tobliug de sauvegarder la configuration initiale avant que la WebView ne la remplace a fonctionné pour moi, dans mon cas particulier, j'ai trouvé cela plus facile à mettre en œuvre que les autres solutions postées. Il est important que la méthode fix soit appelée après avoir quitté le WebView, par exemple en appuyant sur back et dans onStop. Si le WebView est affiché dans une boîte de dialogue, vous devez faire attention à ce que la méthode fix 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 pas ensuite dans un nouveau fragment, la méthode fix doit également être appelée directement après setContentView avant que le titre de l'activité ne soit défini, etc. Si le WebView est chargé à l'intérieur d'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 surcharge et ce n'est pas nécessaire. Ce problème ne se résout pas non plus en remplaçant la WebView par des onglets Google Chrome ou en ouvrant un navigateur externe.

Si vous avez vraiment besoin que la configuration des ressources permette de définir la liste complète des langues et pas seulement une, vous devez fusionner cette solution avec celle de l'adresse suivante https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce Dans mon cas, ce n'était pas nécessaire.

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

1voto

Tobliug Points 1980

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

Parce que j'observe que la locale est encore bonne dans la fonction Activity.onCreate(...) et n'est plus valide dans la fonction Activity.onPostCreate(...), j'enregistre simplement la locale et la force à la fin de la fonction onPostCreate(...).

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 linguistiques :

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 ça aidera.

0 votes

Mais ce problème avec Chrome se produit à partir de la version 7.0, donc je pense que la vérification isAtLeastJellyBeanMR1 est inutile.

0voto

EBLiS Points 120

Aucune des réponses ci-dessus ne m'a aidé, j'ai réussi à réinitialiser l'application locale à l'intérieur. onStop() de l'activité contenant le Webview

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X