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

咳咳,各位观众老爷们,欢迎来到今天的“WordPress 源码大保健”讲座! 今天咱们要聊的是一个藏得比较深的家伙,但作用却举足轻重的函数:wp_maybe_load_translations_early()。 名字有点长哈,但别怕,咱们一层层扒开它的底裤,看看它到底在干些什么见不得人的事情,啊不,是重要的工作。

一、 故事的开端:为啥要“Early”加载?

想象一下,你要去参加一个国际会议,结果到了现场,发现主办方只说当地语言,你一脸懵逼,啥也听不懂。 这时候,有个好心人递给你一个实时翻译耳机,瞬间世界都美好了。

wp_maybe_load_translations_early() 在 WordPress 里的作用,就有点像这个“实时翻译耳机”。 WordPress 本身是用英文写的,但为了服务全球用户,需要支持各种语言。 为了让用户在访问网站的时候,第一时间看到的就是自己熟悉的语言,而不是满屏的英文,我们就需要在 WordPress 启动的早期,就把核心翻译文件加载进来。

“Early”在这里,意味着尽可能早。 越早加载,用户体验越好。 想象一下,如果等你点开文章,甚至评论的时候才开始加载翻译,那用户体验会糟糕到什么程度!

二、 wp_maybe_load_translations_early() 的身世背景

这个函数是在 wp-settings.php 文件中被调用的,这个文件是 WordPress 启动的“心脏”。 咱们来看看它在 wp-settings.php 中的位置:

// Load early WordPress files.
require( ABSPATH . WPINC . '/load.php' );
require( ABSPATH . WPINC . '/default-constants.php' );

//... 一堆代码 ...

// Set internal encoding.
if ( function_exists( 'mb_internal_encoding' ) ) {
    mb_internal_encoding( 'UTF-8' );
}

// Run wp_maybe_load_translations_early.
wp_maybe_load_translations_early();

//... 更多代码 ...

可以看到,它在一些核心文件加载之后,但在 WordPress 初始化之前被调用。 这保证了在 WordPress 核心功能启动之前,翻译文件已经准备就绪。

三、 wp_maybe_load_translations_early() 的源码剖析

好了,重头戏来了,咱们来看看 wp_maybe_load_translations_early() 函数的庐山真面目:

function wp_maybe_load_translations_early() {
    /**
     * Filters whether to load the translations early.
     *
     * Passing a non-null value will effectively short-circuit the function.
     *
     * @since 5.0.0
     *
     * @param null|bool $enable_loading Whether to enable loading the translations early. Default null.
     */
    $enable_loading = apply_filters( 'enable_loading_translations_early', null );

    if ( null !== $enable_loading ) {
        if ( $enable_loading ) {
            load_default_textdomain();
        }
        return;
    }

    if ( defined( 'WP_SETUP_CONFIG' ) || is_multisite() ) {
        return;
    }

    if ( defined( 'DOING_AJAX' ) ) {
        return;
    }

    if ( defined( 'XMLRPC_REQUEST' ) ) {
        return;
    }

    if ( defined( 'REST_REQUEST' ) ) {
        return;
    }

    if ( defined( 'WP_INSTALLING' ) ) {
        return;
    }

    if ( ! is_admin() ) {
        load_default_textdomain();
    }
}

是不是感觉有点长? 别怕,咱们慢慢拆解:

  1. Filter钩子:enable_loading_translations_early

    $enable_loading = apply_filters( 'enable_loading_translations_early', null );
    
    if ( null !== $enable_loading ) {
    if ( $enable_loading ) {
        load_default_textdomain();
    }
    return;
    }

    首先,它使用了一个 Filter钩子 enable_loading_translations_early。 这个钩子允许开发者通过插件或主题,来控制是否提前加载翻译文件。 如果这个钩子返回了非 null 的值,那么函数就会根据这个值来决定是否加载翻译,并直接返回。 也就是说,开发者可以通过这个钩子来“短路”这个函数,自定义加载策略。

    举个例子,如果你想禁用提前加载翻译,可以在你的插件或主题的 functions.php 文件中添加如下代码:

    add_filter( 'enable_loading_translations_early', '__return_false' );

    反之,如果你想强制提前加载翻译,可以这样:

    add_filter( 'enable_loading_translations_early', '__return_true' );

    这个钩子的存在,给了开发者很大的灵活性。

  2. 各种条件判断:避免不必要的加载

    if ( defined( 'WP_SETUP_CONFIG' ) || is_multisite() ) {
    return;
    }
    
    if ( defined( 'DOING_AJAX' ) ) {
    return;
    }
    
    if ( defined( 'XMLRPC_REQUEST' ) ) {
    return;
    }
    
    if ( defined( 'REST_REQUEST' ) ) {
    return;
    }
    
    if ( defined( 'WP_INSTALLING' ) ) {
    return;
    }

    接下来,函数进行了一系列的条件判断。 这些判断的目的,是避免在一些特定的情况下,进行不必要的翻译文件加载。 具体来说:

    • WP_SETUP_CONFIG: 如果在运行安装程序(wp-config.php 未配置),就不加载。
    • is_multisite(): 如果是多站点环境,通常由主站点处理翻译,避免重复加载。
    • DOING_AJAX: 如果是 AJAX 请求,通常不需要立即加载翻译,可以延迟加载。
    • XMLRPC_REQUEST: 如果是 XML-RPC 请求,同样可以延迟加载翻译。
    • REST_REQUEST: 如果是 REST API 请求,也可以延迟加载翻译。
    • WP_INSTALLING: 如果 WordPress 正在安装过程中,也不需要加载翻译。

    这些判断都是为了优化性能,避免在不需要的时候浪费资源。

  3. 最终的加载:load_default_textdomain()

    if ( ! is_admin() ) {
    load_default_textdomain();
    }

    最后,如果以上条件都不满足,并且不在后台管理界面(! is_admin()),那么函数就会调用 load_default_textdomain() 函数来加载默认的文本域的翻译文件。 load_default_textdomain() 函数负责加载 WordPress 核心的翻译文件。

    为什么要排除后台管理界面呢? 因为后台管理界面的翻译文件通常由当前用户的语言设置决定,而不是默认的站点语言。 在后台管理界面,会使用 load_textdomain() 函数加载用户的语言设置对应的翻译文件。

四、 load_default_textdomain() 函数深入

既然提到了 load_default_textdomain(),咱们也顺便看看它做了些什么:

function load_default_textdomain( $locale = null ) {
    global $l10n, $locale;

    // Allow for the $locale to be passed in from the constant.
    if ( null === $locale ) {
        $locale = get_locale();
    }

    // wp-config.php defines install language.
    if ( defined( 'WPLANG' ) ) {
        $locale = WPLANG;
    }

    /**
     * Filters the locale to load early.
     *
     * @since 5.0.0
     *
     * @param string $locale The locale to load.
     */
    $locale = apply_filters( 'default_textdomain_locale', $locale );

    if ( empty( $locale ) ) {
        return false;
    }

    $mofile = WP_LANG_DIR . '/languages/'. determine_locale() . '.mo';

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

    $loaded = load_textdomain( 'default', $mofile );

    if ( $loaded ) {
        return true;
    }

    return false;
}
  1. 获取 Locale:确定语言

    if ( null === $locale ) {
    $locale = get_locale();
    }
    
    if ( defined( 'WPLANG' ) ) {
    $locale = WPLANG;
    }
    
    $locale = apply_filters( 'default_textdomain_locale', $locale );

    首先,它会尝试获取当前的 Locale(语言区域设置)。 Locale 决定了要加载哪个翻译文件。 获取 Locale 的顺序是:

    • 如果传入了 $locale 参数,则使用该参数。
    • 否则,使用 get_locale() 函数获取 WordPress 的默认 Locale。
    • 如果定义了 WPLANG 常量(通常在 wp-config.php 中定义),则使用该常量的值。
    • 最后,使用 default_textdomain_locale 过滤器,允许开发者自定义 Locale。

    get_locale() 函数会从 WordPress 的选项表中获取 WPLANG 选项的值,如果没有设置,则返回 ‘en_US’。

  2. 构建 MO 文件路径

    $mofile = WP_LANG_DIR . '/languages/'. determine_locale() . '.mo';

    接下来,它会构建 MO 文件的完整路径。 MO 文件是 machine object 的缩写,是编译后的二进制翻译文件,WordPress 使用 MO 文件来加载翻译。

    • WP_LANG_DIR 常量定义了存放翻译文件的目录,通常是 wp-content/languages
    • determine_locale() 函数会返回有效的 Locale,并确保其格式正确。
  3. 加载翻译文件

    if ( ! file_exists( $mofile ) ) {
    return false;
    }
    
    $loaded = load_textdomain( 'default', $mofile );
    
    if ( $loaded ) {
    return true;
    }
    
    return false;

    最后,它会检查 MO 文件是否存在,如果存在,则调用 load_textdomain() 函数来加载翻译文件。 load_textdomain() 函数会将翻译文件中的翻译内容加载到 $l10n 全局变量中。

    如果加载成功,则返回 true,否则返回 false

五、 核心流程总结

为了方便大家理解,我把整个流程用表格的形式总结一下:

函数 作用 关键步骤
wp_maybe_load_translations_early() 尝试在 WordPress 启动早期加载核心翻译文件。 1. 使用 enable_loading_translations_early 过滤器,允许开发者控制是否提前加载翻译。
2. 进行一系列条件判断,避免在不必要的场景下加载翻译(如安装程序、多站点、AJAX 请求等)。
3. 如果条件都满足,且不在后台管理界面,则调用 load_default_textdomain() 函数。
load_default_textdomain() 加载默认文本域的翻译文件。 1. 确定 Locale,决定要加载哪个翻译文件。
2. 构建 MO 文件的完整路径。
3. 检查 MO 文件是否存在,如果存在,则调用 load_textdomain() 函数加载翻译文件。
4. 将翻译内容加载到 $l10n 全局变量中。
load_textdomain() 真正执行翻译文件加载的函数。 1. 解析 MO 文件,将翻译字符串存储到全局 $l10n 数组中。
2. 将翻译文件与特定的文本域(text domain)关联起来。文本域是用于标识一组翻译字符串的唯一名称,例如 ‘default’ 用于 WordPress 核心,插件和主题可以使用自定义的文本域。
3. 允许通过 load_textdomain 过滤器修改加载行为。
get_locale() 获取 WordPress 的默认 Locale。 1. 从数据库的 options 表中获取 WPLANG 选项的值。
2. 如果 WPLANG 选项不存在或者为空,则返回默认的 Locale ‘en_US’。
determine_locale() 确定并规范化 Locale 字符串。 1. 从 get_locale() 获取的 Locale 字符串中提取语言代码和区域代码。
2. 将 Locale 字符串转换为标准格式,例如 ‘en_US’、’zh_CN’。
3. 确保 Locale 字符串是有效的,并且存在对应的翻译文件。

六、 总结与思考

wp_maybe_load_translations_early() 函数虽然看起来不起眼,但它却保证了 WordPress 在启动的早期就能加载核心翻译文件,从而为用户提供更好的本地化体验。

通过分析源码,我们了解了它的工作原理,以及它如何通过 Filter钩子和条件判断来优化性能。 我们还深入了解了 load_default_textdomain() 函数,以及它如何加载翻译文件。

希望今天的讲座能帮助大家更好地理解 WordPress 的本地化机制。 记住,源码是最好的老师! 多看源码,多思考,你也能成为 WordPress 大神!

好了,今天的讲座就到这里,感谢大家的收听! 咱们下期再见!

发表回复

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