Internationalization, often abbreviated as i18n, means making your site tailored for different audiences around the world. This includes translation (e.g. 'Contact Us' vs '聯繫我們') and localization (e.g. dd/mm/yyyy vs yyyy年m月d日).
In this article, we will focus on translation.
Theme vs Content Translation
There are two areas that needs to be translated - the templates, and the user-submitted content.
The templates must be internationalized so the text "Featured" on the menu in the English version of the theme will be shown as "焦點" in the Traditional Chinese version. Because templates are provided by the theme, translating the template is the job of the theme author, and does not rely on plugins.
Although some plugins that uses machine-translation can translate the entire page, including theme-content as well as user-content, we will not consider machine-translation here.
The user content must also be translatable; this is not supported natively in WordPress, but there are community plugins which enables this.
This series focuses on developing themes, and so we will consider only theme translation here. You can explore more on content translation in the article WordPress - Content Translation.
Translation in WordPress is done through
gettext, a system which provides libraries and utilities for translation, originally developed as part of the GNU Translation Project.
gettext, we'd need to wrap any content in our theme that we want translated, with the special
After translation has been done, these functions will use those translations and output the translated string.
There are five functions we will be dealing with:
returnthe translated string; used in functions. Example:
echothe translated string; used in templates. Example:
returnthe translated string, but also specify the plural version. It takes three arguments - the singular version, the plural version, and a function that returns a number. If the number is higher than 1, the plural version will be outputted. Example:
_n( 'comment' , 'comments' , get_comments_number() );
returnthe translated string, but provides a short description to give ambiguous terms some context. For example, the term
tablecan mean 'a piece of furniture with a flat top and one or more legs' or 'a set of facts or figures systematically displayed, especially in columns'.
_x()takes two arguments - the string to be translated, and the short description. Example:
_x('table', 'a piece of furniture with a flat top and one or more legs'). This will give the translators some context to translate correctly.
_ex()- Same as
The following are equivalent:
WordPress actually has 12 functions available, you can find them all in the Codex
This is our string before wrapping it in a
And here it is after:
<span><?php _e('Warning!', 'brew'); ?></span>
brew is our text domain, which should be the same as the one we specified as the
Text Domain header name in our
style.css. The text domain acts as a sort of namespace to house our translations.
Remember to specify the text domain! Many developers forget!
Sometimes you'd have a variable in your to-be-translated string. For example, the copyright notice at the bottom of most pages:
© 2015 Brew Creations. All Rights Reserved.
The year is a variable, and, ideally, it should update automatically.
<?php _e('© 2015 Brew Creations. All Rights Reserved.', 'brew'); ?>, we can instead use a placeholder,
echo sprintf( __( '© %s Brew Creations. All Rights Reserved.', 'brew' ) , date("Y") );
%s is a placeholder for a string, which is defined as the second argument of
See the PHP documentation on
sprintffor more examples of placeholders.
This is especially useful for translation between languages which have very different word order typology - some languages put the verb at the end of a sentence whereas others put the verb in the middle.
After you've wrapped all the content inside
gettext functions, you'd need to run a program which will scan the PHP source code for those
gettext functions, and produce a
.pot (Portable Object Template) file, which contains all the translatable strings (those wrapped in
_e()) in the original language.
.pot file is in plaintext and acts a template for translators to translate from.
Translators then use this
.pot template and fill it in with the translation for the strings. There should be one language for each
.pot - A template containing original translation
... msgid "Description" msgstr "" ...
.po - A filled
.pot file - there should be one for each language
... msgid "Description" msgstr "簡介" ...
As you can see, in terms of structure,
.po are the same, only difference being that the latter is filled in with the translated string.
On Ubuntu, you can install
$ apt-get install poedit
A GUI will pop up.
Go to File > New Catalog.
Enter details about your project. Input
nplurals=2; plural=n != 1; for the Plural Forms field. Refer to the documentation on Plural Forms for help.
Next, go to the 'Sources paths' tab and specify the directory you want
poedit to scan. There are two input areas - 'Base path' and 'Paths'. The paths in 'Paths' are relative to the 'Base path', which in turn is relative to the directory where the
.pot is located. So if your file is in the
/languages directory of your theme directory, enter
.. as the base path.
If your templates are in the theme directory root, add
. as a path.
In the 'Sources keywords' tab, enter the following entries to tell which function names
poedit should scan for.
__ _e __ngettext:1,2 _n:1,2 __ngettext_noop:1,2 _n_noop:1,2 _c _nc:4c,1,2 _x:1,2c _nx:4c,1,2 _nx_noop:4c,1,2 _ex:1,2c esc_attr__ esc_attr_e esc_attr_x:1,2c esc_html__ esc_html_e esc_html_x:1,2c
If you're lazy, just enter the tags that you have used, although I'd recommend you to be as thorough as practical.
Click 'OK' save the file as a
After you click save,
poedit will scan the directories you specified and generate a list of translatable texts, which should appear on your screen.
It will save this into the
.pot file. It also generates a
.mo file; you'd need to delete that.
.pot file in your theme; it will be used by translators.
If you are a translator, or you want to provide some translations with your theme, you can. Open up your
.pot file in
poedit and start translating. Save the file this time as
.po files are in plaintext and human-readable, but machines can't parse this file quickly. So
.po files are compiled to their binary equivalent -
.mo (Machine Object) files - using
msgfmt. WordPress will use the
.mo to serve the actual translations.
In WordPress, the
.mo files must be named based on the locale exactly. You can find all WordPress locale codes at wpcentral.
next, we must let WordPress know which
.mo files it should be looking for in the
Also, inside one of the base templates that always gets loaded (e.g.
This will load all the theme's translated strings in the specified text domain.
Now we are done making our theme translatable. When parsing the templates,
gettext will use the key to look for the translation, and if none are found, the supplied string is used as a fallback.
If you enjoyed this article, why not take a look at the rest of the series on Developing WordPress Themes!