In this article, you will learn when and how to use all available i18n functions and the best practices for writing your strings.
The most commonly used function is
__(). It returns the translation of its argument:
__( 'Blog Options', 'my-theme' );
Another simple one is
_e(), which outputs the translation of its argument. Instead of writing:
echo __( 'WordPress is the best!', 'my-theme' );
you can use the shorter:
_e( 'WordPress is the best!', 'my-theme' );
If you are using variables in strings, similar to the example below, you need to use placeholders.
echo 'Your city is $city.'
printf( /* translators: %s: Name of a city */ __( 'Your city is %s.', 'my-theme' ), $city );
Notice that the string for translation is the template
"Your city is %s.", which is the same in both the source and at run-time.
If you have more than one placeholder in a string, it is recommended that you use argument swapping. In this case, single quotes
(') are mandatory : double quotes
(") tell php to interpret the
$s as the
s variable, which is not what we want.
printf( /* translators: 1: Name of a city 2: ZIP code */ __( 'Your city is %1$s, and your zip code is %2$s.', 'my-theme' ), $city, $zipcode );
Here the zip code is displayed after the city name. In some languages, displaying the zip code and city in opposite order is more appropriate. Using %s prefix, as in the above example, allows for this. A translation can be written:
printf( /* translators: 1:ZIP code 2:Name of a city */ __( 'Your zip code is %2$s, and your city is %1$s.', 'my-theme' ), $city, $zipcode );
const zipCodeMessage = sprintf( /* translators: 1:ZIP code 2:Name of a city */ __( 'Your zip code is %2$s, and your city is %1$s.', 'my-theme'), city, zipcode );
The following example tells you what not to do
// This is incorrect do not use. _e( "Your city is $city.", 'my-theme' );
The strings for translation are extracted from the source without executing the PHP associated with it. For example: The variable
$city may be Vancouver, so your string will read
"Your city is Vancouver" when the template is run but gettext won’t know what is inside the PHP variable in advance.
As the value of the variable is unknown when your string is translated, it would require the translator to know every case for the variable
$country. This is not ideal, and it is best to remove dynamic content and allow translators to focus on static content.
If you have a string that changes when the number of items changes. In English you have
"One comment" and
"Two comments". In other languages, you can have multiple plural forms. To handle this in WordPress, you can use the
printf( _n( '%s comment', '%s comments', get_comments_number(), 'my-theme' ), number_format_i18n( get_comments_number() ) );
_n() accepts 4 arguments:
- singular – the singular form of the string (note that it can be used for numbers other than one in some languages, so
'%s item'should be used instead of
- plural – the plural form of the string
- count – the number of objects, which will determine whether the singular or the plural form should be returned (there are languages, which have far more than 2 forms)
- text domain – the theme’s text domain
The return value of the functions is the correct translated form, corresponding to the given count.
$comments_plural = _n_noop( '%s comment.', '%s comments.' );
At a later point in the code, you can use
translate_nooped_plural() to load the strings.
printf( translate_nooped_plural( $comments_plural, get_comments_number(), 'my-theme' ), number_format_i18n( get_comments_number() ) );
Sometimes a term is used in more than one context and must be translated separately in other languages, even though the same word is used for each context in English. For example, the word
Post can be used both as a verb
"Click here to post your comment" and as a noun
"Edit this Post". In such cases the
_ex() function should be used. It is similar to
_e(), but it has an additional argument — the context:
_x( 'Post', 'noun', 'my-theme' ); _x( 'post', 'verb', 'my-theme' );
Using this method in both cases, we get the string Comment for the original version. However, translators will see two Comment strings for translation, each in a different context.
Taking an example from the German version of WordPress as an illustration: Post is Beiträge. The corresponding verb form in German is beitragen.
Note that similar to
_x() has an
_ex(). The previous example could be written as:
_ex( 'Post', 'noun', 'my-theme' ); _ex( 'post', 'verb', 'my-theme' );
Use the one you feel enhances legibility and ease of coding.
You can add a clarifying comment in the source code, so translators know how to translate a string like
__( 'g:i:s a' ) . It must start with the word
translators:, in all lowercase, and be the last PHP comment before the gettext call. Here is an example:
/* translators: draft saved date format, see http://php.net/date */ $saved_date_format = __( 'g:i:s a' );
/* * translators: Replace with a city related to your locale. * Test that it matches the expected location and has upcoming * events before including it. If no cities related to your * locale have events, then use a city related to your locale * that would be recognizable to most users. */ ?> <input placeholder="<?php esc_attr_e( 'Cincinnati' ); ?>" id="location" type="text" name="location" />
Gettext doesn’t like
r (ASCII code: 13) in translatable strings, so avoid it and use
The empty string is reserved for internal Gettext usage, and you must not try to internationalize the empty string. It also doesn’t make any sense because translators won’t have context.
If you have a valid use-case to internationalize an empty string, add context to both help translators and be in peace with the Gettext system.
It is good to escape all of your strings, preventing translators from running malicious code. There are a few escape functions that are integrated with internationalization functions.
<a title="<?php esc_attr_e( 'Skip to content', 'my-theme' ); ?>" class="screen-reader-text skip-link" href="#content" > <?php _e( 'Skip to content', 'my-theme' ); ?> </a>
<label for="nav-menu"> <?php esc_html_e( 'Select Menu:', 'my-theme' ); ?> </label>
Here are the best practices for writing strings
- Use decent English style – minimize slang and abbreviations.
- Use entire sentences – in most languages, word order is different than English.
- Split at paragraphs – merge related sentences, but do not include a whole page of text in one string.
- Do not leave leading or trailing whitespace in a translatable phrase.
- Assume strings can double in length when translated.
- Avoid unusual markup and unusual control characters – do not include tags that surround your text.
- Do not put unnecessary HTML markup into the translated string.
- Do not leave URLs for translation, unless they could have a version in another language.
- Add the variables as placeholders to the string as in some languages the placeholders change position.
printf( __( 'Search results for: %s', 'my-theme' ), get_search_query() );
- Use format strings instead of string concatenation – translate phrases and not words –
printf( __( 'Your city is %1$s, and your zip code is %2$s.', 'my-theme' ), $city, $zipcode );
is always better than
__( 'Your city is ', 'my-theme' ) . $city . __( ', and your zip code is ', 'my-theme' ) . $zipcode;
- Try to use the same words and symbols to prevent translating multiple similar strings (e.g. don’t do the following)
__( 'Posts:', 'my-theme' ); and __( 'Posts', 'my-theme' );