- Статус
- Решено
- Тип
- Не будет исправлено
- Заявлено для
- WPML Multilingual CMS 5.0
- Теги темы
- Compatibility
Обзор проблемы
Начиная с WPML 5.0, переключения языков поддерживают вложенность. Каждый код языка открывает новую область видимости, и только null закрывает её.
Плагины, написанные для WPML 4.x, часто переключаются обратно, указывая сохранённый ими язык:
$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 допускал это. Он сохранял один слот для языка, поэтому текущий язык в итоге оказывался правильным, а несбалансированное переключение оставалось незамеченным. Это никогда не было правильным способом закрыть переключение.
WPML 5.0 больше не поглощает это: последний вызов открывает вторую область видимости вместо того, чтобы закрыть первую, и WPML продолжает сообщать, что язык переключён.
WPML не может обнаружить это, поскольку тот же вызов также является допустимым переключением на язык, из которого пришёл окружающий код.
Симптом проявляется не сразу. Сразу после этой пары текущий язык является правильным, поэтому тест, проверяющий только это, проходит успешно. Затем следующее правильное восстановление в окружающем коде закрывает вашу открытую область видимости вместо своей, и этот код выполняется на неправильном языке. Это проявляется в электронных письмах, ответах REST и на экранах админ-панели.
Переключение без восстановления или с пустым кодом языка оставляет область видимости открытой точно так же.
Обходное решение
Закрывайте каждую область видимости с помощью null в блоке finally:
do_action( 'wpml_switch_language', 'de' );
try {
// ...your work in German...
} finally {
do_action( 'wpml_switch_language', null ); // Closes the scope above.
}
Поддержка WPML 4.x и 5.0 в одном релизе
Скопируйте эти вспомогательные функции и переименуйте префикс:
// 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 );
}
Тогда каждое место вызова примет следующий вид:
$previous = myplugin_wpml_open_language_switch( 'de' );
try {
// ... work ...
} finally {
myplugin_wpml_close_language_switch( $previous );
}
Выполните grep -rn "wpml_switch_language" ., чтобы найти все места вызовов. Для каждого из них требуется соответствующее закрытие. Никогда не восстанавливайте язык по его названию и никогда не выполняйте переключение по коду, который может быть пустым.
Хуки для email также поддерживают вложенность: вызывайте wpml_restore_language_from_email один раз для каждого wpml_switch_language_for_email.