- 状态
- 已解决
- 类型
- 不予修复
- 报告对象
- 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 响应和管理后台屏幕中会暴露出此问题。
没有还原操作或语言代码为空的切换,同样会留下一个未关闭的作用域。
临时解决方法
在 finally 代码块中,使用 null 结束每个作用域:
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" . 以查找每个调用点。每个调用点都需要一个匹配的结束操作。绝不要通过指定语言名称来恢复语言,也绝不要切换到可能为空的语言代码。
电子邮件钩子也是嵌套的:每调用一次 wpml_switch_language_for_email,就需要调用一次 wpml_restore_language_from_email。