深入理解 WordPress `load_plugin_textdomain()` 函数的源码:如何加载插件的国际化文件。

各位观众老爷们,晚上好!我是今天的主讲人,咱们今晚来聊聊 WordPress 插件国际化的核心:load_plugin_textdomain()。相信大家对这个函数都不陌生,但真正深入了解它的工作原理,才能更好地掌控插件的国际化,让你的插件走向世界,俘获更多用户的心!

第一部分:load_plugin_textdomain() 的基本认识——“你是谁?从哪儿来?要到哪儿去?”

首先,我们得搞清楚 load_plugin_textdomain() 究竟是个什么玩意儿。 简单来说,它就是一个负责加载插件翻译文件的函数,让你的插件能够根据用户的语言设置,显示不同的文字。

它的原型是这样的:

<?php
function load_plugin_textdomain( string $domain, bool $deprecated = false, string $plugin_rel_path = '' ) : bool {}
?>
  • $domain (string, required): 这是你的插件的文本域名(text domain)。 文本域名是用来区分不同插件的翻译文件的唯一标识符。 就像你的身份证号码一样,保证你的翻译不会张冠李戴。通常,建议你使用插件的名称作为文本域名,并且全部小写,用短横线连接。 比如你的插件叫 "Awesome Plugin",那么文本域名可以是 awesome-plugin

  • $deprecated (bool, optional): 这个参数已经被废弃了, 别管它! 直接设置为 false 就行了。 历史的尘埃,我们不捡!

  • $plugin_rel_path (string, optional): 这是翻译文件相对于插件主文件的路径。 默认情况下,WordPress 会在 wp-content/languages/plugins/{$domain}/{$domain}-{locale}.mowp-content/plugins/{plugin-directory}/languages/{$domain}-{locale}.mo 路径下查找翻译文件。如果你的翻译文件放在了其他地方,就需要通过这个参数来指定。

返回值:

  • bool: 如果成功加载翻译文件,则返回 true;否则返回 false

第二部分:load_plugin_textdomain() 源码剖析——“扒开它的皮,看看里面长啥样”

接下来,我们来深入源码,看看 load_plugin_textdomain() 到底是怎么工作的。 源码位于 wp-includes/l10n.php 文件中, 咱们把它简化一下, 抓住核心逻辑:

<?php
function load_plugin_textdomain( string $domain, bool $deprecated = false, string $plugin_rel_path = '' ) : bool {
    global $l10n, $wp_plugin_paths;

    // 1. 获取当前语言环境 (locale)
    $locale = determine_locale();

    // 2. 如果没有语言环境,就直接返回 false
    if ( empty( $locale ) ) {
        return false;
    }

    // 3. 构建翻译文件的路径
    // 3.1 获取插件目录
    $plugin_path = WP_PLUGIN_DIR . '/' . dirname( plugin_basename( __FILE__ ) ); // 注意:此处需要替换为实际的插件主文件路径

    // 3.2 默认的翻译文件路径
    $mofile = $plugin_path . '/languages/' . $domain . '-' . $locale . '.mo';

    // 3.3 如果指定了 $plugin_rel_path,则使用指定的路径
    if ( ! empty( $plugin_rel_path ) ) {
        $mofile = WP_PLUGIN_DIR . '/' . $plugin_rel_path . '/' . $domain . '-' . $locale . '.mo';
    }

    // 4. 检查翻译文件是否存在
    if ( ! is_readable( $mofile ) ) {
        return false;
    }

    // 5. 加载翻译文件
    if ( isset( $l10n[ $domain ] ) ) {
        unload_textdomain( $domain ); // 先卸载之前的翻译
    }

    $l10n[ $domain ] = new MO(); // 创建一个 MO 对象,用于加载翻译数据
    $loaded = $l10n[ $domain ]->import_from_file( $mofile ); // 从 .mo 文件导入翻译数据

    // 6. 如果加载成功,则返回 true;否则返回 false
    return $loaded;
}
?>

我们来逐行解读一下:

  1. 获取当前语言环境 (locale)determine_locale() 函数负责获取当前的语言环境。 语言环境决定了使用哪种语言的翻译。 WordPress 会根据用户的设置、站点的设置以及浏览器的设置,来确定最终的语言环境。
  2. 检查语言环境是否为空: 如果没有语言环境,说明没有设置任何语言,那就没必要加载翻译文件了,直接返回 false
  3. 构建翻译文件的路径: 这是最关键的一步。 我们需要根据文本域名和语言环境,构建出翻译文件的完整路径。 WordPress 默认会在两个地方查找翻译文件:

    • wp-content/languages/plugins/{$domain}/{$domain}-{locale}.mo: 这是全局的翻译文件目录,可以存放所有插件的翻译文件。
    • wp-content/plugins/{plugin-directory}/languages/{$domain}-{locale}.mo: 这是插件自身的翻译文件目录,建议将翻译文件放在这里,方便管理。

    如果指定了 $plugin_rel_path 参数,则使用指定的路径。

  4. 检查翻译文件是否存在is_readable() 函数检查翻译文件是否存在并且可读。 如果文件不存在,或者没有读取权限,那就无法加载翻译,返回 false
  5. 加载翻译文件: 这是真正加载翻译数据的步骤。

    • 如果已经加载了相同文本域名的翻译,先使用 unload_textdomain() 函数卸载之前的翻译,避免冲突。
    • 创建一个 MO 对象。 MO 是 Message Object 的缩写,是 WordPress 用来处理翻译数据的类。
    • 调用 MO 对象的 import_from_file() 方法,从 .mo 文件导入翻译数据。 .mo 文件是编译后的二进制翻译文件,效率更高。
  6. 返回加载结果import_from_file() 方法返回一个布尔值,表示是否成功加载翻译文件。 将这个值作为 load_plugin_textdomain() 函数的返回值。

第三部分:load_plugin_textdomain() 的使用方法——“怎么用它才能让我的插件说外语?”

现在我们知道了 load_plugin_textdomain() 的原理,接下来看看如何在插件中使用它。

  1. 确定文本域名: 选择一个合适的文本域名,建议使用插件名称的小写形式,用短横线连接。 比如 awesome-plugin
  2. 创建翻译文件: 创建 .po 文件和 .mo 文件。 .po 文件是纯文本的翻译文件,方便编辑。 .mo 文件是编译后的二进制文件,效率更高。 可以使用 Poedit 等工具来创建和编辑翻译文件。
    • .po 文件的命名格式为 {$domain}-{$locale}.po,例如 awesome-plugin-zh_CN.po
    • .mo 文件的命名格式为 {$domain}-{$locale}.mo,例如 awesome-plugin-zh_CN.mo
    • 建议将翻译文件放在插件目录下的 languages 目录中,例如 awesome-plugin/languages/awesome-plugin-zh_CN.poawesome-plugin/languages/awesome-plugin-zh_CN.mo
  3. 调用 load_plugin_textdomain() 函数: 在插件的主文件中,调用 load_plugin_textdomain() 函数,加载翻译文件。 建议在插件激活时或者在 plugins_loaded 动作钩子上调用。

    <?php
    /**
     * Plugin Name: Awesome Plugin
     * Description: A super awesome plugin.
     * Version: 1.0.0
     */
    
    add_action( 'plugins_loaded', 'awesome_plugin_load_textdomain' );
    
    function awesome_plugin_load_textdomain() {
        load_plugin_textdomain( 'awesome-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
    }
    ?>
    • 第一个参数是文本域名 awesome-plugin
    • 第二个参数是 $deprecated,设置为 false
    • 第三个参数是翻译文件相对于插件主文件的路径,这里使用了 dirname( plugin_basename( __FILE__ ) ) . '/languages/' 来获取插件目录下的 languages 目录。
  4. 在代码中使用翻译函数: 使用 __()_e()_x()_ex() 等翻译函数,将需要翻译的文本包裹起来。

    <?php
    echo __( 'Hello, world!', 'awesome-plugin' );
    _e( 'Welcome to my plugin!', 'awesome-plugin' );
    ?>
    • __() 函数返回翻译后的文本。
    • _e() 函数输出翻译后的文本。
    • 第二个参数是文本域名 awesome-plugin

第四部分:案例分析——“实战演练,让你看得更明白”

我们来举一个实际的例子,假设我们有一个名为 "My Contact Form" 的插件, 它的主要功能是显示一个联系表单。 我们希望让这个插件支持中文。

  1. 文本域名: 我们选择 my-contact-form 作为文本域名。

  2. 创建翻译文件: 我们在插件目录下的 languages 目录中创建 my-contact-form-zh_CN.pomy-contact-form-zh_CN.mo 文件。

    • my-contact-form-zh_CN.po 文件的内容如下:

      msgid ""
      msgstr ""
      "Project-Id-Version: My Contact Formn"
      "POT-Creation-Date: 2023-10-27 12:00+0800n"
      "PO-Revision-Date: 2023-10-27 12:00+0800n"
      "Language-Team: Chinese (Simplified)n"
      "Language: zh_CNn"
      "MIME-Version: 1.0n"
      "Content-Type: text/plain; charset=UTF-8n"
      "Content-Transfer-Encoding: 8bitn"
      "Plural-Forms: nplurals=1; plural=0;n"
      "X-Generator: Poedit 3.0.1n"
      
      msgid "Your Name"
      msgstr "您的姓名"
      
      msgid "Your Email"
      msgstr "您的邮箱"
      
      msgid "Message"
      msgstr "留言"
      
      msgid "Send"
      msgstr "发送"
    • 使用 Poedit 等工具将 my-contact-form-zh_CN.po 文件编译成 my-contact-form-zh_CN.mo 文件。

  3. 调用 load_plugin_textdomain() 函数: 在插件的主文件中,调用 load_plugin_textdomain() 函数。

    <?php
    /**
     * Plugin Name: My Contact Form
     * Description: A simple contact form plugin.
     * Version: 1.0.0
     */
    
    add_action( 'plugins_loaded', 'my_contact_form_load_textdomain' );
    
    function my_contact_form_load_textdomain() {
        load_plugin_textdomain( 'my-contact-form', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
    }
    
    function my_contact_form_display() {
        ?>
        <form>
            <label for="name"><?php _e( 'Your Name', 'my-contact-form' ); ?></label>
            <input type="text" id="name" name="name">
    
            <label for="email"><?php _e( 'Your Email', 'my-contact-form' ); ?></label>
            <input type="email" id="email" name="email">
    
            <label for="message"><?php _e( 'Message', 'my-contact-form' ); ?></label>
            <textarea id="message" name="message"></textarea>
    
            <button type="submit"><?php _e( 'Send', 'my-contact-form' ); ?></button>
        </form>
        <?php
    }
    
    add_shortcode( 'contact_form', 'my_contact_form_display' );
    ?>
  4. 效果: 当用户的语言环境设置为中文时,联系表单的标签和按钮上的文字将显示为中文。

第五部分:注意事项——“细节决定成败,别踩坑!”

在使用 load_plugin_textdomain() 函数时,有一些注意事项需要牢记:

  • 文本域名必须唯一: 确保你的插件的文本域名与其他插件不冲突。 建议使用插件名称的小写形式,用短横线连接。
  • 翻译文件命名要规范: 翻译文件的命名格式必须正确, 否则 WordPress 无法识别。 .po 文件的命名格式为 {$domain}-{$locale}.po.mo 文件的命名格式为 {$domain}-{$locale}.mo
  • 翻译文件必须存在并且可读: 确保翻译文件存在并且具有读取权限。
  • 使用正确的翻译函数: 使用 __()_e()_x()_ex() 等翻译函数,将需要翻译的文本包裹起来。
  • 清理缓存: 在修改翻译文件后,可能需要清理 WordPress 的缓存,才能看到最新的翻译效果。
  • 插件更新: 在插件更新时,需要注意更新翻译文件,保持翻译的完整性和准确性。

第六部分:高级技巧——“更上一层楼,玩转国际化”

除了基本的使用方法,还有一些高级技巧可以帮助你更好地实现插件的国际化:

  • 使用 load_muplugin_textdomain() 函数加载 MU 插件的翻译文件: MU 插件 (Must-Use Plugins) 是 WordPress 自动加载的插件,不需要手动激活。 可以使用 load_muplugin_textdomain() 函数加载 MU 插件的翻译文件。
  • 使用 load_child_theme_textdomain() 函数加载子主题的翻译文件: 子主题是基于父主题创建的,可以自定义主题的外观和功能。 可以使用 load_child_theme_textdomain() 函数加载子主题的翻译文件。
  • 使用 wp_set_script_translations() 函数加载 JavaScript 文件的翻译: 可以使用 wp_set_script_translations() 函数加载 JavaScript 文件的翻译,让你的 JavaScript 代码也支持国际化。
  • 利用在线翻译平台: 可以使用 Loco Translate 等在线翻译平台,方便地管理和编辑翻译文件,并与其他翻译者协作。

第七部分:常见问题解答——“解疑答惑,扫清障碍”

  • 为什么我的翻译没有生效?

    • 检查文本域名是否正确。
    • 检查翻译文件是否存在并且可读。
    • 检查翻译文件命名是否规范。
    • 检查是否使用了正确的翻译函数。
    • 清理 WordPress 的缓存。
  • 如何更新翻译文件?

    • 修改 .po 文件。
    • 使用 Poedit 等工具将 .po 文件编译成 .mo 文件。
    • 上传新的 .mo 文件到服务器。
    • 清理 WordPress 的缓存。
  • 如何让我的插件支持多种语言?

    • 为每种语言创建一个 .po 文件和 .mo 文件。
    • 确保翻译文件命名正确,例如 awesome-plugin-zh_CN.poawesome-plugin-en_US.poawesome-plugin-fr_FR.po 等。
    • WordPress 会根据用户的语言环境,自动加载对应的翻译文件。

表格总结

函数/方法 描述
load_plugin_textdomain() 加载插件的翻译文件。
load_muplugin_textdomain() 加载 MU 插件的翻译文件。
load_child_theme_textdomain() 加载子主题的翻译文件。
wp_set_script_translations() 加载 JavaScript 文件的翻译。
__() 返回翻译后的文本。
_e() 输出翻译后的文本。
_x() 返回带有上下文的翻译后的文本。
_ex() 输出带有上下文的翻译后的文本。
.po 文件 纯文本的翻译文件,方便编辑。
.mo 文件 编译后的二进制翻译文件,效率更高。

希望今天的讲解能够帮助大家更深入地了解 WordPress 插件的国际化,让你的插件能够走向世界,服务更多的用户。 记住,国际化不仅仅是翻译文字,更是一种对不同文化的尊重和理解。 谢谢大家!

有问题欢迎提问,咱们下期再见!

发表回复

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