功能插件定价资源
更改语言
资源WordPress JSON 翻译:翻译区块编辑器 JavaScript

WordPress JSON 翻译:翻译区块编辑器 JavaScript

SimplePoTranslate Team2026年5月27日
WordPress JSON 翻译:翻译区块编辑器 JavaScript

你已经翻译了你的插件。PHP 字符串在管理设置、前端模板、电子邮件通知中都完美显示——全部都已本地化。然后你打开区块编辑器,你自定义的 Gutenberg 区块中的每个标签都顽固地、嘲讽地显示为英文。“添加项目”按钮、检查器面板标题、占位符文本。你的 .mo 文件已经加载。那么,为什么这些字符串没有被翻译呢?

因为自 WordPress 5.0 和 Gutenberg 问世以来,JavaScript 字符串根本不来源于你的 .mo 文件。它们需要一个完全独立的、针对每个脚本的 WordPress JSON 翻译文件——如果你不生成它,无论你的 .po 文件有多完整,你的区块编辑器都将保持英文。这是现代 WordPress 开发中最常见和最令人困惑的本地化缺陷之一。本指南将精确解释为何会出现这种情况、JSON 翻译系统如何工作、让所有人困惑的 MD5 散列文件名,以及解决它的完整工具链。

为什么你的区块编辑器字符串保持英文

简而言之:PHP 和 JavaScript 在 WordPress 中使用两种完全不同的翻译交付系统,而你的 .mo 文件只为 PHP 系统提供数据。

两个翻译系统,一个插件

当 WordPress 运行 load_plugin_textdomain() 时,它会将你编译好的 .mo 文件读入 PHP 内存。你的 PHP 代码中每一个 __()_e()_x() 调用都会在那里查找其翻译。这之所以有效,是因为 PHP 在服务器端渲染——.mo 数据就在同一个进程中。

JavaScript 则不同。你的区块代码在浏览器中运行,远在 PHP 完成之后。它无法访问服务器端的 .mo 文件。相反,@wordpress/i18n 包——Gettext 的 JS 等价物,将 __()_x()sprintf() 暴露给你的脚本——期望翻译以 JSON 有效负载的形式交付,并附加到需要它们的特定脚本上。

未翻译的 JS 字符串会发生什么

因此,一个包含如下字符串的区块:

import { __ } from '@wordpress/i18n';

registerBlockType( 'myplugin/feature-box', {
    title: __( 'Feature Box', 'myplugin' ),
    edit: () => {
        return <Button>{ __( 'Add Item', 'myplugin' ) }</Button>;
    },
} );

将永远无法在你的 .mo 文件中找到“Feature Box”或“Add Item”,因为浏览器从不读取 .mo 文件。这些字符串需要以 JSON 形式送达,并连接到这个确切的脚本句柄。如果你没有设置好,JS __() 调用会直接返回原始英文——静默执行,控制台中不会有任何错误。

使用 wp_set_script_translations() 连接 JSON 翻译

你的脚本与其 JSON 翻译之间的桥梁是一个简单的 PHP 函数:wp_set_script_translations()。答案是:“WordPress 如何知道哪个 JSON 文件属于哪个脚本”:你告诉它,通过注册脚本,然后声明其文本域以及 JSON 所在的文件夹。

注册脚本及其翻译文件夹

add_action( 'init', function () {
    wp_register_script(
        'myplugin-editor',
        plugins_url( 'build/index.js', __FILE__ ),
        array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
        '1.0.0'
    );

    // Tell WordPress where this script's JSON translations live
    wp_set_script_translations(
        'myplugin-editor',        // the registered script handle
        'myplugin',               // text domain
        plugin_dir_path( __FILE__ ) . 'languages'
    );
} );

当编辑器加载 myplugin-editor 时,WordPress 现在知道会在你的 languages/ 文件夹中查找与此脚本和当前用户区域设置匹配的 JSON 文件。如果找到,它会在你的脚本运行之前注入翻译,并且 JS __() 调用将正确解析。你传递的句柄必须与已注册的脚本完全匹配——不匹配或缺失的句柄是翻译静默失败的第二常见原因。

没人预料到的 MD5 散列文件名

这是一个几乎让所有人望而却步的细节。WordPress 查找的 JSON 文件不会被命名为 myplugin-fr_FR.json 这样整洁的名称。它会使用脚本源路径的 MD5 散列来命名:

解码文件名模式

myplugin-fr_FR-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6.json

模式是 {textdomain}-{locale}-{md5}.json,其中散列是注册的脚本文件相对路径(例如 build/index.js)的 MD5 值。WordPress 在运行时计算此散列以找到适用于相应脚本的正确 JSON。如果你手动命名文件,WordPress 将找不到它,你就会认为系统出了问题,而实际上它只是在寻找一个不同的文件名。

你不需要自己计算散列。WP-CLI i18n 命令会为你完成,这正是你必须使用工具而不是手动创建这些文件的原因。然而,理解散列的存在,可以在 languages/ 中存在 JSON 文件却仍被忽略时为你节省数小时——这几乎总是因为脚本路径更改导致的文件名散列不匹配。

完整工作流程:从 make-potmake-json

好消息是,你将翻译保存在你已使用的相同 .po 文件中。JSON 是最后生成的派生工件。答案是:“我需要单独维护 JS 字符串吗?”不是——它们与你的 PHP 字符串一起存在于同一个 .pot/.po 中,并且一个额外的命令会将 JS 字符串分离到 JSON 中。

四步命令流程

以下是完整的流程:

# 1. Extract ALL translatable strings (PHP and JS) into one template
wp i18n make-pot . languages/myplugin.pot

# 2. Translate languages/myplugin-fr_FR.po as usual (Poedit, AI, etc.)

# 3. Compile the .mo for PHP strings (server-side, as always)
wp i18n make-mo languages/

# 4. Generate the MD5-hashed JSON files for JS strings
wp i18n make-json languages/ --no-purge

第一步的 make-pot 足够智能,可以扫描你的 .php 文件和 .js/.jsx 源代码,因此每个区域设置一个 .po 文件即可包含所有内容。第四步的 make-json 读取每个翻译后的 .po 文件,找到来自 JavaScript 文件的条目,并为每个脚本写出一个正确散列的 JSON 文件。--no-purge 标志也将 JS 字符串保留在你的 .po 中,这样后续的 make-mo 就不会丢失它们——如果没有它,make-json 会从 .po 中删除 JS 条目,这会让那些以错误顺序运行命令的人感到惊讶。

生成的 JSON 文件看起来像 Jed 格式的翻译集:

{
  "translation-revision-date": "2026-06-12 10:00+0000",
  "generator": "WP-CLI/2.x",
  "domain": "messages",
  "locale_data": {
    "messages": {
      "": { "domain": "messages", "lang": "fr_FR" },
      "Feature Box": [ "Bloc fonctionnalité" ],
      "Add Item": [ "Ajouter un élément" ]
    }
  }
}

WordPress 在你的脚本运行之前读取 locale_data 并将其提供给 @wordpress/i18n。现在浏览器中的 __( 'Add Item', 'myplugin' ) 返回 Ajouter un élément,你的区块编辑器最终也实现了本地化。

这与 i18next JSON 有何不同

这两个系统都使用 JSON,都针对 JavaScript,这种表面上的相似性导致了真正的困惑。它们不可互换。WordPress 区块编辑器 JSON 是一个 Gettext 派生的、MD5 散列的、由 @wordpress/i18n 消费的每脚本有效负载。i18next JSON 是一个由 react-i18nextnext-intl 消费的扁平或嵌套键值文件,它有自己的 {{interpolation}} 语法和复数键约定。

如果你在 WordPress 之外使用纯 React 或 Next.js,你会希望采用 i18next 方法,我们将在在 React 和 Next.js 中翻译 i18next JSON中介绍。在 WordPress 内部,你需要上述的 make-json 工作流程。将它们混淆——例如,手动编写扁平的 i18next 风格 JSON 并期望 wp_set_script_translations() 加载它——是行不通的,因为 WordPress 正在寻找散列的 Jed 格式,而不是任意的键值对。

一处来源,满足所有格式需求

所有这一切的脆弱之处在于中间的翻译步骤。你的 .po 文件同时为 .mo (PHP) 和 JSON (JavaScript) 提供数据,因此一个拙劣的翻译——一个损坏的 %s、一个破损的 <strong> 标签、一个重命名的复数形式——会同时污染两种输出。而且由于 JS 字符串在浏览器中是异步加载的,所以那里的结构性错误通常表现为空白标签或硬崩溃,而不是优雅的降级。

一次上传,覆盖 PHP 和 JavaScript

这就是理解 Gettext 结构的翻译流程发挥作用的地方。SimplePoTranslate 接收一个源 .po.pot 文件,并从一次上传中生成多种格式的清晰翻译输出——.po.mo.json.php.xliff——这样你就无需为 PHP 和 JavaScript 层拼凑单独的工具了。它的 语法锁定 功能可以固定 %s%1$s{count} 和内联 HTML,这对于区块编辑器字符串尤为重要,因为一个损坏的占位符可能会导致整个编辑器面板崩溃。我们将在一处文件,五种格式输出中深入探讨这种一源多输出模型。

你仍然需要运行 make-json 来生成 WordPress 期望的散列文件——这一步是 WordPress 特有的,并保留在你的构建中。但翻译本身,即最有可能破坏你的 JS 字符串的部分,则由上下文感知的引擎处理,而不是查找替换脚本。

结论

你的区块编辑器保持英文的原因是结构性的,而非一个 bug:WordPress JSON 翻译是一个独立于 .mo 文件的交付系统,专门构建是因为浏览器无法读取服务器端的 Gettext 数据。一旦你理解了 JavaScript 字符串需要由 wp i18n make-json 生成的、针对每个脚本的、MD5 散列的 JSON 文件,并通过 wp_set_script_translations() 连接起来,那么修复就是机械性的。将你的字符串保存在一个 .po 文件中,为 PHP 编译 .mo,并为 JS 运行 make-json

正确执行翻译步骤,两种输出都能成功。如果弄错了,你可能要花一个下午来调试空白的编辑器面板。

准备好从一个干净的来源翻译你的 WordPress JSON 和 PHP 字符串了吗?免费试用 SimplePoTranslate — 无需信用卡。免费套餐支持翻译真实的 .po.pot 文件,并提供占位符安全的语法锁定功能,确保你的区块编辑器字符串正确无误地发布。