翻译额外的 Gutenberg 区块
Gato AI Translations for Polylang 可以翻译基于区块的文章。
该插件内置了对多种区块的支持。对于超出此范围的情况——您自己的自定义区块,或来自不附带 wpml-config.xml 的第三方插件的区块——您可以通过 PHP 钩子扩展支持。
翻译字符串
要为区块注册额外的可翻译属性,请使用 gatompl:gutenberg_block_type_translatable_attribute_regexes 过滤器。
为什么使用正则表达式?
Gutenberg 区块以包含区块 JSON 属性的 HTML 注释形式持久化存储到 post_content,后面跟着区块渲染后的 HTML,例如:
<!-- wp:my-plugin/my-block {"title":"Hello"} -->
<div class="wp-block-my-plugin-my-block">Hello</div>
<!-- /wp:my-plugin/my-block -->翻译一个区块意味着在该标记中找到需要翻译的特定子字符串,将其替换为译文,同时保持其他所有内容不变(区块名称、其他属性、HTML 结构、周围的区块)。正则表达式是插件精确定位需要替换的子字符串的方式:值前后的样板代码由捕获组获取,值本身是被替换的部分。
标准字符串属性(存储在区块的 JSON 中)
如果属性是存储在区块 JSON 属性中的普通字符串,传入 true,插件将使用其默认正则表达式。
例如,要翻译 kadence/countdown 区块的 daysLabel、hoursLabel、minutesLabel 和 secondsLabel 属性——其标记如下所示:
<!-- wp:kadence/countdown {"uniqueID":"_abc123","date":"2026-12-31 00:00:00","daysLabel":"Days","hoursLabel":"Hours","minutesLabel":"Minutes","secondsLabel":"Seconds"} -->
<div class="wp-block-kadence-countdown">…</div>
<!-- /wp:kadence/countdown -->……通过以下方式注册属性:
add_filter(
'gatompl:gutenberg_block_type_translatable_attribute_regexes',
static function (array $regexes): array {
$regexes['kadence/countdown'] = [
'daysLabel' => true,
'hoursLabel' => true,
'minutesLabel' => true,
'secondsLabel' => true,
];
return $regexes;
}
);true 值在内部被展开为以下默认正则表达式:
#(<!-- wp:%3$s \{.*?\"%2$s\":\")%1$s(\".*?\}/?-->)#……其中占位符为:
%1$s→ 属性值%2$s→ 属性名%3$s→ 区块名
对于 kadence/countdown 上的 daysLabel 属性,占位符被替换为 %3$s → kadence/countdown、%2$s → daysLabel、%1$s → Days,生成:
#(<!-- wp:kadence/countdown \{.*?\"daysLabel\":\")Days(\".*?\}/?-->)#只有 Days 被替换;区块名称、其他属性和闭合注释由捕获组保留。
正则表达式的形式为:
#(值之前的所有内容)属性值(值之后的所有内容)#存储在区块 HTML 中的字符串
如果值不是存储在 JSON 属性中,而是在渲染后的 HTML 中,请提供您自己的正则表达式。您可以在属性值所在位置使用 %s(代替 %1$s),并在正则表达式中将区块名称和属性名硬编码。
示例——翻译 generateblocks/text 区块的 content 属性。其标记如下所示——注意 Hello world 不在 JSON 中,而是位于渲染标签之间:
<!-- wp:generateblocks/text {"uniqueId":"abc123","tagName":"p"} -->
<p class="gb-text">Hello world</p>
<!-- /wp:generateblocks/text -->默认正则表达式无法找到该子字符串,因此您需要提供自己的正则表达式:
add_filter(
'gatompl:gutenberg_block_type_translatable_attribute_regexes',
static function (array $regexes): array {
$regexes['generateblocks/text'] = [
'content' => '#(<!-- wp:generateblocks/text [^>]*?-->\n?<[a-z0-9]+ ?[^>]*?>)%s(</[a-z0-9]+>\n?<!-- /wp:generateblocks/text -->)#',
];
return $regexes;
}
);同一值出现在多个位置的情况
如果同一属性同时出现在 JSON 属性和 HTML 中(或两个不同位置),请传入一个正则表达式数组——每个正则表达式都需要执行,以便翻译字符串的每个副本。
例如,在 generateblocks/media 区块上,alt 和 title 同时存储在 JSON 的 htmlAttributes 中,以及渲染后的 <img> 的 HTML 属性中:
<!-- wp:generateblocks/media {"mediaId":42,"htmlAttributes":{"alt":"Cat sitting","title":"My cat"}} -->
<figure class="gb-media"><img src="…" alt="Cat sitting" title="My cat"></figure>
<!-- /wp:generateblocks/media -->每个属性对应两个正则表达式——一个针对 JSON,一个针对 <img>——确保翻译后两个副本保持同步:
add_filter(
'gatompl:gutenberg_block_type_translatable_attribute_regexes',
static function (array $regexes): array {
$regexes['generateblocks/media'] = [
'htmlAttributes.alt' => [
'#(<!-- wp:generateblocks/media \{.*?\"htmlAttributes\":\{.*?\"alt\":\")%s(\".*?\}.*?\} -->)#',
'#(<!-- wp:generateblocks/media [^>]*?-->\n?.*<img [^>]*alt=\")%s(\"[^>]*?>.*\n?<!-- /wp:generateblocks/media -->)#',
],
'htmlAttributes.title' => [
'#(<!-- wp:generateblocks/media \{.*?\"htmlAttributes\":\{.*?\"title\":\")%s(\".*?\}.*?\} -->)#',
'#(<!-- wp:generateblocks/media [^>]*?-->\n?.*<img [^>]*title=\")%s(\"[^>]*?>.*\n?<!-- /wp:generateblocks/media -->)#',
],
];
return $regexes;
}
);如果属性值是一个 JSON 对象,您可以在属性名中使用 .(点号)来指向特定的子属性,如上面 generateblocks/media 的 htmlAttributes.alt 和 htmlAttributes.title 所示。
禁用自动翻译属性的翻译
传入 false 或 null 可移除插件原本会自动翻译的属性的翻译功能。例如,这对于将某个字符串属性从纯 PHP 区块的自动翻译中排除,或者对于从 wpml-config.xml 引入的、您不希望翻译其已声明属性的区块,非常有用:
add_filter(
'gatompl:gutenberg_block_type_translatable_attribute_regexes',
static function (array $regexes): array {
// Disable translation of the `header` attribute on the
// `my-plugin/duplicate-alert` block (either form works)
unset($regexes['my-plugin/duplicate-alert']['header']);
$regexes['my-plugin/duplicate-alert']['implications'] = false;
return $regexes;
}
);翻译实体引用
实体引用(存储在区块属性中的文章/媒体/分类法/菜单 ID)可以在翻译时重新映射到目标语言中对应的实体。根据引用的类型,使用以下相应的过滤器:
| 引用类型 | 过滤器 |
|---|---|
| 自定义文章和媒体 | gatompl:gutenberg_block_type_custompost_and_media_reference_attribute_regexes |
| 分类法词条 | gatompl:gutenberg_block_type_taxonomy_term_reference_attribute_regexes |
| 按 ID 引用的菜单 | gatompl:gutenberg_block_type_menu_reference_by_id_attribute_regexes |
| 按别名引用的菜单 | gatompl:gutenberg_block_type_menu_reference_by_slug_attribute_regexes |
每个过滤器接收与可翻译属性过滤器相同的结构(默认正则表达式传 true,自定义正则表达式传字符串或数组)。
例如,woocommerce/single-product 区块将关联的商品存储为数值型 productId:
<!-- wp:woocommerce/single-product {"productId":42} /-->当文章被翻译时,该 42(源语言商品)需要被重新映射到目标语言中对应的商品(例如 87)。将 productId 标记为自定义文章引用,以便插件在翻译时捕获并替换该 ID:
add_filter(
'gatompl:gutenberg_block_type_custompost_and_media_reference_attribute_regexes',
static function (array $regexes): array {
$regexes['woocommerce/single-product'] = [
'productId' => true,
// …or a custom regex if `productId` is not stored in the standard JSON shape:
// 'productId' => '#(<!-- wp:woocommerce/single-product \{.*?\"productId\":)%s([,\}].*? /?-->)#',
];
return $regexes;
}
);其他引用类型也使用相同的模式。它们在区块标记中的外观相同——嵌入在 JSON 中的数值 ID 或别名——区别在于插件将其解析为目标语言的方式:
<!-- wp:my-plugin/related-category {"categoryId":17} /-->
<!-- wp:my-plugin/menu-picker {"menuId":5} /-->
<!-- wp:my-plugin/menu-picker {"menuSlug":"main-nav"} /-->// Taxonomy term reference
add_filter(
'gatompl:gutenberg_block_type_taxonomy_term_reference_attribute_regexes',
static function (array $regexes): array {
$regexes['my-plugin/related-category'] = [
'categoryId' => true,
];
return $regexes;
}
);
// Menu reference by ID
add_filter(
'gatompl:gutenberg_block_type_menu_reference_by_id_attribute_regexes',
static function (array $regexes): array {
$regexes['my-plugin/menu-picker'] = [
'menuId' => true,
];
return $regexes;
}
);
// Menu reference by slug
add_filter(
'gatompl:gutenberg_block_type_menu_reference_by_slug_attribute_regexes',
static function (array $regexes): array {
$regexes['my-plugin/menu-picker'] = [
'menuSlug' => true,
];
return $regexes;
}
);查找属性名称
查找属性名称及其存储方式的最快方法是运行 Translate custom posts GraphQL Query,并查看相关区块的 blockFlattenedDataItems 响应字段。
有关如何运行该 Query 及读取其输出的方法,请参阅获取待翻译的页面构建器数据指南。
处理属性需要预处理的区块
上述钩子假设通过 blockFlattenedDataItems 公开的属性值已经是待翻译的值(标量或数组)。
如果值被包装——例如,属性存储的是 <li>Some text</li>,而您只想翻译 Some text——则需要先通过 gatompl:gutenberg_block_flattened_data_item_attributes 过滤器将其提取出来。
generateblocks/image 区块是一个真实的例子:其 alt 和 title 没有作为独立属性公开,它们存在于 innerContent 的 HTML 中,需要用正则表达式提取。
add_filter(
'gatompl:gutenberg_block_flattened_data_item_attributes',
static function (?\stdClass $attributes, string $blockTypeName, \stdClass $blockDataItem): ?\stdClass {
if ($attributes === null || $blockTypeName !== 'generateblocks/image') {
return $attributes;
}
$innerContent = $blockDataItem->innerContent ?? null;
if (!is_array($innerContent) || !isset($innerContent[0]) || !is_string($innerContent[0])) {
return $attributes;
}
$html = $innerContent[0];
if (preg_match('#<img [^>]*alt="([^"]*)"[^>]*?>#', $html, $matches) === 1 && $matches[1] !== '') {
$attributes->alt = $matches[1];
}
if (preg_match('#<img [^>]*title="([^"]*)"[^>]*?>#', $html, $matches) === 1 && $matches[1] !== '') {
$attributes->title = $matches[1];
}
return $attributes;
},
10,
3
);一旦属性对象上存在 alt 和 title,上述基于正则表达式的钩子就可以像对待任何其他属性一样对其进行处理。
查找示例参考
插件自身的集成是很好的真实参考案例。请查看您已安装的插件中的以下文件:
- 区块属性正则表达式:
wp-content/plugins/gato-ai-translations-for-polylang/src/Constants/BlockTypeAttributeValues.php - 区块属性预处理:
wp-content/plugins/gato-ai-translations-for-polylang/src/ConditionalOnContext/LicenseIsActive/Hooks/CoreBlockFlattenedDataItemAttributesHookSet.php