深入理解 WordPress `wp_load_translations_early()` 函数源码:在启动初期加载核心翻译文件的原因。

咳咳,各位观众老爷们,晚上好!我是今晚的讲师,小码农,很高兴能和大家一起聊聊 WordPress 里一个可能被忽略,但又非常重要的函数:wp_load_translations_early()。 咱们今天就扒一扒它的底裤,看看它究竟在 WordPress 的启动过程中扮演着什么角色,以及为什么要这么早就加载核心翻译文件。

一、开场白:WordPress 的国际化之路

话说 WordPress 作为一个全球流行的 CMS,支持多种语言是基本操作。想象一下,如果你的网站只能显示英文,那得损失多少潜在用户啊! 所以,WordPress 从一开始就非常重视国际化 (i18n) 和本地化 (l10n)。

  • 国际化 (i18n): 指的是让你的代码具备支持多种语言的能力,简单来说,就是提前埋好“翻译接口”,方便后续接入各种语言包。
  • 本地化 (l10n): 指的是将你的程序适配到特定的语言环境,包括翻译文本、日期格式、货币符号等等。

wp_load_translations_early(),就是 WordPress 在启动初期,为了实现本地化,而进行的一项关键操作。

二、wp_load_translations_early() 在哪里?什么鬼?

这个函数位于 wp-settings.php 文件中,是 WordPress 初始化流程中非常早期被调用的函数之一。 可以这样理解:在WordPress这栋大厦开始盖的时候,地基还没打好,它就已经开始准备翻译材料了。

// wp-settings.php
if ( defined( 'WPLANG' ) && '' != WPLANG ) {
    // Load manually, since load_default_textdomain() does not exist yet.
    $locale = WPLANG;
} else {
    $locale = get_locale();
}

/**
 * Loads the main translation file early.
 *
 * @since 5.0.0
 */
function wp_load_translations_early() {
    global $locale;

    if ( ! function_exists( 'load_default_textdomain' ) ) {
        return;
    }

    load_default_textdomain( 'default', ABSPATH . 'wp-content/languages' );
}

从代码里我们可以看到:

  1. 它首先判断 WPLANG 常量是否定义,如果定义了,就使用该常量的值作为语言区域设置 (locale),否则就通过 get_locale() 函数获取。WPLANG 允许你强制指定网站的语言,而 get_locale() 通常是从 WordPress 的设置中获取的。

  2. 然后,它会检查 load_default_textdomain() 函数是否存在。如果不存在,说明 WordPress 的核心函数还没有完全加载,那它就直接返回,啥也不干。这是一种安全措施,防止在核心函数尚未准备好时出错。

  3. 最后,它会调用 load_default_textdomain() 函数,加载名为 "default" 的文本域(text domain)的翻译文件。这个 "default" 文本域包含了 WordPress 核心的翻译,路径指向 wp-content/languages 目录。

三、为啥要这么早加载?早起的鸟儿有虫吃?

这就要说到 WordPress 启动流程的特殊性了。 很多核心功能,例如错误提示、日期格式化、数字格式化等等,都依赖于翻译文件。 如果等到 WordPress 初始化流程的后期才加载翻译文件,那么在早期阶段,这些功能就可能会显示成未翻译的字符串,或者使用错误的格式。

举个例子,想象一下,如果你的网站在数据库连接失败的时候,显示 "Unable to connect to database" 而不是 "无法连接到数据库",那对于不懂英文的用户来说,岂不是一脸懵逼?

所以,wp_load_translations_early() 的目的就是确保在 WordPress 的早期阶段,核心的翻译文件就已经加载完毕,从而保证核心功能的正常显示。

四、load_default_textdomain() 的幕后功臣

load_default_textdomain() 函数实际上是 load_textdomain() 函数的一个特殊版本,专门用于加载 "default" 文本域的翻译文件。 让我们来看看 load_textdomain() 函数的源码(简化版):

// wp-includes/l10n.php
function load_textdomain( $domain, $mofile, $locale = null ) {
    global $l10n, $l10n_unloaded;

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

    $mofile = validate_file( $mofile );

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

    $mo = new MO();
    if ( ! $mo->import_from_file( $mofile ) ) {
        return false;
    }

    if ( isset( $l10n[ $domain ] ) ) {
        $mo->merge_with( $l10n[ $domain ] );
    }

    $l10n[ $domain ] = &$mo;
    unset( $l10n_unloaded[ $domain ] );

    return true;
}

这个函数做了几件事情:

  1. 确定语言区域设置: 如果没有传入 $locale 参数,就调用 determine_locale() 函数来确定。

  2. 验证文件路径: 使用 validate_file() 函数来验证 $mofile 路径的安全性。

  3. 读取 .mo 文件: 创建一个 MO 对象,并使用 import_from_file() 方法从 $mofile (通常是 .mo 文件) 中读取翻译数据。

  4. 合并翻译数据: 如果已经存在相同文本域的翻译数据,就将新的翻译数据合并到已有的数据中。

  5. 存储翻译数据: 将翻译数据存储到全局变量 $l10n 中,以便后续使用。

.mo 文件是什么鬼?

.mo 文件是 Machine Object 的缩写,它是一种二进制文件,包含了翻译后的字符串。 它是从 .po 文件(Portable Object,一种文本格式的翻译文件)编译而来的。 WordPress 使用 .mo 文件来提高翻译文件的加载速度,因为二进制文件比文本文件更容易解析。

五、determine_locale(): 语言设置的侦探

determine_locale() 函数负责确定当前网站的语言区域设置。 它的源码如下(简化版):

// wp-includes/l10n.php
function determine_locale() {
    global $wpdb;

    $locale = get_option( 'WPLANG' );

    if ( empty( $locale ) ) {
        $locale = get_locale();
    }

    return apply_filters( 'locale', $locale );
}

这个函数做了几件事情:

  1. 从数据库获取语言设置: 尝试从 wp_options 表中获取 WPLANG 选项的值。

  2. 使用 get_locale() 如果 WPLANG 选项为空,就调用 get_locale() 函数获取语言区域设置。

  3. 应用过滤器: 使用 apply_filters( 'locale', $locale ) 允许插件或主题修改最终的语言区域设置。

六、get_locale(): 语言设置的基石

get_locale() 函数负责获取 WordPress 的语言区域设置。 它的源码如下(简化版):

// wp-includes/l10n.php
function get_locale() {
    global $wp_locale;

    if ( isset( $wp_locale ) && is_object( $wp_locale ) ) {
        return apply_filters( 'locale', $wp_locale->locale );
    }

    if ( defined( 'WPLANG' ) ) {
        $locale = WPLANG;
    } else {
        $locale = get_option( 'WPLANG' );
    }

    if ( empty( $locale ) ) {
        $locale = 'en_US';
    }

    return apply_filters( 'locale', $locale );
}

这个函数做了以下事情:

  1. 检查 $wp_locale 对象: 如果 $wp_locale 对象已经存在,并且是一个对象,就返回它的 locale 属性。 $wp_locale 对象包含了语言区域设置的详细信息,例如日期格式、数字格式等等。

  2. 检查 WPLANG 常量: 如果定义了 WPLANG 常量,就使用该常量的值。

  3. 从数据库获取语言设置: 尝试从 wp_options 表中获取 WPLANG 选项的值。

  4. 使用默认值: 如果以上方法都失败了,就使用默认值 en_US (美国英语)。

  5. 应用过滤器: 使用 apply_filters( 'locale', $locale ) 允许插件或主题修改最终的语言区域设置。

七、$l10n 全局变量: 翻译数据的集散地

$l10n 是一个全局变量,它是一个数组,用于存储所有已加载的翻译数据。 数组的键是文本域 (text domain),值是一个 MO 对象,包含了该文本域的翻译数据。

当你使用 __()_e()_x() 等翻译函数时,WordPress 就会从 $l10n 数组中查找对应的翻译字符串。

八、翻译函数: 翻译的搬运工

WordPress 提供了许多翻译函数,用于在代码中标记需要翻译的字符串。 常用的翻译函数包括:

  • __(): 返回翻译后的字符串。
  • _e(): 输出翻译后的字符串。
  • _x(): 允许指定上下文 (context) 的翻译,用于区分相同字符串在不同语境下的翻译。
  • _n(): 用于处理单数和复数形式的翻译。
  • esc_attr__(): 用于 HTML 属性中的翻译,并进行 HTML 编码。
  • esc_html__(): 用于 HTML 内容中的翻译,并进行 HTML 编码。

九、一个简单的例子

假设你的主题中有一个这样的代码:

<?php
echo __( 'Hello, world!', 'my-theme' );
?>
  • 'Hello, world!' 是需要翻译的字符串。
  • 'my-theme' 是文本域,用于区分不同主题或插件的翻译文件。

当 WordPress 执行这段代码时,它会:

  1. 首先,查找 $l10n 数组中是否存在 'my-theme' 键。
  2. 如果存在,就从对应的 MO 对象中查找 'Hello, world!' 的翻译。
  3. 如果找到了翻译,就返回翻译后的字符串。
  4. 如果没有找到翻译,就返回原始字符串 'Hello, world!'

十、总结:wp_load_translations_early() 的重要性

功能 描述
早期加载核心翻译文件 确保 WordPress 核心的翻译文件在启动流程的早期阶段就被加载,避免在早期阶段出现未翻译的字符串。
保证核心功能正常显示 确保错误提示、日期格式化、数字格式化等核心功能在早期阶段就能正常显示,提供更好的用户体验。
依赖 load_default_textdomain() 使用 load_default_textdomain() 函数加载 "default" 文本域的翻译文件,该函数实际上是 load_textdomain() 函数的一个特殊版本。
使用 .mo 文件 使用 .mo 文件存储翻译数据,提高翻译文件的加载速度。
利用 $l10n 全局变量 将所有已加载的翻译数据存储到 $l10n 全局变量中,方便后续使用。
配合翻译函数 配合 __()_e() 等翻译函数,在代码中标记需要翻译的字符串,并从 $l10n 数组中查找对应的翻译。
影响用户体验 影响 WordPress 网站的国际化和本地化,直接影响用户体验。

总而言之,wp_load_translations_early() 函数虽然看起来不起眼,但它却是 WordPress 国际化和本地化策略中的一个重要组成部分。 它确保了 WordPress 的核心功能在早期阶段就能正常显示,为用户提供更好的体验。

十一、Q&A 环节

好了,今天的讲座就到这里。 现在进入 Q&A 环节,各位观众老爷们有什么问题,尽管放马过来吧! 只要我知道的,一定知无不言,言无不尽。 如果我不知道,我就… 我就回去查资料再告诉你们!

希望今天的讲解能帮助大家更深入地理解 WordPress 的国际化机制。 感谢各位的收听! 祝大家编码愉快,bug 远离!

发表回复

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