Translating Your WordPress Themes and Plugins

Preparing your themes and plugins for an international audience is not as difficult as you may believe. WordPress provides a set of handy functions that, once learned, can easily be rolled into your existing workflow.

Why Bother?

Let’s say you have a plugin, currently in English, that has not been internationalized (making your theme/plugin translatable). According to the list of languages by number of speakers, you would have a potential audience of roughly 360 million people worldwide.

Increase Your Audience/Market Reach

If you offered a version in Spanish you would more than double your potential audience with an additional 405 million people worldwide. But that’s assuming you have gone through and translated your plugin. However, since this theoretical plugin has not been internationalized you would need two separate versions of the plugin to achieve this.

You also block anyone from translating the plugin themselves unless they overwrite your code. One of the advantages of internationalizing your code is that others can create a translation themselves. Whether you hire someone to translate your code to enter new markets, or a passionate individual decides to donate some of their time, you benefit and gain an advantage over themes/plugins that don’t offer a translation.

Internationalization is one of those things easier to do during development, rather than re-visiting down the road. Similar to using hooks in your theme/plugin, adding translatable strings doesn’t significantly impact development time.

How to Internationalize Your WordPress Code


In your theme’s style.css file you’ll need to define two items:

  1. Text Domain: Used to identify text belonging to your theme. The text domain must match the slug of your theme, e.g. if your theme is contained in a folder called my-theme the domain name should be my-theme. The domain name must use dashes and be lowercase.
  2. Domain Path: By default the Domain Path will be set to /languages, but if you wanted to use another folder you would specify it as well.
* Theme Name: My Theme
* Text Domain: my-theme
* Domain Path: /languages


For plugins you follow a similar format to themes, except your definitions will go in your main plugin file:

* Plugin Name: My Plugin
* Text Domain: my-plugin
* Domain Path: /languages

Making Your Strings Translatable

In order for your strings to be translatable they need to be run through special functions before being used/displayed. Pretty much anywhere you would have hard-coded a string you’ll put it into one of these functions instead:


The most commonly used function takes a string, along with your Text Domain from your theme/plugin, and returns a translation:

$translated_string = __( 'My string to translate.', 'my-theme' );


A shorthand function to output the translated string from __():

_e( 'My string to translate.', 'my-theme' );

Which is the same as:

echo __( 'My string to translate.', 'my-theme' );

If you have a variable to be included in the translated string you’ll want to use placeholders to give flexibility to the translator. This is done using the sprintf() and printf() functions.

So rather than doing:

echo 'Hi, $name!';

You would do:

printf( __( 'Hi, %s!', 'my-theme' ), $name );

This gives the translator flexibility, if in their language the name should appear before the greeting, within a greeting, or any other ordering.

If you need to include multiple variables it’s best to utilize argument swapping – which allows you to specify which variable should go where. For example if we modify our string to include the user’s title:

printf( __( 'Hi, %s! You are currently a %s.', 'my-theme' ), $name, $title );

Then the $name always appears before the $title, since each %s just uses the next variable. If the string is modified to:

printf( __( 'Hi, %1$s! You are currently a %2$s.', 'my-theme' ), $name, $title );

Then the translator can swap things around, putting the title first if necessary since the arguments are specified numerically. Note: For this to work you’ll need to make sure to use single quotes (') otherwise the $s gets interpreted by PHP.


If dealing with string that need pluralization the _n() function can be used:

printf( _n( 'One view', '%s views', $number_of_views, 'my-theme' ), $number_of_views );

The _n() function takes four arguments:

  1. Singular: singular form of the string
  2. Plural: plural form of the string
  3. Count: the number that determines whether the singular or plural form is used
  4. Text Domain: theme/plugin text domain

In the example above we are also using the printf() function to insert the actual count from $number_of_views into the plural form of the string.

These functions cover the most frequently used for translation. There are others to specify context, as well as escaping html or attribute strings for display. The full listing of these functions can be viewed on the WordPress codex.

Localization (Doing the Translation)

Now that your theme/plugin is using localization functions you can create the file needed to do the translation. There are several methods to create the translation file:

For this example I’ll be using Poedit.

When first loading up the application you’re presented with some options, one of which is “Translate WordPress theme or plugin”.

Poedit application screenshot

You will then be presented with a popup to select your theme/plugin folder, along with what you would like to create:

Poedit application translation select screenshot
  • Create a new translation: creates a translation for a specific language and will name the file generated for that language
  • Create a POT: creates a empty template file

If you are distributing this translation file with your theme/plugin so others can translate it select “Create a POT.” If you have a specific language you’re translating into at this point choose “Create a new translation” and select the target language.

After hitting “Continue” Poedit will run through your theme/plugin looking for translatable strings and generate a listing of everything it finds:

Poedit translatable strings screenshot

If you hit “Save” and selected “Create a POT” a .pot file will be created using your text domain, e.g. my-plugin.pot. Otherwise a .po file will be created for the text domain and selected language locale, e.g. my-plugin-ja.po. This file can be used by translators to insert the translated strings for your theme/plugin:

.pot file example

The translator will enter their translations into the msgstr double quotes.

Compiling Your Translation

There is one final step to make your translation available to WordPress and that is to compile it to an .mo file. Once your translated strings are in your .po/.pot file you’ll open it back up in Poedit and select File > Compile to MO… which should generate a file with your text domain and language locale. Something like:

Now if the WordPress site language is changed to match the language locale of your .mo file it will use the translated strings wherever they appear in your theme/plugin.

Additional information about localization can be found on the WordPress codex.

Loading the Translation

Once you’ve created your translation file you’ll need to load your text domain into your theme/plugin for it to be recognized.

Loading a Translation Into Your Theme

function my_theme_load_theme_textdomain() {
  load_theme_textdomain( 'my-theme', FALSE, basename( dirname( __FILE__ ) ) . '/languages/' );

add_action( 'after_setup_theme', 'my_theme_load_theme_textdomain' );

Loading a Translation Into Your Plugin

function my_plugin_load_plugin_textdomain() {
  load_plugin_textdomain( 'my-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );

add_action( 'init', 'my_plugin_load_plugin_textdomain' );

Changing Your Site Language

Now everything is set up and ready to activate. You can change your site language in your admin dashboard under Settings > General > Site Language.

Or you can edit your wp-config.php to change the language as well, for example into Japanese:

define ('WPLANG', 'ja');

Questions or comments? Hit me up on Twitter @ractoon