WP DEVELOP

EP129. “插件国际化:文本域、翻译函数与 Loco Translate”

首页 WordPress 开发课程 插件开发 CH1:PHP 基础 · EP129
约 12 分钟· #EP129#插件开发 CH1:PHP 基础
🔒 登录后可标记已读

学习怎么让插件方便被翻译成别的语言。分两步:① 在代码层面「挖空」写死的英文字符串——给插件头信息加 Text Domain/Domain Path,用 __()/esc_html__() 等翻译函数包住需要翻译的文字,并在 init 钩子里加载对应语言的翻译文件;② 用免费插件 Loco Translate 扫描代码自动生成翻译模板(.pot 文件),再基于模板为具体语言(这里演示西班牙语)逐条填写翻译,生成 .po/.mo 文件。最后提醒一个安全细节:翻译内容本质上也是「外部输入」,输出到页面时同样需要转义,防止翻译文件被篡改后注入恶意 HTML/JS。


涉及文件

  • wp-content/plugins/our-first-unique-plugin/our-first-unique-plugin.php (修改)
  • wp-content/plugins/our-first-unique-plugin/languages/ (新建目录,存放翻译文件;.pot/.po/.mo 由 Loco Translate 插件生成,不是手写代码)

代码实现

插件头信息新增 Text Domain / Domain Path

/*
  Plugin Name: Our Test Plugin
  Description: A truly amazing plugin.
  Version: 1.0
  Author: Brad
  Author URI: https://www.udemy.com/user/bradschiff/
  Text Domain: wcpdomain
  Domain Path: /languages
*/

__construct() 新增加载语言文件的钩子

add_action('init', array($this, 'languages'));

languages():加载对应语言的翻译文件

function languages() {
  load_plugin_textdomain('wcpdomain', false, dirname(plugin_basename(__FILE__)) . '/languages');
}

用翻译函数「挖空」两处示例文字(其余文字这一讲未处理,只做两个示范):

// 后台 Settings 菜单里的链接文字
function adminPage() {
  add_options_page('Word Count Settings', __('Word Count', 'wcpdomain'), 'manage_options', 'word-count-settings-page', array($this, 'ourHTML'));
}

// 正文统计信息里 "This post has ... words." 这句
if (get_option('wcp_wordcount', '1')) {
  $html .= esc_html__('This post has', 'wcpdomain') . ' ' . $wordCount . ' ' . __('words', 'wcpdomain') . '.<br>';
}

关键改动点:

  • Text Domain 是这个插件所有可翻译字符串的统一标识符(自己取名,只要唯一),Domain Path 指向存放翻译文件的目录——两者都是标准的插件头信息属性,Loco Translate 等翻译工具靠这两个值定位该扫描/加载哪些文件
  • load_plugin_textdomain($domain, $deprecated, $path):第二个参数官方文档标注为已弃用,固定传 false;第三个参数用 dirname(plugin_basename(__FILE__)) . '/languages' 拼出「相对于插件目录的语言文件夹路径」——plugin_basename(__FILE__) 拿到当前文件相对 wp-content/plugins/ 的路径,dirname() 再去掉文件名只留目录部分
  • __($text, $domain) 是最基础的翻译函数:如果当前系统语言有对应的翻译,返回翻译后的文字;没有的话原样返回英文原文(所以即使还没做任何翻译,代码本身也完全正常工作,不会报错或显示空白)
  • esc_html__($text, $domain)esc_html(__($text, $domain)) 的简写组合函数——同时做翻译和 HTML 转义,专门用在「翻译结果会直接输出到页面」的场景,比分开写两个函数调用更简洁
  • 「words」这个单词被单独抠出来翻译(而不是整句话一起翻译成一个字符串),是因为句子中间还嵌着动态的数字($wordCount),只能把「不变的文字片段」分别处理——转录里特别提到要把原本硬编码在字符串里的空格挪到拼接的地方,这样翻译人员翻译「words」这一个词时不需要自己再操心前后要不要带空格
  • 这一讲只做了两处「挖空」作为示范(后台菜单链接文字、正文统计句的一部分),插件里其他硬编码的英文字符串(比如字符统计、阅读时间那两句)没有做国际化处理,只是教学演示用,不是插件已经全部完成翻译准备

Loco Translate 操作流程(后台插件,非代码)

  1. 安装启用 Loco Translate 插件(作者推荐它优于 PoEdit,理由是对 WordPress 场景优化更到位,且后续章节还能用它处理 JavaScript 里的翻译字符串)
  2. 后台 Loco Translate → Plugins → 找到目标插件 → Create Template(自动扫描代码里所有 __()/esc_html__() 等函数调用,生成 .pot 模板文件到 languages/ 目录)
  3. New Language → 选择目标语言(示例选西班牙语)→ 保存位置选 Author(存进插件自己的 languages/ 目录)→ Start Translating
  4. 对模板里列出的每条原文,在下方翻译框填入对应语言的译文 → Save,生成 .po(可读文本)和 .mo(编译后供程序读取)两个文件
  5. 后台 Settings → General → Site Language 切换成对应语言,即可看到已翻译的部分自动生效,没翻译的部分依然显示英文原文

[截图:wp-admin Loco Translate 插件的翻译编辑界面,左侧原文列表、右侧填写西班牙语译文]

[截图:切换 Site Language 为西班牙语后,前台已翻译部分(如 Settings 菜单里的 Word Count 链接文字)实际显示效果]


Hook / Function 速查

名称类型用途
Text Domain / Domain Path插件头信息属性标识可翻译字符串所属的域名和翻译文件存放路径
initWP hookWordPress 初始化时触发,是加载文本域的标准挂载点
load_plugin_textdomain($domain, $deprecated, $path)WP 内建 function加载指定文本域对应当前系统语言的翻译文件
plugin_basename(__FILE__)WP 内建 function获取当前文件相对于 wp-content/plugins/ 的路径
__($text, $domain)WP 内建 function翻译函数,没有对应翻译时原样返回原文
esc_html__($text, $domain)WP 内建 function__() + esc_html() 的组合简写,翻译并转义

常见坑

  • 把包含动态数字/变量的整句话当成一个整体去翻译——不同语言的语序、单复数规则不同,硬把整句锁死会让翻译没法适配语言差异,应该只抠出真正需要翻译的固定文字片段
  • 翻译结果直接输出、不转义——翻译文件本质上也是一种「外部输入」(哪怕看似只有开发者/受信任的翻译人员会编辑),转录演示了往翻译文本里塞入 <strong> 标签就能让页面把它当成真实 HTML 渲染,如果换成 <script> 就是真实的 XSS 风险,所以只要是会直接输出到页面的翻译文字,就该用 esc_html__() 而不是普通的 __()
  • 忘记在 Text Domain 属性和 load_plugin_textdomain()/__() 各处调用里保持完全一致的字符串——任何一处拼错都会导致翻译系统找不到对应的文本域,所有翻译都不生效

延伸 / 后续讲座会用到

第一个插件(字数统计)彻底完工。下一讲开始做本章最后一个 PHP 插件——敏感词过滤器,会用到跟这次不一样的做法:完全手写后台设置页(不用 Settings API 自动生成表单),并且第一次学怎么给后台菜单项配自定义 SVG 图标、建独立的顶级菜单。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 23, EP129