最近我一直在优化 WordPress 中文技术文章的英文翻译流程。
目前使用的基础方案是:
- WordPress + Polylang
- SlyTranslate 1.9.0
- 智谱 GLM 5.2
- 自定义 MU 插件
- 一次性整篇正文翻译
- 保留 Gutenberg 区块、HTML、短代码和代码块
前面的测试已经证明,GLM 5.2 在技术准确性、英文自然度和长文章稳定性方面表现不错。但在进一步处理 Plaintext 代码块时,又暴露出了几个比较隐蔽的问题:
- Plaintext 中的自然语言是否应该翻译;
- URL、域名、路径等机器内容不能被修改;
- 让模型看到 Plaintext 后,不能破坏 Gutenberg 区块顺序;
- Code Block Pro 区块前台虽然正常,重新打开编辑器却会提示无效;
- 分类、系列和标签虽然写入数据库,前台缓存却可能没有及时更新。
这篇文章记录这一轮完整的排查与修复过程。
一、为什么要单独处理 Plaintext 代码块
技术文章中的 Plaintext 并不都是同一种内容。
有些 Plaintext 只包含 URL、域名或文件路径:
https://en.shuijingwanwq.com/sitemap_index.xml
/data/wwwroot/www.shuijingwanwq.com/
这些内容当然不能翻译。
但也有一些 Plaintext 实际上是界面提示、错误信息或操作结果,例如:
区块包含未预料的或无效的内容。
如果整块保护,英文文章中就会残留中文。
因此,理想方案不是简单地规定:
Plaintext 全部翻译。
也不是:
Plaintext 全部不翻译。
而是让模型结合上下文判断:
- URL、域名、路径、命令和机器数据保持原样;
- 自然语言说明翻译为英文;
- 真实界面证据根据上下文决定是否保留。
这也是这一轮优化的核心目标之一。
二、原生 SlyTranslate 如何保护代码块
SlyTranslate 原有流程会把完整代码块替换成占位符。
例如 Bash、PHP、HTML 等 Code Block Pro 区块,会被替换为类似:
SWQBLOCK000001END
模型看不到代码块内部内容,因此不会误改代码、命令和 HTML。
这种方式对于真正的代码很安全,但用于 Plaintext 时会带来一个问题:
模型完全看不到 Plaintext 中的内容,也无法结合上下文判断其中是否需要翻译。
因此,我在自定义 MU 插件中对 Plaintext 使用了不同处理方式。
三、Plaintext 使用前后标记开放给模型
Plaintext 不再整块替换为单个占位符,而是把其中的文本暴露给模型:
SWQPLAINSTART000001END
这里是 Plaintext 中的内容
SWQPLAINSTOP000001END
模型可以看到中间的文本,也能看到它前后的文章上下文。
这样就能处理类似下面的句子:
本次调整后,在网域资源
shuijingwanwq.com中提交以下 Sitemap。
其中域名和 Sitemap URL 保持不变,普通说明文字则翻译为英文。
对于 Bash、PHP、HTML 等真正的代码块,仍继续使用完整区块占位,不开放内部内容。
四、第一次遇到的问题:模型交换了相邻代码块
测试一篇包含大量 URL、域名和 Plaintext 的长文章时,模型返回的占位符数量完全正确,但两个相邻代码块的顺序发生了交换。
源文结构大致是:
在网域资源:
[域名代码块]
中提交:
[Sitemap URL 代码块]
模型为了适应英文语序,将其改写成:
Submitted:
[Sitemap URL 代码块]
to the domain property:
[域名代码块]
从英文语义看并不错误,但 Gutenberg 区块顺序已经变化。
最终校验结果是:
expected_count=139
actual_count=139
fixed_order_valid=false
占位符没有丢失,也没有重复,只是两个相邻区块交换了位置。
这说明:
仅检查占位符数量还不够,还必须检查顺序。
五、增加 Gutenberg 结构占位符
为避免模型合并段落、删除区块边界或交换代码块,我又增加了:
SWQSTRUCT000001END
所有 Gutenberg 开始和结束注释,例如:
<!-- wp:paragraph -->
<!-- /wp:paragraph -->
都会先转换成固定顺序的结构标记。
模型必须保持:
- 标记数量不变;
- 标记顺序不变;
- 不得合并两个 Gutenberg 区块;
- 不得移动代码块边界。
修复后重新测试,源文和译文的 Gutenberg 区块数量均为:
350
并且:
block_signature_identical=YES
代码块之外的中文残留也降为:
han_outside_code_blocks=0
从文章结构来看,这一阶段已经成功。
六、英文前台正常,编辑器却提示代码块无效
英文文章发布后,前台页面显示完全正常。
但重新打开 WordPress 区块编辑器时,所有 Code Block Pro 区块都出现提示:
区块包含未预料的或无效的内容。

一开始我怀疑是复制按钮的无障碍属性被翻译了。
中文代码块中保存的是:
aria-label="复制"
英文文章中被改成:
aria-label="Copy"
于是先将 79 处 Copy 恢复成 复制。
结果编辑器仍然提示无效,说明问题并不只在 aria-label。
七、逐字节对比后发现 Code Block Pro 被重新生成
我对比了中文文章和英文文章中的第一个 Code Block Pro 区块。
结果如下:
source_bytes=2903
target_bytes=2241
byte_identical=NO
第一处差异出现在 codeHTML。
原始区块中包含:
tabindex="0"
英文区块中这一属性已经丢失。
继续比较 PHP 代码块后,又发现:
\u003e
被重新转义成:
\u0026gt;
也就是说,问题不是模型修改了代码,而是代码块在恢复或保存阶段被重新序列化了。
Code Block Pro 的同一份内容并不只保存一次,而是同时存在于:
- Gutenberg 区块 JSON 中的
code - Gutenberg 区块 JSON 中的
codeHTML <textarea><pre class="shiki">- 实际显示代码所使用的
<code>和<span>
只要其中一个位置的格式、转义方式或属性顺序发生变化,Gutenberg 就可能认为区块无效。
八、用 GLM 5.1 做对照测试
为了确认问题来自 SlyTranslate 原生插件,还是自定义的 GLM 5.2 整篇翻译流程,我又做了一次对照测试。
我选择一篇中文草稿,使用 SlyTranslate 原生流程和 GLM 5.1 翻译。
翻译后的 Plaintext 和 PHP 代码块在编辑器中完全正常,没有出现无效提示。

这次对照基本确认:
问题不在 Code Block Pro,也不在 SlyTranslate 原生保存逻辑,而在自定义的 GLM 5.2 整篇翻译流程。
九、确认 SlyTranslate 输入阶段没有修改代码块
随后检查 SlyTranslate 的 prepare_single()。
源文和准备出的正文单元结果为:
source_chars=377043
unit_chars=377043
whole_content_identical=YES
Code Block Pro 的结果为:
source_code_blocks=79
unit_code_blocks=79
code_blocks_identical=79
code_blocks_different=0
这证明 SlyTranslate 在进入模型翻译之前,并没有修改任何代码块。
真正的问题发生在模型返回后的恢复和保存阶段。
十、Plaintext 原来的恢复方式存在隐患
此前 Plaintext 翻译完成后,插件会调用类似:
rebuild_plaintext_code_block_pro()
重新构建整个 Code Block Pro 区块。
其中包括:
- 重新解析 Gutenberg JSON;
- 修改
code; - 重建
codeHTML; - 重建
<textarea>; - 重建 Shiki
<pre>; - 重新序列化区块属性。
这种方式虽然能够更新 Plaintext 内容,但很容易改变原始区块中的其他细节。
最终方案需要区分两类 Plaintext。
1. 机器型 Plaintext
例如:
https://en.shuijingwanwq.com/
/data/wwwroot/www.shuijingwanwq.com/
模型可以看到这些内容,以理解文章上下文。
但恢复时直接放回完整原始 Code Block Pro 区块,不重新生成任何 JSON 或 HTML。
2. 自然语言 Plaintext
例如错误提示、操作输出或说明文字。
这些内容仍交给 GLM 5.2 判断并翻译。
同时保留 Plaintext 智能翻译能力,而不是退回到整块不翻译。
十一、最终问题出现在 WordPress 保存过滤器
SlyTranslate 的 Polylang 适配器最终使用:
wp_update_post( wp_slash( $update_data ) );
保存英文正文。
WordPress 在保存正文时,可能经过 content_save_pre、KSES 或其他过滤器。
SlyTranslate 自己的源码注释中也明确提到,KSES 可能:
- 删除
tabindex; - 删除自定义区块的
data-*属性; - 修改区分大小写的 SVG 属性;
- 导致 Code Block Pro 被 Gutenberg 判定无效。
虽然当前登录用户是管理员,但自定义整篇翻译流程最终生成的正文,仍可能在完整保存过程中发生变化。
因此,仅靠占位符恢复还不够。
还需要保证:
已经校验通过的最终正文,写入数据库后必须与保存前逐字节一致。
十二、增加最终精确写回和 SHA-256 校验
在 MU 插件 2026.07.16.12 中,最终采用了两阶段保存。
第一阶段:仍由 SlyTranslate 正常处理
SlyTranslate 继续负责:
- 创建或覆盖英文文章;
- 设置文章状态;
- 设置 Polylang 语言;
- 建立中英文文章关联;
- 复制分类、系列和标签;
- 保存 Meta。
第二阶段:精确写回正文
取得英文文章 ID 后,再将已经通过完整结构校验的英文正文直接写回 post_content。
写回前计算:
expected_hash
重新读取数据库后再计算:
stored_hash
然后比较:
expected_hash === stored_hash
如果哈希不同,立即返回错误,不再把这次翻译视为成功。
这一做法避开了最终保存过滤器对 Code Block Pro 区块的再次改写,同时不影响 SlyTranslate 原有的文章、语言和术语处理流程。
十三、最终验证结果
修复后重新覆盖翻译文章:
中文文章 ID:19383
英文文章 ID:19494
英文编辑器中的所有 Code Block Pro 区块恢复正常,不再提示无效内容。
最终逐区块比较结果:
source_total=79
target_total=79
bash: total=5, identical=5, different=0
html: total=3, identical=3, different=0
php: total=3, identical=3, different=0
plaintext: total=68, identical=47, different=21
这个结果基本符合预期:
- 5 个 Bash 区块全部逐字节一致;
- 3 个 HTML 区块全部逐字节一致;
- 3 个 PHP 区块全部逐字节一致;
- 68 个 Plaintext 中:
- 47 个保持原样;
- 21 个根据上下文完成翻译。
也就是说:
真正的代码、URL、域名、路径和机器数据保持不变;Plaintext 中的自然语言由 GLM 5.2 智能判断并翻译。
这正是本轮优化最希望达到的结果。
十四、分类、系列和标签前台未及时更新
英文文章编辑器中已经可以看到正确的英文分类、系列和标签。
数据库检查结果也完全正常:
category=SEO Tools | Yoast SEO
series=Blog SEO Log
post_tag=Cloudflare | English Subdomain | Polylang | Search Engine Submission | Thin Content Tags | W3 Total Cache | WordPress | XML Sitemap | Yoast SEO
但英文前台一开始仍显示:
Uncategorized
随机查询参数绕过页面缓存后,部分标签已经更新,但分类仍然是旧值。
最终在英文 Host 环境下清理:
- 文章对象缓存;
- 术语关系缓存;
- category、series、post_tag 的关系缓存;
前台才恢复正确内容。

这说明:
分类、系列和标签没有丢失,真正的问题是英文域名对应的 W3 Total Cache / Redis 对象缓存没有及时失效。
后台执行“清除所有缓存”或单独清除页面缓存、对象缓存,并不一定能清理到英文 Host 对应的缓存空间。
这与此前英文站出现语言、Canonical 和分类链接缓存异常的问题属于同一类多域名缓存边界。
十五、系列顺序出现了新的异常
缓存清理完成后,英文文章已经正确出现在 Blog SEO Log 系列中。
但它被排到了系列第一篇,而不是最后一篇。
后台 PublishPress Series 的“当前部分”也显示:
当前没有部分编号


这个问题与 Code Block Pro、Plaintext 翻译和正文保存已经不是同一个问题。
目前可以确认的是:
- 系列术语绑定正确;
- 英文文章已经属于正确系列;
- 文章没有明确的系列编号;
- PublishPress Series 因此将其显示在第一位。
这一问题暂时留到后续单独排查,不继续混入本轮翻译插件优化。
十六、本轮最终稳定版本
当前使用的 MU 插件版本为:
2026.07.16.12
这一版本已经验证:
- GLM 5.2 可以一次性翻译完整长文章;
- Gutenberg 区块数量和顺序保持一致;
- 区块边界不会被模型合并;
- Bash、PHP、HTML 完整逐字节保留;
- Plaintext 可以智能判断是否翻译;
- URL、域名和路径保持原样;
- Code Block Pro 编辑器不再提示无效内容;
- 最终正文写入带 SHA-256 校验;
- 分类、系列和标签可以正常写入数据库。
目前剩余的主要问题已经不再是翻译质量本身,而是:
- 多域名环境下的对象缓存失效;
- PublishPress Series 的文章顺序同步。
这两项更适合后续分别处理。
十七、最终体会
这一轮排查最重要的收获,是不能只根据前台页面判断 Gutenberg 内容是否安全。
前台能够正常显示,并不代表:
- 区块结构仍然有效;
- 编辑器可以继续修改;
- Code Block Pro 的序列化内容没有变化;
- JSON、HTML 和 Shiki 代码完全一致。
对于技术博客的自动翻译流程,真正需要同时满足的是:
- 英文自然;
- 技术内容准确;
- 代码不被修改;
- Gutenberg 结构完整;
- 编辑器可以正常重新打开;
- 数据库存储结果可验证;
- 分类、标签和系列关系能够正确刷新。
相比单纯追求某一篇文章更自然的英文,这些结构和保存层面的稳定性,才更决定自动翻译方案能否长期投入生产。
需要长期技术维护或远程问题排查?
我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。
如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:
- ✅ PHP / Laravel / Yii2 老项目无人维护
- ✅ Go / Gin 后端接口需要排查或优化
- ✅ WordPress 网站访问慢、报错或插件冲突
- ✅ Nginx / MySQL / Redis / Linux 服务器异常
- ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
- ✅ 需要长期远程技术支持或兼职维护
更多介绍请查看:关于我 & 合作
微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan


发表回复