In the docs for the translate function __( $text, $domain ), it states that you must put the string directly in place of $text, that you cannot do something clever such as __( $my_text, 'text-domain' );.

However, I am writing a method which takes in a string and needs to pass it to __( ... ) somehow. Is there a way, e.g. via printf or sprintf that I can work around this?

Something like...

function print_description( $text ) {
    echo '<p>' . __( printf( '%s', $text ), 'text-domain' ) . '</p>';

6 Answers 6


You can do it with printf().


printf( __( 'We deleted %d spam messages.', 'my-text-domain' ), $count );

To translate a string in WordPress, as in most CMSs that use PHP, you have to wrap the string in a GetText translation function (i.e. __ (), _e (), _n (), _x () ... etc).

The string has also to be included in a PO file (GetText Portable Object files, the industry standard for multilingual websites in PHP - https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html). This file includes pairs of strings, one of them is the string in the original language and the other is its translation in the target language.

In order to build these PO files for every language (i.e. my-text-domain-es_ES.po for Spanish, my-text-domain-fr_FR.po for French ... etc.) in which we need to translate the plugin or theme, the translator will use a POT file (PO template) that contains all the strings to translate in the original language and will add a translated string for every one of them. PO files are compiled to binary MO files, that can be processed much faster.

At run-time, the strings to translate are fetched through the MO files and replaced by its translation.

The POT files are usually generated using special tools that parse the source code files and extract the translatable strings.

If we write code like this:

    $translated_text = __( $text, 'my_text_domain');

When the code is parsed by the special tools, the value of the string to be translated ($text) is not yet defined and therefore is not present in the code. As a result, the automatic search tools will not include this string in the POT file and therefore will not be included in PO nor MO files.

At run time, when the value of the string $text presumably will be already defined, there will be no correspondence for this value in the translation files and the translation will not be possible.

However, if the set of possible values for that variable is limited and we know them, we have two options to overcome this problem:

Option 1: Manually edit the POT file to add the entries with the possible values of $text. This option is simple and easy to implement. Any code editor and minimal knowledge of the POT format will be enough. But it has one drawback. Every time we use the automatic search tools to update the translations after modifying our code, the modifications that we have made would be lost and we would have to include them manually again.

Option 2: include in our code all the possible values of $text wraped by translation functions. Let's see an example. Suppose that $text can take the values: apple, orange, banana, peach and pear. We would need to write the following code:

    // this variable is used only to include as parameters in
    // translation functions all the possible values of $text 
    $locale = __('apple', 'my_text_domain') .
              __('orange', 'my_text_domain') .
              __('banana', 'my_text_domain') .
              __('peach', 'my_text_domain') .
              __('pear', 'my_text_domain');

    $translated_text = __( $text, 'my_text_domain');

This option is also easy to implement and has the advantage that it will not be lost when we use the automatic search tools to update the translations.

If we have, in our theme or plugin, several variables that we want to be translated and they have a limited set of known possible values, we could include all of them in a separated file that has to be in the root folder or a subfolder (i.e. 'includes' or 'assets' folder) of the theme or plugin as is shown below:

    //values of the variables to be translated
    $locale_var1 = __('text-of-var1_val1', 'my_text_domain') .
                   __('text-of-var1_val2', 'my_text_domain') .
                   __('text-of-var1_val3', 'my_text_domain') .
                   __('text-of-var1_valn', 'my_text_domain');

    $locale_var2 = __('text-of-var2_val1', 'my_text_domain') .
                   __('text-of-var2_val2', 'my_text_domain') .
                   __('text-of-var2_valn', 'my_text_domain');


    $locale_varn = __('text-of-varn_val1', 'my_text_domain') .
                   __('text-of-varn_val2', 'my_text_domain') .
                   __('text-of-varn_valn', 'my_text_domain');

This is a clean and maintainable approach that keeps the definition of these strings in a separate file that can be modified without affecting other code files.

  • 1
    Thanks for that @Juan Guerrero. I'm not sure why this answer didn't get a few thumbs up - I think your idea of a resources file for predictable sets of strings is very nice - as you say, it's a "clean and maintainable" approach. I'll be using it.
    – Nikkorian
    Commented Feb 20, 2020 at 1:25


The tools that help in generating a translation can not parse your code and decide what are the strings that need translation when the strings passed to the translation routines are totally dynamic.

in your example the proper way to code this function is

function print_description( $text ) {
    echo '<p>' . $text . '</p>';

and call it

print_description(__('specific description','text_domain));

No, because one cannot translate text when you don't actually know what that text is.

Translation works via, essentially, a big array. You take your code, find all the strings in it, then build a big list of strings. A translator translates them into another language. Then the various translations functions do a big array lookup and return the translated string.

If you use a variable to hold the text string, then there's no way to know what it is in advance and put it in the initial list of strings to be translated.

Variables cannot be used in translation functions, because that doesn't actually make any sense. You cannot translate a variable.


For some reason, for me it does works to pass variables to _e():

$string = 'ciao';
_e($string, 'textdomain'); // put your own textdomain..
// will output 'ciao' or 'hello', based on current language

Obviously, you must create your textdomain before, ideally in yourtheme/functions.php:

add_action( 'after_setup_theme', 'theme_setup' );
function theme_setup() {
  load_theme_textdomain( 'yourtextdomain', TEMPLATEPATH . '/languages' );
  // [...] other init code

If I translate the word 'ciao' on the .po file, it will be correctly translated in frontend. Use Poedit or the like to compile the .mo. Both .po and .mo must placed in /wp-content/yourtheme/languages (accordingly to the code above).

#. Test en_EN.po
msgid "ciao"
msgstr "hello"

Not sure the reason why it works, but it does, it will output 'ciao' in italian and 'hello' in english.

  • This only works because you are manually adding the translation. If you use a tool which searches your code for strings to translate, it will fail to find the correct string.
    – Milo
    Commented Dec 3, 2016 at 16:49
  • Yes of course, but I think that's not the problem of the original question Commented Dec 3, 2016 at 17:11
  • Well your variable isn't exactly variable, it's fixed- $string doesn't change in your example, and you've added the string to translate yourself. This isn't a working fix.
    – Milo
    Commented Dec 3, 2016 at 17:18

As I mention here http://www.westofwonder.com/2013/12/whats-with-all-the-double-underscores-everywhere/ (please pardon the theme, I've made improvements, just haven't pushed them yet), one possible solution to this is to create a function for the text:

function yourtheme_thisisthetext() {
    return __( 'This is the text.', 'yourthemetextdomain' );

Ugly in and of itself, but for a long or frequently used translation string, the trade-off may be worth it for code readability.

(Is this allowed? To link to my blog in direct answer to a question? If not, I apologize and won't do it again. )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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