Internationalization is the process of enabling your theme to be translated into different languages. We require that all themes be fully internationalized so that they can be enjoyed by the largest audience possible. In this document we will look at the basics of how to prepare your theme for translation as well as explain common errors.

Simple Strings

Take for example the following sentence: “The quick brown fox jumps over the lazy dog”. To prepare this for translation and echo it to the screen, we can use the WordPress core function _e(). This function accepts two parameters, the first is the text to be translated and the second is a textdomain unique to your theme. Here’s an example:

<?php _e( 'The quick brown fox jumps over the lazy dog', 'mytheme' ); ?>

Static Markup

As a best practice, it is important to minimize or completely remove the amount of HTML code that appears in internationalized strings. For example the following code:

<p>The quick brown fox jumps over the lazy dog.</p>

One way of enabling this text to be translated is to use the _e() function:

<?php _e( '<p>The quick brown fox jumps over the lazy dog.</p>' ); ?>

While this code functions perfectly fine, it is not the best approach. There is no need to add the paragraph tags to the string. A better approach to this situation is to move the paragraph tags outside of the _e() function. We do this because there is no need to translate this markup and it is easy to move it.

<p><?php _e( 'The quick brown fox jumps over the lazy dog.' ); ?></p>

There are times when markup is better left inside the string. If we need to emphasize the phrase “jumps over” in the sentence above we would end up with the following code:

<p><?php _e( 'The quick brown fox <em>jumps over</em> the lazy dog.' ); ?></p>

In this case it is not so easy to remove the tags. If we were to do this 3 sentence fragments would be created:

  1. The quick brown fox
  2. <em>jumps over</em>
  3. the lazy dog.

Perserving the sentence as a whole is more important than removing the markup from the translated string.

Dynamic Text

If we need to internationalize a sentence that incorporates the value of a variable or the return value of a function, we can utilize printf() or sprintf(). These functions are very similar, the difference being that printf() will echo the text to the screen while sprintf() returns the value.

What would happen if our theme had a feature that allowed users to choose which animal gets to jump over the dog? Perhaps it provides a function to get the user-defined value called mytheme_get_jumping_animal(). Our theme needs to display this value inside the sentence while still allowing sentence to be translated. The following example will not work because functions cannot be included in internationalized strings.

<p><?php _e( 'The quick brown ' . mytheme_get_jumping_animal() . ' jumps over the lazy dog.' ); ?></p>

We need to move the function outside of the string. This is pretty easy to do utilizing printf(). Here’s what the finished code looks like:

<p><?php printf( __( 'The quick brown %1$s jumps over the lazy dog.' ), mytheme_get_jumping_animal() ); ?></p>

Here we have replaced _e() with __() which allows the string to be returned instead to echoed to the screen. Next we pass the return value of __() as the first parameter to printf(). Then we pass the return value of mytheme_get_jumping_animal() as the second parameter. Finaly, we add a placeholder to the translated string where mytheme_get_jumping_animal() used to be. Let’s take a look at how this works:

  1. A person will translate the string “The quick brown %1$s jumps over the lazy dog.” All words will be translated into the second language and the placeholder will be moved to the appropriate place in the sentence.
  2. __() will return the translated string.
  3. printf() will replace %1$s with the return value of mytheme_get_jumping_animal()
  4. printf() will echo the finished sentence to the screen.

Escaping Text Strings in Attributes

All strings must be escaped with esc_attr() or esc_attr_e() before they are used as HTML attributes. The escaping must be done as late as possible. Here is the best way to escape a simple string used as the title attribute of an anchor:

<a title="<?php esc_attr_e( 'The quick brown fox jumps over the lazy dog', 'mytheme' ); ?>">

Here’s a more complex example with a dynamic value and a placeholder:

<a title="<?php echo esc_attr( sprintf( __( 'The quick brown %1$s jumps over the lazy dog.' ), mytheme_get_jumping_animal() ) ); ?>">

Escaping HTML Strings with Dynamic Attributes

In cases where html is included inside the string and dynamic values are used for the value of an attribute, these values must be escaped immediately before they are sent as parameters to printf(). Here’s an example where a link is included in the translated string and the URL is provided by a function:

	printf( __( 'The quick <a href="%1$s">brown fox</a> jumps over the lazy dog.' ),
		esc_url( mytheme_get_fox_url() )

Common Anti-Patterns

The following examples illustrate situations where internationalization is commonly use incorrectly. While most of these examples will be caught by the Theme Review checks in the VIP Scanner plugin, it is a good idea to keep an eye out for these while developing.

The following values should not be translated:

__( '', 'mytheme' ); // Empty strings.
__( $variable, 'mytheme' ); // Single variables.
printf( __( '%s' ), $var ); // Single placeholders
__( mytheme_function(), 'mytheme' ); // Single functions

The following dynamic values can not be sent directly to a gettext function like __().
Please see the Dynamic Text section for instructions.

__( "Hello there, $name", 'mytheme' ); // variables
__( "Hello there, " . mytheme_get_name(), 'mytheme' );  // functions