WPML: remember user’s language choice

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:

  1. Add ?switch_lang=nl to your WPML language switcher
  2. Store the language in a cookie when the user chooses his language
  3. 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:

  1. In the next step, we will intercept requests with this parameter
  2. 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.

12 Replies to “WPML: remember user’s language choice”

  1. 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!

    1. 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:

      • Automatically switching to the correct language only works when a user visits the home page. If you want this to work with all pages, comment out the block // Check if requested webpage is /
      • Check out Maarten’s comment: https://wpml.org/forums/topic/remember-language-decision/#post-6608975
      • Make sure the “wp-wpml_user_selected_language” cookie is set, this is not a default wpml cookie. This cookie gets set only when you append the url’s of your language switcher with ?switch_language={lang}
      • I have noticed that this approach doesn’t work well when “Post Types Translation” -> pages is set to “Translatable – use translation if available or fallback to default language”.

      Kind regards,
      Tom

    1. I have updated the blogpost, and replaced the plugins_loaded hook by the init hook. This should fix your problems.

  2. 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?

    1. 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?

  3. Hi there,

    Why are you setting a new cookie? WPML already sets a cookie “wp-wpml_current_language”.

    1. 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.

  4. 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?

    1. Hi, thanks for your update!

      You can go ahead and translate the post into German.

      Kind regards,
      Tom

  5. 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

Geef een reactie

Je e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *

Overtuigd?

Neem dan contact op voor een offerte of vrijblijvend gesprek.
Stuur een e-mail naar [email protected] of maak gebruik van het contactformulier.