Un besoin fréquent sur des sites multilingues : un bloc dont les textes d’interface (labels des contrôles, messages d’erreur, placeholders) doivent apparaître dans la langue de l’administration, pas seulement en anglais codé en dur. C’est une chaîne complète, en plusieurs étapes précises, qui permet d’y arriver, avec un piège classique à chaque étape.
Étape 1 : le textdomain, à deux endroits
Le textdomain doit être déclaré à deux endroits distincts, qui doivent impérativement correspondre : dans l’en-tête de l’extension PHP, et dans le block.json de chaque bloc.
<?php
/**
* Plugin Name: Mon Projet Blocs
* Text Domain: mon-projet
*/
{
"name": "mon-projet/encart",
"textdomain": "mon-projet"
}
Un textdomain différent entre les deux (une faute de frappe fréquente, notamment un tiret contre un underscore) suffit à faire échouer silencieusement toute la chaîne de traduction, sans message d’erreur explicite dans la console.
Étape 2 : marquer les chaînes dans le JavaScript
import { __ } from '@wordpress/i18n';
<TextControl
label={ __( 'Titre de l\'encart', 'mon-projet' ) }
placeholder={ __( 'Saisissez un titre…', 'mon-projet' ) }
/>
__(), importé de @wordpress/i18n, fonctionne comme son équivalent PHP : premier argument la chaîne à traduire, second argument le textdomain, identique à celui déclaré dans block.json.
Étape 3 : extraire les chaînes avec WP-CLI

wp i18n make-pot . languages/mon-projet.pot
wp i18n make-json languages/mon-projet-fr_FR.po --no-purge
make-pot parcourt le code PHP et JavaScript pour en extraire toutes les chaînes marquées (__(), _e(), et leurs équivalents JavaScript). Une fois le fichier .po traduit (à la main ou via un outil comme Poedit), make-json génère un ou plusieurs fichiers JSON, un par fichier source JavaScript contenant des chaînes traduisibles, nommés selon un hachage du chemin du fichier d’origine — ce détail de nommage explique pourquoi ces fichiers ne doivent jamais être renommés manuellement.
Étape 4 : wp_set_script_translations, l’étape oubliée
C’est l’étape la plus souvent manquante : sans elle, les fichiers JSON générés existent bien sur le disque, mais rien ne les charge côté navigateur, et les chaînes __() continuent de s’afficher dans leur langue d’origine, quelle que soit la langue de l’administration.
<?php
add_action( 'init', function () {
register_block_type( __DIR__ . '/build/encart' );
wp_set_script_translations(
'mon-projet-encart-editor-script',
'mon-projet',
__DIR__ . '/languages'
);
} );
Le premier argument est le handle du script tel qu’il a été enregistré (visible dans le fichier .asset.php généré ou déduit du nom du bloc et de la clé editorScript), le second le textdomain, le troisième le dossier contenant les fichiers JSON générés à l’étape précédente.
Un piège fréquent : le nom du fichier JSON
Le nom du fichier JSON généré par make-json intègre un hachage MD5 du chemin relatif du fichier source. Si le nom du fichier source change après la génération des traductions (un renommage de index.js en edit.js, par exemple), le hachage change aussi, et le fichier JSON devient introuvable au chargement — une régénération complète des traductions devient alors nécessaire.
- Toujours régénérer les fichiers JSON après tout renommage de fichier source JavaScript.
- Vérifier, dans l’onglet réseau des outils de développement du navigateur, que le fichier JSON attendu se charge réellement avec un code 200, pas une 404 silencieuse.
- Ne jamais committer les fichiers
.potet.posans les fichiers.jsoncorrespondants, sous peine de traductions manquantes en production après un déploiement.
Une chaîne de traduction qui fonctionne côté PHP mais pas côté JavaScript indique presque toujours un
wp_set_script_translationsmanquant ou mal ciblé, avant toute autre hypothèse de débogage.
En résumé
La traduction des chaînes JavaScript d’un bloc suit une chaîne précise, en quatre étapes, où l’oubli d’une seule casse silencieusement tout le reste. Une fois cette mécanique comprise et vérifiée une première fois, son application à un nouveau bloc devient une formalité de quelques minutes, sans surprise particulière.