How to make a WordPress plugin translatable

June 10, 2021 Posted by Editorial Staff

In this tutorial, you will learn how to make a WordPress plugin translatable.

With a translatable WordPress plugin, you can cover a more extensive user base for your plugin hosted on WordPress.org or sell more licenses in marketplaces like Envato Market.

Make a WordPress plugin hosted on WordPress.org translatable

This section will cover the method used to make a plugin hosted on the WordPress.org repository translatable.

Declare the text domain in the plugin header

The first step for making your plugin hosted on WordPress.org translatable is to declare the text domain in the plugin header.

More specifically, the text domain is an identifier used by WordPress to categorize the loaded translations. Its value should match the plugin slug, namely the unique portion of the plugin URL.

As an example, in a plugin available at https://wordpress.org/plugins/example-plugin/ the correct value of the text domain is example-plugin.

The resulting plugin header with our example-plugin text domain:

/*
* Plugin Name: Example Plugin
* Author: Plugin Author
* Text Domain: example-plugin
*/

An exception for plugins compatible with old WordPress versions

If your plugin supports WordPress versions older than 4.6 (you define this with the Requires at least directive in the readme.txt file of the plugin) you should also declare the text domain and the relative path to the translation files with the load_plugin_textdomain() WordPress function.

function initialize_plugin() {
    load_plugin_textdomain( 'example-plugin', false, 'example-plugin/lang/' );
}
add_action( 'plugins_loaded', 'initialize_plugin' );

Note that with plugins hosted on WordPress.org a local translation in the example-plugin/lang/ folder is not required because the translation is defined on Translating WordPress.

Load a local translation with a plugin hosted on WordPress.org

If your plugin is hosted on WordPress.org, but you still want to load a local translation, there is the load_textdomain_mofile filter.

This example demonstrates how to define the location of a local .mo file in a plugin associated with the example-plugin text domain.

function change_translation_file_location( $mofile, $domain ) {
    if ( 'example-plugin' === $domain ) {
        $mo_file_location = WP_LANG_DIR . '/textdomain/yourtranslationfile-' . get_locale() . '.mo';
    }
    return $mo_file_location;
}
add_filter( 'load_textdomain_mofile', 'change_translation_file_location', 10, 2 );

Make a plugin not hosted on WordPress.org translatable

Suppose you are developing a custom WordPress plugin for a client or planning to sell your plugin online on marketplaces like Envato Market. In that case, the declaration of the text domain in the plugin header is not necessary.

Instead, use the load_plugin_textdomain() function to declare the text domain and the translation file location. Then create the translation files for each combination of language and locale.

Declare text domain and file location

Use load_plugin_text_domain() to define the text domain and the translation file location like you did in the previous section.

function initialize_plugin() {
    load_plugin_textdomain( 'example-plugin', false, 'example-plugin/lang/' );
}
add_action( 'plugins_loaded', 'initialize_plugin' );

Create a translation file with a proper name

The translation file should start with the text domain and be followed by the ISO_639-1 code of the language and then the ISO 3166-1 alpha-2 code of the locale.

As an example, the translation file for the Italian version of a plugin with the example-plugin text domain has the following name:

example-plugin-it_IT.po

Translation functions

All the text strings used in the WordPress plugin should be added as arguments in the translation functions provided by WordPress.

A simple example

If a button in your plugin contains a string that you want to make translatable replace your current button HTML:

<input class="button" type="submit" value="Add Item">

with:

<input class="button" type="submit" value="<?php esc_attr_e('Add Connection', 'dahm'); ?>" >

In this example, I’ve used the esc_attr_e() function to make the “Add Connection” string translatable, however other translation functions are available.

In this regard, it’s worth mentioning that you should select the translation function based on the context. To this end, I recommend you to read the next section, which includes an overview of all the translation functions.

The complete list of WordPress translation functions

In this section, I’ve included a complete list of the translation functions provided by WordPress, and each translation function comes with a short explanation and an example.

_e()

The _e() translation function echoes the content of a translatable string.

_e('Hello World', 'example-plugin');

__()

The __() translation function returns the content of a translatable string.

echo __('Hello World', 'example-plugin');

esc_attr__()

The esc_attr__() function is the same as __(), but the resulting string is escaped for HTML attributes.

echo esc_attr__('Hello World', 'example-plugin');

esc_html__()

The esc_html__() function is the same as __(), but the resulting string is escaped for HTML content.

echo esc_html__('Hello World', 'example-plugin');

esc_attr_e()

This esc_attr_e() function is the same as _e(), but the resulting string is escaped for HTML attributes.

esc_attr_e('Hello World', 'example-plugin');

esc_html_e()

The esc_html_e() function is the same as _e(), but the resulting string is escaped for HTML content.

esc_html_e('Hello World', 'example-plugin')

_n()

With the _n() function, you can display two alternative strings based on the value of a numeric variable.

The first string is display when the numeric variable value is equal to 1. The second string is displayed when the numeric variable value is different from 1.

echo _n('item', 'items', $count, 'example-plugin');

_x()

Use the _x() function to create multiple contexts for the same string.

For instance, the word ship is both a noun that means “A large vessel” and a verb that means “To send or transport”.

The first occurrence of the string ship:

$result = _x('ship', 'A large vessel.', 'example-plugin');

Another occurrence of the string ship but with a different context:

$result = _x('ship', 'To send or transport.', 'example-plugin');

Note that with _x(), the translation editor software will produce two entries for the string ship. As a consequence, you will be able to assign the two different translations of the string.

_ex()

The _ex() function is like _x(), but with the result echoed instead of returned.

_ex('country', 'A state or nation.', 'example-plugin');

_nx()

The _nx() function considers both the value of a number and the context.

echo _nx( 'record', 'records', $animals, 'Achievement in sport.', 'text-domain' );

_n_noop() and translate_nooped_plural()

The _n_noop() and translate_nooped_plural() functions work together to achieve the same result of _n(). However, in this case, two steps are required.

In the first step, you register the two strings without providing the numeric variable used to select the considered string:

$nooped_plural =_n_noop( 'item', 'items') );

Later in your code, you can use the $nooped_plural array with a numeric variable:

echo translate_nooped_plural( $nooped_plural, $count );

These two functions are necessary if you are in a situation where you have to pass only the singular and plural forms of a string (without the numeric variable) to a function.

How to verify if the plugin is translatable

To verify if a plugin is translatable, visit the page of your plugin on the Translating WordPress website.

If the plugin is prepared correctly, you will find a table with all the languages and the translation statistics.

A table with the languages and the related translation in a translatable WordPress plugin.
A table with the languages and the related translation status is available in a correctly translate plugin.

Translate the strings on the Translating WordPress website

First, visit the translation page of the plugin and click on the language for which you want to create a translation.

Then select the sub-project that you want to translate. The Stable (latest release) project includes all the translatable strings of the plugin, while the Stable Readme (latest release) project includes all the translatable strings of the plugin readme file.

Consider that a user of an authorized role should approve the translation submitted in Translating WordPress. However, you can approve your own submissions by becoming a Project Translation Editor (PTE).

For more details on the roles of the Polyglots Team, see Roles and Capabilities.

Translate the local strings

The local translations are stored in .po files, usually placed in a dedicated folder. This dedicated folder is typically named lang or languages.

Create a translatable WordPress plugin with translation files in the "lang" folder.
Localization files in a translatable WordPress plugin.

The best tool to create .po and .pot files for a WordPress plugin is the Poedit translation editor, a free program available in Windows, Linux, and Mac.

This translation editor automatically finds the translatable string in your plugin code and provides you an interface to create and modify the translations.

How to use Poedit with a WordPress plugin

Let’s see how to use Poedit to translate the strings of your WordPress plugin.

Create a localized translation file with Poedit

This section describes the steps to create a translation file with Poedit.

Download and install the Poedit translation editor from the official website. 

Create a new translation by selecting New… in the File menu of the application.

Select the language of the translation and then save your new and empty .po file with a name that follows this structure:

[slug]-[languge-code]_[locale-code].po

Below you can find the resulting translation file name of a plugin with the example-plugin slug, Italian as a language, and Italy as a geographic target.

example-plugin-it_IT.po

Now we have to add general information to the translation and also configure Poedit so that it will be able to extract the translatable strings from the plugin.

Open the Properties… menu of the Catalog submenu. In this tab, assign a name to the project, then define the language and the charset in the Translation Properties tab.

The translation properties of Poedit with basic information
The Translation properties tab of Poedit with basic information.

In the Source paths tab, use the Paths section to set the path where Poedit should search the translatable strings.

In the Excluded paths section, define the folders where you don’t want to search for translatable strings. For example, you can include folders with PHP libraries.

The Source paths tab of Poedit
The Source paths tab of Poedit.

Add in the Source keywords section the WordPress translation function (covered in the translation functions section) used in the plugin.

Six common WordPress translation function have been added in the Source keywords tab of Poedit.
Six common WordPress translation functions have been added in the Source keywords tab of Poedit.

Click the Ok button to complete the configuration of the project and start the parsing process by clicking the Update from code button.

If the project has been properly configured, a list of strings with the related translations is displayed. Now you have to click on a specific string and enter its translation.

The translatable strings are now available in Poedit.
The translatable strings are now available in Poedit. The original strings are under the Source text – English column, and the Italian translations are under the Translation – Italian column.

Translate the JavaScript files of a WordPress plugin

It’s possible with WordPress 5.0 and later versions to translate the strings available in the JavaScript files of the plugin. More precisely, you can use translation functions with the same names and behavior as their PHP counterparts.

To make specific translations function available, import them from the wp.i18n object:

//Dependencies
const {__} = wp.i18n;

You can use the JavaScript translation functions in any context of your JavaScript file. For instance, here __() is used to define the value of a constant.

const title = __('My title', 'example-plugin');

How to store the translation of the strings

For plugins distributed in WordPress.org the translatable strings defined in a JavaScript file are automatically added by WordPress in the translatable string of the Translating WordPress interface.

Instead, if the plugin is distributed outside WordPress.org, you have to manually generate a JED file with the translation and define its location with the wp_set_script_translations() function.

Note that a JED file should be created based on an existing .po file with tools like GlotPress or po2json.

In the case of po2json you can install the tool with npm:

npm install po2json

And then use this command to generate a JED file:

po2json example-plugin-it_IT.po example-plugin-it_IT.json -f jed