-1

When sending requests to the WP REST API / custom WP REST endpoints, which is the way to go to retrieve the current locale; a universal way which always works? I'm confused by the many functions available, like determine_locale, get_locale, or even plugin functions like pll_current_language with Polylang.

The reason why I'm asking this is because the retrieved locales are inconsistent; sometimes get_locale() returns the root language (e.g. de) for a page which is actually in french (language attribute in html also set to fr-FR), etc.

So it seems like the according values are only available at a certain moment / specific hooks, and not simply always within a WP REST callback. Hence, which is the way to go to retrieve the current locale of the client within a WP REST Request callback?

7
  • 1
    posts and pages don't have locales, if they do on your site it's because a plugin is providing them and you would need to ask the plugin for it. Remember WordPress has no mechanism for translatable dynamic content, only translated static strings in code, so asking how to get the locale of a post in WordPress makes no sense without a plugin, and your solution is heavily dependent on which plugin you chose to use. It's very likely that what you ask for is impossible to answer here because it requires you to use Polylang specific functions and 3rd party plugins are offtopic here
    – Tom J Nowell
    Mar 14 at 15:27
  • All I wondered is if WP does not have a core function to retrieve the locale of the client within the callback hooked to a custom WP REST API request, defined in your main plugin file, via register_rest_route. So the answer to that seems to be no according to your comment, cheers! And of course also many thanks to @Jacob!
    – DevelJoe
    Mar 14 at 16:07
  • 1
    the answer is yes you can, but that answer doesn't fit into the system Polylang sets up, which is why you get inconsistencies. E.g. requests for a french article that return english for the user locale, because French is not the current users locale in native WP and your expectations have muddled up content locale and user locale together. There will be a unique Polylang specific answer that you need for your situation
    – Tom J Nowell
    Mar 14 at 17:26
  • Always a pleasure to learn from you mate, cheers!
    – DevelJoe
    Mar 14 at 18:16
  • 1
    WPML/Polylang/etc/etc all have their own solutions to this and track the current locale in different ways, be that via cookies or the location on the site you're at etc etc. There are many ways to do multilingual that have their own tradeoffs, you need to work within the system you chose, there is no generic one size fits all solution that works for all plugins everywhere. Until the translation phase of the gutenberg project starts that won't change ( and that's potentially years away )
    – Tom J Nowell
    Mar 14 at 18:56

2 Answers 2

2

There's only 2 real places a locale is defined in a vanilla WordPress install:

  1. For the site.
  2. For the user account.

In the admin the user account's locale will be used, while on the front end the site's locale will be used. For REST API requests the user locale can be used by passing _locale=user as a parameter.

The determine_locale() function is what performs the logic to determine whether the user or site's locale should be used, with additional logic that handles setting the locale during install.

The get_locale() function is used by determine_locale to get the site's locale. It returns the site's language as set in Settings > General. The site language can also be defined as a constant in wp-config.php or filtered by plugins. This is the function you'd normally use to get a site's locale.

Individual pages and posts do not have a locale. If you are using a plugin to create versions of pages in multiple languages then you will need to refer to that plugin's documentation for the correct way to get the correct language.

If you're asking how to get the user's location, then that's something else entirely, and not something WordPress can do. To accomplish that you will either need to use JavaScript in the browser to ask the user to share their location, or use a database of IP addresses to estimate the user's location. WordPress does not provide a function for either of these things.

19
  • So if I understand you correctly; your suggestion is to pass _locale=user to all of my REST API requests fired from the client-side, without the client being logged in into the admin, and then calling get_locale() within the callback you hook on your custom WP REST endpoint, to retrieve the locale properly? Another problem we have is that even our built-in translations created with WPs' built-in __() functions are returned in the sites' root language, instead of the language of the page of the frontend the client uses to makes the request. Any idea why this happens?
    – DevelJoe
    Mar 14 at 15:31
  • I'm asking the thing about _locale=user because I cannot find any documentation about it.
    – DevelJoe
    Mar 14 at 15:34
  • 1
    Correct, WordPress does not have any way to get the user's location in a REST API callback. It has no way to get the user's location in any circumstances. Mar 14 at 16:07
  • 1
    I would not count on WordPress adding multilingual support any time soon. Mar 14 at 16:10
  • 1
    To back-pedal a bit: There is an Accept-Language header that browser may send that may indicate the user's preferred language. Probably the browser language. However this isn't necessarily the same format that WordPress uses, and WordPress's functions don't use it. I can't say whether multilanguage plugins use it either. You may be able to retrieve the value with PHP, but the code for that wouldn't be WordPress specific. Mar 14 at 16:14
0

Given that I've struggled significantly with this, I'd like to share my solution. Note that the solution aims to retrieve the requesting client's locale within a WP REST Request, incoming from the frontend part of your application, hence from a user who's not logged in into the WP admin, in a reliable way, such that:

  • __() localization functions get the translations in the requesting client's locale, instead of the default language of your website.
  • WP core functions like get_locale() return the retrieved client's locale, instead of the default locale of your website.

So here's my approach:

  1. Create a Wrapper function in javascript, which you then use to fire all requests to the WP REST API from your frontend application. Within that wrapper function, pass a custom HTTP header that is specific to your plugin, sth like X-MYPLUGIN-LANG. I could have used Accept-Language, but as you may use a plugin for multilingual functionality already, that header may be affected by that plugin at some stage. So just to avoid any eventual code conflicts, I've decided to use a custom HTTP header.
  2. The value of that header is equal to document.getElementsByTagName("html")[0].lang; determined once only on page load.
  3. Within the main plugin file of the plugin where your targeted endpoints are registered via register_rest_route, you most likely have some code somewhere which loads your plugin's textdomain. Something like:
add_action(
                'init',
                function () {
                    
                    load_plugin_textdomain(
                        'my-plugin',
                        false,
                        dirname(plugin_basename(__FILE__)) . '/languages'
                    );

                }
            );

From what I've learned, this hook is what loads your localizations from your .mo files of the concerned plugin in the language represented by the current value of get_locale(). I've tested that by just putting an echo get_locale() before the load_plugin_textdomain() call above, and indeed: The translations of all of the __() all happen in the language represented by the output of get_locale(). So what must be done is (my thoughts, at least): change the locale for the lifetime of the incoming request, if it's a custom REST API request, into the one of the requesting client. And as well, for the entire lifetime of the incoming request, do this only once; to avoid repeated calls. This is assured by the fact that you'll enter your code within the action hooking above, as the init hook is executed exactly once with every request.

My conclusion was thus to add code that does the following above load_plugin_textdomain() (I won't paste the code as your validation may differs, etc.):

  1. Check if the incoming request has a X-MYPLUGIN-LANG HTTP header set.
  2. If so, verify that its value matches a valid locale of your REST API, before doing anything else (input validation).
  3. If so, remember that the value of your HTTP header is retrieved from the html tags' lang attribute, hence sth like en-US. WP locales however seem to work with underscores; hence transform the locale obtained from the client accordingly; hence sth like en-US --> en_US. Although I do not know if that step is strictly necessary.
  4. Simply call switch_to_locale(<your_transformed_locale>).

Doing the above-mentioned within your callback hooked to your init hook, right above load_plugin_textdomain(), returned all of my __() translations in the client's language, and get_locale() can also be used for other stuff, now retrieving the correct client locale, too!

UPDATE

As weird as things can get, when using rest_pre_dispatch and short-circuiting your response to your custom request, the init hook gets executed, hence the locale gets successfully changed to your client's locale, but the translations retrieved via __() calls are still in the root website language. Even if I echo get_locale() right above echo __('test translation','my-plugin'), and I get a locale different from the default locale, test translation gets output instead of its translation in the output locale. The reason so far seems to be that the call to load_plugin_textdomain() failed; as it returns false. I'm puzzled.

UPDATE 2

The call of load_plugin_textdomain() (and if it actually succeeds) seems to be completely irrelevant. It seems to be more crucial to which locale you switch via switch_to_locale before outputting the response holding localizations with __(). I've noticed this because, within WP REST requests which are entirely executed (hence not short-circuited via rest_pre_dispatch), the call to load_plugin_textdomain() within the init hook call of the same request still returns false, while the localizations of the WP REST Response actually get output in the language corresponding to the locale to which you switch within the init hook. So far, any call to load_plugin_textdomain() on page load succeeds (returns true), while any call to it within a REST request failes (returns false). Digging further.

Your Answer

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

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