Is this the correct way to translate a sting of text?

_e( '<h2 class="title">' . $var . '</h2>', 'text-domain' );

or, is this?

echo '<h2 class="title">';
_e( $var, 'text-domain' );
echo '</h2>';

or this?

printf( __( '<h2 class="title">%s</h2>', 'text-domain' ), $var );

Edit: Update or this?

printf( '<h2 class="title">%s</h2>', esc_html__( $var, 'text-domain' )  );
  • 2
    I would never go with _e( '<h2 class="title">', ... because this would allow your translator to mess with your HTML markup. Moreover, translating a variable $var won't allow your translating tools (poedit or anything else) to detect the translation.
    – Tim
    Sep 28, 2020 at 14:06

2 Answers 2


None of them are correct. These APIs are not intended for dynamic data or content in the database.

This would be the best practice answer:

echo '<h2 class="title">';
esc_html_e( 'static string', 'text-domain' );
echo '</h2>';

If you want to translate posts and other database user entered content, use a translation plugin, not the localisation API.

But Why?

You should never use variables as the first parameter of a localisation API call.

Aside from being extreme bad practice, it means the string will not get picked up by tools, so it's impossible to generate translation files to translaters.

It does not matter how you pass $var to the function, it will always be incorrect. Do not pass variables to localisation functions in WordPress. These functions are intended for static strings, not dynamic values.

If you want to localise content from the database, this API is not the way to do it.

HTML tags

Localised strings shouldn't include H2 tags and other HTML tags, but instead provide the text content that goes in those tags. There are rare cases when you do, in which case __() and wp_kses_post or sprintf can be useful.

_e() vs __()

_e() is equivalent to echo __()

Why are _e and __ wrong?

Because neither do escaping.

This is the canonical correct answer:

echo '<h2 class="title">';
esc_html_e( 'static string', 'text-domain' );
echo '</h2>';

esc_html_e( is shorthand for echo esc_html( __(. We know that inside the H2 tag there will be text, but no tags, so we use esc_html to escape any HTML that appears

If this were an attribute of a tag, then it would be attribute="<?php esc_attr_e(...); ?>"

So then, why does __( etc exist? Because escaping has to happen as late as possible, and only once. Otherwise we run the risk of double escaping strings.

Fixing The printf example

We can escape the localised string prior to passing it to printf allowing it to be escaped:

printf( '<h2 class="title">%s</h2>', esc_html__( 'static string','text-domain' )  );

So How Do You Translate Dynamic Content?

If you want to enable posts/terms/titles/etc in multiple languages, you need a translation plugin. The i18n APIs are for hardcoded static strings, not database or generated content.

  • I added a update which includes $var for the text. Is that correct for translation and escaping?
    – Dev
    Sep 28, 2020 at 14:19
  • 1
    No, you must never pass a dynamic variable into the localisation functions, only static strings. It is not possible to pass a dynamic variable into those functions correctly. It's not how it's done that's wrong, it's the very act of doing it itself. You're going to have to reconsider and refactor the code that has lead to this situation, it must always be a static string
    – Tom J Nowell
    Sep 28, 2020 at 14:51
  • 1
    If you're trying to enable translated post content then you need a plugin. This API is intended for static strings such as the label on a form field, and other non-editable text in a theme or plugin
    – Tom J Nowell
    Sep 28, 2020 at 14:57
  • It will be translated using a translation plugin but i need to code it in a child theme so its translation ready for variable text. Any examples?
    – Dev
    Sep 28, 2020 at 15:31
  • 1
    You would need to consult with the docs of the translation plugin you're using, however, most translation plugins do this transparently, so the_content and the_title still work as they did before. The only changes you would need to make are to add a language picker. This API is not what you want, and does not do what you think it does. This API is exclusively for static strings, and only static strings. Never under any circumstances pass variables and database content into these functions, even when using a translation plugin. This is not a content translation API
    – Tom J Nowell
    Sep 28, 2020 at 16:26
  • Both of the functions are used in the localisation of wordpress themes.
  • Both are used to display static strings/text.

The major difference is that _e() echoes but __() returns the correctly localised text.

_e() function

_e( 'text', 'domain' )

The above is the general syntax for _e function, text denotes the text and the domain is the text domain of the theme loaded via load_theme_text_domain function.

load_theme_textdomain( 'domain', $path )

Domain denotes the domain name, say the name of the theme and path is the path of the languages directory(where the pot file for localisation resides). The every instance of _e with the loaded text domain gets translated.

__() function

$localised_string = __( 'text', 'domain' );

Here the $localised_string variable holds the localised value of the text, the __()function returns the localised value instead of echoing it (whereas the __e() function does ).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.