Jonathan Weatherhead's wild and exciting world

Multilingual WordPress themes with I18n and L10n

Having a fully translatable WordPress theme can save you a lot of work, and here’s how.

The world is a big place with many difference languages. You might not speak all of them but somebody somewhere knows how to translate from your language to theirs. How useful it would be if your WordPress theme or plugin text could be swapped for a translated version without having to touch the code. This is in fact not a coding nightmare but an ideal easily attainable through a simple API.

Internationalize

The first step is to internationalize your theme or plugin text. Did you know, there are 18 letters between the I and the last N, hence i18n. This step is where thetexts be translated are marked as translatable by wrapping them in I18n functions. Once this is in place, the translation API will be able to look up the strings in a translation table to determine whether translated versions exists for the active language locale in the text domain of the theme or plugin, falling back to the original texts if no translations are found. Simply put, “I want these blocks of text to be eligible for translation.”

The two most common of the functions __() and _e() – the first returns a string while the 2nd echos it. The translation functions each take as the last argument a text domain string. This needs to be consistent as it is the “namespace” for your text strings. The text domain must also be a string literal – PHP would understand if you used a constant or variable, but an external string extraction program will likely not be privy to the PHP runtime. Some of the most widely used i18n functions include:

__( $text, $textdomain )

Translates $text through the $textdomain and returns it.

_e( $text, $textdomain )

Translates $text through the $textdomain and echoes it.

_n( $singular, $plural, $count, $textdomain )

Translates $text through the $textdomain and returns it, using $singular if $count is 1 and $plural otherwise.

_x( $text, $context, $textdomain )

Translates $text through the $textdomain and $context and returns it. This is good for disambiguating homonyms.

To leverage the translation API, the text domain needs to be listed in the theme or plugin header, and it is standard to use the “slug” format of the theme name as your text domain.

Theme Name: My Example Theme
Text Domain: my-example-theme

Depending on whether the codebase is a theme or plugin, load_theme_textdomain() or load_plugin_textdomain() needs to be called in the appropriate hook after_setup_theme or plugins_loaded. That’s it. Your theme or plugin is now internationalized, translation-ready.

Localize

The second step is to localize your theme or plugin text. Did you know, there are 10 letters between the L and the last N, hence L10n. This step is where the texts marked for translation are externalized into a translation template and then used to build translation locales in various languages. Once this is in place, the translation API will be able to look up the strings in suitably provided language locales to determine whether translated versions exist in the text domain of the theme or plugin, falling back to the original texts if no translations are found. Simply put, “is there an available translation for these blocks of text in language ABC?”

The flow is to generate a translation template (.pot) from the codebase which is then used to create specific translations (.po) files which are finally compiled into language locales (.mo) files. Manually generating a translation template (.pot) is tedious and error-prone so at this point I strongly recommend using some software to extract the translatable texts and manage the translation process. Poedit is an excellent program suitable for the task. Scan the codebase for translatable texts, generate the translation template, and build language locales. Place the .mo language locales in their respective theme or plugin base directories to conclude the translation process. They must follow specific names – theme locales must have the format en_EN.mo where the first two characters are a language code and the 2nd two are a country code. Plugins have a slightly more flexible naming convention of text-domain-en_EN.mo and more information about deploying language locales can be found @ I18n for WordPress Developers # Loading a Text Domain