There is no built-in function to remember a user’s language choice in WPML, and redirect him accordingly. WPML can only redirect returning visitors according to their browser language. I figured out a way to store the user’s language choice, and redirect the user to his preferred language when he revisits your website.
There are 3 main steps:
- Add
?switch_lang=nl
to your WPML language switcher - Store the language in a cookie when the user chooses his language
- Redirect the user to his chosen language when certain conditions are met
1. Add ?switch_lang={lang} to your WPML language switcher
We need to append ?switch_lang={lang}
to each url in WPML’s language switcher. We rely on the switch_lang parameter for two reasons:
- In the next step, we will intercept requests with this parameter
- WP Rocket and other caching plugins will not cache requests with query strings
You can rename switch_lang
to any string you want, just make sure to replace the string in the next step as well.
/**
* Add ?switch_lang=lang to all url's in the languaqe switcher
*/
add_filter( 'icl_ls_languages', function( $languages ){
global $sitepress;
foreach( $languages as $lang_code => $language ){
$languages[$lang_code]['url'] = add_query_arg( array( 'switch_language' => $lang_code ), $languages[$lang_code]['url'] );
}
return $languages;
} );
2. Store the user’s preferred language in a cookie
This piece of code runs on the ‘init’ hook. We need WPML’s icl_get_languages()
function to check if the value for switch_lang
is a registered language.
If the switch_lang
variable is present, and the value is registered as a language, we create a cookie with the value, and a far-future expiry date.
The redirect is not mandatory, but as explained in step 1, requests with query strings are not cached, so the server would serve an uncached page. We strip the query string from the request uri, and redirect the user. This will also result in prettier URL’s.
/**
* Update wp-wpml_user_selected_language cookie when user switches language
*/
add_action( 'init', function (){
// GET variables
$switch_language = filter_input(INPUT_GET, 'switch_language', FILTER_SANITIZE_STRING);
if( $switch_language ) {
$languages = icl_get_languages();
// Check if the switch_language variable is a registered language
if( array_key_exists( $switch_language, $languages ) ) {
// Create a cookie that never expires, technically it expires in 10 years
setcookie( 'wp-wpml_user_selected_language', $switch_language, time() + (10 * 365 * 24 * 60 * 60), '/' );
// Let's redirect the users to the request uri without the querystring, otherwise the server will send an uncached page
wp_redirect( strtok( $_SERVER['REQUEST_URI'], '?' ) );
exit;
}
}
}, 1);
3. Redirect returning visitors to their preferred language
We use the wpml_switch_language action to redirect the user to his preferred language, which has been stored in the wp-wpml_user_selected_language
cookie.
/**
* Switch language if the user has selected his language before
*/
add_action( 'pre_get_posts', function ( $query ) {
// Disable for admin area & only for main query
if( is_admin() || !$query->is_main_query() ) {
return;
}
// Disable for logged in users
if( is_user_logged_in() ) {
return;
}
// Check if requested webpage is /
if( strtok( $_SERVER['REQUEST_URI'], '?' ) != '/' ) {
return;
}
// GET & COOKIE variables
$switch_language = filter_input( INPUT_GET, 'switch_language', FILTER_VALIDATE_BOOLEAN );
$user_selected_language = filter_input( INPUT_COOKIE, 'wp-wpml_user_selected_language', FILTER_SANITIZE_STRING );
// If user has selected his language before & user did not just change his language
if ( $user_selected_language && !$switch_language ) {
do_action( 'wpml_switch_language', $user_selected_language );
}
}, 1);
In this case, we only redirect requests to the homepage. You could redirect all requests by commenting out:
if( strtok( $_SERVER['REQUEST_URI'], '?' ) != '/' ) {
return;
}
Extra: Dynamic & mandatory cookie for WP Rocket
To be able to use this code in combination with WP Rocket, there is an extra step. We need to add the wp-wpml_user_selected_language cookie to the WP Rocket dynamic cookies. You can use WP Rocket cache dynamic cookie plugin for this, or implement the plugin in your functionality plugin. Don’t forget to replace your-cookie-id-here
by wp-wpml_user_selected_language
in line 21.
Hi Tom,
Thank you for this function, but unfortunately, It doesn’t work for me. I’m using WPML with directory approach, example “/en”, “/fr”… So can I change the script so it supports directory approach in WPML?
Thank you for your time!
Hi Dejan,
I have tested this on a fresh install with the directory approach & with the latest WPML version and this still works like it should.
Here are some pointers:
// Check if requested webpage is /
Kind regards,
Tom
Hey there, this is great, but the #2 plugins loaded function isn’t working and the cookie isn’t getting set. Any ideas?
This is the problem I’m facing as well
I have updated the blogpost, and replaced the plugins_loaded hook by the init hook. This should fix your problems.
Hi Tom,
First of all thank you for this great post, really helped me out figuring some things out.
I only need to tackle one thing now, when coming from a root page (a default wordpress page) it doesnt seem to remember te language choice. Any help?
Hi BB, this script should work for all kind of pages. Are you sure the ‘wp-wpml_user_selected_language ‘ cookie is set? Is the code in step 3 running?
Hi there,
Why are you setting a new cookie? WPML already sets a cookie “wp-wpml_current_language”.
When you toggle languages the wp-wpml_current_language cookie is not updated immediately (it is set via PHP which is only available on the next page load), and even if you enable “Store a language cookie to support language filtering for AJAX”, which sets it via JS, PHP is not able to pick it up.
Hi Tom,
Many Thanks for the cool code! It still works perfectly.
A positive update: Rocket WP nowadays offers a “Never Cache Cookies” option (https://docs.wp-rocket.me/article/1382-never-cache-cookies) where only the cookie ID wp-wpml_user_selected_language needs to be entered. You save the mentioned extra plugin.
Would it be ok for you if we translate the post on our blog https://elbnetz.com/wordpress-tipps/ into German by mentioning and linking your original?
Hi, thanks for your update!
You can go ahead and translate the post into German.
Kind regards,
Tom
Hi Tom,
Many thanks for your code! It works except for one language.
I’m using WPML with directory approach.
It works well with “/en/”, “/de/”, “/es/”, “/it/” but it doesn’t work with “/en-us/”.
Please do you have any idea where this could come from?
Best Regards,
Marc