wp_unique_post_slug : c’est cette fonction cœur qui décide, pour chaque article publié, si un slug est déjà pris. Sur un site de recrutement traduit en anglais et en français avec Polylang, une conseillère RH signale un comportement étrange : l’offre /offre-consultant-senior/ affiche systématiquement le texte anglais, même quand le sélecteur de langue indique le français.
Le symptôme est net une fois qu’on regarde les deux fiches dans l’administration : la version française porte en réalité le slug offre-consultant-senior-2, mais le lien partagé sur les réseaux pointait vers le slug sans suffixe, donc vers la traduction anglaise. Une collision de slug entre deux langues, silencieuse, qui a fini par tromper tout le monde.
Symptôme : deux traductions, un seul slug propre
Dans WordPress, l’unicité d’un slug se calcule par type de contenu et par parent, indépendamment de toute notion de langue : le cœur ne connaît pas Polylang. Sans intervention, créer une traduction avec le même titre générerait donc automatiquement un slug numéroté (-2, -3…), ce qui casserait l’intérêt même d’une URL localisée.
Polylang corrige ce point depuis longtemps en accrochant un filtre sur wp_unique_post_slug qui autorise un slug identique entre deux posts si leurs langues respectives diffèrent. En temps normal, /en/consultant-senior/ et /consultant-senior/ coexistent sans souci.
Où le filtre casse
Le cas décrit ici vient d’un thème maison qui ajoutait, lui aussi, un filtre sur wp_unique_post_slug pour gérer un ancien système de sous-marques, avec une priorité plus basse que celle de Polylang. Résultat : le filtre du thème renvoyait un slug déjà « nettoyé » avant que Polylang ait pu appliquer sa propre exception linguistique, et WordPress retombait sur son comportement par défaut : suffixer la deuxième publication rencontrée.

Diagnostic : remonter la chaîne de filtres
Trois vérifications suffisent à confirmer l’hypothèse :
- Lister les fonctions accrochées à
wp_unique_post_slugavecwp eval 'var_dump($GLOBALS["wp_filter"]["wp_unique_post_slug"]);'en WP-CLI, pour voir l’ordre des priorités. - Vérifier dans la table
wp_postsque le champpost_namede la traduction française contient bien un suffixe numérique inattendu. - Contrôler la table
wp_term_relationshipsassociée à la taxonomie de langue Polylang (language) pour confirmer que les deux articles sont correctement rattachés chacun à leur langue.
Une fois la liste des filtres obtenue, la priorité du filtre maison apparaît clairement inférieure à celle de Polylang, qui s’exécute par défaut avec une priorité de 20 sur ce hook précis dans les versions récentes du plugin.
Correctif : réordonner puis régénérer les slugs propres
La correction se fait en deux temps. D’abord, on relève la priorité du filtre maison au-delà de celle de Polylang, ou mieux, on fusionne sa logique dans le filtre existant plutôt que d’en ajouter un concurrent :
add_filter( 'wp_unique_post_slug', function ( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
// Logique maison de sous-marques, appliquée après Polylang.
return apply_sous_marque_slug( $slug, $post_ID, $original_slug );
}, 25, 6 );
Ensuite, il faut régénérer manuellement le slug de chaque article touché : renommer temporairement le champ post_name avec une valeur neutre, puis relancer l’enregistrement de l’article pour que la chaîne de filtres, désormais correcte, recalcule un slug propre sans suffixe.
Vérifier les redirections existantes
Comme l’ancien slug numéroté a probablement été indexé, une redirection 301 de offre-consultant-senior-2 vers offre-consultant-senior (dans la bonne langue) évite de perdre le référencement acquis pendant la période où le bug est passé inaperçu.
Prévention : ne jamais empiler des filtres de slug sans les auditer
Avant d’ajouter un nouveau filtre sur wp_unique_post_slug, pre_wp_unique_post_slug ou un hook équivalent, il vaut mieux systématiquement vérifier ce qu’un plugin de traduction y a déjà déposé. Un audit rapide en WP-CLI, ajouté à la checklist de mise en production, évite ce genre de surprise silencieuse.
Un slug qui se termine par
-2,-3ou davantage mérite toujours une vérification : dans un contexte multilingue, ce n’est presque jamais un hasard de nommage, mais souvent un conflit de filtres.
En résumé
La collision de slug entre langues n’est pas un défaut de Polylang, mais la conséquence d’un filtre concurrent mal priorisé. Le réflexe à garder : chaque fois qu’un plugin ou un thème touche à wp_unique_post_slug, s’assurer que sa priorité respecte celle du plugin multilingue en place, et documenter ce choix pour l’équipe qui reprendra le projet plus tard.