WordPress国际化:如何利用`wp_i18n`和`PO/MO`文件实现多语言支持,并处理前端区块的字符串翻译?

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文件,通常需要以下步骤:

  1. 在代码中使用wp_i18n函数: 将所有需要翻译的字符串包裹在__(), _e(), _x(), _ex(), _n(), _nx()等函数中。
  2. 使用工具提取字符串: 使用工具(例如Poedit、WP-CLI)扫描代码,提取所有需要翻译的字符串,并生成一个.pot文件(PO Template)。.pot文件本质上是一个空的PO文件,只包含原始字符串。
  3. 创建PO文件: 将.pot文件复制一份,并将其重命名为对应语言的PO文件。例如,对于德语,可以命名为de_DE.po
  4. 翻译PO文件: 使用Poedit或其他翻译工具打开PO文件,并逐个翻译原始字符串。
  5. 生成MO文件: Poedit会自动根据PO文件生成MO文件。MO文件与PO文件同名,但扩展名为.mo

3.1 使用Poedit创建和翻译PO/MO文件

Poedit是一个流行的PO/MO文件编辑工具,它提供了友好的界面和强大的功能。

  1. 下载和安装Poedit: 从Poedit官网下载并安装最新版本的Poedit。
  2. 创建新目录项: 打开Poedit,选择“文件”->“新建目录项”。
  3. 设置目录项属性:
    • 项目名称和版本: 填写项目的名称和版本号。
    • 基本设置: 设置字符集为UTF-8,换行符为Unix。
    • 源路径: 指定包含源代码的目录。Poedit会扫描这些目录以提取字符串。
    • 关键词: 指定要使用的wp_i18n函数。Poedit默认会识别__(), _e(), _x(), _ex(), _n(), _nx()等函数。
    • 源文本: 设置源文本的语言,通常为英语。
  4. 提取字符串: 点击“更新目录项”按钮,Poedit会扫描指定的源路径,提取所有需要翻译的字符串。
  5. 翻译字符串: 逐个选择需要翻译的字符串,并在下方的翻译框中输入翻译后的文本。
  6. 保存PO文件: 点击“文件”->“保存”,Poedit会自动生成PO和MO文件。

3.2 使用WP-CLI创建PO/MO文件

WP-CLI是WordPress的命令行工具,也可以用于创建和管理PO/MO文件。

  1. 安装WP-CLI: 如果尚未安装WP-CLI,请按照官方文档进行安装。
  2. 生成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)。
  3. 创建PO文件: 使用wp i18n create-translation命令创建PO文件。

    wp i18n create-translation my-theme de_DE
    • my-theme: 指定文本域。
    • de_DE: 指定语言代码。
  4. 翻译PO文件: 使用Poedit或其他翻译工具打开PO文件,并逐个翻译原始字符串。
  5. 生成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()函数接受四个参数:

  1. 单数形式的字符串。
  2. 复数形式的字符串。
  3. 用于确定单数/复数的数字。
  4. 文本域。

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工具。

  1. 安装依赖:

    npm install -g wp-pot @wordpress/babel-plugin-makepot
    npm install @babel/core @babel/cli --save-dev
  2. 配置 Babel: 在你的项目根目录下创建一个 .babelrc 文件,并添加以下内容:

    {
      "plugins": [ "@wordpress/babel-plugin-makepot" ]
    }
  3. 运行 Babel:

    npx babel src/index.js --out-file temp.js

    src/index.js 替换为你的区块入口文件。 运行完成后,会生成一个包含翻译信息的 temp.js 文件。

  4. 创建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: 文本域。
  5. 创建和翻译PO文件: 像之前一样,复制 POT 文件并重命名为 PO 文件 (例如 languages/my-block-de_DE.po),然后使用 Poedit 或其他翻译工具进行翻译。

  6. 将PO文件转换为JSON MO文件: 使用@wordpress/i18n-calypso (需要安装) 或者其他在线工具,将 PO 文件转换为 JSON 格式的 MO 文件。 将JSON 格式的文件命名为 my-block-de_DE.json.

  7. 加载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 文件。

  1. 确保你的插件或主题正确加载了文本域: 参考之前加载文本域的 PHP 代码。

  2. 将 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

  3. 注册和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主题和插件添加多语言支持,包括前端区块的翻译。务必遵循最佳实践,并使用适当的工具来简化翻译流程。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注