Skip to Content

Chapitre 7.8 — Internationalisation (i18n)

⏱️ TL;DRInternationaliser (i18n) = rendre un plugin/thème traduisible. Toute chaîne affichée passe par une fonction de traduction (__(), _e(), esc_html__()…) avec votre text domain (le slug du plugin). WordPress génère ensuite un fichier .pot (modèle), traduit en .po/.mo par langue. Pour un plugin publié sur wp.org, les traductions sont même communautaires (translate.wordpress.org). Règle d’or : jamais de texte affiché en dur — toujours enveloppé dans une fonction i18n avec le bon domain.

🎯 Objectifs

  • Envelopper les chaînes avec les bonnes fonctions i18n.
  • Utiliser le text domain et le charger.
  • Gérer variables, pluriels et contexte.
  • Générer les fichiers de traduction.

1. Les fonctions de traduction

FonctionRôle
__( 'Texte', 'domain' )Retourne la traduction.
_e( 'Texte', 'domain' )Affiche (echo) la traduction.
esc_html__() / esc_html_e()Traduit + échappe (à privilégier à l’affichage).
esc_attr__() / esc_attr_e()Traduit + échappe pour un attribut.
_n( 'singulier', 'pluriel', $n, 'domain' )Pluriel.
_x( 'Texte', 'contexte', 'domain' )Traduit avec contexte (désambiguïsation).
echo esc_html__( 'Ajouter un livre', 'wpr-books' ); // affiche + échappe $label = __( 'Genres', 'wpr-books' ); // récupère

⚠️ Piège n°1 — le double emploi traduction/échappement. À l’affichage, préférez esc_html_e() (traduit et échappe) plutôt que _e() seul : une traduction peut contenir du HTML inattendu. Sécurité (ch. 7.7) et i18n vont ensemble.


2. Le text domain

Le text domain est l’identifiant de vos traductions : il doit correspondre au slug du plugin (dossier) et à l’en-tête Text Domain: (ch. 7.1).

// En-tête du plugin : Text Domain: wpr-books / Domain Path: /languages // Charger les traductions (sur init) add_action( 'init', function () { load_plugin_textdomain( 'wpr-books', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); } );

⚠️ Piège n°2 — text domain incohérent ou variable. Le domain doit être une chaîne littérale identique partout ('wpr-books'), jamais une variable ($domain) : les outils d’extraction (§4) lisent le code source statiquement. Un domain qui ne correspond pas au slug empêche le chargement des traductions (chaînes non traduites).

📚 Aller plus loin — Depuis WordPress 4.6, les plugins/thèmes hébergés sur wp.org chargent automatiquement leurs traductions depuis translate.wordpress.org (souvent, load_plugin_textdomain n’est même plus nécessaire pour eux). Pour un plugin distribué hors wp.org, gardez l’appel explicite.


3. Variables, pluriels, contexte

Variables — n’insérez jamais une variable dans la chaîne à traduire ; utilisez des placeholders avec sprintf/printf :

// ✅ BON : le traducteur voit "%s livres trouvés" et peut réordonner printf( esc_html( _n( '%s livre trouvé', '%s livres trouvés', $count, 'wpr-books' ) ), number_format_i18n( $count ) ); // ❌ MAUVAIS : chaîne cassée, intraduisible correctement _e( 'Nombre : ' . $count . ' livres', 'wpr-books' );

Pluriel_n() gère les règles de pluriel (différentes selon les langues). Contexte_x() désambiguïse un même mot (« Post » = publier vs article) : _x( 'Post', 'verbe : publier', 'wpr-books' ).

⚠️ Piège n°3 — la concaténation. Concaténer des morceaux de phrase (__('Bonjour') . $nom . __('bienvenue')) est intraduisible proprement (l’ordre des mots varie selon la langue). Toujours une phrase complète avec placeholders (sprintf( __( 'Bonjour %s, bienvenue', 'domain' ), $nom )).


4. Générer les fichiers de traduction

  1. .pot (Portable Object Template) : le modèle listant toutes vos chaînes. Généré avec WP-CLI : wp i18n make-pot . languages/wpr-books.pot.
  2. .po : le fichier traduit par langue (wpr-books-fr_FR.po), édité avec Poedit ou GlotPress.
  3. .mo : la version compilée (binaire) que WordPress charge. wp i18n make-mo languages/.

Convention de nommage : {textdomain}-{locale}.mo (ex. wpr-books-fr_FR.mo).

💡 Pour un dev React — C’est le même pipeline que i18next / react-intl : on extrait les clés du code (make-pot ≈ l’extracteur), on traduit par locale (.po ≈ vos fichiers JSON de langue), on charge la bonne locale au runtime. La différence : le domain remplace le namespace, et l’extraction est statique (d’où l’exigence de chaînes et domain littéraux). Côté JS de vos blocs (Partie 8), WordPress fournit aussi @wordpress/i18n (__() en JavaScript) avec le même principe.


5. Checklist i18n

  • Toute chaîne visible enveloppée dans une fonction i18n avec le bon domain littéral.
  • esc_html__ / esc_html_e à l’affichage (i18n + échappement).
  • Placeholders (%s, %d) + sprintf, jamais de concaténation.
  • _n() pour les pluriels, _x() pour le contexte.
  • .pot fourni ; Text Domain/Domain Path renseignés dans l’en-tête.

✏️ Exercices

  1. Réécrivez correctement : _e( 'Il y a ' . $n . ' résultats', 'wpr-books' );.
  2. Pourquoi le text domain ne doit-il jamais être une variable ?
  3. Quelle fonction utilisez-vous pour afficher une chaîne traduite et échappée ?

✅ Solution

printf( esc_html( _n( 'Il y a %s résultat', 'Il y a %s résultats', $n, 'wpr-books' ) ), number_format_i18n( $n ) );

On utilise un placeholder (%s) + sprintf/printf, _n() pour le pluriel, et l’échappement. 2. Parce que l’extraction des chaînes (wp i18n make-pot) lit le code statiquement : un domain variable ne peut être identifié, et un domain incohérent empêche le chargement des traductions. 3. esc_html_e() (ou esc_html__() pour récupérer) : elle traduit et échappe le texte affiché.


🧠 Quiz de révision

1. Quelle fonction retourne une chaîne traduite ?

__( 'Texte', 'domain' ) (_e() l’affiche ; esc_html__/esc_html_e traduisent + échappent).

2. Qu’est-ce que le text domain ?

L’identifiant des traductions, égal au slug du plugin, présent dans chaque appel i18n et l’en-tête Text Domain:.

3. Comment insérer une variable dans une chaîne traduite ?

Avec un placeholder (%s, %d) et sprintf/printf — jamais par concaténation.

4. À quoi servent .pot, .po, .mo ?

.pot = modèle des chaînes ; .po = traduction par langue ; .mo = version compilée chargée par WordPress.

5. Quel outil génère le .pot ?

WP-CLI : wp i18n make-pot . languages/domain.pot.


Chapitre suivant : Tutoriel : le plugin wpr-books.