vendredi 25 septembre 2026

À propos

Contact

Extensions

Internationaliser correctement une extension WordPress : le guide i18n

Text domain, fonctions __() et esc_html__(), fichiers .pot et traductions différées : tout ce qu'il faut savoir pour rendre une extension traduisible sans erreur.

Par Clément Hadrot • 12 octobre 2021 • 5 min de lecture • Aucun commentaire
Internationaliser correctement une extension WordPress : le guide i18n

WordPress équipe plus de 40 % des sites web dans le monde, avec une proportion significative d’installations en dehors des pays anglophones. Une extension qui n’est pas correctement internationalisée exclut d’emblée une partie substantielle de son public potentiel, ou pire, affiche un mélange incohérent de texte traduit et de texte resté en anglais.

Cet article détaille la mise en place de l’internationalisation (i18n) dans une extension, des fonctions de traduction aux fichiers .pot, en passant par les pièges qui empêchent WordPress.org de générer correctement les traductions automatiques.

Le text domain : une chaîne littérale, pas une variable

Chaque chaîne traduisible est associée à un « text domain », identifiant unique de l’extension qui doit correspondre exactement au slug du dossier de l’extension sur WordPress.org.

/**
 * Plugin Name: Mon Extension Acme
 * Text Domain: acme-mon-extension
 * Domain Path: /languages
 */

echo esc_html__( 'Réglages enregistrés avec succès.', 'acme-mon-extension' );

Un piège fréquent et facile à commettre : passer une variable ou une constante comme text domain plutôt qu’une chaîne littérale directement écrite dans le code.

// À NE JAMAIS FAIRE
define( 'ACME_TEXT_DOMAIN', 'acme-mon-extension' );
echo __( 'Réglages enregistrés.', ACME_TEXT_DOMAIN );

// Toujours écrire la chaîne en dur
echo __( 'Réglages enregistrés.', 'acme-mon-extension' );

La raison est purement technique : l’outil WP-CLI i18n make-pot ainsi que les outils de scan de WordPress.org analysent le code source de façon statique, sans l’exécuter. Une constante ou une variable ne peut pas être résolue par cette analyse statique, et toutes les chaînes concernées sont alors invisibles pour la génération automatique du fichier de traduction, cassant silencieusement toute la chaîne de traduction pour cette extension.

Choisir la bonne fonction selon le contexte

FonctionUsage
__()Retourne la chaîne traduite, à assigner à une variable
_e()Affiche directement la chaîne traduite (echo implicite)
esc_html__()Traduit puis échappe pour un affichage HTML sûr
esc_attr__()Traduit puis échappe pour un attribut HTML
_n()Gère le pluriel selon une quantité
_x()Ajoute un contexte pour désambiguïser un même mot
// Pluriel correctement géré
printf(
    esc_html( _n( '%d produit en stock', '%d produits en stock', $quantite, 'acme-mon-extension' ) ),
    $quantite
);

// Contexte pour désambiguïser
esc_html_x( 'Poste', 'poste de travail', 'acme-mon-extension' );
esc_html_x( 'Poste', 'envoi postal', 'acme-mon-extension' );

Le mot « Poste » en français illustre bien l’intérêt de _x() : sans contexte, un traducteur vers une autre langue ne peut pas deviner s’il s’agit d’un poste de travail ou d’un envoi postal, et risque une traduction erronée sans que rien ne l’alerte.

L'essentiel à retenir : Le text domain doit être une chaîne littérale, jamais une variable ; load_plugin_textdomain devenu inutile pour WordPress.org depuis 4.6 ; esc_html__ combine sécurité et traduction en un appel

Chaînes avec variables : toujours via printf ou sprintf

Concaténer une traduction avec une variable casse la structure grammaticale dans les langues où l’ordre des mots diffère du français ou de l’anglais.

// À éviter : structure figée qui ne s'adapte pas à toutes les langues
echo esc_html__( 'Bonjour', 'acme-mon-extension' ) . ' ' . esc_html( $prenom );

// Préférable : la traduction contrôle la position de la variable
printf(
    /* translators: %s : prénom de l'utilisateur */
    esc_html__( 'Bonjour %s', 'acme-mon-extension' ),
    esc_html( $prenom )
);

Le commentaire /* translators: ... */ juste avant l’appel n’est pas facultatif dans une extension sérieuse : il apparaît directement dans l’interface de traduction de WordPress.org (GlotPress) et aide les traducteurs à comprendre ce que représente chaque paramètre %s ou %d, en particulier lorsqu’il y en a plusieurs dans la même chaîne.

Charger les traductions : un besoin qui a disparu depuis 2016

Pendant des années, la documentation recommandait un appel explicite à load_plugin_textdomain() dans le hook init ou plugins_loaded. Depuis WordPress 4.6 (2016), ce chargement est automatique pour les extensions hébergées sur WordPress.org, à condition que le Text Domain et le Domain Path soient correctement déclarés dans l’en-tête du plugin. L’appel manuel reste nécessaire uniquement pour une extension distribuée en dehors de WordPress.org (vente directe, marketplace privé), car WordPress ne connaît alors pas l’emplacement des fichiers de traduction sans cette indication explicite.

// Nécessaire seulement pour une extension hors WordPress.org
add_action( 'plugins_loaded', function () {
    load_plugin_textdomain(
        'acme-mon-extension',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages'
    );
} );

Générer le fichier .pot avec WP-CLI

Le fichier .pot (Portable Object Template) recense toutes les chaînes traduisibles extraites du code. WP-CLI simplifie sa génération, plus fiable qu’un scan manuel :

wp i18n make-pot . languages/acme-mon-extension.pot

Cette commande détecte aussi, en avertissement, les cas de text domain non littéral évoqués plus haut, ce qui en fait un bon outil de vérification avant chaque publication d’une nouvelle version.

Sur nos extensions distribuées, un contrôle systématique avant chaque mise à jour : lancer wp i18n make-pot et vérifier qu’aucun avertissement n’apparaît dans la console. C’est un filet de sécurité qui coûte dix secondes et évite des traductions cassées côté utilisateur final.

En résumé

Internationaliser une extension correctement repose sur trois disciplines simples mais non négociables : un text domain toujours écrit en dur, la fonction de traduction adaptée au contexte d’affichage (échappement compris), et des variables toujours insérées via printf avec un commentaire pour les traducteurs. Depuis 2016, WordPress se charge du reste pour toute extension correctement hébergée sur WordPress.org.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi