Last Updated:

Translations in JS files

Localization in PHP files has long been commonplace for WordPress, but translation in JS files, which was implemented through the wp_localize_script() function, left much to be desired for a more dynamic and convenient approach. With the version of WordPress 5.0, such an opportunity has appeared, now you can use localization functions as conveniently as in PHP: , directly in JS files.__()_x()

 

How it works

You need to "tell" WP that your script has a translation, and what text domain (ID) you need to use for this translation. This is done by the wp_set_script_translations() function.

Thus, the registration of your script with translation should look like this:

wp_register_script( 'my-handle', plugins_url( '/js/my-file.js', __FILE__ ) );
wp_set_script_translations( 'my-handle', 'my-domain' );

The function adds a dependency of the main script on and includes the file.wp_set_script_translations()wp-i18n.json

Be sure to read the description of the wp_set_script_translations() function.

Now in JS code, you need to use the wp-18n object as follows:

const { __, _x, _n, _nx } = wp. i18n; __( 'Hello', 'myl10n' ); _x( 'Hi', 'short word', 'myl10n' ); _n( '%s star', '%s stars', 5, 'myl10n' ); _nx( '%s star', '%s stars', 5, 'superstars', 'myl10n' ); sprintf( __( 'See Link: %s', 'myl10n' ), 'http://site.com' );

The translation functions used are completely similar to the PHP functions of the same name and are used in exactly the same way. Cm. __(), _n(), _x(), _nx().

Setting up the translation for JS files is ready!

It remains only to create translation files from which line translations will be taken. Read below how to do this..json

Notes:
  • The lack of functions, etc. in JS is due to the fact that they are not needed in JavaScript, because the browser itself is able to escape unsafe characters.esc_html()esc_html__()

  • The JSON content of the translation file is displayed directly in the HTML page in the form of js code, right before the script for which the translation is connected.

  • Although it is not recommended to use HTML in translated strings, sometimes it is necessary, for example, to add links: . Right now, it's not that easy to do without using innerHTML (which can be dangerous). This situation is being discussed on GitHub.Check <a href="%s">my website</a>.

  • If several scripts are registered for which translations are needed, then translations must be connected for each script separately (each must have its own JSON translation file):

    wp_register_script( 'my-plugin-script', plugins_url( 'js/my-script.js', __FILE__ ), [ 'wp-i18n' ], '0.0.1' ); wp_set_script_translations( 'my-plugin-script', 'my-plugin' ); wp_register_script(  'my-awesome-block', plugins_url( 'js/my-block.js', __FILE__ ), [ 'wp-i18n' ], '0.0.1' ); wp_set_script_translations( 'my-awesome-block', 'my-plugin' );

    This approach is needed for optimization: only translation strings used in the pluggable script are added to HTML - nothing superfluous.

Option 1: Create with wp-cli (recommended)

 

The first thing you need for this method is to have a ready-made PO file, in which, in addition to translations for php files, there are also translations for js files. Usually, in order for the basic PO file to get translations from js files, you do not need to do anything separately, everything is done as usual, and JS files use functions that are automatically picked up by the Poedit program or another parser, for example, the Loco Translate plugin.__()_x()

Let's imagine that our plugin has a folder and it contains several PO files with translations for different languages, which, among others, have strings from JS files.languages

Then, you can create files from existing files with the wp i18n make-json command..json.po

wp i18n make-json languages --no-purge
  • languages is the path relative to your theme/plugin to the folder where the files are located. the files will appear in the same folder. If you want the files to be created elsewhere, specify the path in the second parameter of the command..po.json.json
  • --no-purge is a flag that asks not to remove translation lines from the PO file. By default, they are deleted (I don't understand why this is needed by default).
How an md5 hash is created

When you create a file in this way, it will get the name of the following format: , where the md5 hash will be generated from the file path taken from the PO file. .json{domain}-{locale}-{md5}.json

For example, in a PO file, the translation string looks like this:

#: scripts.js:9 msgid "Hello" msgstr "Hello"

Then md5 will be created from the string: . This variant of hash generation is the same as that used as standard in WP when using the wp_set_script_translations() file's json connection function.md5('scripts.js')

Multiple json files: If there are translations for several files in the PO file, several files will be created for each of them..js.json

No extra lines: the generated json file will only contain strings related to a specific js file.

Option 2: Convert with gulp

Step 1. Create a file.po

It will contain the lines from the JS file and their translation. I wrote about how to create PO files in the article about translations. Here everything is done in the same way, only to search for translation strings you need to specify our js file.

Let's look at an example. Let's let us create a file for the JS file that lies at the root of the theme..poscripts.js

Create a file in the theme and add the following code to it (for clarity, we make the file name similar to the future json file):/languages/js/myl10n-ru_RU-my-script.po

msgid "" msgstr "" "Project-Id-Version: my-plugin-name\n" "POT-Creation-Date: 2020-04-28 12:17+0500\n" "PO-Revision-Date: \n" "Last-Translator: Kama <foo@bk.ru>\n" "Language-Team: WP Kama\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 &&n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;" "_nx_noop:1,2,3c;esc_attr__;esc_attr_e;esc_html__;esc_html_e\n" "X-Generator: Poedit 2.3\n" "X-Poedit-Basepath: .. /.. \n" "X-Poedit-SearchPath-0: scripts.js\n" "X-Poedit-SearchPathExcluded-0: node_modules\n"

Note the lines:

  • "X-Poedit-Basepath: ../..\n" — here you need to specify the path from the PO file to the theme folder. Since we have a file in two folders we go up to two folders./languages/js
  • "X-Poedit-SearchPath-0: scripts.js\n" — here you need to specify the path to the js file, relative to the theme folder (base path, Basepath). Since we have the file right in the theme, just specify the file name here.

Now open the Poedit program, throw our PO file there, click the "Update from code" button, translate the received lines and click "save".

 

Step 2. Convert the file to .po.json

Note that all lines from the PO file (not just js) will appear in the json file.

To do this, we will need the po2json and jed libraries, because we need to convert to jed format. Install them in the project in teams:

npm install -D po2json
npm install -D jed

In add the following code:gulpfile.js

let po2json = require('po2json'), Jed = require('jed'), Write = require('write'); gulp. task( 'po2json', async ()=>{  path to the PO file let PO_file = 'languages/js/myl10n-ru_RU-my-script.po' po2json. parseFile( PO_file, { format:'jed' }, ( err, jsonData )=>{ let i18n = new Jed( jsonData ) let JSON_file = PO_file. replace( '.po', '.json' ) Write. sync( JSON_file, JSON. stringify( i18n, null, 2 ), { newline: true } )
	})

} )

In this code, you need to change the name of the generated json file to suit your case.

Now, create a json file with this command:

gulp po2json

As a result, a file should appear next to the file. .po.json

The procedure for creating a JSON file can be hung on the watcher, so that when you change the PO file, the JSON file is automatically changed.

Option 3: Publish the plugin/theme to the WordPress directory

In this case, everything is done automatically: all JS files will be automatically analyzed in the same way as it is already done with PHP files. All discovered translations will be added to to allow the community to make translations for them.translate.wordpress.org

Thus, translation files are created automatically. You only need to specify the parameters and - the translation domain and the path to the translation folder in the plugin headers, and then translate the resulting lines on the GlotPress website (translate.wordpress.org)..jsonText Domain:Domain Path:

To parse translation strings for JS on GlotPress, the WP-CLI commands i18n are used. This option is more stable compared to makepot.php.

JSON name of the translation file

Let's look at what name the JSON translation file should have so that the wp_set_script_translations() function finds the appropriate file when trying to connect it.

There are two options here, when the third parameter of the function is specified and when not:$path

  • The third parameter is NOT specified:$path

    /wp-content/languages/themes/{domain}-{locale}-{md5}.json  // plugin-ru_RU-db8f629adc6c4c33f29613cfb71a6038.json
  • The third parameter is specified$path
    SPECIFIED_PATH/{domain}-{locale}-{handle}. json  plugin-ru_RU-script.json SPECIFIED_PATH/{domain}-{locale}-{md5}. json // plugin-ru_RU-db8f629adc6c4c33f29613cfb71a6038.json /wp-content/languages/themes/{domain}-{locale}-{md5}. json // plugin-ru_RU-db8f629adc6c4c33f29613cfb71a6038.json

The MD5 hash in the name is formed from the path to the file relative to the plugin/theme folder. See here for details.

Hooky

WordPress provides filters, such as load_textdomain and gettext, that allow you to override the path to translation files or individual translations.

In WordPress 5.0.2, the following filters have been added to allow you to filter the behavior so that you can do the same for JavaScript translations. The following filters are available:wp_set_script_translations()

  • pre_load_script_translations is triggered at the beginning of the function and allows you to completely override the operation of the function .load_script_translations()
  • load_script_translation_file — allows you to change the path to the translation file for a particular script.
  • load_script_translations — allows you to change the resulting JSON code of the file.