分析 WordPress `load_textdomain()` 函数源码:加载插件或主题翻译文件的原理。

各位观众老爷,大家好!今天咱们来聊聊 WordPress 翻译背后的功臣:load_textdomain() 函数。这玩意儿听起来高大上,但其实就像个辛勤的搬运工,负责把各种语言包(也就是 .mo 文件)搬到 WordPress 的大脑里,让你的网站能说多国语言。

咱们今天就来扒一扒它的源码,看看它是怎么工作的,以及我们作为开发者该如何正确使用它。准备好了吗?发车!

1. load_textdomain() 函数的身世背景

load_textdomain() 函数是 WordPress 内核中的一个核心函数,定义在 wp-includes/l10n.php 文件中。它的作用就是加载指定文本域(Text Domain)的翻译文件,让 WordPress 能够根据用户的语言设置显示相应的翻译文本。

简单来说,它就是个翻译器,把你的英文(或其他语言)代码里的文本,翻译成用户选择的语言。

2. 函数签名及参数详解

先来看看 load_textdomain() 的函数签名:

/**
 * Loads a .mo file into the text domain.
 *
 * If the text domain already exists, the translations will be merged. If both
 * sets of translations have the same string, the translation from the original
 * text domain will be used.
 *
 * @since 1.5.0
 *
 * @global array $l10n An array of translations.
 *
 * @param string      $domain  Text domain. Unique identifier for retrieving translated strings.
 * @param string      $mofile  Path to the MO file.
 * @param string|null $locale  Optional. Locale to load. If not set, uses the active locale.
 * @return bool True on success, false on failure.
 */
function load_textdomain( $domain, $mofile, $locale = null ) {
  // 函数体,后面再详细分析
}

参数解释:

  • $domain (string): 文本域。这是个非常重要的参数,它就像你的插件或主题的“名字”,WordPress 用它来区分不同的翻译文件。 建议使用插件或主题的slug命名,避免冲突。
  • $mofile (string): .mo 文件的完整路径。 .mo 文件是编译后的机器可读的翻译文件,包含了所有翻译信息。
  • $locale (string, optional): 可选参数,指定要加载的语言区域设置。 如果为空(null),则使用 WordPress 当前的语言区域设置。

返回值:

  • bool: 成功加载返回 true,失败返回 false

3. 源码深度解析:load_textdomain() 的工作流程

现在,让我们深入 load_textdomain() 的源码,看看它到底做了些什么:

function load_textdomain( $domain, $mofile, $locale = null ) {
    global $l10n;

    /**
     * Filters the locale to use when loading a text domain.
     *
     * @since 4.7.0
     *
     * @param string|null $locale The locale to use. Null if the locale is not set.
     * @param string      $domain Text domain. Unique identifier for retrieving translated strings.
     * @param string      $mofile Path to the MO file.
     */
    $locale = apply_filters( 'load_textdomain_locale', $locale, $domain, $mofile );

    if ( null === $locale ) {
        $locale = determine_locale();
    }

    if ( ! is_readable( $mofile ) ) {
        return false;
    }

    $l10n[ $domain ] = new MO();

    if ( ! $l10n[ $domain ]->import_from_file( $mofile ) ) {
        unset( $l10n[ $domain ] );
        return false;
    }

    $l10n[ $domain ]->set_domain( $domain );

    /**
     * Fires after the text domain is loaded.
     *
     * @since 2.9.0
     *
     * @param string $domain Text domain. Unique identifier for retrieving translated strings.
     * @param string $mofile Path to the MO file.
     */
    do_action( 'load_textdomain', $domain, $mofile );

    return true;
}

逐行分析:

  1. global $l10n;: 声明全局变量 $l10n。 这个全局数组是 WordPress 用来存储所有已加载的翻译信息的“大仓库”。 每个文本域对应一个 MO 对象,MO 对象里包含了该文本域的所有翻译。

  2. $locale = apply_filters( 'load_textdomain_locale', $locale, $domain, $mofile );: 应用 load_textdomain_locale 过滤器。 这个过滤器允许开发者修改要加载的语言区域设置。 比如,你可以根据用户的 IP 地址,动态选择加载不同的语言包。

  3. if ( null === $locale ) { $locale = determine_locale(); }: 如果 $locale 为空,则使用 determine_locale() 函数获取 WordPress 当前的语言区域设置。 determine_locale() 函数会从 WordPress 的配置中读取 WPLANG 常量,如果 WPLANG 没有设置,则会使用默认的 'en_US'

  4. if ( ! is_readable( $mofile ) ) { return false; }: 检查 .mo 文件是否存在且可读。 如果文件不存在或无法读取,则加载失败,直接返回 false

  5. $l10n[ $domain ] = new MO();: 创建一个 MO 对象,并将其赋值给 $l10n 数组中以 $domain 为键的元素。 MO 类负责解析 .mo 文件,并将其中的翻译信息存储在内存中。

  6. if ( ! $l10n[ $domain ]->import_from_file( $mofile ) ) { unset( $l10n[ $domain ] ); return false; }: 调用 MO 对象的 import_from_file() 方法,从 .mo 文件中导入翻译信息。 如果导入失败,则从 $l10n 数组中删除该文本域,并返回 false

  7. $l10n[ $domain ]->set_domain( $domain );: 设置 MO 对象的文本域。 这主要是为了方便后续的翻译查找。

  8. do_action( 'load_textdomain', $domain, $mofile );: 触发 load_textdomain 动作。 这个动作允许开发者在文本域加载完成后执行一些自定义操作,比如记录日志或更新缓存。

  9. return true;: 加载成功,返回 true

4. 核心类:MO

MO 类是 WordPress 中处理 .mo 文件的核心类,它负责解析 .mo 文件,并将其中的翻译信息存储在内存中。 MO 类的源码位于 wp-includes/pomo/mo.php 文件中。

MO 类的主要方法包括:

  • import_from_file( $filename ): 从指定的文件中导入翻译信息。
  • translate( $string ): 翻译指定的字符串。
  • set_domain( $domain ): 设置文本域。

MO 类的内部实现比较复杂,涉及到文件读取、字节序转换、哈希表查找等技术。 这里就不深入分析了,有兴趣的同学可以自行研究。

5. 如何正确使用 load_textdomain()

要正确使用 load_textdomain(),需要注意以下几点:

  • 选择合适的文本域: 文本域应该具有唯一性,通常使用插件或主题的 slug。
  • 放置正确的 .mo 文件: .mo 文件应该放在插件或主题的 languages 目录下,并以 [text-domain]-[locale].mo 的格式命名。例如,my-plugin-zh_CN.mo
  • 在合适的时间调用 load_textdomain() 建议在插件或主题的初始化阶段调用 load_textdomain(),比如在 plugins_loadedafter_setup_theme 动作中。
  • 使用 __()_e()_x()_n() 等翻译函数: 这些函数会自动使用已加载的翻译信息,将你的代码中的文本翻译成用户选择的语言。

6. 示例代码:在插件中使用 load_textdomain()

下面是一个简单的示例,演示如何在插件中使用 load_textdomain()

<?php
/**
 * Plugin Name: My Awesome Plugin
 * Description: A simple plugin to demonstrate how to use load_textdomain().
 * Version: 1.0.0
 */

add_action( 'plugins_loaded', 'my_awesome_plugin_load_textdomain' );

function my_awesome_plugin_load_textdomain() {
  load_plugin_textdomain( 'my-awesome-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
}

function my_awesome_plugin_output() {
  echo '<p>' . __( 'Hello, world!', 'my-awesome-plugin' ) . '</p>';
}

add_action( 'wp_footer', 'my_awesome_plugin_output' );

代码解释:

  1. add_action( 'plugins_loaded', 'my_awesome_plugin_load_textdomain' );: 在 plugins_loaded 动作中注册 my_awesome_plugin_load_textdomain() 函数。

  2. function my_awesome_plugin_load_textdomain() { ... }: my_awesome_plugin_load_textdomain() 函数调用 load_plugin_textdomain() 函数加载翻译文件。

  3. load_plugin_textdomain( 'my-awesome-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );: load_plugin_textdomain() 函数是 WordPress 提供的一个便捷函数,用于加载插件的翻译文件。 它实际上是对 load_textdomain() 函数的封装。

    • 'my-awesome-plugin':文本域。
    • false: 是否加载全局文本域。通常设置为 false
    • dirname( plugin_basename( __FILE__ ) ) . '/languages/': 翻译文件的相对路径。
  4. function my_awesome_plugin_output() { ... }: my_awesome_plugin_output() 函数输出一个包含翻译文本的段落。

  5. echo '<p>' . __( 'Hello, world!', 'my-awesome-plugin' ) . '</p>';: __() 函数用于翻译字符串。 第一个参数是要翻译的字符串,第二个参数是文本域。

7. load_plugin_textdomain()load_theme_textdomain()

WordPress 还提供了两个方便的函数,用于加载插件和主题的翻译文件:

  • load_plugin_textdomain( $domain, $deprecated, $plugin_rel_path ): 加载插件的翻译文件。
  • load_theme_textdomain( $domain, $template_directory ): 加载主题的翻译文件。

这两个函数实际上是对 load_textdomain() 函数的封装,简化了参数的设置。

8. 翻译文件格式:.po.mo

翻译文件有两种格式:.po.mo

  • .po (Portable Object) 文件是人类可读的文本文件,包含了原始字符串和对应的翻译。 开发者可以使用 .po 文件进行翻译。
  • .mo (Machine Object) 文件是编译后的机器可读的文件,包含了所有翻译信息。 WordPress 使用 .mo 文件进行翻译。

可以使用 Poedit 等工具创建和编辑 .po 文件,并将其编译成 .mo 文件。

9. 常见问题及解决方案

  • 翻译不生效: 检查文本域是否正确,.mo 文件是否存在且可读,以及是否在合适的时间调用了 load_textdomain() 函数。 另外,确保 WordPress 的语言设置正确。 还可以尝试清除 WordPress 的缓存。
  • 文本域冲突: 确保你的插件或主题的文本域与其他插件或主题不冲突。 建议使用插件或主题的 slug 作为文本域。
  • .mo 文件损坏: 重新编译 .po 文件生成 .mo 文件。

10. 总结

load_textdomain() 函数是 WordPress 翻译机制的核心。 理解它的工作原理,可以帮助你更好地进行插件和主题的国际化。 记住,选择合适的文本域,放置正确的 .mo 文件,并在合适的时间调用 load_textdomain() 函数,就可以让你的网站说多国语言,走向世界!

今天的讲座就到这里,希望对大家有所帮助。 感谢收看!

发表回复

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