哈喽,各位观众老爷们,我是你们的码农老司机。今天咱们聊聊 WordPress 插件国际化的幕后英雄—— load_plugin_textdomain()
函数。 这玩意儿,看着挺唬人,其实就是个帮你的插件说外语的翻译官。
开场白:为什么需要国际化?
想象一下,你辛辛苦苦写了个超级牛逼的插件,结果只有说中文的人能用,那多可惜啊! 为了让你的插件走向世界,服务全球人民,国际化 (i18n) 就显得尤为重要了。 而 load_plugin_textdomain()
就是 i18n 流程中,加载翻译文件,让你的插件显示不同语言的关键步骤。
主角登场:load_plugin_textdomain()
的庐山真面目
load_plugin_textdomain()
函数的原型如下:
/**
* Loads a plugin's translated strings.
*
* @since 2.7.0
*
* @param string $domain Unique identifier for retrieving translated strings.
* @param string $deprecated Deprecated since 2.7.0. Use the plugin base path instead.
* @param string|bool $plugin_rel_path (optional) Relative path to .mo file from plugin directory.
* Default false.
* @return bool True when textdomain is successfully loaded, false on failure.
*/
function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
global $l10n, $wp_plugin_paths;
// First, check if the textdomain is loaded already.
if ( isset( $l10n[ $domain ] ) ) {
return true;
}
$locale = determine_locale();
$locale = apply_filters( 'plugin_locale', $locale, $domain );
// If no textdomain is supplied, exit gracefully.
if ( empty( $domain ) ) {
return false;
}
// If we don't have a plugin_rel_path, derive it from the constant.
if ( false === $plugin_rel_path ) {
$plugin_rel_path = dirname( plugin_basename( __FILE__ ) ); // Assuming this is called from the plugin's main file
}
// Sanitize the plugin path.
$plugin_path = untrailingslashit( $plugin_rel_path );
$mofile = $domain . '-' . $locale . '.mo';
// Prepend constants if available.
if ( defined( 'WP_LANG_DIR' ) ) {
$mofile_global = WP_LANG_DIR . '/plugins/' . $mofile;
}
$mofile_local = WP_PLUGIN_DIR . '/' . $plugin_path . '/' . $mofile;
// Look in global /wp-content/languages/plugins/ folder first.
if ( ! empty( $mofile_global ) && file_exists( $mofile_global ) ) {
return load_textdomain( $domain, $mofile_global );
}
// Then look in /wp-content/plugins/<plugin_path>/ folder.
if ( file_exists( $mofile_local ) ) {
return load_textdomain( $domain, $mofile_local );
}
// Finally, look in WP_LANG_DIR subfolder if defined.
if ( defined( 'WPMU_PLUGIN_DIR' ) && file_exists( WPMU_PLUGIN_DIR . '/' . $plugin_path . '/' . $mofile ) ) {
return load_textdomain( $domain, WPMU_PLUGIN_DIR . '/' . $plugin_path . '/' . $mofile );
}
if ( ( defined( 'WP_LANG' ) && WP_LANG ) || ( defined( 'WPLANG' ) && WPLANG ) ) {
$mofile = $domain . '-' . substr( get_locale(), 0, 2 ) . '.mo';
// Prepend constants if available.
if ( defined( 'WP_LANG_DIR' ) ) {
$mofile_global = WP_LANG_DIR . '/plugins/' . $mofile;
}
if ( ! empty( $mofile_global ) && file_exists( $mofile_global ) ) {
return load_textdomain( $domain, $mofile_global );
}
}
return false;
}
别被这么长的代码吓到,咱们把它拆开揉碎了细细分析。
参数详解
$domain
: 你的插件的文本域 (text domain)。 这是一个唯一的字符串,用来区分不同插件的翻译文本。 就像你的插件的身份证号一样,一定要独一无二!通常使用插件的slug,比如my-awesome-plugin
。$deprecated
: 这个参数已经过时了,不用管它。 属于时代的眼泪,留着只是为了兼容旧版本。$plugin_rel_path
: 你的翻译文件 (.mo 文件) 相对于插件主目录的路径。如果你的.mo
文件直接放在插件主目录下,就设为false
。 如果放在比如languages
目录下,就设为languages
。
工作原理:翻译官的寻宝之旅
load_plugin_textdomain()
的主要任务是找到并加载正确的 .mo
翻译文件。 它会按以下顺序搜索:
-
检查是否已经加载: 首先,它会检查
$l10n
全局变量,看看你的文本域是否已经加载过了。 如果加载过了,就直接返回true
,省事儿! -
确定当前语言环境: 调用
determine_locale()
获取当前的语言环境 (locale),比如zh_CN
(简体中文),en_US
(美国英语) 等。 还会应用plugin_locale
过滤器,允许开发者自定义语言环境。 -
处理空的文本域: 如果
$domain
为空,啥也不做,直接返回false
。 -
计算相对路径: 如果
$plugin_rel_path
为false
,则尝试自动获取插件主目录的相对路径。 这里假设你是在插件的主文件中调用load_plugin_textdomain()
,如果不是,就需要手动指定$plugin_rel_path
。 -
构建
.mo
文件名: 根据文本域和语言环境构建.mo
文件的文件名,例如my-awesome-plugin-zh_CN.mo
。 -
寻宝开始! 依次在以下目录中寻找
.mo
文件:/wp-content/languages/plugins/
: 这是 WordPress 存放全局插件翻译文件的地方。/wp-content/plugins/<plugin_path>/
: 这是插件自己的翻译文件存放的地方。WPMU_PLUGIN_DIR/<plugin_path>/
: 如果是多站点 (Multisite),并且插件是网络激活的,则会在这里寻找。
-
备用方案: 如果上面的寻宝都失败了,它还会尝试使用简化的语言代码 (比如
zh
而不是zh_CN
) 再次查找。 -
加载
.mo
文件: 如果找到了.mo
文件,就调用load_textdomain()
函数来真正加载翻译文件。 -
宣告结果: 如果成功加载了
.mo
文件,就返回true
,否则返回false
。
代码实战:让你的插件说中文
假设你的插件叫做 my-awesome-plugin
,并且你的翻译文件放在 languages
目录下。 那么,在你的插件主文件中,你可以这样调用 load_plugin_textdomain()
:
<?php
/**
* Plugin Name: My Awesome Plugin
* Plugin URI: https://example.com/my-awesome-plugin
* Description: A super awesome plugin.
* Version: 1.0.0
* Author: Your Name
* Author URI: https://example.com
* License: GPL2
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-awesome-plugin
* Domain Path: /languages
*/
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' );
}
这段代码做了以下几件事:
- 插件头部信息: 在插件头部信息中,定义了
Text Domain
和Domain Path
。Text Domain
必须和load_plugin_textdomain()
的第一个参数一致。Domain Path
指定了翻译文件存放的目录。 plugins_loaded
钩子: 使用add_action()
将my_awesome_plugin_load_textdomain()
函数挂载到plugins_loaded
钩子上。 这意味着在所有插件加载完毕后,才会执行这个函数。 确保 WordPress 已经准备好加载翻译文件。my_awesome_plugin_load_textdomain()
函数: 这个函数调用load_plugin_textdomain()
,传入了正确的参数。dirname( plugin_basename( __FILE__ ) )
获取插件主目录的路径。
准备翻译文件:.po
和 .mo
的爱恨情仇
光有 load_plugin_textdomain()
还不够,你还需要准备翻译文件。 翻译文件分为两种:
- .po 文件: 人类可读的文本文件,包含了原始字符串和对应的翻译。 你需要编辑这个文件,填写翻译内容。
- .mo 文件: 机器可读的二进制文件,是
.po
文件的编译版本。 WordPress 实际上加载的是.mo
文件。
你可以使用 Poedit 等工具来创建和编辑 .po
文件,然后将其编译成 .mo
文件。
翻译函数的妙用:__()
, _e()
, _x()
, _ex()
有了翻译文件,你还需要在你的插件代码中使用翻译函数来标记需要翻译的字符串。 常用的翻译函数有:
__()
: 返回翻译后的字符串。_e()
: 直接输出翻译后的字符串。_x()
: 带有上下文的翻译,用于区分相同字符串在不同语境下的翻译。_ex()
: 带有上下文的翻译,并直接输出翻译后的字符串。
例子:
<?php
// 使用 __() 函数
$translated_string = __( 'Hello, world!', 'my-awesome-plugin' );
echo $translated_string;
// 使用 _e() 函数
_e( 'Welcome to my plugin!', 'my-awesome-plugin' );
// 使用 _x() 函数
$translated_string = _x( 'Post', 'noun', 'my-awesome-plugin' ); // 作为名词的 Post
echo $translated_string;
$translated_string = _x( 'Post', 'verb', 'my-awesome-plugin' ); // 作为动词的 Post
echo $translated_string;
// 使用 _ex() 函数
_ex( 'Add New', 'button label', 'my-awesome-plugin' );
?>
表格总结:load_plugin_textdomain()
的关键要素
要素 | 说明 |
---|---|
文本域 (Domain) | 插件的唯一标识符,用于区分不同插件的翻译文本。 必须和插件头部信息中的 Text Domain 一致。 |
翻译文件 (.mo) | 包含了翻译后的字符串的二进制文件。 |
语言环境 (Locale) | 表示特定语言和地区的代码,例如 zh_CN (简体中文), en_US (美国英语)。 |
翻译函数 | __() , _e() , _x() , _ex() 等,用于标记需要翻译的字符串。 |
加载时机 | 建议在 plugins_loaded 钩子中调用 load_plugin_textdomain() ,确保 WordPress 已经准备好加载翻译文件。 |
文件存放位置 | WordPress 会按顺序在以下目录中查找 .mo 文件: /wp-content/languages/plugins/ , /wp-content/plugins/<plugin_path>/ , WPMU_PLUGIN_DIR/<plugin_path>/ 。 建议将翻译文件放在插件的 languages 目录下。 |
常见问题解答 (FAQ)
-
Q: 为什么我的翻译不起作用?
- A: 检查你的文本域是否正确。 确保它和插件头部信息中的
Text Domain
以及load_plugin_textdomain()
的第一个参数一致。 - A: 检查你的
.mo
文件是否存在,并且文件名是否正确。 文件名必须是textdomain-locale.mo
的格式,例如my-awesome-plugin-zh_CN.mo
。 - A: 检查你的翻译文件是否放在正确的位置。
- A: 清除 WordPress 的缓存。
- A: 检查你的文本域是否正确。 确保它和插件头部信息中的
-
Q: 我可以使用在线翻译服务来翻译我的插件吗?
- A: 当然可以! 但是,最好人工校对一下,确保翻译的质量。
-
Q: 我需要为每种语言都创建一个
.mo
文件吗?- A: 是的。 每种语言都需要一个单独的
.mo
文件。
- A: 是的。 每种语言都需要一个单独的
高级技巧:动态文本域
有时候,你可能需要在运行时动态地改变文本域。 例如,你可能想根据用户的设置加载不同的翻译文件。 你可以使用 plugin_locale
过滤器来实现这个目标:
<?php
add_filter( 'plugin_locale', 'my_awesome_plugin_dynamic_locale', 10, 2 );
function my_awesome_plugin_dynamic_locale( $locale, $domain ) {
if ( 'my-awesome-plugin' === $domain ) {
// 根据用户设置获取语言代码
$user_language = get_user_meta( get_current_user_id(), 'my_plugin_language', true );
if ( ! empty( $user_language ) ) {
return $user_language;
}
}
return $locale;
}
这段代码会根据用户的 my_plugin_language
元数据来动态地改变语言环境。
总结:让你的插件说遍全世界
load_plugin_textdomain()
是 WordPress 插件国际化的基石。 掌握了它的用法,你就能轻松地让你的插件支持多种语言,走向世界,服务全球人民。 记住,国际化不仅仅是翻译字符串,更是一种对不同文化和语言的尊重。 希望今天的讲座能帮助你更好地理解和使用 load_plugin_textdomain()
函数。 下次再见!