Creating Translatable WordPress Themes and Plugins: Developers Guide
Last year we featured a series on translating different aspects of WordPress, which focused on internationalizing an existing theme or plugin.
In today’s post, we’re going to extend and complement that series with some information for theme and plugins developers, namely how to localize your theme or plugin or it’s ready to translate.
We’ll take a look at what you need to do when creating new products to ensure users can easily translate them into their own language. Since 29% of WordPress.com websites are used in a foreign language and a greater focus is being placed on internationalization within the WordPress community, it’s more important than ever to ensure your themes and plugins are translation-ready.
So let’s go through the process in detail to make sure we use the right functions and methodology when creating themes and plugins.
The Text Domain
The first thing you need to do is define your text domain.
What’s a text domain? A text domain is a way of marking translations that belong together. It makes language files much more portable, modular and readable.
Naming text domains is a simple matter because the naming of your plugin/theme dictates what it is. The text domain should match the slug of your theme or plugin, which is usually the name of the folder it is contained in.
For the upcoming Twenty Fifteen theme the name of the theme is, of course, Twenty Fifteen. The slug – which is the name of the folder – is “twentyfifteen.” Since the text domain must be the same as the slug, the text domain is also “twentyfifteen.”
The first place you’ll need to show your text domain is the initial comment block of your theme or plugin. This is used to define basic properties of your plugin like author name, plugin name, theme tags and so on. Make sure to add a “Text Domain” property as well. Here’s Twenty Fifteen’s comment block in its
Marking Strings For Translation
Most developers know that to translate strings you should wrap them in the
__() function to return them or the
_e() function to echo them. There are actually 11 more functions you can use for translation perfection, although in most cases you really will use the two above.
Let’s take a look at all our options and what they are for:
This is one of the most basic translation functions. Like many of its siblings it takes two parameters: the string to be translated and the text domain.
It is used when you want to mark a simple string for translation and return the value to use somewhere else. This is frequently the case within functions – think of registering post types. In the example from the Codex below you can see how all members of the array are marked for translation:
This is almost the same as the function above, except it echoes the value. It can be used when you’re translating in HTML content directory:
This function is used when translating strings with a conditional plural in them. This means that you don’t know in advance if the string will use the plural or singular form because it depends on the momentary value of some parameter. A good example would be a comment count.
For example, if a comment count is one, you would need to use a singular form: “One comment.” If a comment count is 0 or more than one you would use the plural: “Many comments”. This can be done in one go using the
It takes four parameters: the singular form, the plural form, the number to check and the text domain:
sprintf() is a native PHP function, which is used to output a formatted string according to some parameters. It is used generously in conjunction with some more complex translations where variables are frequently used. Here’s how it looks in general:
The two most frequent placeholders are
%s for strings and
%d for integers. If you are using these placeholders you need to pass as many additional arguments to
sprintf as the number of placeholders you are using. It is also possible to index placeholders to use the second placeholder first and so on. For advanced usage of this function take a look at the PHP Documentation.
To make our translation example above a bit better, to account for 0 values we can use
_n() coupled with
In this example the use of the
$comment_count variable may be a bit confusing. When used within the
_n() function we are determining which version to use based on its value, the singular or plural version. When used later in the line, as the second parameter for
sprintf(), we are using its value as a replacement for
“x”-flavored functions exist to clear up any confusion which arises from similar pieces of translated text. For example the word “post” is used as a verb and as the name of a type of entry in a blog. In a different language they might use two very different words. In order to distinguish between them, we use
_x() to add context and return the result,
_ex() to add context and echo the result and
_nx() to add context to a plural translation.
There are two sets of three functions for escaping translated strings. One set takes care of strings used in attributes, the other set takes care of escaping for HTML. Here they are:
Since we’ve looked at all these types of functions before they should be pretty self explanatory. Here is a quick example:
This function is not named very well and is only useful in certain circumstances, which makes explaining it pretty difficult.
_nx_noop() register strings for translation but they don’t translate them right away.
English is pretty straightforward because there is one singular and one plural form. In other languages – such as Russian – it isn’t so simple. Nooped functions allow you to register the translations separately from their usage. The actual output is generated using the
translate_nooped_plural() function. The Codex provides a great example of how this works:
wp_localize_script() function we can get it done on the server-side. The idea is to create an object of our strings and pass it to our script by adding it to our document. If there is a translation file available the strings will be translated before they are output.
Loading a Text Domain
To enable our translated strings we need to provide a translation file. Whenever WordPress sees a translation function it finds the current language’s translation file and looks up the text specified within the function. If the text can’t be found it returns as is.
We need to tell WordPress where the translation files for our plugin/theme are held. This can be done using the
load_plugin_textdomain() function, which needs to be hooked into the
You will see three file types during the course of translation. The first, .POT files, are translation template files. These files are human-readable translation files, while .MO files, another type of translation file, are machine-readable translation files. When a new language file is created it usually starts from the POT file. It is duplicated and named using the locale, for example zh_CH.PO (zh is the language code for Chinese and CH is the country code for China). It is then opened using a tool such as Poedit and all the strings are translated. When saved, Poedit creates a .MO version.
In the end WordPress uses the .MO version as it is highly condensed for efficiency. This is all well and good, but how do we create our initial .POT file?
There are quite a few ways, I suggest using Poedit on your first go.
Install the open Poedit and go to File > New Catalog and fill out the details.
Switch to the next tab (Source paths) and set your paths. The path should be relative to the location-to-be of your .POT file. If you are working on a plugin and the catalog file will be in
languages you should use
../ for your source path.
In the “Source keywords” tab you’ll need to se the functions you want to check. If you are only using the basic functions you can simply add
_e, but it’s best if you add everything.
Once you click “OK” you’ll be able to select a location for your file. Poedit will actually create .PO and .MO files. You can create a POT file by simply copy-pasting the empty .PO file and renaming it.
In my view internationalization is a must. It is so easy to do that there is just no excuse for excluding visitors from your site who don’t speak or read English.
In this article we had a look at the basics of translations, what a text domain is, the functions we can use and how to create translation files.
Do you know of a faster way to implement translations? Do you speak a very obscure language and want to translate into it? Let us know in the comments below.