Contents
Introduction — Why internationalize Gutenberg blocks with @wordpress/i18n
If you build blocks for the WordPress editor (Gutenberg) and want them to work for non-English sites, you must internationalize (i18n) the strings in your block JavaScript. The official JavaScript i18n library for WordPress is @wordpress/i18n. It provides the same kinds of translation functions available in PHP (__, _x, _n, _nx, sprintf, etc.) and integrates with WordPress’ translation infrastructure so translations can be loaded as language packs or from JSON files.
What @wordpress/i18n gives you
- String translation with __() to mark translatable text.
- Context support with _x() when the same source text can mean different things.
- Plural forms with _n() and _nx().
- Formatted strings using sprintf() (from the same package).
- Interop with WordPress loader via wp_set_script_translations() so translations are delivered to the block script in the editor.
High-level workflow
- Mark strings in your block JS using @wordpress/i18n functions.
- Bundle your block JS (typical modern flow uses @wordpress/scripts or a custom build pipeline).
- Register/enqueue the script in PHP and call wp_set_script_translations() so WordPress will load translations for that script handle using your plugin/theme textdomain.
- Create POT/PO files and convert the PO files to JSON (or let language packs handle distribution). Use WP-CLI commands to build JSON for JavaScript translations or rely on WordPress.org language packs.
- Deploy the plugin/theme with the languages/.json files (or rely on language packs) so the editor receives translations.
Importing and using the functions in JS
In your block’s editor code import the functions you need from @wordpress/i18n. Always provide the same text domain you use in PHP so the translator lookup succeeds (the second argument is the domain).
Simple example (edit component)
import { __, _n, sprintf, _x } from @wordpress/i18n import { useBlockProps } from @wordpress/block-editor export default function Edit( { attributes, setAttributes } ) { const blockProps = useBlockProps() // translators: A short title shown in the block editor header. const title = __( Hello from my block, my-plugin ) // translators: Count of items in the block. %d will be replaced by the number. const countText = sprintf( _n( %d item, %d items, attributes.count, my-plugin ), attributes.count ) // Using contextual translation when Close means different things in other contexts: const closeLabel = _x( Close, close the dialog, my-plugin ) return () }{ title }
{ countText }
Block metadata and script handles
If you use block.json and register_block_type() (or register_block_type_from_metadata()), WordPress will register block assets and create script handles. To ensure translations are available for your editor script you must call wp_set_script_translations() with the correct script handle and the textdomain.
One safe pattern is to call register_block_type() and then read the returned WP_Block_Type objects script handle property and pass that to wp_set_script_translations(). That way you do not need to guess the handle name produced by the metadata-based registration.
Example block.json (metadata)
{ apiVersion: 2, name: my-plugin/my-block, title: My Block, category: widgets, icon: smiley, editorScript: file:./build/index.js, textdomain: my-plugin, attributes: { count: { type: number, default: 1 } } }
PHP registration with wp_set_script_translations (recommended pattern)
editor_script ) ) { // Ensure WordPress loads translations for the editor script using the plugin textdomain. wp_set_script_translations( block_type->editor_script, // script handle my-plugin, // textdomain __DIR__ . /languages // path to .json translation files ) } // If your block uses a front-end script as well, set translations for that handle too: if ( block_type ! empty( block_type->script ) ) { wp_set_script_translations( block_type->script, my-plugin, __DIR__ . /languages ) } } add_action( init, my_plugin_register_block )
Creating translation files (POT → PO → JSON)
The recommended flow for translations of JS strings is:
- Extract all translatable strings to a POT file (single pot per project is common).
- Translators create language PO files (e.g., fr_FR.po).
- Convert PO to JSON files consumable by the JavaScript runtime with WP-CLI (creates e.g., fr_FR.json).
- Place JSON files in your plugin/theme languages directory or rely on WordPress.org language packs.
Use WP-CLI to build POT and JSON
Example commands:
# 1) Create POT (scans PHP and can be configured to scan JS files if set up) wp i18n make-pot . languages/my-plugin.pot --exclude=build,node_modules # 2) Translators create PO files like languages/fr_FR.po (edit with Poedit or other tools) # 3) Convert PO files into the JSON format used by the JS i18n runtime: wp i18n make-json languages/ languages/ # This creates languages/fr_FR.json (and any other PO → JSON)
Notes:
- Make sure your extractor includes your JS source files if you keep strings in src/. You may need to adjust the wp-cli extraction or use specialized extraction tools that parse JS/JSX (some setups use extra Babel plugins to extract JS strings to POT).
- The JSON output will be keyed by the text domain and loaded by wp_set_script_translations() and WordPress’ script translation loader.
Example of a generated language JSON (fr_FR.json)
{ : { domain: my-plugin, lang: fr_FR, plural_forms: nplurals=2 plural=(n > 1) }, Hello from my block: [ Bonjour depuis mon bloc ], %d item: [ %d élément, %d éléments ], Close: [ Fermer ] }
Translators comments in JavaScript
You should add translator comments for complex or ambiguous strings using the exact / translators: … / comment format immediately above the string. Most extractor tools will recognize this and copy the comment into the POT so translators have context.
/ translators: The placeholder will be replaced with the users name. / const greeting = sprintf( __( Welcome, %s, my-plugin ), userName )
Plural and contextual APIs
- __() — simple translation.
- _x() — translation with context to disambiguate identical source strings. Use when one word can have multiple meanings.
- _n() — plural translations (singular, plural) where the number is passed for proper plural selection.
- _nx() — plural plus context.
- sprintf() — format translated strings with placeholders (use positional placeholders carefully if languages reorder components).
Plural example
import { _n, sprintf } from @wordpress/i18n const items = 3 const message = sprintf( _n( %d item in list, %d items in list, items, my-plugin ), items )
Escaping and safety
Translations may contain HTML if translators include it, so prefer to keep translatable strings as plain text. If you must output HTML, sanitize or escape properly in the right context:
- For attributes use appropriate escaping (in PHP use esc_attr__() in JS avoid injecting raw HTML).
- For content that requires HTML, build sanitized components rather than printing translated HTML without checks.
Dynamic blocks and server-side rendering
If your block is dynamic (rendered via PHP render_callback), keep translatable strings in PHP where possible and mark them with PHP translation functions. If you also have JS strings (editor-only), translate those using @wordpress/i18n in JS and still register translations for your editor script handle with wp_set_script_translations().
Automatic loading via language packs
If you distribute through WordPress.org plugin repository, language packs can be provided by translate.wordpress.org. If language packs are available, WordPress will automatically load translations for your plugin using your textdomain and script handles — but only if you called wp_set_script_translations() so WordPress knows which script handle uses which textdomain.
Development tips and troubleshooting checklist
- Make sure the second argument to __(), _x(), etc., is the same textdomain used in PHP.
- Confirm wp_set_script_translations() is called early (typically on init after registering blocks/assets).
- Ensure generated JSON files are located in the path you pass to wp_set_script_translations().
- Verify translator comments are in the right format: / translators: … / immediately above the string.
- Clear any caches (object cache, browser cache) when changes to translation files don’t appear.
- Use the browser console to check network requests and confirm the JSON file for the locale is requested and served.
Example: Full minimal plugin layout
Suggested files (simplified):
my-plugin/ | Plugin root |
my-plugin/build/ | Built block assets (index.js, index.css) produced by the bundler |
my-plugin/src/ | Your source (edit.js, save.js, index.js, block.json) |
my-plugin/languages/ | Generated translations (fr_FR.json, etc.) |
my-plugin/my-plugin.php | Plugin bootstrap and block registration |
Common pitfalls
- Textdomain mismatch between PHP and JS translations.
- Forgetting to generate JSON from PO files, so JS runtime has nothing to translate against.
- Not calling wp_set_script_translations() with the correct script handle.
- Strings concatenated at runtime (do not build translatable strings by concatenation use placeholders and sprintf instead).
- Failing to provide translator comments where the meaning is ambiguous.
Additional tooling and advanced extraction
Large projects often use an extraction tool in the build process to find translatable strings in JS/JSX/TSX files and produce POT files. You can:
- Use WP-CLI’s i18n commands and configure them to scan your source folders.
- Use babel/i18n extractor plugins or third-party extractors that understand ESNext/JSX syntax.
- Integrate string extraction into CI so POT updates and PO merges happen as part of the release process.
Summary — best practices
- Always mark every user-facing string in JS with @wordpress/i18n functions.
- Keep a consistent textdomain across PHP and JS for the plugin/theme.
- Register translations for the exact script handle via wp_set_script_translations() and supply the languages path.
- Provide translator comments for ambiguous strings and use plural/context variants where needed.
- Use WP-CLI to create JSON translation files from PO files or rely on WordPress.org language packs.
Useful reference
Official documentation pages you will commonly consult include the package docs for @wordpress/i18n, the register_block_type() and wp_set_script_translations() references in the WordPress developer handbook, and the WP-CLI i18n command documentation.
Final note
Internationalizing blocks correctly makes them accessible to the global WordPress community. Keep strings stable (do not change the source text unless necessary) and keep translators’ context in mind. With a consistent workflow (mark strings in JS, generate POT/PO, build JSON, and call wp_set_script_translations()) your blocks will display translated strings in the editor and provide a better experience to multilingual users.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |