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 也能找到翻译来翻译主题元数据(比如主题描述)。
常用 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 里,这部分需要单独处理:
- 注册脚本时声明
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
);
- 代码里用
wp.i18n的函数,签名与 PHP 版一一对应:
import { __, _n, _x } from '@wordpress/i18n';
const title = __( 'Settings', 'my-theme' );
- 告诉 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(展开)
.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
- 确定 Text Domain(与主题目录/slug 一致,小写短横线),写进 style.css 头部并加 Domain Path 。
- 全局搜索硬编码中文/英文输出,逐个替换为 __() 或 esc_html_e(),第二个参数统一为文本域字面量。
- 把所有字符串拼接改写成 sprintf + %s 占位符形式。
- 在 after_setup_theme 钩子里调用 load_theme_textdomain(),指向 languages 目录。
- 给含 JS 的 handle 加 wp-i18n 依赖,并调用 wp_set_script_translations()。
- 用 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 应该怎么写?
翻译文件放在主题目录和 wp-content/languages/themes 下,命名有什么区别?
主题的 JavaScript 里的文字怎么翻译?
为什么我的 .po 翻译好了但前台还是英文?
已经发布到 WordPress.org 的主题还需要调用 load_plugin_textdomain 吗?
相关阅读
- WordPress 全站编辑(FSE)指南:站点编辑器与区块主题
- WordPress 子主题怎么做:覆盖模板、样式与 functions 的正确姿势
- WordPress Abilities API 是什么:给 AI 暴露站点能力的统一层
本文依据 WordPress 官方主题国际化文档与区块编辑器 i18n 指南整理,检查清单与常见坑为本站实践补充。







评论 (0)