0

I developed several plugins which I localized using the standard approach with __() functions and the .po or rather the .mo files. All of this works like a charm.

What I now want to be able to do is set the locale for a function call of __(), to retrieve the translation you defined on your plugin's .mo for a translation which is different from the current locale. Example:

  1. Let's say you're creating a user for your website in your wp admin, on a german wp admin page.

  2. This user wants to have a profile in English.

  3. In your wp admin, you've automated a mailing, such that, when a user gets created, he / she gets notified with an e-mail about that, in the language according to 2).

Problem: Mail contents are translated using __() too. So, when I create a user on a german admin page, the german version of the notification mail will be sent to the user, instead of the english one. So what I'm looking for is a solution to call the translation defined via your .mo files which is different from the current locale; and only for this single function call (like, not permanently change the locale due to this). Sth like:

__( 'get-this-translation', 'of-this-text-domain', 'of-this-locale' );

instead of

__( 'get-this-translation', 'of-this-text-domain' );, which obviously always defaults to currently active locale; in the example above german.

I know that simply hardcoding the translation strings on the admin side, instead of using __() here, would solve the problem; but I'd prefer my translations to be translated all in the same way, using the same logic provided by wordpress. Hence I'd like to use an approach via the __(), if possible.

3
  • There's no code in your question to debug. Your previous questions also reference Polylang which is super important information you've left out in this question, the answer you get may not be usable as a result or make sense in a Polylang context. It may also be that you need special Polylang specific steps
    – Tom J Nowell
    Aug 11 at 23:36
  • Thanks for the note Tom, but this question has nothing to do with polylang, but exclusively with the localizations of my custom plugin, as I explained in my question. And yes, it's not a debug question, rather a solution-question to see if I'm on a completely wrong track or if that's a common issue, hence the reason for my question. And last, I really prefer the forum here for devs over the polylang forum, and what is the polylang tag of this forum good for if you can't ask questions about it here? Cheers
    – DevelJoe
    Aug 12 at 5:26
  • But again, this question has absolutely nothing to do with polylang. Still, I don't see why you've just closed my three last questions about custom uses of polylang, they may help other devs and are simple questions about wp plugin code, just like the tons of questions about woocommerce or whatever in here...
    – DevelJoe
    Aug 12 at 5:37
1

What I now want to be able to do is set the locale for a function call of __(), to retrieve the translation you defined on your plugin's .mo for a translation which is different from the current locale.

I'd prefer my translations to be translated all in the same way, using the same logic provided by wordpress. Hence I'd like to use an approach via the __(), if possible.

__(), _e(), _n() and other core translation/gettext functions do not have a parameter for setting the locale, but just like how wp_new_user_notification() did it, you can use switch_to_locale() to manually (and just temporarily) switch to the target locale (like English in your example, which is a user-defined locale) and then just call __() or the relevant function to retrieve the translation in the locale which you've just switched to.

So for example, you would do:

$switched_locale = switch_to_locale( get_user_locale() );
// Or explicitly specify the locale:
//$switched_locale = switch_to_locale( 'of-this-locale' );

$foo = __( 'get-this-translation', 'of-this-text-domain' );
error_log( $foo );
// or do whatever necessary..

if ( $switched_locale ) {
    restore_previous_locale();
}

Update

Note that you need to re-load your plugin's translations after you've switched to a different locale and also after you've restored the original/previous locale.

So you would call load_plugin_textdomain() or load_muplugin_textdomain() like this:

// So in your main plugin file, you might have something like:
add_action( 'init', 'your_plugin_load_textdomain' );
function your_plugin_load_textdomain() {
    load_plugin_textdomain( 'your-domain', false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}

// Then wherever you want to switch the current locale, call the above function
// to re-load the plugin's translations.
if ( $switched_locale = switch_to_locale( get_user_locale() ) ) {
    your_plugin_load_textdomain(); // call it here

    // do your gettext stuff, e.g.
    _e( 'Hello World!', 'your-domain' );

    restore_previous_locale();
    your_plugin_load_textdomain(); // and then here
}

And if you want, you can try my plugins below to test the above functions (switch_to_locale() and restore_previous_locale()):

  • WPSE 393178 DE — this plugin used German as the main/base language and the plugin came with two MO files, namely wpse-393178-de-en_US.mo and wpse-393178-de-fr_FR.mo.

  • WPSE 393178 EN — this plugin is essentially the same as the one above, except that the main language is English, and the MO files are wpse-393178-en-de_DE.mo and wpse-393178-en-fr_FR.mo.

So I hope that helps and to check whether the localization works correctly, just set your site or profile language to the opposite of the main language of those plugins and visit the plugin admin page (at wp-admin → Tools → "WPSE 393178 DE/EN"). (Both plugins can be activated at the same time)

16
  • 1
    PS: If you're indeed looking for a Polylang solution, then see their documentation at polylang.wordpress.com/documentation/…. But even so, I posted this answer just so you know how it's done by/in core or without a plugin.
    – Sally CJ
    Aug 12 at 0:54
  • Thx a lot Sally, that was pretty much what I was looking for. And this will not influence requests to the server which are made at the same time from another client, right? Just to double-check...
    – DevelJoe
    Aug 12 at 5:28
  • 1
    Yes that's what I meant, the solution you provided only changes and then resets the locale for the running PHP process for the http request of the running client, right? Which means when you register client A on device a needing his notification in language X, and someone else from the admin staff registers client B on another device needing his notification to be in language Y, all of this shouldn't cause any issues right?
    – DevelJoe
    Aug 12 at 10:13
  • 1
    Yehyeh no my question has nothing to do with polylang at all don't worry, it's exactly what you said, retrieve a translation in a different locale than the current locale, without using a 3rd-party plugin! Thx a lot mate, was exactly what I was looking for.
    – DevelJoe
    Aug 12 at 12:55
  • 1
    In that case, then I liked/upvoted your previous comment ("all of this shouldn't cause any issues right") because yes, that's actually correct, particularly if you used get_user_locale() which means if the current user has a different locale set via his/her profile, then the current locale would be switched to that user's preferred locale; otherwise, then the current locale remains unchanged. And remember to switch back to the original/previous locale, or otherwise, then that's an example scenario in which an issue would happen..
    – Sally CJ
    Aug 12 at 13:42
0

Alright so although I want to thank @Sally CJ's efforts; his / her solution unfortunately did have no effect on my output. After digging somewhat and reading through this post, I can now provide you with this solution, which worked like a charm (you may integrate this code in your plugins includes / php folder as a spearate file, worked for mine at least. I've added namespacing etc. so just use a not-yet used namespace name and you should be good to go):

<?php
// This script is used for special actions related to translations / locales
namespace YourNamespace;

/*########################################################
##  TABLE OF CONTENTS                                   ##
##------------------------------------------------------##
##  1. Switch locale used for muplugin translations     ##                           
##------------------------------------------------------##
##  2. Reset locale used for muplugin translations      ##
########################################################*/

if ( ! class_exists( 'YourNamespace\LocalizationFeatures' ) ) {

  class LocalizationFeatures {

     /*########################################################
    ##  1. Switch locale used for muplugin translations      ##
    #########################################################*/

    /**
     * If you're on a plugin's page with a loaded textdomain (let's say for
     * ex. english), and you want to retrieve translations from that plugins'
     * .mo files accessed via __(), _e(), etc. in another language (e.g. es)
     * you cannot achieve this via switch_locale() if your translations were
     * actually defined via a given text domain. What has to be done in this
     * case is to reload the textdomain in the targeted language, to update 
     * global $l10n variable, which is used as resource for the gettext
     * translations wp retrieves.
     *
     * @param   string  $domain   text domain to be reloaded in the specified
     *                            target language
     *
     * @param   string  $locale   two-digit locale which determines to which
     *                            locale the function shall temp. switch;
     *                            one of:
     *
     *                            "de" (which switches to root locale)
     *                            "en" (which switches to locale en_GB)
     *                            "fr" (which switches to locale fr_FR)
     *                            "it" (which switches to locale it_IT)
     *                            "es" (which switches to locale es_ES)
     *                            "pt" (which switches to locale pt_PT)
     *
     * @param   mixed   value of global $l10n[{domain}] before the switch; false
     *                  if it was not set by then (which is the case if the page
     *                  was previously loaded in the website's default language)
     *                  ALWAYS REMEMBER TO RESET $l10n[{domain}] after executing
     *                  whatever needed to be executed after the switch!!! If it
     *                  was not set (i.e. if this value is false); make sure to
     *                  unset it again instead of reassigning it.
     *                  To facilitate this whole procedure, a second callback is
     *                  created; always to be called after the use of this one.
     */

    public static function switch_textdomain( $domain, $locale, $answer ) {

      // First of all, before switching, get the previous state of $l10n:
      global $l10n;
      $previous_language = isset( $l10n[$domain] ) ? $l10n[$domain] : false;

      // Next, if the target locale is the default language, there will be no
      // associated .mo - file to be loaded. To switch the textdomain being used
      // in this case, we have to remove the $l10n[$domain] from the $l10n array
      if ( $locale === "de" ) {

        unset( $l10n[$domain] );

      } else {

        // If the target locale is different from the default language; load the
        // according text domain
        $target_locale = "";

        switch ( $locale ) {

          case 'en':
            $target_locale = "en_GB";
            break;

          case 'fr':
            $target_locale = "fr_FR";
            break;

          case 'es':
            $target_locale = "es_ES";
            break;

          case 'pt':
            $target_locale = "pt_PT";
            break;

          case 'it':
            $target_locale = "it_IT";
            break;

        }

        if ( ! load_textdomain(
          $domain,
          WPMU_PLUGIN_DIR . "/{$domain}/languages/{$domain}-{$target_locale}.mo"
        ) ) {

          // output error feedback you may want

        }

      }

      return $previous_language;

    } // end of switch_textdomain() function definition

    /*##########################################################################
    ##  2. Reset locale used for muplugin translations (after calling 1.)     ##
    ##########################################################################*/

    /**
     * This function is ALWAYS to be used after using self::switch_textdomain(),
     * to always reset the textdomain's language back to its previous state
     * before switching it; as soon as the wanted tasks have been executed.
     *
     * @param   string  $domain         concerned text domain
     * @param   mixed   $previous_l10n  state of global $l10n[$domain] b4 switch
     *                                  corresponds to return value of
     *                                  self::switch_textdomain()
     */

    public static function reset_textdomain( $domain, $previous_l10n ) {

      global $l10n;

      // If $previous_l10n is === false, this means that , before using
      // self::switch_textdomain(), the default language was being used, so to
      // reset it, we need to re-unset it, as it was set via
      // self::switch_textdomain()
      if ( $previous_l10n === false ) {

        unset( $l10n[$domain] );

      } else {

        // If it was previously set to something, reset it back to that
        $l10n[$domain] = $previous_l10n;

      }

    } // end of reset_textdomain() function definition

  } // end of LocalizationFeatures class definition

} // loop which opens if there's currently no YourNamespace\LocalizationFeatures class defined
?>

Note that my code works for MU-plugins; but you should be able to reach the same for simple plugins by using the WP_PLUGIN_DIR constant instead of the WPMU_PLUGIN_DIR.

I elaborated a summary of my thoughts which concluded from my research; feel free to correct them in case of any mistakes. I also could have made only one function 1. which resets back to the previous textdomain locale after you do whatever you want, for example with an additional parameter passed to 1. which is a callback with *args or whatever. This design just fit much better into my plugin for this project.

UPDATE

This solution does not seem to be perfect; as soon as you change the language of the admin user making use of the localized page(s); things get horribly buggy when it comes to dynamically retrieved localizations from the server. I could actually reproduce this issue in an environment which makes no use of the script here; which is why I think it may be an unrelated issue, but maybe also related; I'm quite confused tbh. For further details; plz check here: Changing wp admin user's language does not update all of the plugin localizations in backend.

4
  • Most users won't scroll down as far as the update at the bottom, which implies you improved your answer. If you didn't find a solution to your question it might be best not to post a solution to your question and instead update the question itself. Right now it looks like you wrote an extensive answer and are waiting the 24 hours to be able to mark it as correct
    – Tom J Nowell
    Aug 14 at 2:44
  • 1
    Also are you sure Sally is a he?
    – Tom J Nowell
    Aug 14 at 2:44
  • yes what shall I do, simply delete the answer? It solved the problem of this question via the solution above; and this is where a follow-up problem ( for which I've opened a new question ) showed up. Thought it might be better to leave it as is, but I've no problem in removing my answer. Was just to show what actually improved / almost resolved my issue.
    – DevelJoe
    Aug 14 at 2:48
  • 1
    Sorry for the assumption @Sally, English's not my first language, I'm not the best with English names as a consequence..
    – DevelJoe
    Aug 14 at 2:49

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.