Advertisement

Translating Your Theme

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

It's very easy to turn a blind eye to other languages when developing your WordPress theme, but this is a very bad habit and immediately turns away a whole market of WordPress users and potentially thousands of dollars in lost revenue. Out of the top 10 countries that search Google for "WordPress themes", only one is native English speaking (the USA) and that comes 9th. As of writing this, there are only 269 themes in WordPress' theme database that are tagged as translation-ready out of more than 1,500 themes. That's only 18% of the themes. I'm going to show you how to make yours one of them.


How It Works

When you usually make a theme, you will simply hardcode any theme text such as the 404 error message in the 404.php file, or labels such as "comments:" or "author:". If the user's WordPress is German for example, these snippets of text will still display in English. The solution to this is to return or echo these statements using one of four WordPress functions which are designed to reference a language file for the correct text. Once you have your text wrapped in these functions, you can create a file containing all the translations that is referenced every time the theme is loaded. There are three translation files that we use:

  • .pot (Portable Object Template) – This is the template file that contains a reference to each text string in your theme that needs translating. This file does not contain any translation. It is a plaintext file.
  • .po (Portable Object) – Made from the .pot file, the .po contains all your string references as well as their translations to one specific language. This is also a plaintext file that can be edited.
  • .mo (Machine Object) – A binary version of the .po file. By using machine code, the file can be used much faster than its plaintext alternative.

Step 1 The Four Functions

Each of the four functions requires at least one argument, which is the text that is to be translated. The functions are:

  • __()(two underscores) The basic function that you will use the majority of the time. It returns the text in the correct language.
  • _e() – The same as __() except it echoes the text instead of returning it.
  • _n() – Used when the text has the potential to be plural, so for example if you were to display how many comments have been made, you might want to output either "X comments" or "X comment" depending on how many comments you have.
  • _x() – Useful for when the translation of the word depends on the context. "Post" could mean "a post (noun)" or "to post (verb)" depending on context. It is important for the translator to know which you mean when translating to be accurate. _x() is mainly used where single words are used.

__() & _e()

These are the simplest translation functions that WordPress has to offer. Let's take a look at an example of each:

<?php
if( is_single() ) { //If this is a "post"
	echo __( 'This is a post.' );
}
?>
<?php
if( is_single() ) { //If this is a "post"
	_e( 'This is a post.' );
}
?>

Both of these functions are doing exactly the same thing here. The statement "this is a post" is being checked against the .mo file if there is one, and returning the result. __() and _e() only require one argument to be passed to them, which is the text we want to be translated. A second optional argument is available and we'll come to that later. The only difference between the two is that __() needs the echo statement here. Let's look at an example where __() works better than _e():

<?php
the_content( __( 'Click here to read more' ) );
?>

Instead of passing a string to the the_content() function, we have used __() so that the text can be translated. If we had used _e() here instead, you would have the translation of "Click here to read more" echoed to the document instead of being passed to the_content() which would cause all sorts of unhelpful problems.

_n()

What if you have a situation where the text you output could potentially be a plural or a singular like the "X comments" example above? Instead of giving two different text strings for the translator, you can say that you have a single piece of text that needs a singular and plural translation. The following two examples both output the same result to the user:

<?php
if(get_comments_number() == 1) {
	_e( 'There is a comment' );
} else {
	_e( 'There are comments' );
}
?>
<?php
echo _n( 'There is a comment' , 'There are comments' , get_comments_number() );
?>

_n() requires three arguments. The first is the singular version of the text, the second is the plural, and the third is the number that it's referencing. In this case, get_comments_number() is finding how many comments are on a post and then _n() is choosing the appropriate text to use.

_x() & _ex()

Let's say you're translating a .pot file, and you come across an entry "scroll". Are you going to interpret that as "a piece of rolled paper" or "pan up or down the website"? You could really do with some context to describe what you need to translate it to. These functions give you that ability, by having a second required attribute which asks for a short description to describe the phrase or word. Check the example below:

<?php
echo _x( 'post link' , 'A link to the post' );
_ex( 'post link' , 'Submit a link' );
?>

The example shows you the difference between _x() and _ex(). It is the same e, as with _e, to make the function echo the output instead of returning it. For both, our first parameter is our text that needs translating, and the second is a comment or note about the translation text to make it clear what is meant.

Advanced Techniques

Let's say that you have a situation where the text you want to generate is composed of a text string with the result of a function or the value of a variable put somewhere inside it. You might be tempted to right something like this:

<?php
$color = the_color();
_e( "You have chosen the $color theme" );
?>

When it comes to making your .pot file, POEdit will ignore this because it doesn't want to use a variable in the middle of a sentence. The reason being is that it will submit the string You have chosen the $color theme to the .pot file, but when it comes to searching for the translation when the script is executed, it will search for the string You have chosen the blue theme which it won't find. So what if we do this instead:

<?php
$color = the_color();
echo __( 'You have chosen the ' ) . $color . __( ' theme.' );
?>

The script will now be able to retrieve the translations, but now it's become too difficult to translate because the sentence has been broken up. This sentence might not even be translatable in some languages that have largely different syntax, such as in German where their word for "chosen" would appear at the end of the sentence. You would need to go through the trouble of explaining that these two separate text strings are part of one, and that "theme" might not translate to "theme" at all.

The solution is to use a single text string that has single-quotation-friendly syntax. This is where the printf() or sprintf() functions become useful. Let's take a look at what our code needs to look like:

<?php
echo sprintf( __( 'You have chosen the %s theme.' ) , the_color() );
printf( __( 'You have chosen the %s theme.' ) , the_color() );
?>

Not only does this solve all our problem we had before, but it's a lot tidier and only uses one line of code. The printf() or sprintf() functions' first arguments are the string to output which contains at least one placeholder, in this case %s (which means "string"), and any other arguments are variables that are to be placed inside the initial string. There are many different placeholders you can use inside your string and you can find a full list under sprintf in the PHP manual. Note that the different between printf() and sprintf() is similar to _e() and __() respectively.


Step 2 Introducing POEdit

Now that you have successfully tagged all your text output on your theme, you now need to collect this information into a .pot file. POEdit is a fantastic program that gives you the ability to create your .pot file, and also provides an easy to use GUI that can be used to make your .po and, more importantly, your .mo files too.

First, you will need to download POEdit, which you can find here for Windows, Mac and Linux:
http://www.poedit.net/download.php

Once POEdit is installed, you can create your .pot file. To do this, go to File > New Catalog. You will be presented with a dialog box where you need to enter some basic information. The essentials in the "Project Info" tab are the project name and your language/country. You also must enter the following in the "Plural Forms" box:

Plural Forms: nplurals=2; plural=n != 1;

POEdit project info

On the "paths" tab, enter the path where the files can be found relative to the save destination of this .pot file. For example, if you are putting the .pot file in the theme's root folder, enter . (period). If you want to place the .pot file in a "language" folder inside the theme root, enter .. (two periods).

POEdit path

Next, you need to tell POEdit which keywords to look for when scanning our files. Enter the following:

  • __
  • _e
  • _n:1,2
  • _x:1,2c
  • _ex:1,2c

The :1,2 extension tell POEdit that these keywords have two parts to them. By default, the second argument is the plural unless you include the c which mean the second argument is a comment.

POEdit keywords

You're good to go! Click "OK" and choose a place to save your .pot file. Remember that it has to relate to the path you defined earlier. POEdit will now scan through your files and find all the occurrences of your translation functions and save them without translations into your .pot file. If you only want to provide the bare minimum support for international translation you can ship your .pot file with your theme and stop here, however if you are able to translate your theme into another language yourself, you can ship your theme with a premade translation as described in step 3.


Step 3 Translating and Making Your .po File

POEdit translating singular
  1. A list of all the text strings to be translated
  2. The current string being translated
  3. Your translation of the string
POEdit translating plural
  1. Singular and plural of current string
  2. Tabs to switch between singular and plural of your translation
  3. Your translation

Once you have translated all the strings in the .pot file, you can save this as your .po file. Any string that you haven't provided a translation for will show in the original language when someone views your theme.

The filename of your .po is crucial. Gettext uses the ISO 639 standard for language abbreviations and ISO 3166 for locales. If your translation is written in American English for example, your file name will look like en-US.po. Capitalization is also important here. For a full list of language and country codes, check out these two links:

Once you save, POEdit by default automatically creates a .mo file alongside your .po file. It is recommended you include all three of your translation files with your theme so that people can create their own translations and edit your existing translations easily.


Step 4 Setting Up WordPress

Let's recap what you have done so far. You have told WordPress all the text that you want to be translatable, and then you used POEdit to collect each string and place them into a .pot file which can be translated into a .po and .mo file. These files are then included in the theme files. The final step is to zip your theme, install it and let WordPress know which language .mo file you want it to use. This is a very straight forward procedure where you access your wp-config.php file found in your WordPress' root folder.

/**
 * WordPress Localized Language, defaults to English.
 *
 * Change this to localize WordPress. A corresponding MO file for the chosen
 * language must be installed to wp-content/languages. For example, install
 * de_DE.mo to wp-content/languages and set WPLANG to 'de_DE' to enable German
 * language support.
 */
define('WPLANG', '');

Your file should already contain define('WPLANG', ''); but if it does not, you can add it in. You simply need to add your language and locale code into the define. If you were to translate your theme into German, you would have this:

define('WPLANG', 'de_DE');

Your internationalization is complete! Remember to include your .pot file with your theme, and if you were able to translate your theme to another language, to include the .po and .mo files too.

Advertisement