WordPress国际化:wp_i18n
与前后端翻译实战
大家好,今天我们来深入探讨WordPress的国际化(i18n),以及如何利用wp_i18n
库和PO/MO文件来实现多语言支持,特别是前端区块的字符串翻译。WordPress作为一个全球化的内容管理系统,多语言支持至关重要,它可以让你的网站服务于更广泛的受众。
1. 国际化(i18n)与本地化(L10n)的概念
首先,我们需要区分国际化(i18n)和本地化(L10n)这两个概念:
-
国际化(i18n,Internationalization): 指的是设计和开发软件应用程序,使其能够在无需修改源代码的情况下适应不同的语言和文化区域。这通常涉及到将文本、日期、货币等元素与代码分离,以便进行翻译和格式化。
-
本地化(L10n,Localization): 指的是将国际化的应用程序适配于特定的语言和文化区域。这包括翻译文本、调整日期和时间格式、使用当地货币等。
在WordPress中,国际化是通过wp_i18n
库和相关函数来实现的,而本地化则是通过创建和使用PO/MO文件来完成的。
2. wp_i18n
库的核心函数
wp_i18n
库提供了一系列函数,用于在WordPress主题和插件中实现国际化。以下是一些核心函数:
函数名 | 描述 |
---|---|
__() |
返回翻译后的字符串。如果找不到翻译,则返回原始字符串。 |
_e() |
输出翻译后的字符串。 |
_x() |
返回翻译后的字符串,并允许指定上下文(context)。上下文用于区分具有相同字符串但含义不同的情况。 |
_ex() |
输出翻译后的字符串,并允许指定上下文。 |
_n() |
返回翻译后的单数或复数形式的字符串。 |
_nx() |
返回翻译后的单数或复数形式的字符串,并允许指定上下文。 |
esc_attr__() |
返回转义后的翻译字符串,适用于HTML属性。 |
esc_attr_e() |
输出转义后的翻译字符串,适用于HTML属性。 |
esc_html__() |
返回转义后的翻译字符串,适用于HTML内容。 |
esc_html_e() |
输出转义后的翻译字符串,适用于HTML内容。 |
load_theme_textdomain() |
加载主题的文本域。 |
load_plugin_textdomain() |
加载插件的文本域。 |
3. PO/MO文件的作用与生成
-
PO (Portable Object) 文件: 是一种文本文件,包含了原始字符串和对应的翻译。它通常由开发人员创建,并交给翻译人员进行翻译。PO文件是人类可读的。
-
MO (Machine Object) 文件: 是PO文件的二进制编译版本,由WordPress使用。它比PO文件更小、加载更快。
要生成PO/MO文件,通常需要以下步骤:
- 在代码中使用
wp_i18n
函数: 将所有需要翻译的字符串包裹在__()
,_e()
,_x()
,_ex()
,_n()
,_nx()
等函数中。 - 使用工具提取字符串: 使用工具(例如Poedit、WP-CLI)扫描代码,提取所有需要翻译的字符串,并生成一个
.pot
文件(PO Template)。.pot
文件本质上是一个空的PO文件,只包含原始字符串。 - 创建PO文件: 将
.pot
文件复制一份,并将其重命名为对应语言的PO文件。例如,对于德语,可以命名为de_DE.po
。 - 翻译PO文件: 使用Poedit或其他翻译工具打开PO文件,并逐个翻译原始字符串。
- 生成MO文件: Poedit会自动根据PO文件生成MO文件。MO文件与PO文件同名,但扩展名为
.mo
。
3.1 使用Poedit创建和翻译PO/MO文件
Poedit是一个流行的PO/MO文件编辑工具,它提供了友好的界面和强大的功能。
- 下载和安装Poedit: 从Poedit官网下载并安装最新版本的Poedit。
- 创建新目录项: 打开Poedit,选择“文件”->“新建目录项”。
- 设置目录项属性:
- 项目名称和版本: 填写项目的名称和版本号。
- 基本设置: 设置字符集为UTF-8,换行符为Unix。
- 源路径: 指定包含源代码的目录。Poedit会扫描这些目录以提取字符串。
- 关键词: 指定要使用的
wp_i18n
函数。Poedit默认会识别__()
,_e()
,_x()
,_ex()
,_n()
,_nx()
等函数。 - 源文本: 设置源文本的语言,通常为英语。
- 提取字符串: 点击“更新目录项”按钮,Poedit会扫描指定的源路径,提取所有需要翻译的字符串。
- 翻译字符串: 逐个选择需要翻译的字符串,并在下方的翻译框中输入翻译后的文本。
- 保存PO文件: 点击“文件”->“保存”,Poedit会自动生成PO和MO文件。
3.2 使用WP-CLI创建PO/MO文件
WP-CLI是WordPress的命令行工具,也可以用于创建和管理PO/MO文件。
- 安装WP-CLI: 如果尚未安装WP-CLI,请按照官方文档进行安装。
-
生成POT文件: 使用
wp i18n make-pot
命令生成POT文件。wp i18n make-pot . languages/my-theme.pot --slug=my-theme
.
: 指定要扫描的目录(当前目录)。languages/my-theme.pot
: 指定POT文件的路径和名称。--slug=my-theme
: 指定文本域(text domain)。
-
创建PO文件: 使用
wp i18n create-translation
命令创建PO文件。wp i18n create-translation my-theme de_DE
my-theme
: 指定文本域。de_DE
: 指定语言代码。
- 翻译PO文件: 使用Poedit或其他翻译工具打开PO文件,并逐个翻译原始字符串。
-
生成MO文件: Poedit会自动根据PO文件生成MO文件。或者可以使用WP-CLI:
wp i18n compile-mofile languages/my-theme-de_DE.po languages/my-theme-de_DE.mo
4. 在主题和插件中使用wp_i18n
函数
在主题和插件中,你需要使用wp_i18n
函数来包裹所有需要翻译的字符串。以下是一些示例:
4.1 基本用法
<?php
// 翻译字符串
echo __( 'Hello, world!', 'my-theme' );
// 输出翻译后的字符串
_e( 'Welcome to my website!', 'my-theme' );
// 使用上下文
echo _x( 'Post', 'noun', 'my-theme' ); // 用于文章
echo _x( 'Post', 'verb', 'my-theme' ); // 用于发布
// 单数/复数形式
echo sprintf( _n( '%s comment', '%s comments', $comment_count, 'my-theme' ), number_format_i18n( $comment_count ) );
// 转义字符串,适用于HTML属性
echo '<a href="' . esc_attr__( 'https://example.com', 'my-theme' ) . '">' . esc_html__( 'Learn more', 'my-theme' ) . '</a>';
?>
4.2 加载文本域
在主题的functions.php
文件或插件的主文件中,你需要加载文本域,以便WordPress能够找到PO/MO文件。
<?php
// 主题
function my_theme_setup() {
load_theme_textdomain( 'my-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_theme_setup' );
// 插件
function my_plugin_load_textdomain() {
load_plugin_textdomain( 'my-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
add_action( 'plugins_loaded', 'my_plugin_load_textdomain' );
?>
'my-theme'
和'my-plugin'
是文本域(text domain)。文本域用于区分不同的主题和插件。get_template_directory() . '/languages'
和dirname( plugin_basename( __FILE__ ) ) . '/languages'
指定了PO/MO文件所在的目录。通常,PO/MO文件会放在languages
目录下。
4.3 使用上下文(Context)
当同一个字符串在不同的上下文中具有不同的含义时,可以使用_x()
和_ex()
函数来指定上下文。
<?php
// "Post" 作为名词 (例如: 文章)
echo _x( 'Post', 'noun', 'my-theme' );
// "Post" 作为动词 (例如: 发布)
echo _x( 'Post', 'verb', 'my-theme' );
?>
在PO文件中,你可以为每个上下文提供不同的翻译。
4.4 处理单数和复数形式
对于需要处理单数和复数形式的字符串,可以使用_n()
和_nx()
函数。
<?php
$comment_count = get_comments_number();
echo sprintf( _n( '%s comment', '%s comments', $comment_count, 'my-theme' ), number_format_i18n( $comment_count ) );
?>
_n()
函数接受四个参数:
- 单数形式的字符串。
- 复数形式的字符串。
- 用于确定单数/复数的数字。
- 文本域。
4.5 转义字符串
在输出字符串时,应该根据上下文进行转义,以防止XSS攻击。
esc_attr__()
和esc_attr_e()
用于HTML属性。esc_html__()
和esc_html_e()
用于HTML内容。
5. 前端区块(Block)的字符串翻译
WordPress的区块编辑器(Gutenberg)使用JavaScript来构建前端内容。因此,我们需要使用JavaScript来实现区块的字符串翻译。
5.1 使用wp.i18n
库
wp.i18n
库是WordPress提供的JavaScript国际化库,它提供了类似于wp_i18n
库的功能。
以下是一些核心函数:
函数名 | 描述 |
---|---|
__() |
返回翻译后的字符串。 |
_x() |
返回翻译后的字符串,并允许指定上下文(context)。 |
_n() |
返回翻译后的单数或复数形式的字符串。 |
sprintf() |
格式化字符串。 |
setLocaleData() |
设置本地数据。 |
5.2 在JavaScript代码中使用wp.i18n
函数
import { __, _x, _n, sprintf, setLocaleData } from '@wordpress/i18n';
// 翻译字符串
const translatedString = __( 'Hello, world!', 'my-block' );
// 使用上下文
const translatedPostNoun = _x( 'Post', 'noun', 'my-block' );
const translatedPostVerb = _x( 'Post', 'verb', 'my-block' );
// 单数/复数形式
const commentCount = 5;
const translatedComments = sprintf(
_n( '%s comment', '%s comments', commentCount, 'my-block' ),
commentCount
);
// 在区块的编辑和保存函数中使用翻译后的字符串
const Edit = ( props ) => {
return (
<div>
<p>{ translatedString }</p>
<p>{ translatedPostNoun }</p>
<p>{ translatedComments }</p>
</div>
);
};
const Save = ( props ) => {
return (
<div>
<p>{ translatedString }</p>
<p>{ translatedPostNoun }</p>
<p>{ translatedComments }</p>
</div>
);
};
5.3 设置本地数据
在使用wp.i18n
函数之前,需要设置本地数据,以便wp.i18n
库能够找到翻译。这通常在区块的入口文件中完成。
import { setLocaleData } from '@wordpress/i18n';
// 设置本地数据
setLocaleData( { '': {} }, 'my-block' );
'my-block'
是文本域。
5.4 生成JavaScript的MO文件
WordPress的@wordpress/i18n
库使用JSON格式的MO文件。你需要使用工具将PO文件转换为JSON格式的MO文件。可以使用wp-pot
和@wordpress/babel-plugin-makepot
工具。
-
安装依赖:
npm install -g wp-pot @wordpress/babel-plugin-makepot npm install @babel/core @babel/cli --save-dev
-
配置 Babel: 在你的项目根目录下创建一个
.babelrc
文件,并添加以下内容:{ "plugins": [ "@wordpress/babel-plugin-makepot" ] }
-
运行 Babel:
npx babel src/index.js --out-file temp.js
将
src/index.js
替换为你的区块入口文件。 运行完成后,会生成一个包含翻译信息的temp.js
文件。 -
创建POT文件: 使用
wp pot
命令将翻译信息提取到 POT 文件:wp pot temp.js languages/my-block.pot --domain=my-block
temp.js
: Babel转换后生成的临时文件。languages/my-block.pot
: 生成的 POT 文件路径和名称。--domain=my-block
: 文本域。
-
创建和翻译PO文件: 像之前一样,复制 POT 文件并重命名为 PO 文件 (例如
languages/my-block-de_DE.po
),然后使用 Poedit 或其他翻译工具进行翻译。 -
将PO文件转换为JSON MO文件: 使用
@wordpress/i18n-calypso
(需要安装) 或者其他在线工具,将 PO 文件转换为 JSON 格式的 MO 文件。 将JSON 格式的文件命名为my-block-de_DE.json
. -
加载JSON MO文件: 在你的区块 JavaScript 代码中,加载 JSON MO 文件,并使用
setLocaleData
函数:import { setLocaleData } from '@wordpress/i18n'; import translations from './languages/my-block-de_DE.json'; // 替换为你的 JSON MO 文件路径 setLocaleData( translations, 'my-block' );
5.5 WordPress过滤器 load_script_translations
WordPress 5.0+ 引入了 load_script_translations
过滤器,允许你在运行时加载 JavaScript 翻译文件,而无需手动导入 JSON 文件。
-
确保你的插件或主题正确加载了文本域: 参考之前加载文本域的 PHP 代码。
-
将 JavaScript 翻译文件 (JSON MO) 放在
languages
目录中: 文件命名格式必须是{textdomain}-{locale}-{handle}.json
。{textdomain}
: 你的文本域 (例如my-block
)。{locale}
: 语言环境 (例如de_DE
)。{handle}
: 你的 JavaScript 脚本的 handle。 这个handle是在你用wp_register_script()
注册脚本时使用的。
例如:
my-block-de_DE-my-block-script.json
-
注册和enqueue你的 JavaScript 文件:
<?php function my_block_register_block() { wp_register_script( 'my-block-script', // Handle plugins_url( 'build/index.js', __FILE__ ), array( 'wp-i18n', 'wp-blocks', 'wp-editor', 'wp-components' ), filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' ) ); register_block_type( 'my-block/my-block', array( 'editor_script' => 'my-block-script', // 与 wp_register_script 中的 handle 匹配 ) ); } add_action( 'init', 'my_block_register_block' ); ?>
关键在于确保
wp_register_script()
函数中的 handle (这里是my-block-script
) 与 JSON MO 文件名中的 handle 部分匹配。 WordPress 会自动加载正确的翻译文件。
5.6 结合PHP和JavaScript翻译
有些情况下,你可能需要在PHP中生成一些动态的字符串,然后在JavaScript中使用。例如,你可能需要从数据库中读取一些数据,并将它们传递给JavaScript。
<?php
// 在PHP中生成字符串
$dynamic_string = sprintf(
__( 'You have %s new messages.', 'my-block' ),
'<span class="message-count">' . get_user_meta( get_current_user_id(), 'unread_messages', true ) . '</span>'
);
// 将字符串传递给JavaScript
wp_localize_script( 'my-block-script', 'myBlockData', array(
'dynamicString' => $dynamic_string,
) );
?>
然后在JavaScript中,你可以使用myBlockData.dynamicString
来访问该字符串。
import { __ } from '@wordpress/i18n';
const Edit = ( props ) => {
return (
<div>
<p dangerouslySetInnerHTML={{ __html: myBlockData.dynamicString }}></p>
</div>
);
};
注意: 使用dangerouslySetInnerHTML
时要小心,确保传递的字符串是安全的,以防止XSS攻击。
6. 最佳实践
- 始终使用文本域: 为每个主题和插件指定一个唯一的文本域,以避免冲突。
- 保持PO/MO文件最新: 定期更新PO/MO文件,以确保所有字符串都得到翻译。
- 使用版本控制: 将PO/MO文件纳入版本控制,以便跟踪更改和协作。
- 测试多语言支持: 在不同的语言环境下测试你的主题和插件,以确保一切正常工作。
- 考虑性能: 避免在前端加载不必要的翻译文件。
- 代码审查: 进行代码审查,确保所有需要翻译的字符串都使用了
wp_i18n
函数。 - 自动化: 使用工具和脚本自动化PO/MO文件的生成和管理,例如使用Grunt或Gulp任务。
7. 语言切换功能
WordPress本身不提供内置的语言切换功能。你需要使用插件或编写自定义代码来实现语言切换功能。一些流行的多语言插件包括:
- Polylang: 一个免费的多语言插件,可以让你轻松地创建和管理多语言网站。
- WPML: 一个商业多语言插件,提供了更高级的功能和支持。
总结:让网站拥抱世界
通过深入理解wp_i18n
库和PO/MO文件的使用,我们可以轻松地为WordPress主题和插件添加多语言支持,包括前端区块的翻译。务必遵循最佳实践,并使用适当的工具来简化翻译流程。