分析 WordPress `translate()` 函数的源码:WordPress 的核心翻译函数。

早上好,各位代码农!今天咱们要聊聊WordPress的“翻译官”——translate() 函数。这可是WordPress国际化(i18n)和本地化(l10n)的幕后英雄,让你的网站能说各国语言,吸引全球粉丝!准备好迎接一场源码解剖之旅了吗?

一、translate() 函数:你的网站多语种的秘密武器

translate() 函数,顾名思义,就是用来翻译文本的。但它不仅仅是简单地替换单词,它还涉及到文本域(text domain)、复数形式处理等一系列复杂机制。简单来说,它的主要作用是:

  • 查找翻译:根据提供的文本、文本域和可选的上下文,在已加载的翻译文件中查找对应的翻译。
  • 返回翻译后的文本:如果找到翻译,则返回翻译后的文本;否则,返回原始文本。
  • 支持复数形式:对于不同数量的物品,返回不同的翻译文本(例如,"1 comment" vs "2 comments")。

二、translate() 函数的语法结构

先来认识一下translate() 函数的基本语法:

<?php
/**
 * 翻译文本。
 *
 * @since 2.1.0
 *
 * @param string $text       要翻译的文本。
 * @param string $domain     文本域。应该与gettext函数加载的mo文件匹配。
 * @param string $context    (可选)文本的上下文,用于区分相同的字符串的不同含义。
 * @return string 翻译后的文本。
 */
function translate( string $text, string $domain = 'default', string $context = null ): string {
    global $l10n;

    // 如果没有可用的翻译或者文本为空,则返回原始文本。
    if ( empty( $l10n[ $domain ] ) || empty( $text ) ) {
        return $text;
    }

    if ( isset( $context ) && is_scalar( $context ) ) {
        $key = _get_context_key( $text, $context );
    } else {
        $key = $text;
    }

    // 查找翻译。
    if ( isset( $l10n[ $domain ]->translations[ $key ] ) ) {
        return $l10n[ $domain ]->translations[ $key ];
    }

    // 如果没有找到翻译,则返回原始文本。
    return $text;
}

参数说明:

  • $text (string): 需要翻译的原始文本。这是你的“原文”,等待着被翻译成目标语言。
  • $domain (string): 文本域。它就像一个分类标签,用于区分不同的翻译文件。 默认值是 'default'。 不同的插件或主题可以使用不同的文本域,避免翻译冲突。
  • $context (string, optional): 上下文。这是一个可选参数,用于区分相同的文本在不同语境下的含义。例如,"Post" 可以是 "发布文章" 或 "邮局",上下文可以帮助翻译者选择正确的翻译。默认值为 null

返回值:

  • (string): 翻译后的文本。如果找到了翻译,返回翻译后的文本;否则,返回原始文本。

三、translate() 函数源码剖析

现在,让我们深入 translate() 函数的源码,看看它是如何工作的:

  1. 全局变量 $l10n:

    global $l10n;

    $l10n 是一个全局数组,存储了已加载的翻译数据。它的结构大概是这样的:

    $l10n = [
        'default' => (object) [
            'domain'       => 'default',
            'headers'      => [ /* ... */ ],
            'entries'      => [ /* ... */ ],
            'translations' => [
                'Original Text' => 'Translated Text',
                'Another Text'  => 'Another Translated Text',
            ],
        ],
        'my-plugin' => (object) [
            'domain'       => 'my-plugin',
            'headers'      => [ /* ... */ ],
            'entries'      => [ /* ... */ ],
            'translations' => [
                'Hello' => '你好',
                'World' => '世界',
            ],
        ],
        // ... 更多文本域
    ];

    每个文本域(例如 'default''my-plugin')都对应一个对象,其中 'translations' 属性存储了原文和译文的键值对。

  2. 检查 $l10n$text:

    if ( empty( $l10n[ $domain ] ) || empty( $text ) ) {
        return $text;
    }

    首先,函数会检查 $l10n 数组中是否存在指定的文本域,以及要翻译的文本是否为空。如果文本域不存在,或者文本为空,则直接返回原始文本,不做任何翻译。

  3. 生成翻译键 $key:

    if ( isset( $context ) && is_scalar( $context ) ) {
        $key = _get_context_key( $text, $context );
    } else {
        $key = $text;
    }

    这里,函数会根据是否提供了上下文($context)来生成一个唯一的翻译键。如果提供了上下文,则使用 _get_context_key() 函数生成键;否则,直接使用原始文本作为键。_get_context_key() 函数的作用是将文本和上下文组合成一个唯一的字符串,以区分相同的文本在不同语境下的含义。

    _get_context_key() 函数的源码如下:

    <?php
    /**
     * 为带上下文的字符串创建一个唯一的键。
     *
     * @ignore
     * @since 3.0.0
     *
     * @param string $string  原始字符串。
     * @param string $context 字符串的上下文。
     * @return string 带上下文的字符串的唯一键。
     */
    function _get_context_key( string $string, string $context ): string {
        return $string . "x04" . $context;
    }

    它简单地将原始字符串和上下文用一个特殊的字符 x04 连接起来。这个字符不太可能出现在正常的文本中,所以可以保证生成的键是唯一的。

  4. 查找翻译:

    if ( isset( $l10n[ $domain ]->translations[ $key ] ) ) {
        return $l10n[ $domain ]->translations[ $key ];
    }

    接下来,函数会在 $l10n 数组中查找与 $key 对应的翻译。如果找到了翻译,则返回翻译后的文本。

  5. 返回原始文本:

    return $text;

    如果 $l10n 数组中没有找到与 $key 对应的翻译,则返回原始文本。

四、translate() 函数的实际应用

translate() 函数通常与其他 gettext 函数(例如 __(), _e(), _x(), _ex(), _n(), _nx())一起使用,来实现完整的国际化和本地化功能。

  • __(): 返回翻译后的文本。
  • _e(): 输出翻译后的文本。
  • _x(): 返回带上下文的翻译后的文本。
  • _ex(): 输出带上下文的翻译后的文本。
  • _n(): 返回单复数形式的翻译后的文本。
  • _nx(): 返回带上下文的单复数形式的翻译后的文本。

例如:

<?php
// 使用 __() 函数翻译文本
$translated_text = __( 'Hello World', 'my-plugin' );
echo $translated_text; // 输出:你好世界 (如果已加载对应的翻译)

// 使用 _e() 函数输出翻译后的文本
_e( 'Welcome', 'my-theme' ); // 输出:欢迎 (如果已加载对应的翻译)

// 使用 _x() 函数翻译带上下文的文本
$translated_text = _x( 'Post', 'noun', 'my-plugin' ); // 'Post' 作为名词
echo $translated_text; // 输出:文章 (如果已加载对应的翻译)

$translated_text = _x( 'Post', 'verb', 'my-plugin' ); // 'Post' 作为动词
echo $translated_text; // 输出:发布 (如果已加载对应的翻译)

// 使用 _n() 函数翻译单复数形式的文本
$comment_count = 1;
$translated_text = sprintf( _n( '%s comment', '%s comments', $comment_count, 'my-theme' ), $comment_count );
echo $translated_text; // 输出:1 comment (如果已加载对应的翻译)

$comment_count = 5;
$translated_text = sprintf( _n( '%s comment', '%s comments', $comment_count, 'my-theme' ), $comment_count );
echo $translated_text; // 输出:5 comments (如果已加载对应的翻译)

// 使用 _nx() 函数翻译带上下文的单复数形式的文本
$book_count = 1;
$translated_text = sprintf( _nx( '%s book', '%s books', $book_count, 'book', 'my-plugin' ), $book_count );
echo $translated_text; // 输出:1 book (如果已加载对应的翻译)

$book_count = 10;
$translated_text = sprintf( _nx( '%s book', '%s books', $book_count, 'book', 'my-plugin' ), $book_count );
echo $translated_text; // 输出:10 books (如果已加载对应的翻译)
?>

五、文本域(Text Domain)的重要性

文本域是 WordPress 国际化的关键概念。它是一个唯一的标识符,用于区分不同的翻译文件。每个插件或主题都应该使用自己的文本域,以避免翻译冲突。

  • 命名规范: 文本域通常使用插件或主题的 slug(简短的、URL 友好的名称)。例如,如果你的插件的名称是 "My Awesome Plugin",那么你的文本域可以是 "my-awesome-plugin"。
  • 加载翻译文件: 你需要使用 load_plugin_textdomain()load_theme_textdomain() 函数来加载你的翻译文件。这些函数会将翻译数据加载到 $l10n 全局数组中。

示例:

<?php
/**
 * 加载插件的文本域。
 */
function my_awesome_plugin_load_textdomain() {
    load_plugin_textdomain(
        'my-awesome-plugin',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages/'
    );
}
add_action( 'plugins_loaded', 'my_awesome_plugin_load_textdomain' );
?>

在这个例子中,load_plugin_textdomain() 函数加载了名为 "my-awesome-plugin" 的文本域的翻译文件。翻译文件位于插件目录下的 languages 文件夹中。

六、复数形式的处理

不同的语言对复数形式的处理方式不同。例如,英语只有单数和复数两种形式,而某些斯拉夫语言有多种复数形式。WordPress 使用 gettext 库来处理复数形式。

_n()_nx() 函数可以根据数量返回不同的翻译文本。它们接受三个参数:

  • $single: 单数形式的文本。
  • $plural: 复数形式的文本。
  • $number: 数量。
  • $domain: 文本域
  • $context: 上下文 (仅 _nx() 函数)

WordPress 会根据当前的语言环境和数量,自动选择正确的复数形式。

七、性能考量

虽然 translate() 函数是 WordPress 国际化的核心,但过度使用它可能会影响网站的性能。每次调用 translate() 函数,都需要在 $l10n 数组中查找翻译。如果你的网站有很多文本需要翻译,这可能会导致性能瓶颈。

以下是一些优化建议:

  • 缓存翻译: 可以使用对象缓存或瞬态缓存来缓存翻译后的文本,避免重复查找。
  • 避免在循环中调用 translate() 函数: 尽量在循环外部获取翻译后的文本,然后在循环中使用。
  • 使用静态分析工具: 可以使用静态分析工具来检查你的代码,找出潜在的性能问题。

八、调试技巧

如果你的网站的翻译不正确,或者某些文本没有被翻译,可以使用以下调试技巧:

  • 检查文本域: 确保你使用的文本域是正确的,并且与你的翻译文件匹配。
  • 检查翻译文件: 确保你的翻译文件已正确加载,并且包含所有需要翻译的文本。
  • 检查缓存: 清除 WordPress 的缓存,以及浏览器缓存。
  • 使用调试工具: 可以使用 WordPress 调试模式或插件(例如 "Loco Translate")来帮助你找到问题。

九、总结

translate() 函数是 WordPress 国际化的基石。理解它的工作原理,可以帮助你更好地构建多语种网站,吸引全球用户。记住,良好的国际化实践不仅可以提高用户体验,还可以提升你的网站的 SEO 排名。

希望今天的讲解对大家有所帮助! 现在,谁想分享一下自己在使用 translate() 函数时遇到的有趣经历或难题? 欢迎提问和讨论!

发表回复

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