开发者指南:如何实现WordPress区块主题支持多种语言

开发者指南:如何实现WordPress区块主题支持多种语言

文章目录

  • 区块主题为何打破传统的翻译方法(以及如何解决这些问题)
  • 如何构建block.json翻译
  • 将静态HTML模板转换为可翻译的PHP组件
  • 如何为自定义字段和用户输入实现动态内容翻译
  • 使用翻译插件:兼容性和优化
  • 小结

开发者指南:如何实现WordPress区块主题支持多种语言

区块主题对 WordPress 的翻译方式与传统方法不同。传统的带有翻译功能的 PHP 模板文件无法与 HTML 模板、JavaScript 驱动的区块和站点编辑器兼容。这种转变需要您以不同的方式理解 WordPress 区块国际化系统。

本指南提供了使您的区块主题支持多语言的策略。您将学习如何应对区块主题翻译的挑战、实施解决方案以及如何与翻译插件集成。

区块主题为何打破传统的翻译方法(以及如何解决这些问题)

区块主题用包含区块标记的 HTML 模板替换了 WordPress 的许多 PHP 文件。然而,这种转换带来了挑战,因为 HTML 模板无法执行 PHP 翻译函数,例如 _()_e()。因此,您已有的翻译字符串会毫无用处地堆积在静态文件中。

WordPress 6.8 带来了一些改进,简化了区块主题的国际化。首先,具有正确文本域域路径标头的主题不再需要手动调用 load_theme_textdomain()

相反,WordPress 会自动加载翻译文件,并优先加载 wp-content/languages/themes/,而不是主题目录,以提高性能。

首先,使用经典方法设置您的主题,即向 style.css 文件添加元数据。

/*
Theme Name: My Block Theme
Text Domain: my-block-theme
Domain Path: /languages
*/

请注意,Text Domain 标头必须与主题的文件夹名称(通常采用短横线命名)匹配,以确保在较新的 WordPress 版本中翻译文件能够正确自动加载。

style.css 类似,您的 functions.php 文件需要进行最少的设置:

<?php
// Optional in WordPress 6.8+ but included for backward compatibility
function my_block_theme_setup() {
load_theme_textdomain( 'my-block-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_block_theme_setup' );
// Register block scripts with translation support
function my_block_theme_scripts() {
wp_enqueue_script(
'my-block-theme-scripts',
get_template_directory_uri() . '/assets/js/theme.js',
array( 'wp-i18n' ),
'1.0.0',
true
);
wp_set_script_translations( 
'my-block-theme-scripts', 
'my-block-theme', 
get_template_directory() . '/languages' 
);
}
add_action( 'wp_enqueue_scripts', 'my_block_theme_scripts' );

经典主题和 Block 主题之间的主要区别在于,后者将翻译任务拆分为服务器端 PHP 和客户端 JavaScript。相比之下,经典主题必须依赖 PHP 来处理大多数翻译任务。

如何构建block.json翻译

block.json 文件是您要翻译的区块的“配置中心”。设置正确的国际化设置可确保您的区块在编辑器和前端都能正确翻译。

注册 Block 的规范方法是通过 block.json。从 textdomain 配置开始,这意味着 WordPress 可以在设置 textdomain 后翻译标题描述关键字字段:

{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-theme/testimonial",
"title": "Testimonial",
"category": "text",
"description": "Display customer testimonials",
"keywords": ["quote", "review", "testimonial"],
"textdomain": "my-block-theme",
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "blockquote"
}
}
}

然而,需要“上下文”的场景需要在服务器端注册。在这种情况下,上下文很重要,因为同一个词可能会根据其用法而有不同的翻译。例如,“post”作为名词和动词,在许多语言中需要不同的翻译:

function my_theme_register_testimonial_block() {
register_block_type_from_metadata(
get_template_directory() . '/blocks/testimonial',
array(
'title' => _x( 'Testimonial', 'block title', 'my-block-theme' ),
'description' => _x(
'Display customer testimonials', 
'block description', 
'my-block-theme'
),
'keywords' => array(
_x( 'quote', 'block keyword', 'my-block-theme' ),
_x( 'review', 'block keyword', 'my-block-theme' )
)
)
);
}
add_action( 'init', 'my_theme_register_testimonial_block' );

您添加的任何区块变体也需要结构化命名,因为 WordPress 在加载翻译时会查找特定的模式。每个变体名称都成为翻译键的一部分:

{
"name": "my-theme/button",
"title": "Button",
"textdomain": "my-block-theme",
"variations": [{
"name": "primary",
"title": "Primary Button",
"attributes": {
"className": "is-style-primary"
}
},
{
"name": "secondary",
"title": "Secondary Button",
"attributes": {
"className": "is-style-secondary"
}
}
]
}

JavaScript 国际化需要您导入 WordPress i18n 函数并配置脚本翻译。这是因为站点编辑器在浏览器中运行,而不是在服务器上运行。由于 JavaScript 中不存在 PHP 翻译函数,因此 WordPress 通过 @wordpress/i18n 包提供了等效函数:

import {
registerBlockType
} from '@wordpress/blocks';
import {
__
} from '@wordpress/i18n';
import {
useBlockProps,
RichText
} from '@wordpress/block-editor';
registerBlockType('my-theme/testimonial', {
edit: ({
attributes,
setAttributes
}) => {
const blockProps = useBlockProps();
return ( < div { ...blockProps } >
< RichText tagName = "blockquote" value = { attributes.content } onChange = { (content) => setAttributes({
content
})
}
placeholder = {
__('Add testimonial text...', 'my-block-theme')
}
/> < cite >
< RichText tagName = "span" value = { attributes.author } onChange = { (author) => setAttributes({
author
})
}
placeholder = {
__('Author name', 'my-block-theme')
}
/> < /cite> < /div>
);
}
});

此外,为 JavaScript 生成 JSON 翻译文件是个好主意,因为 WordPress 使用不同的格式进行客户端翻译。PHP 使用 .mo 文件,但 JavaScript 需要具有特定命名约定的 .json 文件。您可以使用 WP-CLI 命令自动执行此操作:

# Extract strings from JavaScript files into POT
wp i18n make-pot . languages/my-block-theme.pot
# Convert PO files to JSON for JavaScript
wp i18n make-json languages/ --no-purge --pretty-print

生成的 JSON 文件遵循一致的格式: {textdomain}-{locale}-{handle}.json。当您调用 wp_set_script_translations() 时,WordPress 可以加载这些文件。

将静态HTML模板转换为可翻译的PHP组件

由于 HTML 模板是静态的,因此使用它们进行 Block 主题国际化是一项挑战,因为您现有的翻译函数和技术将无法正常工作。

PHP 驱动的模板部分可以解决这个问题,因为尽管它们在 HTML 模板中被引用,WordPress 仍会将它们作为 PHP 文件处理。这种混合方法在支持动态内容的同时,保留了 Block 主题的结构:

<!-- templates/page.html -->
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:post-title {"level":1} /-->
<!-- wp:post-content /-->
<!-- wp:template-part {"slug":"post-meta"} /-->
</main>
<!-- /wp:group →
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

请注意,模板部分可以包含 PHP:

<!-- parts/post-meta.html -->
<!-- wp:group {"className":"post-meta"} -->
<div class="wp-block-group post-meta">
<?php
echo sprintf(
/* translators: 1: Post date, 2: Post author */
__( 'Published on %1$s by %2$s', 'my-block-theme' ),
get_the_date(),
get_the_author()
);
?>
</div>
<!-- /wp:group -->

复杂的区块需要 render.php 文件,因为某些内容需要服务器端处理,而仅靠区块属性无法处理。数据库查询条件逻辑和动态内容生成都需要 PHP 执行:

// blocks/recent-posts/render.php
<?php
$recent_posts = get_posts( array( 
'numberposts' => $attributes['count'] ?? 5 
) );
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<h3><?php echo esc_html__( 'Recent Posts', 'my-block-theme' ); ?></h3>
<?php if ( $recent_posts ) : ?>
<ul>
<?php foreach ( $recent_posts as $post ) : ?>
<li>
<a href="<?php echo get_permalink( $post ); ?>">
<?php echo get_the_title( $post ); ?>
</a>
<span class="post-date">
<?php echo get_the_date( '', $post ); ?>
</span>
</li>
<?php endforeach; ?>
</ul>
<?php else : ?>
<p><?php esc_html_e( 'No posts found.', 'my-block-theme' ); ?></p>
<?php endif; ?>
</div>

这意味着配置您的区块以使用 block.json 中的渲染文件:

{
"name": "my-theme/recent-posts",
"render": "file:./render.php",
"attributes": {
"count": {
"type": "number",
"default": 5
}
}
}

如何为自定义字段和用户输入实现动态内容翻译

尽管动态内容在 WordPress 网站上非常普遍,但它仍可能导致翻译问题,因为它存在于数据库中,而不是主题文件中。因此,您使用的任何第三方翻译插件都需要识别和管理此类内容,并将其与静态主题字符串区分开来。

注册自定义字段并使用正确的元配置非常重要,因为翻译插件会连接到 WordPress 的元系统来检测任何可翻译的内容。show_in_rest 参数可以与网站编辑器兼容:

function my_theme_register_meta_fields() {
register_post_meta( 'page', 'custom_subtitle', array(
'type' => 'string',
'description' => __( 'Page subtitle', 'my-block-theme' ),
'single' => true,
'show_in_rest' => true,
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
}
));
}
add_action( 'init', 'my_theme_register_meta_fields' );
// Display with plugin compatibility
function my_theme_display_subtitle( $post_id ) {
$subtitle = get_post_meta( $post_id, 'custom_subtitle', true );
if ( ! $subtitle ) {
return;
}
// WPML compatibility
// (documented at wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/)
if ( function_exists( 'icl_t' ) ) {
$subtitle = icl_t( 
'my-block-theme', 
'subtitle_' . $post_id, 
$subtitle 
);
}
// Polylang compatibility
// (documented at polylang.pro/doc/function-reference/)
if ( function_exists( 'pll_translate_string' ) ) {
$subtitle = pll_translate_string( $subtitle, 'my-block-theme' );
}
echo '<h2 class="page-subtitle">' . esc_html( $subtitle ) . '</h2>';
}

数据库查询也需要语言过滤,因为 WordPress 不会自动按语言过滤内容。翻译插件会添加查询修改,您必须适应以下情况:

function my_theme_get_localized_posts( $args = array() ) {
$defaults = array(
'post_type' => 'post',
'posts_per_page' => 10
);
$args = wp_parse_args( $args, $defaults );
// Polylang adds language taxonomy
// (documented at polylang.pro/doc/developpers-how-to/)
if ( function_exists( 'pll_current_language' ) ) {
$args['lang'] = pll_current_language();
}
// WPML filters queries automatically when suppress_filters is false
// (wpml.org/documentation/getting-started-guide/translating-custom-posts/)
if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
$args['suppress_filters'] = false;
}
return get_posts( $args );
}

您的表单处理混合了动态和静态内容,但表单标签、错误消息和管理通知都需要语言感知的翻译。电子邮件收件人也可能因语言而异:

function my_theme_process_contact_form() {
if ( ! isset( $_POST['contact_nonce'] ) || 
! wp_verify_nonce( $_POST['contact_nonce'], 'contact_form' ) ) {
return;
}
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
$message = sanitize_textarea_field( $_POST['message'] );
// Get admin email in current language
$admin_email = get_option( 'admin_email' );
// For language-specific admin emails, use WPML's string translation
// (documented at wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/)
if ( function_exists( 'icl_t' ) ) {
// First register the string if not already registered
if ( function_exists( 'icl_register_string' ) ) {
icl_register_string( 'my-block-theme', 'contact_email', $admin_email );
}
$admin_email = icl_t(
'my-block-theme',
'contact_email',
$admin_email
);
}
$subject = sprintf(
/* translators: %s: Sender name */
__( 'Contact form submission from %s', 'my-block-theme' ),
$name
);
wp_mail( $admin_email, $subject, $message );
}
add_action( 'init', 'my_theme_process_contact_form' );

评估导航语言的认知度也很重要,因为不同语言的菜单项、URL 和结构可能有所不同。你的翻译插件可能包含用于构建语言切换器的 API:

function my_theme_language_switcher_block() {
if ( ! function_exists( 'pll_the_languages' ) && 
! function_exists( 'icl_get_languages' ) ) {
return;
}
$output = '<div class="language-switcher">';
// Polylang language switcher 
// (documented at polylang.pro/doc/function-reference/)
if ( function_exists( 'pll_the_languages' ) ) {
$languages = pll_the_languages( array( 'raw' => 1 ) );
foreach ( $languages as $lang ) {
$output .= sprintf(
'<a href="%s" class="%s">%s</a>',
esc_url( $lang['url'] ),
$lang['current_lang'] ? 'current-lang' : '',
esc_html( $lang['name'] )
);
}
}
// WPML language switcher
// (documented at wpml.org/documentation/support/wpml-coding-api/multi-language-api/)
elseif ( function_exists( 'icl_get_languages' ) ) {
$languages = icl_get_languages();
foreach ( $languages as $lang ) {
$output .= sprintf(
'<a href="%s" class="%s">%s</a>',
esc_url( $lang['url'] ),
$lang['active'] ? 'current-lang' : '',
esc_html( $lang['native_name'] )
);
}
}
$output .= '</div>';
return $output;
}

使用翻译插件很可能是你工作中的很大一部分,所以接下来让我们来看一下这方面。

使用翻译插件:兼容性和优化

每个 WordPress 翻译插件处理区块主题的方式都不同。了解不同解决方案所采用的方法有助于你从一开始就构建兼容性和灵活性。

WPML 的整站编辑文档概述了如何为区块主题进行特定配置:

// WPML FSE compatibility based on official documentation
add_action( 'init', function() {
if ( ! defined( 'WPML_VERSION' ) ) {
return;
}
// FSE themes are automatically detected in WPML 4.5.3+ // Enable FSE support
add_filter( 'wpml_is_fse_theme', '__return_true' );
// Register custom strings per WPML String Translation documentation
// (documented at wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/)
if ( function_exists( 'icl_register_string' ) ) {
icl_register_string(
'my-block-theme',
'footer-copyright',
'© My Company.'
);
}
});

Polylang Pro 自 3.2 版本起支持站点编辑器。该插件通过其标准字符串翻译接口处理区块主题

// Polylang string registration per official documentation
if ( function_exists( 'pll_register_string' ) ) {
pll_register_string( 
'Footer Copyright',
'© My Company.',
'my-block-theme',
true // Multiline support
);
}

TranslatePress 的文档显示,为了获得最佳性能,需要排除某些动态元素:

// TranslatePress optimization based on official recommendations
// (documented at translatepress.com/docs/developers/)
add_filter( 'trp_stop_translating_page', function( $stop, $url ) {
// Skip admin and API requests per TranslatePress documentation
if ( is_admin() || wp_is_json_request() ) {
return true;
}
// Skip pattern preview URLs that can cause rendering issues
if ( strpos( $url, 'pattern-preview' ) !== false ) {
return true;
}
return $stop;
}, 10, 2 );

最后,在使用第三方代码库(例如插件)时,有一些通用的技巧可以传授。首先,确保使用系统的方法来调试翻译问题。

// Debug helper for translation issues
function my_theme_debug_translations() {
if ( ! WP_DEBUG || ! current_user_can( 'manage_options' ) ) {
return;
}
error_log( 'Text domain loaded: ' . is_textdomain_loaded(
'my-block-theme' ) );
error_log( 'Current locale: ' . get_locale() );
error_log( 'Translation test: ' . __(
'Hello World',
'my-block-theme'
)
);
// Check JSON translations for blocks
$json_file = WP_LANG_DIR . '/themes/my-block-theme-' . get_locale() . '-script-handle.json';
error_log( 'JSON translation exists: ' . file_exists( $json_file ) );
}
add_action( 'init', 'my_theme_debug_translations' );

网站缓存可能会干扰翻译更新,因此您可能需要在翻译文件发生变化时清除缓存:

# Clear WordPress transients
wp transient delete --all
# Generate fresh translation files
wp i18n make-pot . languages/my-block-theme.pot
wp i18n make-json languages/ --no-purge

对于翻译插件来说,性能优化至关重要。每个插件都会增加数据库查询和处理开销,而缓存常用的翻译则能带来好处:

function my_theme_cached_translation( $text, $domain = 'my-block-theme' ) {
$cache_key = 'translation_' . md5( $text . get_locale() );
$cached = wp_cache_get( $cache_key, 'my_theme_translations' );
if ( false === $cached ) {
$cached = __( $text, $domain );
wp_cache_set( $cache_key, $cached, 'my_theme_translations', HOUR_IN_SECONDS );
}
return $cached;
}

或者,最好在部署前先跳过缓存。使用预发布环境是理想的选择,而且通常不需要缓存带来的性能提升。

小结

区块主题的国际化需要您同时使用 WordPress 的翻译方法,并在站点编辑器中使用新的方法。

通过配置主题元数据、实施模板策略以及了解翻译插件的要求,您可以创建性能良好且提供高质量用户体验的多语言区块主题。

评论留言

闪电侠

(工作日 10:00 - 18:30 为您服务)

2025-12-05 10:32:50

您好,无论是售前、售后、意见建议……均可通过联系工单与我们取得联系。

您也可选择聊天工具与我们即时沟通或点击查看:

您的工单我们已经收到,我们将会尽快跟您联系!
取消
选择聊天工具: