How would you go about setting the correct language returned in AJAX calls, when you are not using any plugin?

All other strings in page work fine just by defining WPLANG to a proper locale.


function lpml_get_quiz_result_callback() 

    // do plenty of stuff before this 

    // this is always returned in original english language, even if I setlocale before or load textdomain manually.
    $message = __('Some english defined string', 'my_txt_domain');
    $status = true;

    echo json_encode(array('status' => $status, 'message' => $message));


add_action('wp_ajax_lpml_get_quiz_result', 'lpml_get_quiz_result_callback');
add_action('wp_ajax_nopriv_lpml_get_quiz_result', 'lpml_get_quiz_result_callback');

I checked these questions: q1, q2, but none applies / solves my case. :/

I tried setting locale and loading textdomain before using __() strings in ajax, but to no avail (tried it just before, and also in admin_init hook for DOING_AJAX), both did not work.

setlocale(LC_ALL, $_POST['lang']);

load_theme_textdomain( 'my_txt_domain', get_template_directory() . '/languages' );

Any tips appreciated!

  • DId you manage to solve this? Cheers! Aug 19, 2019 at 15:21
  • 1
    @ValentinGenev added an answer below :)
    – trainoasis
    Aug 20, 2019 at 7:19

2 Answers 2


I did manage to solve this later on by using WPML, but over time found that WPML is quite bloaty and slow, support not very good, and if you need only 1 language (but original strings for theme are in English and the language you need is not) or perhaps 2, you do not need it. The only thing you need is:

  1. Set LOCALE in your theme .env or wp-config.php. An example would be LOCALE=de_DE or define('LOCALE', 'de_DE') for wp-config.
  2. Have your text domain loaded based on where your .MO files will be (run this in after_setup_theme hook)

load_theme_textdomain( 'your_domain', get_template_directory() . '/languages' );

  1. Set up your locale via filters based on where you set it

/** * Set locale based on our .env LOCALE */ add_filter( 'locale', function($locale) { if ( !is_admin() && getenv('LOCALE') !== false) $locale = getenv('LOCALE'); return $locale; });

Also, I very much recommend https://roots.io/sage/ to start your theme off, but it is not required.

It provides a nice way to generate your .pot file for translations via Yarn too. Based on that it is quite easy to create .PO files from it for the languages you need and send them for translation and just follow the steps above (dont forget to put .mo file in languages folder, named de_DE.mo based on your LOCALE, and everything works as expected.

  • thank you for your response! :) However this wasn't enough to solve my issue but it gave me the starting point I needed. I'll submit an answer since it ended up being a lengthy text for the other who might run into this same issue. Aug 20, 2019 at 11:34
  • 1
    no problem, glad to be of help. I see you had a more specific solution for WooCommerce, glad your made it work!
    – trainoasis
    Aug 20, 2019 at 12:37

In short, here's the solution that worked in my case:

function my_theme_ajax_load_more_products() {
    // Using the load_textdomain() function to make sure that
    // the translation file used in the template below is loaded.
    // This is the line that solves the issue, more on it bellow.
    load_textdomain('woocommerce', WP_CONTENT_DIR . '/languages/plugins/woocommerce-' . get_locale() . '.mo');

    // Some code not related to the issue to give some plain context.
    $args           = json_decode(stripslashes($_POST['query_vars']), true);
    $args['offset'] = $_POST['offset']; 
    $query          =new WP_Query($args);

    if ($query->have_posts()) :  
       while ($query->have_posts()) : $query->the_post();

            // This is standart template part.
            // It contains plenty gettext calls pointed to
            // WooCommerce's text domain.

            // The gettext calls were ending untranslated before I
            // added the load_textdomain().
            wc_get_template_part('content', 'product');



add_action('wp_ajax_load_more_products', 'my_theme_ajax_load_more_products');
add_action('wp_ajax_nopriv_load_more_products', 'my_theme_ajax_load_more_products');

I used the get_locale() function in several places inside my code, even in my AJAX action, to make sure that I was getting the right locale. Getting the right code led me to think that the untranslated text problem is tied with the text domain. I already have added my theme's text domain as described in WP's docs and I had some test text translated to Bulgarian (hence I had bg_BG.mo generated), but the gettext function within the AJAX action didn't translate the test text.

After some more research I found that one can add a single .mo file with load_textdomain() instead of using load_theme_textdomain(). Since all the translations where in the WooCommerce's text domain I needed load_textdomain() to point to WooCommerce's .mo files. WooCommerce's docs says the files are in wp-content/languages/.

// For translations of plugins as WooCommerce
load_textdomain('woocommerce', WP_CONTENT_DIR . '/languages/plugins/woocommerce-' . get_locale() . '.mo');

// WP_CONTENT_DIR gets the directory path of the wp-content folder
// get_locale() returns the current language code of the page

// For translations of your theme
load_textdomain('woocommerce', get_template_directory() . '/languages/' . get_locale() . '.mo');

Load text domain's docs.

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.