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:
- 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.
- The value of that header is equal to
document.getElementsByTagName("html")[0].lang
; determined once only on page load.
- 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.):
- Check if the incoming request has a
X-MYPLUGIN-LANG
HTTP header set.
- If so, verify that its value matches a valid locale of your REST API, before doing anything else (input validation).
- 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.
- 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.
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!