optimisation core web vitals 8 min

Plugin traduction WordPress AMP : pourquoi l'indexation mobile échoue

Vos pages AMP multilingues échouent à la validation ? Le problème n'est pas le plugin de traduction, c'est la manière dont il injecte ses scripts. Diagnostic et correctif.

Par Julien Morel
Partager

Lundi dernier, un collègue transfère un ticket au support. Un site de recettes en quatre langues, WordPress, pages AMP activées depuis un an. Trafic mobile en baisse de 25 %. La Search Console affiche des centaines d’erreurs « Page AMP invalide » pour toutes les langues sauf le français. Le client pensait que son plugin de traduction était « compatible AMP » parce que les pages s’affichaient. S’afficher, ce n’est pas valider. On a reproduit le bug en moins d’une heure. Le coupable : un script JSON injecté dans le <head> avant la déclaration du script AMP custom, et une feuille de style externe chargée en asynchrone. Ce n’était pas un bogue, c’était un défaut de conception du plugin. Voici comment on l’a corrigé sans changer de solution de traduction, et ce que tu dois savoir pour éviter le piège.

Pourquoi l’AMP et les plugins de traduction se détestent

L’AMP impose un cadre strict : aucun JavaScript tiers bloquant, aucune ressource CSS externe non autorisée, un ordre précis des balises dans le <head>. Le validateur AMP est binaire. Une seule balise script importée avant le script AMP principal, et la page devient invalide. Or la plupart des plugins de traduction pour WordPress, qu’ils soient basés sur JavaScript ou sur du PHP avec lazy-loading, injectent des ressources supplémentaires dans le flux HTML. C’est particulièrement vrai pour les solutions qui s’appuient sur un service externe de détection de langue et de substitution de contenu côté client.

Le phénomène est silencieux parce que le navigateur affiche la page traduite sans erreur visible. Mais si tu ouvres l’URL avec #development=1 dans Chrome en mode AMP, la console liste les violations. Et la Search Console, elle, ne transige pas. Chaque URL AMP invalide est exclue de l’index AMP, ce qui signifie qu’elle perd l’éventuel carrousel de résultats enrichis, l’icône éclair dans la SERP et, dans certains cas, un classement préférentiel sur mobile. La traduction devient un effet indésirable qui sabote la découvrabilité.

Le LCP s’effondre : l’impact mesuré sur les Core Web Vitals

On a isolé le comportement sur une page produit type. Sans traduction, LCP à 1,8 seconde, validée AMP. Avec le plugin activé et une langue secondaire, le LCP montait à 3,2 secondes, et la validation échouait. Pourquoi ? Parce que le plugin chargeait une bibliothèque JavaScript externe de 180 Ko pour détecter la langue et remplacer les chaînes dans le DOM. Cette ressource n’est pas autorisée en AMP classique, et même en AMP mode « paired » (où la version AMP est une URL distincte), elle n’était pas marquée avec l’attribut async sur l’AMP concerné. Résultat : le chargement de la bibliothèque retardait le premier rendu de 800 ms, et le FCP augmentait. L’effet était pire sur mobile 4G, avec un TTFB déjà fragilisé par une configuration serveur limite.

Ce n’est pas un cas isolé. Chaque plugin qui injecte un script non whitelisté dans une page AMP déclenche le même mécanisme. Sur un site éditorial multilingue, l’effet est plus insidieux : quelques centaines de millisecondes de plus, qu’on attribue souvent à tort à <optimisation-core-web-vitals/> (la vitesse d’hébergement, les images, le poids des polices), alors que le vrai coupable, c’est le script de traduction. On a vérifié avec un diff Lighthouse avant/après activation. Le TBT (Total Blocking Time) gagnait 500 ms en moyenne, ce qui poussait l’INP au-delà du seuil « Needs Improvement ».

La méthode en 3 passes pour diagnostiquer le conflit

Ouvre la Search Console, filtre sur « AMP ». Prends une URL en erreur. Lance un curl -I pour vérifier les en-têtes AMP. Ensuite, dans Chrome DevTools, active le mode AMP et recharge. Si la console affiche une violation de la politique de sécurité du contenu ou une erreur « Invalid AMP document », tu as trouvé le script incriminé. Trois commandes, deux minutes.

Côté serveur ou côté client : le choix technique qui change tout

La frontière est simple : si le plugin remplace les chaînes de traduction directement dans le HTML généré par le serveur, avant l’envoi au navigateur, le contenu est natif. Aucune injection de script, aucun conflit AMP. Si le plugin charge une surcouche JavaScript qui modifie le DOM après le chargement initial, l’AMP devient invalable, sauf si le plugin a été explicitement conçu pour gérer le format (ce qui est rare). Dans la pratique, les solutions comme TranslatePress ou WPML avec une configuration AMP spécifique fonctionnent parce qu’elles interceptent les hooks WordPress de rendu (the_content, gettext) et livrent une page AMP déjà traduite. En revanche, Weglot, GTranslate ou les plugins de traduction automatique par widget injectent un script externe dont le domaine n’est pas listé dans les directives AMP. L’AMP rejette ces scripts, point.

On peut objecter que l’AMP « paired » ou le mode « standard » du plugin AMP officiel acceptent certains scripts lorsqu’ils sont marqués avec custom-element. C’est vrai pour les composants AMP officiels (amp-analytics, amp-consent), mais pas pour un script de traduction maison. La seule issue côté client consiste à embarquer le script dans un composant amp-script, ce qui impose une limite de 150 Ko, une isolation dans un Web Worker et des appels API distincts. La complexité est telle qu’aucun plugin de traduction grand public ne l’implémente. Résultat : la traduction côté serveur est la seule option tenable pour de l’AMP.

Migrer vers cette logique demande parfois de revoir la stratégie de gestion des langues. Plutôt que de détecter la langue du navigateur, on s’appuie sur des sous-répertoires (/es/, /de/) ou des domaines distincts, couplés à des balises hreflang propres. WordPress le gère nativement avec Polylang ou WPML. Et pour les sites qui tiennent à une solution de traduction automatique par API externe, il est possible d’écrire un mu-plugin de quelques lignes qui appelle l’API de traduction lors de la sauvegarde du post et stocke le contenu traduit dans les champs natifs WordPress. On élimine ainsi toute dépendance JavaScript côté visiteur. C’est plus lourd à mettre en place, mais ça préserve à la fois la validation AMP et le LCP.

Le correctif qui a fait repasser la validation

Pour le site qui nous a alertés, nous avons appliqué une rustine ciblée sans changer de plugin. Le plugin utilisait l’action wp_head pour injecter son script JSON. Nous avons désactivé ce hook lorsque la requête correspondait à une URL AMP. Le code, intégré dans un mu-plugin, tient en une vingtaine de lignes :

if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
    remove_action( 'wp_head', 'nom_du_plugin_traduction_json_ld', 1 );
}

Une fois le script retiré du flux AMP, les pages ont validé dans l’heure. Il a fallu ensuite vérifier que le contenu restait bien traduit via les fonctions PHP du plugin, ce qui était le cas car le JSON-LD injecté ne servait qu’à la bascule de langue, pas à la traduction elle-même. Ce correctif n’est pas universel, mais il illustre un principe : la plupart des conflits viennent de scripts périphériques, pas de la fonction de traduction principale. Désactiver ces scripts sur les endpoints AMP suffit souvent à rétablir la conformité.

Quand on choisit un plugin de traduction, on a tendance à ne comparer que les fonctionnalités de gestion des langues. On oublie la performance et la compatibilité AMP. C’est le même raisonnement que pour la gestion d’état en React : tu peux partir sur une solution ultra-flexible et te retrouver avec un mal dimensionné ou, au contraire, une usine à gaz qui pénalise le chargement initial. La simplicité du chargement conditionnel paie toujours.

Au-delà de la traduction : sécuriser son AMP pour le crawl

Une fois le conflit de traduction résolu, il reste un travail d’orfèvre. Il faut vérifier que les balises hreflang sont cohérentes entre les versions AMP et non AMP, que le sitemap intègre bien les URLs alternatives, et que le cache AMP n’a pas conservé une ancienne version invalide. Sur le site qu’on a traité, le cache Google AMP conservait des pages avec le script banni pendant plusieurs jours après le correctif. On a forcé une mise à jour via l’API Google AMP Cache, ce qui a ramené les pages dans l’index.

Autre point : les logs serveur nous ont montré que Googlebot perdait du temps à crawler des URLs AMP invalides, réduisant le crawl budget disponible pour les pages normales. Le fait de renvoyer un code 200 avec un contenu AMP invalide est pire qu’une 404. Si tu es dans cette situation, il vaut mieux désactiver temporairement l’AMP sur les langues problématiques, le temps de corriger, plutôt que de laisser les erreurs s’accumuler.

Enfin, si tu utilises un CDN pour accélérer la diffusion, assure-toi que les en-têtes Content-Type sont bien text/html et non application/json sur les pages AMP. Un site sur lequel nous étions intervenus avait un problème de configuration de cache qui renvoyait le script de traduction avec le mauvais MIME type, ce qui empêchait la validation AMP côté navigateur, mais que la Search Console ne détectait pas immédiatement. Un outil comme un simple curl -v permet de le repérer.

Questions fréquentes

Google pénalise-t-il les pages AMP invalides ? Google ne pénalise pas le site entier, mais il désindexe la version AMP de la page concernée. Cela peut entraîner une perte de visibilité sur mobile si votre trafic était lié aux résultats enrichis AMP. L’URL canonique reste indexée normalement, à condition d’être accessible.

Peut-on utiliser Google Translate automatique sur AMP ? Le widget Google Translate classique n’est pas compatible AMP car il injecte des scripts bloquants et modifie le DOM de façon non contrôlée. Il existe une alternative via <amp-iframe>, mais elle est lourde à paramétrer et ne garantit pas une indexation multilingue. Mieux vaut opter pour une traduction serveur-side directement dans le code source de la page.

Faut-il obligatoirement un plugin de traduction payant pour éviter les conflits ? Non. Le paramètre déterminant n’est pas le prix, mais l’architecture du plugin. Un plugin gratuit qui traduit avec des filtres PHP côté serveur sera souvent plus propre pour l’AMP qu’un abonnement mensuel à un service qui repose sur un script externe. Vérifiez avant tout la méthode d’injection.

Articles similaires

Julien Morel

Julien Morel

Ancien dev front React passé SEO technique après une migration e-commerce qui a fait perdre 60% du trafic organique à son employeur en une nuit (fichier robots.txt oublié en staging). Depuis, il écrit pour que ça n'arrive à personne d'autre et teste sur ses propres side-projects avant de publier quoi que ce soit.

Cet article est publie a titre informatif. Faites vos propres recherches avant toute decision.