跳到主内容

WordPress 国际化(i18n)落地:文本域、load_theme_textdomain 与 JS 翻译的完整流程

100%
WordPress 国际化(i18n)落地:文本域、load_theme_textdomain 与 JS 翻译的完整流程

WordPress 国际化(i18n)只要做对三件事:所有面向用户的字符串都过一遍 i18n 函数、 Text Domain 全局统一且用短横线小写、 PHP 用 load_theme_textdomain() 而 JS 用 wp_set_script_translations() 分别加载翻译。漏掉任意一环,最典型的症状就是”翻译文件明明在,界面还是英文”。

本文按官方主题国际化文档与区块编辑器 i18n 指南,把文本域约定、 MO 文件命名、 JS 翻译接线和 WP-CLI 生成流程一次讲透,并附上线前检查清单。

Text Domain:三条不能破的约定

Text Domain 是 WordPress 区分不同翻译来源的唯一标识。约定如下:

  • 必须小写、用短横线不用下划线:my-theme,不是 my_theme。
  • 托管在 WordPress.org 的主题,Text Domain 必须等于主题 URL 的 slug,否则 translate.wordpress.org 的翻译包对不上。
  • 必须写成字符串字面量,不能传变量——解析工具靠静态分析提取字符串。
// 错:text domain 用了变量,解析工具提取不到
__( 'Translate me.', $text_domain );

// 对:字面量
__( 'Translate me.', 'my-theme' );

同一个 Text Domain 要出现在三个地方:style.css 头部、所有 i18n 函数的第二个参数、以及 load_theme_textdomain() 的调用参数。

style.css 头部与 Domain Path

头部写法(注意是注释块里的裸字段):

/*
Theme Name: My Theme
Author: Theme Author
Text Domain: my-theme
Domain Path: /languages
*/

Domain Path 仅在翻译文件不在默认 languages 目录时才需要,且必须以斜杠开头。写它的意义是:主题未启用时,WordPress 也能找到翻译来翻译主题元数据(比如主题描述)。

很多”后台主题名能翻译、前台内容不能翻译”或反之的问题,根因都是头部 Text Domain 与代码里的不一致。先全局搜索比对这三个位置,再怀疑别的。

常用 i18n 函数速查

函数用途输出是否转义
__()返回翻译字符串否,需自行转义
_e()直接输出翻译字符串否
esc_html__() / esc_html_e()翻译并做 HTML 转义是
esc_attr__() / esc_attr_e()翻译并做属性转义是
_n()单复数形式否
_x()带上下文消歧的翻译否

三条实践原则:

  • 字符串里不要拼接变量:__( 'Hello ' . $name ) 无法翻译,应该用占位符 sprintf( __( 'Hello %s', 'my-theme' ), $name )。
  • 不要翻译 HTML 标签,标签留在外面,只包文本内容。
  • 输出到 HTML 一律用 esc_ 版本,别自己拼。

加载 PHP 翻译

function my_theme_load_theme_textdomain() {
    load_theme_textdomain( 'my-theme', get_template_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'my_theme_load_theme_textdomain' );

MO 文件命名有个容易踩的二分规则:

放置位置文件命名
主题目录内(如 /languages)zh_CN.mo,即 {locale}.mo
wp-content/languages/themes/my-theme-zh_CN.mo,即 {text-domain}-{locale}.mo

自 WordPress 4.6 起,核心会自动检查 wp-content/languages 下来自 translate.wordpress.org 的翻译包。也就是说,通过官方渠道翻译的插件可以不再手写 load_plugin_textdomain();但主题仍建议显式加载,保证自带翻译可用。

JS 翻译:三步接线

区块编辑器与 modern 主题大量逻辑在 JS 里,这部分需要单独处理:

  1. 注册脚本时声明 wp-i18n 依赖:
wp_register_script(
    'my-theme-editor',
    get_template_directory_uri() . '/assets/editor.js',
    array( 'wp-blocks', 'wp-i18n', 'wp-block-editor' ),
    '1.0.0',
    true
);
  1. 代码里用 wp.i18n 的函数,签名与 PHP 版一一对应:
import { __, _n, _x } from '@wordpress/i18n';

const title = __( 'Settings', 'my-theme' );
  1. 告诉 WordPress 这个脚本有翻译:
wp_set_script_translations( 'my-theme-editor', 'my-theme' );

第三个参数可选,用于指定自带翻译文件目录:wp_set_script_translations( $handle, $domain, get_template_directory() . '/languages' )。不传时,WordPress 会去 translate.wordpress.org 找对应语言包并在脚本执行前注入。

JS 翻译文件是 JED 1.x JSON,不是 .mo(展开)
JS 侧的翻译文件必须导出为 JED 1.x JSON 格式,通常由 .po 转换而来。用 WP-CLI 生成 POT 后交由 Poedit 或 GlotPress 产出各语言 .po,再在构建流程里转成 {domain}-{locale}-{handle}.json。手写 JSON 很容易在复数形式上出错,不建议。

用 WP-CLI 生成 POT

mkdir languages
wp i18n make-pot ./ languages/my-theme.pot

生成的 POT 里 msgid 是待翻译原文,msgstr 恒为空。新增语种时复制一份改名即可:

cp my-theme.pot my-theme-zh_CN.po
# 翻译后生成 .mo

  1. 确定 Text Domain(与主题目录/slug 一致,小写短横线),写进 style.css 头部并加 Domain Path 。

  2. 全局搜索硬编码中文/英文输出,逐个替换为 __() 或 esc_html_e(),第二个参数统一为文本域字面量。

  3. 把所有字符串拼接改写成 sprintf + %s 占位符形式。

  4. 在 after_setup_theme 钩子里调用 load_theme_textdomain(),指向 languages 目录。

  5. 给含 JS 的 handle 加 wp-i18n 依赖,并调用 wp_set_script_translations()。

  6. 用 wp i18n make-pot 生成 POT,检查是否有遗留未包裹字符串,再交给翻译流程。

上线前检查清单

  • style.css 头部 Text Domain 与代码里完全一致。
  • 没有用变量传递 Text Domain 。
  • 没有拼接式字符串,全部使用占位符。
  • 输出到 HTML 的字符串走 esc_ 系列函数。
  • MO 文件命名符合所在目录规则。
  • 所有含 JS 的 handle 都已调用 wp_set_script_translations()。
  • 用 wp i18n make-pot 复扫,未包裹字符串数为 0 。

WordPress 主题国际化中 Text Domain 应该怎么写?
全部小写、用短横线分隔,并与主题在 WordPress.org 上的 slug 完全一致。它要同时出现在 style.css 头部、所有 i18n 函数的第二个参数、以及 load_theme_textdomain() 调用中,三处必须一致。

翻译文件放在主题目录和 wp-content/languages/themes 下,命名有什么区别?
主题目录内用 {locale}.mo(如 zh_CN.mo);放到 wp-content/languages/themes/ 则用 {text-domain}-{locale}.mo(如 my-theme-zh_CN.mo)。命名错了加载不到,是翻译不生效的高频原因。

主题的 JavaScript 里的文字怎么翻译?
注册脚本时把 wp-i18n 加入依赖数组,代码里用 @wordpress/i18n 提供的 __()、_n()、_x(),最后在 PHP 侧调用 wp_set_script_translations( handle, textDomain ) 完成接线。

为什么我的 .po 翻译好了但前台还是英文?
按顺序排查:MO 是否编译生成(只有 .po 不生效)、 MO 命名是否符合所在目录规则、 Text Domain 是否三处一致、加载函数是否挂在 after_setup_theme 上、以及主题目录是否真的可读。

已经发布到 WordPress.org 的主题还需要调用 load_plugin_textdomain 吗?
自 4.6 起核心会自动加载 wp-content/languages 下来自 translate.wordpress.org 的语言包,插件可以不调用;但主题为了让自带翻译可用,仍建议显式调用 load_theme_textdomain()。

相关阅读

本文依据 WordPress 官方主题国际化文档与区块编辑器 i18n 指南整理,检查清单与常见坑为本站实践补充。

这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

795文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示