分析 WordPress `_x()` 函数的源码:如何处理带有上下文的翻译。

各位观众老爷们,大家好!今天咱们来聊聊WordPress里一个相当重要,但又容易被忽视的小家伙——_x() 函数。这玩意儿专门负责处理那些“上下文很复杂”的翻译,让你的网站在不同语境下都能说人话。

开场白:为什么我们需要上下文?

想象一下,英文单词 "Post" 可以是“发布文章”,也可以是“邮局”。 如果直接翻译成中文,在不同的语境下就会闹笑话。WordPress 为了解决这个问题,就引入了上下文这个概念。_x() 函数就是用来处理这种“一词多义”的情况。

_x() 函数的庐山真面目

_x() 函数的定义如下:

<?php
/**
 * Retrieve translated string with gettext context.
 *
 * Quite a few times, the exact same word or phrase can have a different
 * meaning depending on context. The function _x() allows specifying the
 * context.
 *
 * @since 2.8.0
 *
 * @param string $text       Text to translate.
 * @param string $context    Context information for the translators.
 * @param string $domain     Optional. Text domain. Unique identifier for retrieving translated strings.
 *                           Default 'default'.
 * @return string Translated text.
 */
function _x( $text, $context, $domain = 'default' ) {
    return translate_with_gettext_context( $text, $context, $domain );
}

参数说明:

  • $text: 需要翻译的文本字符串。
  • $context: 上下文信息,告诉翻译人员这个字符串在什么语境下使用。
  • $domain: 文本域,用于区分不同的插件或主题,默认为 ‘default’。

简单来说,_x() 函数就是把 $text$context 一起打包,然后交给 WordPress 的翻译系统去寻找对应的翻译。

深入源码:translate_with_gettext_context() 函数

_x() 函数实际上只是个“马甲”,真正干活的是 translate_with_gettext_context() 函数。咱们来看看它的源码(简化版):

<?php
/**
 * Retrieve the translation of $text with gettext context.
 *
 * @since 3.0.0
 * @access private
 *
 * @param string $text    Text to translate.
 * @param string $context Context information for the translators.
 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
 *                          Default 'default'.
 * @return string Translated text.
 */
function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
    global $l10n;

    if ( isset( $l10n[ $domain ] ) && is_a( $l10n[ $domain ], 'MO' ) ) {
        return $l10n[ $domain ]->translate( $text, $context );
    } else {
        return $text; // No translation found.
    }
}

这个函数做了以下几件事:

  1. 全局变量 $l10n 它从全局变量 $l10n 中获取对应 $domain 的 MO 对象。 $l10n 是 WordPress 存放翻译数据的“仓库”。
  2. MO 对象: 如果找到了对应的 MO 对象,并且这个对象确实是个 MO 对象,那么就调用 MO 对象的 translate() 方法进行翻译。
  3. translate() 方法: 如果没有找到对应的 MO 对象,就直接返回原始的 $text,不做任何翻译。

MO 对象:翻译的幕后英雄

MO 对象 (Machine Object) 是 WordPress 存储翻译数据的核心。它是一个 PHP 对象,包含了大量的翻译条目,每个条目都包含了原始文本、上下文和对应的翻译文本。

咱们可以把 MO 对象想象成一本“翻译词典”,translate() 方法就是在这本词典里查找对应的翻译。

MO 对象的 translate() 方法

MO 对象的 translate() 方法负责根据 $text$context 查找对应的翻译。它的简化版逻辑如下:

<?php
class MO {
    // ... 省略其他代码 ...

    function translate( $text, $context ) {
        $key = $this->make_key( $text, $context ); // 构造唯一的键
        if ( isset( $this->entries[ $key ] ) ) {
            return $this->entries[ $key ]; // 找到了翻译
        } else {
            return $text; // 没有找到翻译
        }
    }

    function make_key( $text, $context ) {
        return md5( $context . "4" . $text ); // 将上下文和文本合并成唯一的键
    }
}

这个方法的核心在于:

  1. 构造唯一的键: 它使用 make_key() 方法将 $text$context 合并成一个唯一的键。这个键是用来在 $this->entries 数组中查找翻译的。
  2. 查找翻译: 如果找到了对应的翻译,就直接返回。否则,返回原始的 $text

make_key() 函数使用 md5() 哈希算法,确保键的唯一性。4 是一个特殊字符,用于分隔上下文和文本,防止上下文和文本中出现相同的字符导致键冲突。

举个栗子:_x( 'Post', 'noun', 'my-theme' )

假设我们在主题 ‘my-theme’ 中使用了 _x( 'Post', 'noun', 'my-theme' )

  1. _x() 函数会将 ‘Post’ 和 ‘noun’ 传递给 translate_with_gettext_context() 函数。
  2. translate_with_gettext_context() 函数会从 $l10n['my-theme'] 中获取 MO 对象。
  3. MO 对象的 translate() 方法会使用 make_key() 方法构造键:md5( 'noun' . "4" . 'Post' )
  4. MO 对象会在 $this->entries 数组中查找这个键对应的翻译。
  5. 如果找到了,比如找到了 ‘文章’ 这个翻译,那么就返回 ‘文章’。
  6. 如果没有找到,就返回原始的 ‘Post’。

使用场景:那些年,我们一起踩过的坑

以下是一些使用 _x() 函数的常见场景:

  • 动词和名词的区分: 例如,’Edit’ 可以是“编辑”(动词)也可以是“编辑框”(名词)。
  • 不同模块的区分: 例如,’Menu’ 可以是网站的“菜单”,也可以是后台的“菜单管理”。
  • 单复数的区分: 虽然 WordPress 还有 _n() 函数处理单复数,但在某些特殊情况下,_x() 也可以用来区分。

代码示例:

<?php
// 动词
echo _x( 'Edit', 'verb', 'my-theme' );

// 名词
echo _x( 'Edit', 'noun', 'my-theme' );

// 菜单
echo _x( 'Menu', 'admin menu', 'my-theme' );

// 菜单管理
echo _x( 'Menu', 'website menu', 'my-theme' );

表格总结:_x() 函数的关键点

关键点 说明
函数作用 处理带有上下文的翻译
函数参数 $text (文本), $context (上下文), $domain (文本域)
核心函数 translate_with_gettext_context()
翻译数据存储 MO 对象 (Machine Object)
上下文的作用 区分相同文本在不同语境下的含义
键的生成方式 md5( $context . "4" . $text )
使用场景 动词/名词区分,不同模块区分,单复数区分 (特殊情况)

注意事项:避免踩坑的正确姿势

  • 上下文要明确: 上下文描述一定要清晰明了,让翻译人员能够准确理解你的意图。
  • 保持一致性: 在整个项目中,对于同一个词,要使用相同的上下文。
  • 文本域要正确: 确保使用了正确的文本域,避免翻译冲突。
  • PO/MO 文件要更新: 每次修改代码后,都要更新 PO/MO 文件,确保翻译是最新的。可以使用 wp i18n make-pot 命令生成 POT 文件,然后使用 Poedit 等工具进行翻译。

高级用法:自定义上下文

除了使用预定义的上下文,我们还可以自定义上下文,以满足更复杂的需求。例如,我们可以根据用户的角色来定义上下文:

<?php
$user = wp_get_current_user();
if ( in_array( 'administrator', (array) $user->roles ) ) {
    $context = 'administrator';
} else {
    $context = 'subscriber';
}

echo _x( 'Welcome', $context, 'my-theme' );

这样,管理员和订阅者看到的欢迎语就可以不同了。

与其它翻译函数的比较:__(), _e(), _n()

WordPress 提供了多个翻译函数,它们各有不同的用途:

  • __(): 最基本的翻译函数,只接受文本参数,没有上下文。
  • _e(): 与 __() 类似,但直接输出翻译后的文本。
  • _n(): 处理单复数情况的翻译。
  • _x(): 处理带有上下文的翻译。

选择哪个函数取决于你的具体需求。如果不需要上下文,就用 __()_e()。如果需要处理单复数,就用 _n()。如果需要处理带有上下文的翻译,就用 _x()

调试技巧:如何排查翻译问题

如果你的网站翻译有问题,可以尝试以下方法进行排查:

  1. 检查 PO/MO 文件: 确保 PO/MO 文件存在,并且包含了正确的翻译。
  2. 检查文本域: 确保代码中使用的文本域与 PO/MO 文件中的文本域一致。
  3. 清除缓存: 清除 WordPress 缓存和浏览器缓存,确保显示的是最新的翻译。
  4. 启用 WP_DEBUG 模式: 启用 WP_DEBUG 模式,可以显示一些错误信息,帮助你找到问题所在。
  5. 使用 Loco Translate 插件: Loco Translate 是一个强大的 WordPress 翻译插件,可以帮助你管理 PO/MO 文件,查找缺失的翻译,等等。

总结:_x() 函数的价值

_x() 函数是 WordPress 国际化 (i18n) 和本地化 (l10n) 的重要组成部分。它可以帮助我们创建多语言网站,让不同语言的用户都能流畅地使用我们的网站。虽然它看起来很简单,但却隐藏着许多细节。理解 _x() 函数的原理,可以帮助我们更好地利用它,避免踩坑,打造更专业的 WordPress 网站。

结尾:希望今天的讲座对大家有所帮助!

希望通过今天的讲解,大家对 _x() 函数有了更深入的理解。记住,上下文很重要!在编写 WordPress 代码时,一定要注意使用正确的翻译函数,确保你的网站能够说人话。下次再见!

发表回复

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