Un projet headless multilingue diffère d’un site WordPress classique multilingue sur un point essentiel : le routage par langue (préfixe d’URL, sous-domaine, ou domaine dédié) n’est plus géré par WordPress et l’extension de traduction, mais par le front lui-même. WordPress reste la source de vérité pour le contenu traduit, mais c’est au front de décider comment structurer les URL et de récupérer la bonne langue au bon moment.
Cet article couvre l’exposition des langues et traductions via WPGraphQL avec Polylang ou WPML, et la construction du routage localisé côté front. Il ne traite pas la configuration d’un site WordPress multilingue classique avec thème PHP, dont les mécanismes de routage intégrés (sous-répertoires générés automatiquement) ne s’appliquent pas de la même façon en headless.
Polylang avec WPGraphQL for Polylang
L’extension WPGraphQL for Polylang ajoute au schéma GraphQL les champs nécessaires pour connaître la langue de chaque contenu et accéder à ses traductions liées :
query ArticleAvecTraductions($slug: ID!) {
post(id: $slug, idType: SLUG) {
title
content
language {
code
locale
}
translations {
language {
code
}
slug
}
}
}
Le champ translations renvoie la liste des versions traduites du même contenu, avec leur code de langue et leur slug propre — indispensable pour construire un sélecteur de langue qui pointe vers la bonne URL plutôt que de rediriger vers la page d’accueil traduite par défaut, une erreur fréquente sur les premiers projets multilingues headless que j’ai vus passer.
WPML avec WPGraphQL for WPML
WPML suit un principe équivalent avec sa propre extension, WPGraphQL for WPML, dont le schéma diffère légèrement dans les noms de champs :
query ArticleWPML($id: ID!) {
post(id: $id, idType: DATABASE_ID) {
title
language {
code
name
}
translations {
code
href
}
}
}
Une différence pratique entre les deux extensions : WPML expose directement un champ href dans translations, correspondant à l’URL WordPress native de la traduction, alors que Polylang expose plutôt un slug à recomposer soi-même. Cette différence influence directement la façon dont le front doit reconstruire ses propres routes.
Construire le routage localisé côté front

Avec Next.js, l’internationalisation intégrée au routeur (préfixes de langue automatiques comme /fr/... et /en/...) se combine avec les données de langue récupérées via WPGraphQL, mais les deux restent des mécanismes distincts à synchroniser explicitement :
// next.config.js
module.exports = {
i18n: {
locales: ['fr', 'en'],
defaultLocale: 'fr',
},
};
// pages/[locale]/article/[slug].js
export async function getStaticProps({ params }) {
const { post } = await requeteGraphQL(REQUETE_ARTICLE, {
slug: params.slug,
language: params.locale.toUpperCase(),
});
return { props: { post } };
}
Le préfixe de langue de l’URL Next.js et le code de langue transmis à la requête GraphQL doivent rester strictement synchronisés : une désynchronisation (par exemple un slug français demandé avec un code de langue anglais) renvoie généralement une réponse vide plutôt qu’une erreur explicite, ce qui complique le débogage si ce point n’est pas testé tôt.
Exposer les menus par langue
Les menus de navigation suivent la même logique que le contenu : chaque langue dispose de son propre menu, enregistré séparément dans Polylang ou WPML. Côté WPGraphQL, le champ language s’applique aussi aux menus si l’extension de langue est bien compatible avec le type Menu du schéma :
query MenuParLangue($emplacement: MenuLocationEnum!, $langue: LanguageCodeFilterEnum!) {
menus(where: { location: $emplacement, language: $langue }) {
nodes {
menuItems {
nodes {
label
url
path
}
}
}
}
}
Gérer les contenus non traduits
Un contenu peut exister dans une langue sans avoir d’équivalent traduit dans une autre — un article de blog publié uniquement en français, par exemple. Le front doit gérer ce cas explicitement : soit en masquant le sélecteur de langue vers une traduction inexistante, soit en proposant un repli vers l’accueil de la langue cible plutôt qu’une erreur 404 brute.
| Situation | Comportement recommandé |
|---|---|
| Traduction existante | Lien direct vers le slug traduit |
| Pas de traduction pour ce contenu précis | Masquer l’option de langue, ou proposer l’accueil localisée avec un message explicite |
- Vérifier systématiquement que le champ
translationsn’est pas vide avant d’afficher un sélecteur de langue actif. - Prévoir une page 404 localisée par langue, pas une 404 générique dans une seule langue par défaut.
- Tester le comportement du sitemap et des balises
hreflanggénérées, un point souvent oublié dans les premiers déploiements multilingues headless.
Sur un projet multilingue, je considère la synchronisation entre le préfixe d’URL du front et le code de langue transmis à l’API comme le point de fragilité numéro un : une bonne partie des bugs multilingues que j’ai corrigés venaient d’un décalage silencieux entre les deux, jamais signalé par une erreur explicite.
En résumé
WPGraphQL for Polylang et WPGraphQL for WPML exposent tous deux les langues et traductions liées au schéma GraphQL, avec des noms de champs légèrement différents selon l’extension utilisée. Le routage localisé reste entièrement à la charge du front, qui doit maintenir une synchronisation stricte entre son propre système de préfixes de langue et les codes de langue transmis à chaque requête.