Skip to content Skip to sidebar

This is the technical support forum for WPML - the multilingual WordPress plugin.

Everyone can read, but only WPML clients can post here. WPML team is replying on the forum 6 days per week, 22 hours per day.

This topic contains 5 replies, has 1 voice.

Last updated by HANS 2 months, 2 weeks ago.

Assisted by: Claudio Palacios.

Author Posts
January 19, 2026 at 2:57 pm #17743037

HANS

When I reload a post in gutenberg, the admin language cookie is not being set correctly.
When I have more than one tab with different posts open, then switch the language in one tab, go to the other tab, reload the page.
The admin language cookie keeps the language, from when I last used the language switcher.

The wrong language in the cookie regularly causes WooCommerce variations to not load, because they do not match the language in the cookie.
Can you please explain to me how this is supposed to work?
In $sitepress->maybe_set_this_lang() the Backend request handlers get_requested_lang() is used to retrieve the language for the current post, but this only works for places, where the p query var is used, not where post is used (edit post screen).

But even if setting the post language during load would work, we still have the issue that the cookie can change, and if we then try to load the variations, the ajax call will return an empty body because the language of the cookie and the language of the variation do not match.
Again, please explain to me how this whole implementation with storing the admin language in the cookie is supposed to work.
Would it not be much better to pass the language only via query params and post arguments instead of relying on cookies?
This way we would be able to have a different admin language for each browser tab, which currently does not seem to work properly, even though that fact is documented nowhere.

January 20, 2026 at 3:38 pm #17747331

Claudio Palacios
Supporter

Languages: English (English )

Timezone: America/La_Paz (GMT-04:00)

I understand you are reporting an issue related to the Language Cookie/Multi-tab Issue.
I'll investigate this and update you as soon as possible.

January 21, 2026 at 3:02 pm #17750991

Claudio Palacios
Supporter

Languages: English (English )

Timezone: America/La_Paz (GMT-04:00)

Hello,

Thank you for the detailed bug report. I’m a developer on the WPML Dev Team.

To help you as quickly as possible, could you please share the exact steps to reproduce the issue?

Please also provide temporary admin access and confirm that you authorize us to clone your website using Duplicator for troubleshooting purposes.

Thank you,
Claudio

January 22, 2026 at 11:30 am #17753508

HANS

Hi Claudio,

To comply with the data protection laws, I can't give you admin access.
I would need to create a clean WP env, with the same plugin setup etc.

The main issue is that because of the cookie, it's not really possible to edit multiple posts in parallel, because the language cookie is always only correct for one of the tabs.
If the language in the cookie does not match the language of the post in the current tab, api / ajax requests which look at the language cookie, don't work properly.
An example is the WooCommerce "woocommerce_load_variations" action, if the language cookie differs from the post language, no variations are loaded.

What's especially annoying is that simply reloading the tab, does not set the cookie to the language of the post, being edited in the tab.
If you could fix this aspect, the situation would already improve.

I had a look at the implementation and the SitePress class' maybe_set_this_lang() method sets the cookie, but when determining the language, it does not look at the language of the post where the id is passed via the "post" query var, that only happens when the post id is passed via the "p" query var.
The specific implementation is in the Backend class' getPostElementLanguage() method.
If this was modified to also work for the "post" query var, in addition to the "p" query var, reloading the editor would set the cookie correctly.

I already tested this by replacing the line "->map( Obj::path( [ 'get', 'p' ] ) )" with "->map( Obj::path( [ 'get', 'post' ] ) )" and it works.
I'm just not sure how it needs to be implemented, to work for both "p" and "post".
This would be easier if there was a documentation for your internally used FP library.

Thanks for your help!

Regards

January 22, 2026 at 1:04 pm #17754002

Claudio Palacios
Supporter

Languages: English (English )

Timezone: America/La_Paz (GMT-04:00)

Hi,

Thank you for testing this! I really appreciate you taking the time to validate the fix.

You're absolutely right about the implementation - and excellent work identifying the exact issue. I can provide you with the complete solution that works for both `post` and `p` query parameters.

The Solution

The key is to check for `post` first, then fall back to `p` if not found. Here's exactly how to implement it:

Fix 1: Backend.php (The one you tested)

File:
/wp-content/plugins/sitepress-multilingual-cms/classes/language/Detection/Backend.php

Find (around line 73):

private function getPostElementLanguage() {
		return function ( $system ) {
			/** @var \WPML_Post_Translation $wpml_post_translations */
			global $wpml_post_translations;

			return Maybe::of( $system )
						->map( Obj::path( [ 'get', 'p' ] ) )
						->filter( Relation::gt( Fns::__, 0 ) )
						->map( [ $wpml_post_translations, 'get_element_lang_code' ] )
						->getOrElse( null );
		};
	}

Replace with:

private function getPostElementLanguage() {
		return function ( $system ) {
			/** @var \WPML_Post_Translation $wpml_post_translations */
			global $wpml_post_translations;

			$post_id = Maybe::of( $system )
						->map( Obj::path( [ 'get', 'post' ] ) )
						->getOrElse(
							Maybe::of( $system )
								->map( Obj::path( [ 'get', 'p' ] ) )
								->getOrElse( null )
						);

			return Maybe::of( $post_id )
						->filter( Relation::gt( Fns::__, 0 ) )
						->map( [ $wpml_post_translations, 'get_element_lang_code' ] )
						->getOrElse( null );
		};
	}

How it works:
- First tries to get the post ID from $_GET['post'] (Gutenberg/classic editor)
- If not found, falls back to $_GET['p'] (preview)
- Then proceeds with the language detection as before

This ensures reloading the editor always sets the cookie to match the post's language.

Fix 2: Ajax.php (For WooCommerce variations)

This addresses the woocommerce_load_variations issue you mentioned.

File:
/wp-content/plugins/sitepress-multilingual-cms/classes/language/Detection/Ajax.php

Step 1 - Add import (around line 5):

Find:

use WPML\FP\Maybe;
use WPML\FP\Obj;
use WPML\FP\Lst;
use WPML\FP\Str;
use WPML\FP\Fns;
use \WPML_Request;

Replace with:

use WPML\FP\Maybe;
use WPML\FP\Obj;
use WPML\FP\Lst;
use WPML\FP\Str;
use WPML\FP\Fns;
use WPML\FP\Relation;
use \WPML_Request;

Step 2 - Update method (around line 14):

Find:

public function get_requested_lang() {
		return Maybe::of( $_REQUEST )
					->map( Obj::prop( 'lang' ) )
					->filter( Lst::includes( Fns::__, $this->active_languages ) )
					->map( 'sanitize_text_field' )
					->getOrElse(
						function () {
							return $this->get_cookie_lang();
						}
					);
	}

Replace with:

public function get_requested_lang() {
		return Maybe::of( $_REQUEST )
					->map( Obj::prop( 'lang' ) )
					->filter( Lst::includes( Fns::__, $this->active_languages ) )
					->map( 'sanitize_text_field' )
					->getOrElse(
						function () {
							$post_lang = $this->get_post_lang_from_request();
							return $post_lang ? $post_lang : $this->get_cookie_lang();
						}
					);
	}

	private function get_post_lang_from_request() {
		global $wpml_post_translations;

		$post_id_keys = [ 'post_id', 'product_id', 'post', 'p', 'id' ];
		
		foreach ( $post_id_keys as $key ) {
			$post_id = Maybe::of( $_REQUEST )
						->map( Obj::prop( $key ) )
						->filter( Relation::gt( Fns::__, 0 ) )
						->getOrElse( null );
			
			if ( $post_id ) {
				$lang = $wpml_post_translations->get_element_lang_code( $post_id );
				if ( $lang && isset( $this->active_languages[ $lang ] ) ) {
					return $lang;
				}
			}
		}
		
		return null;
	}

How it works:
- Before falling back to cookie, checks if the AJAX request contains a post ID (checks post_id, product_id, etc.)
- If found, uses the actual post's language instead of the cookie
- This fixes WooCommerce variations loading with the correct language

About the FP Library

You mentioned wanting documentation. The FP functions you're seeing are from WPML\FP (functional programming helpers). Here's a quick reference for the pattern used:

Maybe::of( $value )           // Wraps value in Maybe monad
  ->map( function )           // Transforms if value exists
  ->filter( predicate )       // Keeps value if predicate true
  ->getOrElse( fallback )     // Returns value or fallback

Obj::path( ['key', 'subkey'] )  // Gets nested array value
Obj::prop( 'key' )              // Gets array property
Relation::gt( Fns::__, 0 )      // Greater than 0 check

Your specific case:

$post_id = Maybe::of( $system )
    ->map( Obj::path( [ 'get', 'post' ] ) )  // Try $_GET['post']
    ->getOrElse(                              // If not found...
        Maybe::of( $system )
            ->map( Obj::path( [ 'get', 'p' ] ) )  // Try $_GET['p']
            ->getOrElse( null )                    // If not found, return null
    );

This is equivalent to:

$post_id = $_GET['post'] ?? $_GET['p'] ?? null;

Next Steps

1. Apply both fixes (make a backup first)
2. Test - Reload tabs, test WooCommerce variations
3. Let me know if everything works correctly

Thank you again for the excellent bug report and for testing the fix. Your technical analysis really helped us identify and solve this quickly.

Best regards,
Claudio

February 2, 2026 at 3:53 pm #17784640

HANS

Hi Claudio,

Thanks for your help, your solution was mostly working.
We only needed to add a cast when getting the post id from $REQUEST like this:
+ $post_id = Maybe::of( $_REQUEST )
+ ->map( Obj::prop( $key ) )
+ ->map( 'intval' ) // cast added
+ ->filter( Relation::gt( Fns::__, 0 ) )
+ ->getOrElse( null );

If you want, I can send you the whole patch.
Do you plan on integrating this solution into wpml core?

Kind Regards
Simon