Make Your Plugin Translation Ready

WordPress provides us with all the tools (and functions) we need to make our plugins translatable or localizable. Why make your plugin translatable, you might ask? Well, to increase your plugin’s target audience by allowing any person, who speaks any language, the ability to understand and use it. But making your plugin ready for translation doesn’t mean you need to do the translations yourself. But it does provide a really easy way for your plugin users to provide their own translation files. This is what you need to do in order to make your plugin translation ready:

  1. Read up more about localization in WordPress.
  2. Write your code with localization in mind.
  3. Prepare/generate your translation files.
  4. Tell your plugin to use your translation files.

Before We Begin the Tutorial

As you may know, Matt is following this tutorial and posting his results on his own blog. His posts always give great insight into how he found my articles, along with any hurdles he had. His “sister-post” to this article is particularly inspiring this week and I recommend you check it out. Now back to the code…

Write Code With Localization In Mind

Whenever you use a string in your code files, don’t just echo or print them out. Rather wrap them with the WordPress functions __() or _e(). An example will explain best:

//this string is not translatable
echo 'I am not translatable';

//rather rewrite it using __()
echo __('I am translatable', 'my-plugin-domain');

Or you could have used the _e() function to translate and echo:

_e('I am translatable', 'my-plugin-domain');

Notice I am passing in a second parameter ‘my-plugin-domain’ to these functions. This is the domain that WordPress uses when performing the translation. In our plugin’s context, the domain will be the plugin’s slug or unique identifier.

What’s the difference between __(), _e(), _x(), and _ex()? is another great article which outlines the differences between the different localization functions within WordPress.

Prepare / Generate Your Translation Files

WordPress uses the GNU gettext localization framework to do these translations. But in order for translations to take place a few files are needed. They are:

  • POT (Portable Object Template) file – the template file containing every string used within the plugin that is translatable.
  • PO (Portable Object) file – translation files that holds all translations for a specific language per file. Every string listed in the POT will be in the PO file together with it’s correct translation.
  • MO (Machine Object) file – A binary representation of the PO file. WordPress uses this file when doing the translations.

I typically store all my translation files in a sub directory called lang or languages. The naming convention for your .PO and .MO files are:

{plugin-domain}-{locale}.po
{plugin-domain}-{locale}.mo

Some things to note:

  • {plugin-domain} is the same as the domain parameter you passed into your __() and _e() function calls above.
  • {locale} is your current WordPress install’s language code which is defined in the wp-config.php file.

So example German and Spanish .PO files that we would include for our FooDocs plugin could be:

foodocs-de_DE.po
foodocs-es_ES.po

There are a bunch of ways to generate your .PO and .POT files, but I have found the quickest way is by using a tool called POEdit.

Generate Your .POT Template File

  1. Install POEdit and run the application.
  2. Click File->New Catalog
  3. Fill in all details under the Translation properties tab
  4. Click Sources Paths tab:
    1. Set base path to be .
    2. Under Paths, add a path of ..
  5. Click Sources keywords tab:
    1. Remove all keywords
    2. Add __
    3. Add _e
  6. Click OK and save the file to your plugin’s language directory. In our example it is /wp-content/plugins/foodocs/languages/foodocs.pot

TIP : If you did not find any strings and your .POT file is empty, check under Catalog -> Properties -> Sources Paths Tab and make sure you have .. added as a path.

Generate Your .PO and .MO Translation Files

As an example, I want to create a German translation:

  1. Run POEdit
  2. Click File -> New Catalog from POT file. Select the POT file you created above
  3. Fill in the translation properties including the language. In this example : German
  4. Click OK and save the file using the {plugin-domain}-{locale}.po format from above. In this example it would be foodocs-de_DE.po
  5. Go through every string and translate it.
  6. Save your changes when you are done.

TIP : If you are not sure what to name your .PO files, you can get the locales from this codex page.

Setup Your Plugin To Use Translation Files

You now need to tell your plugin how to load the appropriate .MO files. To do this we need to hook into the init action from our plugin class constructor:

add_action( 'init', array($this, 'load_plugin_textdomain') );

Please note : make sure you do not use any translatable string function calls before the call to our ‘load_plugin_textdomain’ otherwise these strings will not be translated. To be safe, make sure you hook up this event first in your plugin class constructor.

Here is the code for the function load_plugin_textdomain:

function load_plugin_textdomain() {
	$domain = $this->plugin_slug;
	$mo_file = WP_LANG_DIR . '/' . $domain . '/' . $domain . '-' . get_locale() . '.mo';

	load_textdomain( $domain, $mo_file ); 
	load_plugin_textdomain( $domain, false, dirname( plugin_basename( __FILE__ ) ) . '/lang/' ); 
}

Let’s work through this function line by line:

$domain = $this->plugin_slug;

– The domain for our plugin is the plugin’s slug which we have previously defined. (Remember the second parameter we passed in when using the __() or _e() functions up above?)

$mo_file = WP_LANG_DIR . '/' . $domain . '/' . $domain . '-' . get_locale() . '.mo';

– We want to allow for an alternate path to a language file for our plugin’s translations. So we specify a path to this potential file which will be in the WP_LANG_DIR path. The value for this path for our FooDocs plugin will be something like the following:

wp-content/languages/foodocs/foodocs-en_US.mo

load_textdomain( $domain, $mo_file );

– Load the translations from the above file path.

Why do we want to load from this strange path? Pretty simple : if custom translations files are used by the user, and these are stored in the languages directory inside the plugin, then they will be deleted when the plugin is updated! By loading translations from the path above, we are giving the user an interim way to safely host any translations files they might have created, until such time as the plugin author includes and deploys the translations along with the plugin.

load_plugin_textdomain( $domain, false, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );

– Load the translations from the directory within our plugin. This will cater for translation files that are shipped with our plugin.

All Done, Now What?

That was a lot of work, but our plugin is now ready to be translated. Users can choose to translate the plugin and store their .mo files in the wp-content/lanaguages/foodocs directory or they can submit a pull request and the plugin author can include the translation files within the /lang directory within the plugin. All our translation bases are covered.

Homework & What’s Next

As mentioned above, check out Matt’s article, which is greatly inspiring for other new WordPress developers just getting started with plugins.

In the next post, I am going to focus on adding more features to the FooDocs plugin, now that our foundation is pretty solid.

Comments are closed.