Wordpress - How to make a WordPress plugin translation ready?

1. Write with localization in mind

Don't use echo or print() to produce text output, instead use the WordPress functions __() and _e():

/** Not localization friendly */
echo "Welcome to my plugin";    
// OR
print("Welcome to my plugin");

/** Localization friendly */
_e('Welcome to my plugin', 'my-plugin');
// OR
$my_text = __('Welcome to my plugin', 'my-plugin');
echo $my_text;

_e() and __() will provide the translation — in the current language — of the text provided as the first parameter. _e() will output the text whereas __() will return it.

The second parameter is the text domain, you will be using it to tell WordPress that the text provided as the first parameter belongs to this plugin, you can use any name you want but I prefer to use the same name as I used for the plugin file of directory, I find it more intuitive.

How to output dynamic text like: "Hello <username>"?

With __() and sprintf():

/** Get the username */
$username = 'Magictrick';

/** Not localization friendly */
echo "Hello $username";     

/** Localization friendly */
printf(__('Hello %s', 'my-plugin'), $username);
// OR 
$my_text = sprintf(__('Hello %s', 'my-plugin'), $username);
echo $my_text;

2. Prepare the .pot/.po/.mo files

Definitions

  • The .pot file: is put at your disposal by the plugin developer and it's used as a starting point to create new translations, WordPress doesn't use it.
  • A .po file: is a translation file you or someone else started, and maybe completed, WordPress doesn't use it.
  • A.mo file: is automatically created by Poedit whenever you save a .po file, all you can do with these files is to upload or re-upload them whenever you create or update a .po file. WordPress gets translations from .mo files.

Open Poedit and create a new catalog (File › New Catallog...) with these settings:

  • Project info: Use your (or your team) information, the language and country should match your plugin default language
  • Paths:
    • Base path: .
    • Paths : remove all & add .., (we will store language file in a plugin subdirectory called languages)
  • Keywords : remove all & add __ and _e

Save the catalog as /my_wordpress_blog/wp-content/plugins/my-plugin/languages/my-plugin.pot and scan your plugin files for translatable text by pressing the update button. When the update is finished close that catalog, you won't need to update that file unless you add new translatable strings (i.e enclosed in __() or _e()) to your plugin.

Now let's create the first translation (I will use fr_FR):

Using Podeit, create a catalog from a POT file (File › New catalog from POT file...):

  • Project info: Use your (or your team) information, change the language and country, I will use French and France
  • Paths:Don't change
  • Keywords : Don't chage

Save the catalog as /my_wordpress_blog/wp-content/plugins/my-plugin/languages/my-plugin-fr_FR.po. Translate some or all the of the strings, save the .po file again, upload both the .po and .mo files.

Note that whenever you save a .po file a .mo file is generated with the same name, the filename of the .po file is crucial, it's composed of the concatenation of the plugin text domain (my-plugin) and the language locale (fr_FR), always name your .po files for plugins like this: [textdomain]-[locale].po, here are some examples:

  • Italian/Italy: wpcf7-it_IT.po
  • Portuguese/Brazil: wpcf7-pt_BR.po
  • Arabic: wpcf7-ar.po... Yes!

Whenever the plugin is updated with new text, update the po file, translate new strings and reupload the .po and .mo files

3. Instruct the plugin to load translated text for the current language

Somewhere in your plugin, you must tell WordPress to use your .mo file, you can do it by using this code in the beginning of your plugin file:

function my_plugin_init() {
  load_plugin_textdomain( 'my-plugin', false, 'my-plugin/languages' );
}
add_action('init', 'my_plugin_init');

Replace my-plugin with your plugin name in the 1st and 3rd parameter of the load_plugin_textdomain function.

4. Test and troubleshoot

Some reasons it may not work:

  • Strings are not imported into the .pot or .po file
    • → Wrong catalog settings (path or keywords or both)
  • Text is not translated on the WordPress site
    • → .mo file for that language missing or has a wrong file name
    • → Text domain not used (replace _e('my text') with _e('my text', 'my-plugin'))
    • → Text domain not loaded (use example above with the right parameters, WP will not warn you about mistakes)

Nabil’s answer is fairly complete but there is an easy variation provided:

  1. Your plugin is on the WordPress.org plugin repository

  2. You’re willing to require that your plugin only work with WordPress 4.6 or higher.

The steps are these:

  1. In your plugin’s readme.txt file, add Requires at least: 4.6. See https://developer.wordpress.org/plugins/wordpress-org/how-your-readme-txt-works/

  2. if it isn’t already, upload your plugin to the WordPress plugin repository. See https://wordpress.org/plugins/developers/add/.

  3. Find your plugin’s slug/text domain. To do that, go to your plugin’s page on the WordPress plugin repository. The URL will be like https://wordpress.org/plugins/your-plugin-slug/. That last part of the URL, “your-plugin-slug”, is your plugin’s slug. That’s what you use for the translation functions’ text domain.

  4. Use WordPress’ translation functions in your plugin (like __e(‘hello’, ‘my-plugin-domain’);). Just make sure to use the correct plugin text domain, acquired in the previous step. See https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/ for more info.

If you do the above steps, WordPress will take care of:

  • Parsing through your Plugin for all the translatable strings (no need to install and run Poedit or anything)
  • Make it easy for anyone to contribute translations of your plugin on translate.wordpress.org (no need to have your own site dedicated to translating your plugin, or having a custom process for translators to submit their translations to you)
  • when someone uses your plugin, WordPress will take care of checking if it’s translates into their language, and if so, showing it in their language (no need for them, or you, to load the translation files onto their website)

(Answer from my blog post here: https://cmljnelson.blog/2019/01/01/the-really-lazy-way-to-translate-a-wordpress-plugin/)