Multilingual WordPress – Internationalising themes

Old typewriter keyboard

Where exactly is the é key?

Here’s the third part of my series of articles on setting up a Multilingual WordPress site.

So far, we’ve looked at the basic requirements for setting up a Multilingual WordPress site, and how to set up the Language Switcher plugin to allow us to create multilingual content and let the site visitor switch between the languages.

The next steps are to:

  • Internationalise our theme files (the subject of this article)
  • Localise our theme files – which means translating English text in the theme files into our second language
  • Set up WordPress to run in the two languages

Internationalising theme files

Although this sounds a little daunting, it is actually quite straightforward. But first, let’s step back for a moment and imagine we are browsing a WordPress driven site. Where does the all the text that you see come from? Three sources:

  1. the content added via Write Posts and Write Pages
  2. WordPress core files, eg standard WP widgets
  3. Text which the theme author has hardcoded into the various theme files

It is the last one in the list which concerns us today. We need to “internationalise” the theme files so that we can translate (in other words “localise”) this text which has been hardcoded into the theme files.

Using gettext

WordPress uses GNU gettext to handle the internationalising and eventual localisation of text in theme files (and WP core and plugins, come to that). It does this at “message” level, ie it treats each discrete section of text as a “message” and each “message” is translated individually. A “message” could be a single word, eg a heading at the top of a block of site content, or it could be several words, even several sentences.

To use this technology, it is necessary to find all text which will be output to the browser, and wrap it in PHP code using one of the two syntaxes which are recognised by gettext. These syntaxes are as follows:

_e("The text which is output to the browser")


__("Some other text which is output to the browser")

Which one you use depends on where the message appears in the theme file. If it is simply within XHTML code, and not already in PHP tags, use _e(). If the text is already within PHP tags, use the other syntax, ie __().

Here’s an example of a heading in a theme file:

Featured Section

You need to change it to this:

Here’s another example where the text is already within in PHP tags:

name; ?>

You need to change it to this:

name; ?>

As you can see, we’ve used the second gettext syntax mentioned earlier. Why? Because the text is already within a block of PHP.

Adding a textdomain

Before you rush out and start changing your theme files, there is one more thing we need to deal with, and that is the “textdomain”. WordPress requires that gettext used in theme and plugin files is attributed to a textdomain. This is simply a unique name which we will use so that WordPress knows which translation files to use for the text. Let’s keep things simple and call our textdomain “mytext”. So, taking the code that I showed you earlier:


becomes this:

And this:

name; ?>

becomes this:

name; ?>

As you can see, it’s simply a matter of adding a comma after the text, then ‘mytext’, before closing the brackets.

We haven’t told WordPress about our textdomain yet, so don’t worry about making these changes in your theme files – the text will still appear normally as before.

Get internationalising!

It’s now time to go through every one of the theme files (apart from style.css of course) and make the changes as shown above. Remember to include the textdomain name in your new code!

Telling WordPress to use the textdomain

Now that we have all the theme files “internationalised”, ready for translation, there’s one important thing we need to do – we need to tell WordPress that we want to use a textdomain.

Simply add this to your theme’s functions.php:

load_theme_textdomain('mytext', TEMPLATEPATH.'/languages/');

This code does 2 things:

  • Tells WordPress that we want to use a textdomain called ‘mytext’
  • Tells WordPress that the translation files can be found in a folder called languages within our theme folder.

Next instalment…

In the next article we shall look at the process of “localising” the themes, in other words translating the text that we have just internationalised and creating the necessary translation files to be used by WordPress.


  1. Hello,
    Thank you very much for this great tutorial. I have followed the steps modifying my theme and everything went great, except for one phrase inside a function where I have no idea how to insert the php code. I tried everything I could think of but I always ended up braking the code.
    I was wondering if you could help me placing it the way it should? This is the code part where the phrase “Read more” is what I would like to translate:
    I can’t tell where the part of the code begins and ends so I just copy pasted the whole function here.
    I hope you can help me please? I would really appreciate it :) Thank you very much!!

    function carousel_featured_posts($max_posts=5, $offset=0) {
    return false;

    global $wpdb, $table_prefix;
    $table_name = $table_prefix."features";

    $sql = "SELECT * FROM $table_name ORDER BY date DESC LIMIT $offset, $max_posts";
    $posts = $wpdb->get_results($sql);

    $html = '';
    $coint_i = 0;
    foreach($posts as $post) {
    $id = $post->id;
    $posts_table = $table_prefix.'posts';
    $sql_post = "SELECT * FROM $posts_table where id = $id";
    $rs_post = $wpdb->get_results($sql_post);
    $data = $rs_post[0];
    $post_title = stripslashes($data->post_title);
    $post_title = str_replace('"', '', $post_title);
    $post_content = stripslashes($data->post_content);
    $post_content = str_replace(']]>', ']]>', $post_content);
    $post_content = strip_tags($post_content);
    $permalink = get_permalink($data->ID);
    $post_id = $data->ID;
    $html .= '


    $thumbnail = get_post_meta($post_id, 'thumbnail', true);

    if( isset($thumbnail) && !empty($thumbnail) ):
    $html .= '';

    $html .= '<strong><a href="'.$permalink.'" rel="nofollow">'.get_string_limit($post_title,50).'</a></strong> '.get_string_limit($post_content,300).'
    <a href="'.$permalink.'" rel="nofollow">"Read more"</a>

    echo $html;
    return $coint_i;

    function theme_twitter_show($count=4)
    $id = obwp_get_meta(SHORTNAME."_twitter_id");

    <script type="text/javascript" src="">

  2. sui_iuris says:

    Great work, great tutorial! Thank you. I’m almost ready with my multilingual site, but still I wonder how to internationalise phrases: “No Responses”, “One Response” and “Responses” in the code below:

    Is it possible at all?

    Thank you in advance!

    • sui_iuris says:

      < ?php comments_number('No Responses', 'One Response', '% Responses') ;?>

      Finally, really, reaaaally, sorry for triple posting but this code didn’t show up, twice :).

  3. sui_iuris says:

    I solved the problem.

    Here’s how “__(” is use when the phrase is already in brackets.

  4. sui_iuris says:

    I solved the problem.

    ?php comments_number(__(‘No Responses’, ‘mytext’), __(‘One Response’, ‘mytext’), __(‘% Responses’, ‘mytext’)) ;?

    Here’s how “__(” is use when the phrase is already in brackets.

Leave a Comment


− 1 = two

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>