Depuis que l’éditeur de blocs est devenu le standard de rédaction sur WordPress, la question de la traduction de blocs personnalisés revient régulièrement sur les projets que nous développons pour des clients multilingues. Un bloc mal conçu, sans réflexion sur la traduction dès sa création, finit presque toujours par exposer un texte figé dans une seule langue au milieu d’un contenu par ailleurs entièrement traduit. Ce tutoriel détaille comment structurer un bloc personnalisé pour qu’il reste correctement traduisible, avec WPML ou Polylang.
Nous prenons pour exemple un bloc « Mise en avant » développé pour un client, affichant un titre, un texte descriptif, et un libellé de bouton — un cas représentatif de la majorité des blocs métier développés sur mesure.
Déclarer les attributs traduisibles dans block.json
Depuis l’introduction du fichier block.json comme méthode standard de déclaration des blocs, chaque attribut peut recevoir un rôle explicite (role) qui indique aux outils de traduction s’il doit être considéré comme du contenu traduisible ou non. C’est ce réglage, souvent oublié, qui permet à un plugin multilingue de détecter automatiquement les attributs à proposer à la traduction :
{
"apiVersion": 3,
"name": "mon-theme/mise-en-avant",
"title": "Mise en avant",
"category": "text",
"attributes": {
"titre": {
"type": "string",
"role": "content",
"source": "html",
"selector": "h3"
},
"texte": {
"type": "string",
"role": "content"
},
"libelleBouton": {
"type": "string",
"role": "content",
"default": "En savoir plus"
},
"couleurFond": {
"type": "string"
}
}
}
Notez que couleurFond ne porte pas de role: content : c’est un réglage de mise en forme, pas un contenu à traduire, et il doit rester identique entre toutes les langues d’un même bloc.
Le cas des blocs statiques et des blocs dynamiques
Un bloc statique enregistre son contenu directement dans le post_content sous forme de commentaires HTML structurés (<!-- wp:mon-theme/mise-en-avant -->). Dans ce cas, la traduction du bloc suit la même logique que la traduction du reste du contenu de l’article : WPML et Polylang analysent le contenu complet du post, y compris la structure de blocs sérialisée, et proposent les attributs marqués content à la traduction dans leur éditeur respectif.
Un bloc dynamique, en revanche, ne stocke que ses attributs dans le post_content et génère son rendu final via une fonction PHP de rappel (render_callback ou fichier render.php depuis WordPress 6.1). Dans ce cas, seuls les attributs sont traduits ; c’est la fonction de rendu qui doit correctement utiliser ces attributs traduits au moment de l’affichage, sans réintroduire de texte codé en dur :
<?php
// render.php du bloc
$titre = $attributes['titre'] ?? '';
$texte = $attributes['texte'] ?? '';
$bouton = $attributes['libelleBouton'] ?? __( 'En savoir plus', 'mon-theme' );
?>
<div class="mise-en-avant">
<h3><?php echo esc_html( $titre ); ?></h3>
<p><?php echo esc_html( $texte ); ?></p>
<a href="#"><?php echo esc_html( $bouton ); ?></a>
</div>
Le libellé par défaut (__( 'En savoir plus', 'mon-theme' )) doit lui-même être internationalisé de façon classique, puisqu’il s’agit d’un texte codé dans le thème et non d’un contenu saisi par l’utilisateur — il suit donc le mécanisme des chaînes de thème, pas celui du contenu de bloc.

Différences de traitement entre WPML et Polylang
WPML propose un éditeur de traduction avancé qui affiche les blocs Gutenberg côte à côte, langue source et langue cible, en respectant la structure visuelle du bloc plutôt qu’un simple champ de texte brut. Cet éditeur reconnaît nativement les attributs marqués comme traduisibles dans block.json, à condition que le bloc ait été correctement enregistré dans les réglages de traduction de contenu personnalisé de WPML si nécessaire.
Polylang s’appuie davantage sur le mécanisme natif de traduction de blocs introduit dans WordPress lui-même (l’attribut role: content étant une convention du cœur de WordPress, pas une invention propre à un plugin), avec un éditeur de traduction plus sobre mais fonctionnellement équivalent pour les cas simples.
Le piège des block bindings
Depuis WordPress 6.5, l’API des block bindings permet de lier dynamiquement un attribut de bloc à une source de données externe (une métadonnée personnalisée, par exemple), sans dupliquer manuellement le texte dans chaque bloc. Ce mécanisme complique légèrement la question de la traduction : un attribut lié via metadata.bindings n’est plus un texte directement présent dans le contenu du bloc, mais une référence vers une métadonnée de post, qui doit elle-même être traduite séparément selon les mêmes règles que n’importe quel champ personnalisé (voir notre article sur la traduction des CPT et de leurs métadonnées).
{
"metadata": {
"bindings": {
"content": {
"source": "core/post-meta",
"args": { "key": "sous_titre_produit" }
}
}
}
}
Un développeur qui ajoute un binding sans vérifier que la métadonnée cible est correctement synchronisée entre langues expose exactement le même risque qu’un champ ACF de relation mal configuré : un contenu affiché dans la mauvaise langue, sans erreur visible pour signaler le problème.
Checklist avant de livrer un bloc personnalisé sur un projet multilingue
- Chaque attribut de contenu porte-t-il bien
role: contentdansblock.json? - Les attributs de mise en forme (couleurs, tailles) sont-ils exclus de la traduction ?
- Pour un bloc dynamique, la fonction de rendu utilise-t-elle exclusivement les attributs traduits, sans texte codé en dur ?
- Les valeurs par défaut sont-elles internationalisées via
__()avec le bon domaine de texte ? - Un test de traduction complet a-t-il été effectué avec le plugin multilingue réellement utilisé sur le projet ?
En résumé
Un bloc Gutenberg personnalisé ne devient réellement compatible avec un site multilingue que si ses attributs sont explicitement déclarés comme traduisibles dès sa conception, avec une distinction claire entre contenu et mise en forme. Les blocs dynamiques et les block bindings introduits avec WordPress 6.5 ajoutent une couche de vigilance supplémentaire, la traduction ne portant plus uniquement sur le contenu du bloc mais potentiellement sur des métadonnées externes à synchroniser séparément.