WPML
Statut
Résolue
Type
Ne sera pas résolue
Signalée pour
WPML Multilingual CMS 5.0
Étiquettes de sujet
Compatibility

Aperçu de l’anomalie

Depuis WPML 5.0, les changements de langue s’imbriquent. Chaque code de langue ouvre une nouvelle portée, et seul null en ferme une.

Les extensions écrites pour WPML 4.x reviennent souvent à la langue précédente en nommant la langue qu’elles ont enregistrée :

$current = apply_filters( 'wpml_current_language', null );

do_action( 'wpml_switch_language', 'de' );
// ...
do_action( 'wpml_switch_language', $current ); // Meant as "and back".

WPML 4.x tolérait cela. Il conservait un seul emplacement de langue, de sorte que la langue actuelle finissait par être correcte et que le changement non équilibré passait inaperçu. Ce n’a jamais été une méthode correcte pour fermer un changement. WPML 5.0 ne l’absorbe plus : le dernier appel ouvre une deuxième portée au lieu de fermer la première, et WPML continue de signaler que la langue a été changée.

WPML ne peut pas détecter cela, car le même appel est également un changement valide vers la langue d’où provient le code environnant.

Le symptôme est différé. Juste après la paire, la langue actuelle est correcte, donc un test qui ne vérifie que cela réussit. La restauration correcte suivante dans le code environnant ferme alors votre portée ouverte au lieu de la sienne, et ce code s’exécute dans la mauvaise langue. C’est dans les e-mails, les réponses REST et les écrans de l’interface d’administration que cela se manifeste.

Un changement sans restauration, ou avec un code de langue vide, laisse une portée ouverte de la même manière.

Solution de contournement

Fermez chaque portée avec null, dans un bloc finally :

do_action( 'wpml_switch_language', 'de' );

try {
	// ...your work in German...
} finally {
	do_action( 'wpml_switch_language', null ); // Closes the scope above.
}

Prenez en charge WPML 4.x et 5.0 dans une seule version publiée

Copiez ces helpers et renommez le préfixe :

// Opens a scope. Returns the value for the close helper, or false if none opened.
function myplugin_wpml_open_language_switch( $language_code ) {
	// '', false and 0 open a scope without changing the language. Only null closes one.
	if ( ! $language_code ) {
		return false;
	}
	$previous = apply_filters( 'wpml_current_language', '' );
	do_action( 'wpml_switch_language', $language_code );
	return $previous;
}

// Closes the scope the open helper opened. WPML 4.x has no stack: there null
// means "the language before the FIRST switch", so restore by name.
function myplugin_wpml_close_language_switch( $previous ) {
	if ( ! $previous ) {
		return;
	}
	$has_stack = defined( 'ICL_SITEPRESS_VERSION' )
		&& version_compare( ICL_SITEPRESS_VERSION, '5.0', '>=' );
	do_action( 'wpml_switch_language', $has_stack ? null : $previous );
}

Chaque point d’appel devient alors :

$previous = myplugin_wpml_open_language_switch( 'de' );
try {
	// ... work ...
} finally {
	myplugin_wpml_close_language_switch( $previous );
}

Exécutez grep -rn "wpml_switch_language" . pour trouver chaque point d’appel. Chacun d’eux nécessite une fermeture correspondante. Ne restaurez jamais en nommant la langue, et ne basculez jamais sur un code qui peut être vide.

Les hooks d’e-mail s’imbriquent également : appelez wpml_restore_language_from_email une fois pour chaque wpml_switch_language_for_email.

Toutes les anomalies connues →