WordPress源码深度解析之:`WordPress`的`L10n`:国际化和本地化函数的底层实现。

各位观众老爷,晚上好!我是今天的主讲人,一个和代码磕了一辈子的码农。今晚咱们聊点有意思的,聊聊WordPress这玩意儿背后的“口语翻译官”——L10n。

啥是L10n?简单说,就是让你的WordPress站点能说各国语言,不管是高贵的英语,还是神秘的中文,亦或是热情似火的西班牙语。这得益于WordPress的国际化 (i18n) 和本地化 (L10n) 机制,而 L10n 就是这个机制的具体实现。

别怕,听着高大上,其实原理贼简单。咱们今天就扒开它的裤衩,看看它到底是怎么运作的。

一、 i18n 和 L10n 的关系:一母同胞的两兄弟

首先,咱们得搞清楚 i18n 和 L10n 的关系。

  • i18n (Internationalization,国际化): 是让你的代码具备“多语种”能力,但它本身并不提供任何具体的翻译。 相当于设计了一套标准,让你的代码能轻松应对各种语言。
  • L10n (Localization,本地化): 是针对特定语言,将 i18n 处理过的文本翻译成目标语言。 相当于把抽象的标准变成了具体的方言。

i18n 是基础,L10n 是应用。 没 i18n,L10n 就无从谈起。

二、 WordPress L10n 的核心函数:几个当家花旦

WordPress 提供了几个核心函数来支持 L10n。 它们是你的代码与翻译文件交互的桥梁。

函数名 功能 备注
__() 获取翻译后的字符串。 这是最常用的函数,用于翻译单个字符串。 简单直接,哪里需要翻译,哪里就用它。
_e() 翻译并输出字符串。 相当于 echo __() 懒人专用,直接输出,省去 echo 步骤。
_n() 根据数量选择不同的翻译。 用于处理单复数情况。 比如 "1 comment" 和 "5 comments",用 _n() 可以自动选择正确的翻译。
_x() 带有上下文的翻译。 用于区分相同字符串在不同语境下的含义。 比如 "Post" 可以是 "文章"(名词) 或者 "发布"(动词)。 _x() 可以根据上下文选择正确的翻译。
_ex() 带有上下文的翻译并输出。 相当于 echo _x() _x() 的懒人版本。
_nx() 带有上下文和数量的翻译。 用于处理复杂情况。 结合了 _n()_x() 的功能。
esc_attr__() 获取翻译后的字符串,并进行 HTML 属性转义。 用于翻译 HTML 属性值。 确保翻译后的字符串可以安全地用于 HTML 属性中,防止 XSS 攻击。
esc_attr_e() 翻译并输出字符串,并进行 HTML 属性转义。 用于翻译并输出 HTML 属性值。 esc_attr__() 的懒人版本。
esc_html__() 获取翻译后的字符串,并进行 HTML 转义。 用于翻译 HTML 内容。 确保翻译后的字符串可以安全地用于 HTML 内容中,防止 XSS 攻击。
esc_html_e() 翻译并输出字符串,并进行 HTML 转义。 用于翻译并输出 HTML 内容。 esc_html__() 的懒人版本。
load_textdomain() 加载翻译文件。 这是启动 L10n 的关键步骤。 告诉 WordPress 去哪里找翻译文件。

三、 实战演练:代码说话,胜过千言万语

光说不练假把式。 咱们来点实际的,看看这些函数怎么用。

1. __()_e():最基础的翻译

<?php
// 翻译 "Hello, world!"
$translated_text = __('Hello, world!', 'my-theme');

// 输出翻译后的字符串
echo $translated_text;

// 或者直接用 _e()
_e('Hello, world!', 'my-theme');
?>

这里的 'my-theme' 是 text domain,用来区分不同主题或插件的翻译文件。 后面会详细讲。

2. _n():处理单复数

<?php
$comment_count = 5;

// 根据评论数量选择不同的翻译
printf(
    _n(
        '%s comment',
        '%s comments',
        $comment_count,
        'my-theme'
    ),
    number_format_i18n($comment_count)
);
?>

这段代码会根据 $comment_count 的值,选择 "1 comment" 或 "5 comments" 的翻译。 number_format_i18n() 函数用于格式化数字,使其符合当前语言环境的习惯。

3. _x()_ex():区分语境

<?php
// "Post" 作为名词(文章)
$post_label = _x('Post', 'post type general name', 'my-theme');

// "Post" 作为动词(发布)
$post_button_text = _x('Post', 'submit button', 'my-theme');

echo "文章类型:" . $post_label . "<br>";
echo "发布按钮:" . $post_button_text;
?>

通过第二个参数(context),我们可以区分 "Post" 在不同语境下的含义,从而获得更准确的翻译。

4. esc_attr__()esc_attr_e()esc_html__()esc_html_e():安全第一

<?php
// 翻译 HTML 属性值
$title_attribute = esc_attr__('Read more', 'my-theme');

// 翻译 HTML 内容
$link_text = esc_html__('Learn more', 'my-theme');

echo '<a href="#" title="' . $title_attribute . '">' . $link_text . '</a>';
?>

这些函数可以防止 XSS 攻击,确保翻译后的字符串可以安全地用于 HTML 中。 记住,安全无小事!

5. load_textdomain():加载翻译文件

<?php
// 在主题的 functions.php 文件中
function my_theme_load_textdomain() {
    load_theme_textdomain( 'my-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_theme_load_textdomain' );
?>

这段代码告诉 WordPress,my-theme 这个 text domain 的翻译文件在 get_template_directory() . '/languages' 目录下。 after_setup_theme action hook 确保在主题初始化完成后加载翻译文件。

四、 Text Domain:翻译文件的身份证

每个主题或插件都应该有一个唯一的 text domain。 就像身份证一样,用来区分不同的翻译文件。

  • 命名规则: 通常是主题或插件的 slug。 比如,你的主题叫 "Awesome Theme",那么 text domain 可以是 "awesome-theme"。
  • 作用: 告诉 WordPress,哪些翻译字符串属于哪个主题或插件。
  • 使用: 在翻译函数中作为第二个参数传递。 比如 __('Hello, world!', 'awesome-theme')

五、 翻译文件:藏宝图的秘密

翻译文件是 L10n 的核心。 它包含了原始字符串和翻译后的字符串的对应关系。

  • 格式: 通常是 .po.mo 文件。
    • .po 文件是人类可读的文本文件,包含了原始字符串和翻译后的字符串。
    • .mo 文件是机器可读的二进制文件,是 .po 文件的编译版本。 WordPress 使用 .mo 文件来提高翻译效率。
  • 位置: 通常放在主题或插件的 /languages 目录下。
  • 生成: 可以使用 Poedit 等工具来创建和编辑 .po 文件。

.po 文件示例:

msgid "Hello, world!"
msgstr "你好,世界!"

msgid "%s comment"
msgid_plural "%s comments"
msgstr[0] "%s 条评论"
msgstr[1] "%s 条评论"

msgctxt "post type general name"
msgid "Post"
msgstr "文章"

msgctxt "submit button"
msgid "Post"
msgstr "发布"
  • msgid: 原始字符串。
  • msgstr: 翻译后的字符串。
  • msgid_plural: 复数形式的原始字符串(用于 _n() 函数)。
  • msgstr[0]msgstr[1]: 不同数量对应的翻译(用于 _n() 函数)。
  • msgctxt: 上下文(用于 _x() 函数)。

六、 WordPress 如何找到翻译文件?

WordPress 会按照以下顺序查找翻译文件:

  1. WP_LANG_DIR/plugins/{plugin-slug}-{locale}.mo
  2. WP_LANG_DIR/themes/{theme-slug}-{locale}.mo
  3. WP_LANG_DIR/{locale}.mo
  4. wp-content/languages/plugins/{plugin-slug}-{locale}.mo
  5. wp-content/languages/themes/{theme-slug}-{locale}.mo
  6. wp-content/languages/{locale}.mo
  7. wp-includes/languages/{locale}.mo

其中:

  • WP_LANG_DIR: 是 WordPress 语言目录的路径,通常是 wp-content/languages
  • {plugin-slug}: 是插件的 slug。
  • {theme-slug}: 是主题的 slug。
  • {locale}: 是语言区域代码,比如 zh_CN(简体中文) 或 en_US(美式英语)。

WordPress 会找到第一个匹配的 .mo 文件,并加载它。

七、 最佳实践:让你的代码更国际化

  1. 使用统一的 text domain: 为你的主题或插件选择一个唯一的 text domain,并在所有翻译函数中使用它。
  2. 尽早开始国际化: 不要等到项目快完成时才考虑国际化。 越早开始,越容易。
  3. 使用清晰的原始字符串: 原始字符串应该简洁明了,易于理解。 避免使用复杂的语法或缩写。
  4. 提供上下文: 对于含义模糊的字符串,使用 _x() 函数提供上下文,以便翻译人员能够做出更准确的翻译。
  5. 测试你的代码: 确保你的代码在不同的语言环境下都能正常工作。
  6. 使用专业的翻译工具: Poedit 是一个流行的翻译工具,可以帮助你创建和编辑 .po 文件。
  7. 参与 WordPress 翻译项目: 你可以参与 WordPress 官方的翻译项目,为 WordPress 贡献你的力量。

八、 进阶技巧:更上一层楼

  1. 动态 Text Domain: 有时候,你可能需要在运行时动态地设置 text domain。 可以使用 load_textdomain() 函数来实现。
  2. 使用 Transients 缓存翻译: 频繁地读取翻译文件会影响性能。 可以使用 Transients API 来缓存翻译结果。
  3. 自定义翻译函数: 如果 WordPress 提供的翻译函数不能满足你的需求,你可以自定义翻译函数。

九、 常见问题:疑难杂症,一网打尽

  1. 翻译不生效: 检查 text domain 是否正确,翻译文件是否存在,以及是否正确加载了翻译文件。
  2. 乱码: 确保你的文件编码是 UTF-8。
  3. 翻译不完整: 检查 .po 文件是否包含所有需要翻译的字符串。
  4. 性能问题: 使用 Transients API 缓存翻译结果。

十、 总结:L10n,让世界倾听你的声音

WordPress L10n 是一套强大而灵活的国际化和本地化机制。 掌握 L10n 的原理和使用方法,可以让你的 WordPress 站点面向全球用户,让世界倾听你的声音。

好了,今晚的讲座就到这里。 希望大家有所收获。 如果有什么问题,欢迎提问。 谢谢大家! 记得点赞关注哦!

发表回复

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