3

I have followed the instructions for internationalization on: https://developer.wordpress.org/block-editor/developers/internationalization/, but it doesn't seem to play well with the development tools for Gutenberg. It will find all the translations in the src directory in the multiple js files and will use that as the relative paths while the npm run build will make one build/index.js file which I am enqueueing. The wp i18n make-json languages --no-purge will create multiple files which the MD5 won't work (probably because of the relative paths) and I can't name them al ${domain}-${local}-${handler}.json.


To have a better understanding what I mean. I now have the following:

npm init
npm i --save-dev --save-exact @wordpress/scripts
mkdir build
mkdir src
cd src 
touch index.js
touch edit.js

index.js

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';

import edit from './edit.js';

registerBlockType( 'gbg/myguten', {
    title: __( "My Gutenberg Example", "gbg" ),
    category: "widgets",
    edit
} );

edit.js

import { __ } from '@wordpress/i18n';

export default () => (<h1>{ __( "Hello World", "gbg" ) }</h1>);

I then generate the translations:

mkdir lang
wp i18n make-pot ./ lang/myguten.pot
cp myguten.pot myguten-en_US.po
wp i18n make-json myguten-en_US.po --no-purge

This will generate multiple json files, while I rather have one combined json file. npm run build will generate one index.js file which I use to register in WordPress.

/**
 * Plugin Name: My Guten
 * Text Domain: gbg
 */
function gbg_myguten_block_init() {
    load_plugin_textdomain( 'gbg', false, dirname( plugin_basename(__FILE__) ) . '/lang/' );

    wp_register_script(
        'gbg-myguten-script',
        plugins_url( 'build/index.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element', 'wp-i18n' ),
        time(),
        true
    );

    register_block_type( 
        'gbg/myguten', 
        array(
            'editor_script' => 'gbg-myguten-script',
        ) 
    );

    wp_set_script_translations( 'gbg-myguten-script', 'gbg', plugin_dir_path( __FILE__ ) . 'lang' );
}
add_action( 'init', 'gbg_myguten_block_init' );

Now it will search for ${domain}-${locale}-${handle}.json while multiple json files exist.

2
  • 1
    ${domain}-${locale}-${handle}.json is indeed loaded before the individual JSON/translation files, but there's a good reason why make-json generates those files as explained here. Nevertheless, you can try po2json. :) Commented May 29, 2020 at 1:54
  • I understand po2json is replaced with wp i18n make-json languages --no-purge. Still don't understand what to do with the multiple json files for relative paths like src/index.js and src/edit.js while the build is build/index.js with all code. Commented May 29, 2020 at 21:55

3 Answers 3

5
+50

(I revised this answer mainly to let other readers and you know the issues I originally noticed in your steps as shown in the question.)

So in my original answer, I was focused on suggesting you to just go with the multiple script translation files, but I thought I should clarify the three things below:

It will find all the translations in the src directory in the multiple js files and will use that as the relative paths while the npm run build will make one build/index.js file which I am enqueueing.

  • Yes, wp i18n make-json will create one JED-formatted JSON file per JavaScript source filemore details on Make WordPress.

  • As with the npm run build (which uses the wp-scripts package), it — as far as I know — only builds JavaScript source files (.js) and not JED files which are JSON files. (There is a JS linter and other tools, though.)

The wp i18n make-json will create multiple files which the MD5 won't work (probably because of the relative paths)

  • The files would work so long as you give the proper file name to your PO files where the format is ${domain}-${locale}.po (see Plugin Handbook » Loading Text Domain) — e.g. my-plugin-it_IT.po if the text domain is my-plugin and the locale is set to it_IT (Italiano).

    But then, you used the wrong text domain, e.g. with the command wp i18n make-pot ./ lang/myguten.pot, you should've used gbg and not myguten because the Text Domain in your plugin header is gbg (i.e. Text Domain: gbg).

    Additionally, your cp ("copy") command should have been preceded by cd lang.

  • As with the MD5 thing, it is the MD5 hash of the JS source file path as you can see in the PO file — so the path is (or should be) relative to the plugin folder.

    E.g. Structure of my sample plugin:

    wpse-366881/
      build/
        index.js <- relative path = build/index.js
      lang/
      src/
        edit.js  <- relative path = src/edit.js
        index.js <- relative path = src/index.js
      index.php
      package.json
    

I rather have one combined JSON file.

  • In that case, you can use po2json and npx to run the executable po2json file.

So how should you internationalize JavaScript spread in multiple files but build in one?

Just follow the steps in the question, except:

  1. Make sure the translation files (POT, PO, MO and JED/JSON) — i.e. the code inside and the file names — are using the text domain as defined in the plugin header. Additionally, put all the translation files in the correct directory.

    E.g. wp i18n make-pot ./ lang/gbg.pot

  2. There's no need to run the wp i18n make-json command which creates the multiple files, and just use po2json to create a single JED/JSON file from a PO file.

    And the command for that is: (Note that as of writing, WordPress uses JED 1.x)

    npx po2json <path to PO file> <path to JSON file> -f jed1.x

    E.g. npx po2json lang/gbg-en_US.po lang/gbg-en_US-gbg-myguten-script.json -f jed1.x

Sample Plugin I used for testing purposes (and for other readers to try)

You can find it on GitHubtried & tested working on WordPress 5.4.1, with the site locale/language set to it_IT/Italiano. And the code are basically all based on your code, except in index.php, I used the build/index.asset.php file to automatically load dependencies and version.

And btw, the plugin was initially based on the example here.

3
  • 1
    Thanks to your sample project I was able to find the most important key to the problem. make-pot makes us of the /* Plugin Name: Descriptor */ or /* Theme Name: Descriptor */ to work out how to generate the.pot file so that it finds the translations and also targets both build/index.js and src/index.js. However if you don't have that description somewhere in a file directly in your source directory. It won't work. Commented May 30, 2020 at 8:35
  • When we investigate MakePotCommand.php this is confirmed by the method get_main_file_data. It runs through the files in the source directory. It will search the style.css-file for the Theme header or any .php file for a valid Plugin header. If none of these can be found it will treat it as a regular project. I haven't investigated what that actually means, however it can't find any string translations. Commented May 30, 2020 at 8:41
  • make-pot is just one way we can use to internationalize a WordPress plugin or theme - one can also manually create the POT file and use tools like Poedit to generate the PO/MO translation files. And yes, the Plugin/Theme Name header has always been a requirement when creating a plugin / theme. Commented May 30, 2020 at 16:39
1

With the help I got from the marked answer written by Sally CJ I was able to write this answer which is a more compact version of what I was looking for as an answer.


I am using the dutch locale (nl_NL) in the samples below, but feel free to use any other locale instead like it_IT or en_US.


The make-pot WP CLI command only does the expected job when used in the root directory of your theme or plugin. This is also where the file lives that contains the Plugin or Theme header.


To get the expected result run the following commands:

1. wp i18n make-pot .
2. cp ./languages/my-plugin-slug.pot ./languages/my-plugin-slug-nl_NL.po
3. npx po2json languages/my-plugin-slug-nl_NL.po languages/my-plugin-slug-nl_NL-my-plugin-script-handler.json -f jed1.x;                    

In your plugin PHP-file use the following code:

function register_block_my_gutenberg_block() {
    load_plugin_textdomain( 'my-plugin-slug', false, plugin_dir_path( __FILE__ ) . 'languages' ); 

    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php' );

    wp_register_script(
        'my-plugin-script-handler',
        plugin_dir_url( __FILE__ ) . 'build/index.js',
        $asset_file['dependencies'],
        $asset_file['version'],
        true
    );

    register_block_type(
        'my-namespace/my-gutenberg-block',
        array(
            'editor_script' => 'my-plugin-script-handler'
        )
    );

    wp_set_script_translations( 
        'my-plugin-script-handler', 
        'my-plugin-slug',
        plugin_dir_path( __FILE__ ) . 'languages' 
    );
}

add_action( 'init', 'register_block_my_gutenberg_block' );

As written in the documentation, How to Internationalize Your Plugin, "The text domain must match the slug of the plugin". That's why my-plugin-slug is used in the places marked for text-domain.

PoEdit

You can also use PoEdit for translation. Just use the make-pot as described above in step 1 and then open the newly create pot file in PoEdit. Create the translation and save it as my-plugin-slug-nl_NL.po. Then use the command in step 3 to generate the .json-file.

Package.json

You can make life a little bit easier by adding make-pot and make-json to your package.json file. Now if you npm run make-pot, the pot file is created. Use it to translate. Then use npm run make-json to create the json files for all the .po files. Make sure you change the my-plugin-handler.json to the name of your script handler defined in your PHP file with wp_register_script.

  "scripts": {
    "build": "wp-scripts build",
    "format:js": "wp-scripts format-js",
    "lint:js": "wp-scripts lint-js src",
    "packages-update": "wp-scripts packages-update",
    "start": "wp-scripts start",
    "make-pot": "wp i18n make-pot .",
    "make-json": "find . -iname \"*.po\" -type f -exec sh -c 'npx po2json $0 $(dirname $0)/$(basename $0 .po)-my-plugin-script-handler.json -f jed1.x' {} \\;"
  },
0

Other answers here suggest the use of npx po2json instead of wp i18n make-json but that might not be ideal for everyone because wp i18n make-json is the standard way of generating json files. Here's how you make it work:

enter image description here

  • When running the make-pot command, explicitly include the JS build directory and exclude the JS source directory: wp i18n make-pot ./ languages/domain.pot --exclude=path/to/js/src --include=path/to/js/build/ --ignore-domain. Now your POT file will only reference the generated files that are actually enqueued on the page.

  • Run make-json. The md5 hashes of the generated JSON files will correctly reference the actual scripts loaded on the page.

2
  • The other answers do mention make-josn though, but po2json if you only want one output file. When you say it won't work for everyone what's wrong with it? Commented Aug 26, 2021 at 10:48
  • Edited for clarity. I meant it might not be ideal for everyone. Commented Aug 27, 2021 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.