深入理解 WordPress `__(‘string’, ‘text-domain’)` 函数的源码:如何获取翻译后的字符串。

咳咳,各位观众老爷们,晚上好!今天咱们来聊聊WordPress里一个看似简单,实则暗藏玄机的函数:__('string', 'text-domain')。别看它只有短短一行,却是WordPress多语言支持的核心部件。今天咱们就把它扒个精光,看看它到底是怎么把英语变成各国语言的。

第一幕:初识__()函数,Hello World的全球之旅

首先,让我们来看一个最简单的例子:

<?php
echo __('Hello World!', 'my-theme');
?>

这段代码的意思很简单:输出 "Hello World!" 这个字符串。但是,重点来了,这个 "Hello World!" 并不是直接输出的,而是先经过了 __() 函数的处理。__() 函数会根据当前 WordPress 的语言设置,以及 my-theme 这个文本域,去查找对应的翻译,然后输出翻译后的字符串。

如果当前语言是中文,并且 my-theme 文本域下有 "Hello World!" 的中文翻译,那么这段代码就会输出 "你好,世界!"。如果没有找到翻译,那么就会原样输出 "Hello World!"。

第二幕:__()函数的真面目:代码解剖

__() 函数实际上是 translate() 函数的别名。也就是说,__('string', 'text-domain') 等同于 translate('string', 'text-domain')

那么,translate() 函数又是什么呢?让我们来看看 WordPress 的源码(位于 wp-includes/l10n.php 文件中):

function translate( $text, $domain = 'default' ) {
    global $l10n;

    if ( isset( $l10n[ $domain ] ) ) {
        return $l10n[ $domain ]->translate( $text );
    }

    return $text;
}

这段代码看起来是不是很简洁?其实它干的事情也不复杂:

  1. 全局变量 $l10n 这个变量保存了所有已加载的文本域的翻译信息。它是一个数组,key 是文本域的名字 (例如 ‘my-theme’,’default’),value 是一个 Translation_Entry 对象,这个对象负责实际的翻译工作。
  2. 检查文本域是否存在: isset( $l10n[ $domain ] ) 这行代码会检查 $l10n 数组中是否存在以 $domain 为 key 的元素。如果不存在,说明这个文本域还没有被加载,直接返回原始的 $text
  3. 调用 Translation_Entry 对象的 translate() 方法: 如果文本域存在,那么就调用 $l10n[ $domain ] 对象的 translate() 方法,将 $text 传递给它进行翻译。
  4. 返回翻译后的字符串: Translation_Entry 对象的 translate() 方法会返回翻译后的字符串。

第三幕:Translation_Entry 对象的秘密:PO/MO 文件

那么,Translation_Entry 对象又是如何进行翻译的呢?这就涉及到 WordPress 的翻译机制了。WordPress 使用 PO 和 MO 文件来存储翻译数据。

  • PO (Portable Object) 文件: PO 文件是文本文件,包含原始字符串和对应的翻译。它通常由开发者创建和维护。
  • MO (Machine Object) 文件: MO 文件是 PO 文件的二进制版本,用于提高翻译查找的效率。它是 WordPress 实际使用的翻译文件。

Translation_Entry 对象会加载 MO 文件,并将原始字符串与 MO 文件中的翻译进行匹配,如果找到匹配的翻译,就返回翻译后的字符串。

让我们来看一个简单的 PO 文件例子:

msgid "Hello World!"
msgstr "你好,世界!"

msgid "Welcome to my website!"
msgstr "欢迎来到我的网站!"

在这个 PO 文件中,msgid 表示原始字符串,msgstr 表示对应的翻译。

第四幕:文本域 (Text Domain) 的作用:翻译的隔离区

文本域 (Text Domain) 在 WordPress 多语言支持中扮演着非常重要的角色。它可以将不同主题或插件的翻译隔离开来,避免冲突。

例如,你的主题使用了 my-theme 文本域,而你的插件使用了 my-plugin 文本域。即使它们都包含 "Hello World!" 这个字符串,它们的翻译也不会互相干扰。

__() 函数中,文本域是第二个参数:__('string', 'text-domain')

第五幕:加载翻译文件:load_theme_textdomain()load_plugin_textdomain()

在 WordPress 中,我们需要手动加载主题或插件的翻译文件。这通常通过 load_theme_textdomain()load_plugin_textdomain() 函数来实现。

  • load_theme_textdomain() 用于加载主题的翻译文件。通常在主题的 functions.php 文件中使用。
  • load_plugin_textdomain() 用于加载插件的翻译文件。通常在插件的主文件中使用。

例如,在主题的 functions.php 文件中,你可以这样加载 my-theme 文本域的翻译文件:

<?php
function my_theme_setup() {
    load_theme_textdomain( 'my-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_theme_setup' );
?>

这段代码会在主题初始化完成后,加载 my-theme 文本域的翻译文件。翻译文件位于主题目录下的 languages 文件夹中,文件名通常是 my-theme-zh_CN.mo (中文简体)。

在插件的主文件中,你可以这样加载 my-plugin 文本域的翻译文件:

<?php
/**
 * Plugin Name: My Plugin
 */

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-plugin 文本域的翻译文件。翻译文件位于插件目录下的 languages 文件夹中,文件名通常是 my-plugin-zh_CN.mo (中文简体)。

第六幕:更高级的用法:复数形式和上下文

__() 函数只是最基本的翻译函数。WordPress 还提供了其他一些更高级的翻译函数,用于处理复数形式和上下文。

  • _n() 用于处理复数形式。它接受两个字符串参数:单数形式和复数形式。WordPress 会根据数量选择正确的形式。

    <?php
    $count = 5;
    echo sprintf( _n( '%d comment', '%d comments', $count, 'my-theme' ), $count );
    ?>

    这段代码会根据 $count 的值输出不同的字符串。如果 $count 是 1,那么输出 "1 comment"。如果 $count 大于 1,那么输出 "5 comments"。

  • _x() 用于处理具有相同字符串但不同含义的翻译。它接受一个字符串参数和一个上下文参数。

    <?php
    echo _x( 'Post', 'noun', 'my-theme' ); // 输出 "文章"
    echo _x( 'Post', 'verb', 'my-theme' ); // 输出 "发布"
    ?>

    在这个例子中,"Post" 既可以作为名词 (文章),也可以作为动词 (发布)。_x() 函数允许我们为不同的上下文提供不同的翻译。

第七幕:总结:__() 函数的生命周期

让我们来总结一下 __() 函数的生命周期:

  1. 开发者在代码中使用 __() 函数: 例如 __('Hello World!', 'my-theme')
  2. WordPress 加载主题或插件的翻译文件: 通过 load_theme_textdomain()load_plugin_textdomain() 函数。
  3. 当代码执行到 __() 函数时,translate() 函数被调用。
  4. translate() 函数在全局变量 $l10n 中查找对应的文本域。
  5. 如果找到文本域,translate() 函数调用 Translation_Entry 对象的 translate() 方法。
  6. Translation_Entry 对象在 MO 文件中查找对应的翻译。
  7. 如果找到翻译,translate() 函数返回翻译后的字符串。否则,返回原始字符串。
  8. 最终,翻译后的字符串被输出。

为了更清晰地理解整个流程,我们可以用表格来表示:

步骤 函数/对象 作用 数据来源
1 __() 标记需要翻译的字符串 开发者代码中的字符串和文本域
2 load_theme_textdomain()/load_plugin_textdomain() 加载 MO 文件到 $l10n 全局变量 主题/插件目录下的 MO 文件
3 translate() $l10n 中查找翻译,并调用 Translation_Entry $l10n 全局变量,原始字符串和文本域
4 Translation_Entry 实际进行翻译查找和替换 加载的 MO 文件
5 返回翻译后的字符串或原始字符串 最终输出的字符串

第八幕:彩蛋:一些需要注意的点

  • 编码问题: 确保你的 PO 和 MO 文件使用 UTF-8 编码。
  • 缓存问题: WordPress 会缓存翻译数据。如果你修改了 PO 或 MO 文件,需要清除缓存才能看到效果。
  • 文本域命名: 文本域的名字应该具有唯一性,避免与其他主题或插件冲突。
  • 翻译工具: 可以使用 Poedit 或 Loco Translate 等工具来编辑 PO 文件。

第九幕:总结中的总结

好了,今天的讲座就到这里。希望通过今天的讲解,大家对 WordPress 的 __() 函数有了更深入的理解。记住,__() 函数不仅仅是一个简单的翻译函数,它是 WordPress 多语言支持的核心部件,也是我们构建国际化网站的重要工具。掌握它,你就能让你的网站走向世界,拥抱全球用户!

散会! 记得给个好评哦! 😉

发表回复

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