In this part we’ll focus on how to translate the texts and values in our custom Gutenberg block. We use WP-CLI to generate the necessary files so that Gutenberg is able to load our translations when switching the WordPress language.

Before going ahead with this, you need to have WP CLI (command-line interface for WordPress) installed. If you don’t have it, just follow the guide at WordPress Handbook for CLI.

To explain in short how to translate for Javascript (Gutenberg) scripts: WordPress requires .mo files for translation of PHP files, but for Javascript WordPress requires a .json file. Each Javascript file needs one JSON file for translation. The JSON should be of a specific format (WP CLI will generate it for us) with our translated strings. We need one JSON file per language we wish to translate to.

So what we need to do is first add the gettext functions (__(), _e() etc.) in our Javascript files and generate a PO file as usual for our theme or plugin. Because we’ve wrapped the texts in our script files with e.g. __(), the PO-file should be able to include them. Then we do the translation as usual in our PO-file. And then finally we use WP CLI to extract the necessary strings from the PO file and generate JSON files for all our Javascript files.

Keep in mind that your theme or plugin’s .po/.mo-files will never have an effect for your Javascript files – even though they contain translated strings from our Javascript files.

Implementing translation in Javascript

The first step is wrapping all texts in our Javascript file inside translation functions. If you’ve handled translation for WordPress in PHP you are probably very familiar with the functions __(), _e(), esc_html__() and so on. WordPress has a package wp.i18n that contains these functions, that work exactly like in PHP.

As with PHP you need to provide a textdomain domain (name/handle). It can be whatever you want, but keep it short as you’ll likely need to type it out very often. For my theme I’ve set up my textdomain with the domain awhitepixel. So within PHP I’ll do __('My string', 'awhitepixel') to translate strings, and it’ll be exactly the same in Javascript files.

Let’s start editing our Javascript file. First we need to destructure the __ and _e function from the wp.i18n package. Because of the nature of React most likely you will mostly or perhaps only use the __ function.

const { __, _e } = wp.i18n;

And then it’s a matter of finding all our hardcoded texts in the Javascript file and update them. Keep in mind that the __ and _e functions requires Javascript context. That means when we type strings as for example object property values, we use __() straight away, but as values for e.g. props we need to wrap everything inside { } to signify that this is Javascript code.

For example our registerBlockType with support for translation will look like:

registerBlockType('awp/firstblock', {
	title: __('My first block', 'awhitepixel'), 
	category: 'common',
	icon: 'smiley',
	description: __('Learning in progress', 'awhitepixel'),
	keywords: [__('example', 'awhitepixel'), __('test', 'awhitepixel')],
	attributes: {
		...

And as for props, i.e. in InspectorControls:

<InspectorControls>
	<PanelBody
		title={__("Most awesome settings ever", 'awhitepixel')}
		initialOpen={true}
	>
	...
		<ToggleControl
			label={__("Toggle me", 'awhitepixel')}
			checked={attributes.toggle}
			onChange={(newval) => setAttributes({ toggle: newval })}
		/>
		...

Wrap all texts you wish to support translation for in __() and _e(). If you’ve followed this tutorial step by step, you shouldn’t have any cases where you need to use _e(). When you are done, recompile the Javascript, and we’ll move away from Javascript.

Setting up po and/or pot files

This step varies a bit depending on what you have already done and set up for your theme or plugin. You might be writing your Gutenberg scripts in a new and empty plugin that hasn’t been set up for PHP translation, or inside a theme that already has a textdomain registered. You might have PO (and MO) files ready, or you might only have a POT file. I’ll try my best to cover all bases.

My theme or plugin already has a po(t)-file

If you already have a PO- or POT-file in your project, you most likely also have the PHP function load_theme_textdomain(), load_child_theme_textdomain() or load_plugin_textdomain() somewhere in your code. Please make sure that the registered domain is the same as you’ve used in your Javascript files.

All you need to do is load the PO file for the language you want to translate (or generate one from the POT file) in for example PoEdit. Click “Update from code” (or similar in other programs) so that the program can scan all the project files (including our recently updated Javascript files) and update the pool of strings for translation. The strings in our Javascript file should appear. Then you just need to translate them as normal and save.

PS: If you are not able to click “Update from code” or re-scan the files, the PO-file has probably not been set up correctly. Look for tips in the next section.

I don’t have any translation files

If your theme or project hasn’t been set up with translation you need to either generate a POT file using WP-CLI or manually create a PO file.

I have a thorough guide in how to create a PO-file in my Theme Tutorial for Beginners – part 8. The post describes how you can create the file and set it up correctly to search your theme files, and the keywords to search for (__, _e, etc.).

If you rather want to create a POT-file you can use the wp i18n make-pot command in WP-CLI, and then create a PO-file out of that using e.g. PoEdit. Keep in mind that you’ll need to regenerate the POT file (and then the PO file) every time you update any strings in your code.

End result

What you ultimately need is a PO-file that has found your Javascript strings where these have been translated. I recommend placing your translation files in a separate folder in your theme or plugin. When we start generating JSON files we’ll end up with quite a few files for translation and it’ll be nice to keep them all together in their own folder.

As a reference point I’m placing all translation files in my theme/assets/lang/. I’ve added a Norwegian translation for my theme, named nb_NO.po, which contains the translated strings from our custom block Javascript file.

Generating JSON files from the po file

The next step is to use WP-CLI to generate JSON files from our po file. To do this we use the command wp i18n make-json.

Be aware that as default this command will take out the translated strings from your PO-file for use in generating JSON-file. This can be cumbersome while in the middle of developing your theme or plugin. Because when you add new or adjust strings you’ll have to re-scan the files and translate all the strings again (and again, and again). Luckily there’s a flag to the command to avoid this.

Let’s start! In your terminal navigate to your language directory for your project. Run the following command and refer to your po-file (as mentioned I have a nb_NO.po file ready).

wp i18n make-json nb_NO.po --no-purge

If you have no problem with removing the translated strings from your PO file (for example if you’re making your final build), you can skip the --no-purge flag.

The terminal should prompt “Success” and state how many JSON files were created. If you see that it generated two JSON files this is because it has read both our source code Javascript file and the build file, and generated one for each. If you have more Javascript files in your project, you’ll end up with even more JSON files.

As of the time of writing this (WordPress v 5.3.2 and WP-CLI version 2.4.0) the JSON files are generated with the language code and a hash – a cryptic string as filenames. We need to find the right one and rename it.

Renaming the JSON file and loading it in PHP

Your language folder probably looks something like this:

Remember that the command has generated one JSON file per Javascript file – and because we actually do have two files for our custom block (the source and the build) it generated two files. If your Javascript code is split into several files, each would get two of their own JSON files.

If you are sitting with only two JSON files (because no other Javascript files were found), you can delete one of them now. If you have more than two you need to open up the JSON files and see which file they are for. The JSON files contain a property “source” which tells you which Javascript file this JSON file is for. Use that to figure out which JSON file to keep. I recommend finding the final build file (as opposed to the dev files) as this should contain all strings from all files.

When you’ve found the correct one we need to rename it. We need to rename it to follow this pattern:

[textdomain]-[language code]-[script handle].json

Use the textdomain you’ve used everywhere (e.g. __('My string', 'awhitepixel')), add a dash and the language code. Then provide a dash and the script handle you used to register your Gutenberg Javascript file (wp_register_script()). As a reference, my textdomain is awhitepixel, my language code is nb_NO, and my script handle for the Gutenberg script is awp-myfirstblock-js. So I rename the JSON file to:

awhitepixel-nb_NO-awp-myfirstblock-js.json

Tell WordPress to load our JSON

All that remains now is the final step – telling WordPress to load our JSON file. We need to use the function wp_set_script_translations(). This is a pretty new WordPress function so I recommend wrapping it inside a function_exists(). It accepts three parameters; the script handle for our block, the textdomain, and the path to our translation folder (note: the path, not the URL).

Inside our function hooked to init, where we registered our block script and call register_block_type we can also call this new function to load our JSON translation file. PS: Keep in mind that the hook enqueue_block_assets won’t work for registering translations.

functions.php
add_action('init', function() {
	wp_register_script('awp-myfirstblock-js', ....);
	register_block_type('awp/firstblock', ....

	if (function_exists('wp_set_script_translations')) {
		wp_set_script_translations('awp-myfirstblock-js', 'awhitepixel', get_template_directory() . '/assets/lang');
	}
});

And that’s all! Your block should now be translated. Switch the WordPress language to the language you translated into and check it for yourself. When I switch my WordPress language to Norwegian and add my block, the name and everything inside it is translated: