各位观众老爷,早上好!今天给大家带来一场关于 WordPress 插件国际化核心函数 load_plugin_textdomain()
的源码剖析讲座,让咱们一起扒一扒它的底裤,看看它到底是怎么把各种语言搬到插件里来的。
一、开场白:国际化,插件的“世界语”
想象一下,你辛辛苦苦开发了一个牛逼的 WordPress 插件,功能强大,界面美观,结果只能让说中文的朋友用,这多可惜啊!如果能让全世界的人都能用你的插件,那岂不是美滋滋?
这就是国际化(i18n)的意义所在。它让你的插件能够适应不同的语言和文化,变成一个真正的“世界语”。而 load_plugin_textdomain()
函数,正是 WordPress 插件国际化的关键一环。
二、load_plugin_textdomain()
:你的翻译“传送门”
load_plugin_textdomain()
函数的作用,简单来说,就是加载你的插件的翻译文件(.mo 文件),让 WordPress 知道你的插件有哪些字符串需要翻译,以及对应的翻译是什么。
它的基本语法如下:
load_plugin_textdomain(
string $domain,
bool $deprecated = false,
string $plugin_rel_path = null
);
$domain
:你的插件的文本域(text domain)。这个东西非常重要,它就像你的插件的身份证号,用于区分不同的插件。$deprecated
:一个已经过时的参数,不用管它,默认设置为false
就行。$plugin_rel_path
:你的翻译文件相对于插件主文件的路径。如果你的翻译文件放在插件的languages
目录下,那么这个参数就应该是'languages'
。
三、源码解剖:load_plugin_textdomain()
的内部世界
接下来,咱们深入 load_plugin_textdomain()
的源码,看看它到底是怎么工作的。
以下是 load_plugin_textdomain()
函数的核心代码(位于 wp-includes/l10n.php
文件中,这里只截取了关键部分,并进行了简化):
function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = null ) {
global $l10n, $wp_plugin_paths;
// 1. 确定插件目录
$plugin_path = null;
if ( ! empty( $plugin_rel_path ) ) {
$plugin_path = plugin_basename( dirname( __FILE__ ) . '/' . $plugin_rel_path ); // 获取插件目录
}
// 2. 确定翻译文件路径
$locale = determine_locale(); // 获取当前站点语言环境
$mofile = $domain . '-' . $locale . '.mo'; // 构建翻译文件名
$mofile_global = WP_LANG_DIR . '/plugins/' . $mofile; // 全局翻译文件路径
$mofile_local = WP_PLUGIN_DIR . '/' . $plugin_path . '/' . $mofile; // 插件目录下的翻译文件路径
// 3. 加载翻译文件
if ( file_exists( $mofile_global ) ) {
load_textdomain( $domain, $mofile_global ); // 加载全局翻译文件
} elseif ( file_exists( $mofile_local ) ) {
load_textdomain( $domain, $mofile_local ); // 加载插件目录下的翻译文件
} else {
// 如果找不到翻译文件,尝试加载主题目录下的翻译文件
if ( ! empty( $plugin_path ) ) {
foreach ( $wp_plugin_paths as $path ) {
$mofile_theme = WP_CONTENT_DIR . $path . '/' . $plugin_path . '/' . $mofile;
if ( file_exists( $mofile_theme ) ) {
load_textdomain( $domain, $mofile_theme );
break;
}
}
}
}
return isset( $l10n[ $domain ] ); // 返回是否成功加载翻译文件
}
现在,咱们一步一步地解读这段代码:
-
确定插件目录:
首先,函数会根据你传入的
$plugin_rel_path
参数,确定你的插件的目录。如果$plugin_rel_path
为空,那么它会尝试从当前文件的目录中获取插件目录。这一步是为了确定翻译文件相对于插件的位置。 -
确定翻译文件路径:
接下来,函数会获取当前站点的语言环境(locale),比如
zh_CN
(简体中文),然后根据这个语言环境和你的文本域$domain
,构建翻译文件的文件名,比如my-plugin-zh_CN.mo
。然后,函数会构建两个可能的翻译文件路径:
- 全局翻译文件路径:
WP_LANG_DIR . '/plugins/' . $mofile
。这个路径指向 WordPress 的语言目录下的plugins
目录,用于存放所有插件的全局翻译文件。 - 插件目录下的翻译文件路径:
WP_PLUGIN_DIR . '/' . $plugin_path . '/' . $mofile
。这个路径指向你的插件目录下的翻译文件。
- 全局翻译文件路径:
-
加载翻译文件:
最后,函数会按照以下顺序尝试加载翻译文件:
- 全局翻译文件: 如果全局翻译文件存在,就加载它。
- 插件目录下的翻译文件: 如果插件目录下的翻译文件存在,就加载它。
- 主题目录下的翻译文件: 如果以上两种文件都不存在,函数会遍历
$wp_plugin_paths
数组,尝试在主题目录下查找翻译文件。
加载翻译文件的核心函数是
load_textdomain()
,它会将翻译文件中的字符串和对应的翻译存储到全局变量$l10n
中。
四、load_textdomain()
:翻译的“搬运工”
load_textdomain()
函数负责真正地读取 .mo
文件,并将翻译数据加载到 WordPress 的全局翻译库中。
以下是 load_textdomain()
函数的核心代码(位于 wp-includes/l10n.php
文件中,同样只截取了关键部分,并进行了简化):
function load_textdomain( $domain, $mofile ) {
global $l10n, $wp_filesystem;
// 1. 检查文件是否存在
if ( ! file_exists( $mofile ) ) {
return false;
}
// 2. 创建 MO 对象
$mo = new MO();
// 3. 加载 .mo 文件
if ( ! $mo->import_from_file( $mofile ) ) {
return false;
}
// 4. 将翻译数据存储到全局变量 $l10n 中
if ( isset( $l10n[ $domain ] ) ) {
$l10n[ $domain ]->merge_with( $mo );
} else {
$l10n[ $domain ] = &$mo;
}
return true;
}
这段代码的逻辑也很清晰:
-
检查文件是否存在:
首先,函数会检查你传入的
.mo
文件是否存在。 -
创建 MO 对象:
然后,函数会创建一个
MO
对象。MO
类是 WordPress 用来处理.mo
文件的类,它负责读取.mo
文件中的数据,并将其存储在内部的数据结构中。 -
加载 .mo 文件:
接下来,函数会调用
MO
对象的import_from_file()
方法,将.mo
文件中的数据加载到MO
对象中。 -
将翻译数据存储到全局变量
$l10n
中:最后,函数会将
MO
对象中的翻译数据存储到全局变量$l10n
中。$l10n
是一个全局数组,用于存储所有已加载的翻译数据。如果
$l10n
中已经存在相同文本域的翻译数据,那么新的翻译数据会与已有的数据合并。否则,新的翻译数据会直接存储到$l10n
中。
五、.mo
文件:翻译的“数据库”
.mo
文件是 Machine Object 的缩写,它是一种二进制文件,用于存储翻译数据。.mo
文件是由 .po
文件(Portable Object)编译而成的。.po
文件是人类可读的文本文件,包含了需要翻译的字符串和对应的翻译。
.mo
文件的结构比较复杂,但它主要包含以下信息:
- 文件头: 包含文件的版本号、字符集等信息。
- 字符串表: 包含需要翻译的字符串和对应的翻译。
.mo
文件的优点是体积小、加载速度快,适合在生产环境中使用。
六、如何使用 load_plugin_textdomain()
函数?
说了这么多,咱们来看看如何在实际的插件中使用 load_plugin_textdomain()
函数。
以下是一个简单的示例:
<?php
/**
* Plugin Name: My Awesome Plugin
* Description: This is an awesome plugin.
* Version: 1.0.0
* Author: Your Name
*/
// 在插件激活时加载翻译文件
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_message() {
echo __( 'Hello, world!', 'my-awesome-plugin' );
}
add_action( 'wp_footer', 'my_awesome_plugin_output_message' );
在这个示例中:
-
我们在插件的
plugins_loaded
钩子上注册了一个函数my_awesome_plugin_load_textdomain()
。这个钩子会在所有插件加载完成后被触发。 -
在
my_awesome_plugin_load_textdomain()
函数中,我们调用了load_plugin_textdomain()
函数,并传入了以下参数:'my-awesome-plugin'
:这是我们的插件的文本域。false
:这是$deprecated
参数,我们设置为false
。dirname( plugin_basename( __FILE__ ) ) . '/languages'
:这是我们的翻译文件相对于插件主文件的路径。我们假设我们的翻译文件放在插件的languages
目录下。
-
在
my_awesome_plugin_output_message()
函数中,我们使用了__()
函数来翻译字符串'Hello, world!'
。__()
函数是 WordPress 提供的一个用于翻译字符串的函数,它会根据当前站点的语言环境,从全局变量$l10n
中查找对应的翻译。
七、一些小贴士:让你的国际化更上一层楼
- 选择一个好的文本域: 文本域应该具有唯一性,最好使用你的插件的名称或缩写。
- 使用一致的字符串: 对于相同的字符串,应该使用完全相同的文本,避免出现细微的差异,否则会导致翻译不一致。
- 使用
__()
、_e()
、_x()
、_ex()
等翻译函数: 这些函数是 WordPress 提供的用于翻译字符串的函数,它们会自动根据当前站点的语言环境查找对应的翻译。 - 使用 Poedit 等工具创建和编辑
.po
和.mo
文件: Poedit 是一款免费的.po
和.mo
文件编辑器,它可以帮助你更方便地创建和编辑翻译文件。 - 将你的插件提交到 WordPress 翻译项目: WordPress 官方有一个翻译项目,你可以将你的插件提交到这个项目,让全球的志愿者帮助你翻译你的插件。
八、总结:让世界听到你的声音
load_plugin_textdomain()
函数是 WordPress 插件国际化的核心,它负责加载翻译文件,让你的插件能够适应不同的语言和文化。通过深入理解 load_plugin_textdomain()
函数的源码和使用方法,你可以轻松地为你的插件添加国际化支持,让世界听到你的声音!
好了,今天的讲座就到这里。感谢大家的观看!希望大家都能开发出让全世界人民都爱用的插件!