WP 博客多语言化实操
(5) 博客菜单翻译实操
在为 WordPress 博客建立自动英文翻译流程时,我一直希望解决一个核心问题:
在保留 Gutenberg 区块、代码块、HTML 标签和链接结构的前提下,让 AI 一次读取文章的完整上下文,从而获得更接近人工整篇重译的英文质量。
SlyTranslate 原有的区块翻译机制更重视兼容性和稳定性,会把 Gutenberg 内容拆分成多个片段,再分别调用模型。对于普通短文,这种方式通常可以接受;但对于包含大量技术术语、上下文关联和代码块的长文,分块翻译容易导致术语不统一、句式割裂,以及前后章节缺少连贯性。
因此,这次测试没有继续提高分块字符上限,而是坚持采用:
- 标题单独翻译;
- 摘要单独翻译;
- 正文只调用 GLM-5.2 一次;
- 代码块和行内代码先替换为占位符;
- 翻译结束后恢复原始 Gutenberg 内容;
- 全部验证通过后,再保存到 Polylang 英文文章。
最终,正文单次调用确实成功了。但后台仍返回了一个看起来十分严重的错误:
{
"code": "invalid_translation_runaway_output",
"message": "The translated output is far longer than the source text, indicating the model entered a runaway generation loop or appended hallucinated content.",
"data": null
}
经过执行路径跟踪后才发现:这并不是模型真的出现了失控生成,而是自定义单次翻译已经完成后,SlyTranslate 原始翻译流程又被执行了一遍。
本文记录这次从“模型输出异常”到“REST 路由重复执行”的完整排查过程。
一、测试文章的复杂度
本次测试使用的是一篇真实的 WordPress 技术长文:
文章 ID:19372
正文字符数:232908
正文大小:242338 Bytes
顶层 Gutenberg 区块:463
Code Block Pro 区块:54
文章内容主要记录 WordPress、Polylang、W3 Total Cache、WPCode、Nginx 和多域名环境下的链接修复过程。
54 个 Code Block Pro 区块中不仅包含命令和配置,还保存了完整的:
textarea原始代码;pre.shiki高亮 HTML;- Gutenberg 区块注释;
- JSON 区块属性;
- 路径、域名和 URL;
- Bash、PHP、JavaScript 与纯文本内容。
如果直接把整篇 Gutenberg 源代码交给模型翻译,不仅会浪费大量上下文,还很容易导致代码块中的中文说明、HTML 属性或 Shiki 高亮结构被改写。
因此,单次翻译并不等于把原始文章不加处理地直接发送给模型。
二、单次调用方案的基本思路
本次方案先通过 SlyTranslate 的客户端翻译工作流读取文章,将内容拆分为三个主要翻译单元:
title
content
excerpt
其中:
- 标题作为短文本单独调用;
- 摘要作为短文本单独调用;
- 正文经过保护后,只调用 GLM-5.2 一次。
完整代码块会先被替换为区块占位符,行内代码、URL 和其他不应翻译的内容则使用行内占位符。
正文交给模型时,模型看到的内容主要是普通段落、标题、列表和占位符,而不是 20 多万字符的完整代码结构。
正文调用阶段使用的主要参数为:
model:glm-5.2
thinking:disabled
max_tokens:32768
timeout:600 秒
同时增加单次请求保护:在正文翻译阶段,只允许出现一次智谱 /chat/completions 请求。任何额外的正文请求都应被视为流程异常,而不是自动重试。

三、为什么没有继续采用分块参数优化
排查过程中,一度考虑过把 SlyTranslate 的正文分块字符上限提高到:
60000
从表面上看,这可以减少分块数量,并提高单次请求中的上下文长度。
但进一步分析后发现,这篇文章包含 54 个 Code Block Pro 区块。SlyTranslate 原有流程会把不翻译的代码块当作区间边界,因此即使把字符上限提高到 60000,也无法把代码块前后的正文合并成完整上下文。
结果仍可能是几十次 API 调用。
这类参数优化也许可以提高稳定性,却无法真正实现最初的目标:
让模型看到整篇文章的逻辑关系,并尽量获得接近人工整篇重译的质量。
因此,最终还是决定继续排查正文单次调用,而不是退回原生分块流程。
四、单次调用成功后,后台仍然报错
测试开始后,后台等待了大约十分钟,最终返回:
invalid_translation_runaway_output
最初最容易得出的判断是:
- 英文输出比中文正文长;
- 模型可能生成了额外说明;
- 输出长度比例校验过于严格;
- 占位符恢复后,正文长度触发了 SlyTranslate 的 runaway 校验。
但这些推测存在一个明显问题:
自定义流程没有生成预期的错误诊断文件。
如果错误发生在我们自己的正文输出校验阶段,应该可以记录:
- 原始输出长度;
- 占位符数量;
- 缺失或重复的占位符;
- 输出片段;
- 自定义错误来源。
实际情况却是:后台直接返回了 SlyTranslate 原始错误码。
这说明错误可能并不来自单次调用本身。
五、增加执行路径跟踪
为了确认请求究竟执行到了哪一步,我为 MU Plugin 增加了一层只记录、不修改流程的执行轨迹。
诊断内容包括:
- REST 路由是否被看到;
- 自定义 REST 接管逻辑是否进入;
- 文章 ID 和模型名称;
- 标题、摘要、正文分别执行了几次;
- 正文阶段实际发出了几次智谱请求;
prepare_single()是否成功;apply_single()是否成功;- 错误是在自定义流程内还是之后产生。
读取执行轨迹的命令为:
cd /data/wwwroot/www.shuijingwanwq.com
php /tmp/swq-read-glm52-execution-trace.php
关键结果如下:
rest_route_seen
rest_input_resolved | post_id=19372 | model_slug=glm-5.2
rest_intercept_entered
job_started | post_id=19372
prepare_succeeded
units=[
title:title:54,
content:content:232908,
excerpt:excerpt:254
]
unit_started | field=content | source_chars=232908
content_raw_started
full_article_request_counted | request_count=1
content_raw_finished
output_chars=33292
result_type=string
error_code=null
request_count=1
model=glm-5.2
unit_finished
field=content
output_chars=243100
result_type=string
error_code=null
apply_started | translation_count=3
apply_finished | result_type=array | error_code=null
rest_intercept_returned | result_type=array | error_code=null
这些记录证明了几个重要事实:
- 正文确实只向 GLM-5.2 请求了一次;
- 模型正常返回了约 3.3 万字符的英文载荷;
- 恢复代码块后,完整正文为 243100 字符;
- 标题、正文和摘要都没有返回错误;
apply_single()已成功执行;- 自定义 REST 流程已经返回正常结果。
也就是说,在出现后台错误之前,文章翻译和保存其实已经完成了。

request_count=1 与 apply_finished 的结果】六、真正异常出现在自定义流程返回之后
执行轨迹中最关键的并不是前面的成功记录,而是:
rest_intercept_returned
之后,仍然出现了大量新的智谱请求:
http_request_args_before | active=no
http_request_args_after | active=no
pre_http_request_seen | active=no
这些请求的输入长度大约只有 1500~2300 字符,与 SlyTranslate 原生的 Gutenberg 小区块翻译非常相似。
而 active=no 表明它们已经不在自定义正文单次调用阶段。
这意味着实际执行顺序变成了:
自定义单次翻译
→ 成功保存英文文章
→ 自定义回调返回
→ SlyTranslate 原始 REST callback 继续执行
→ 原生分块翻译启动
→ 某个分块触发 runaway_output
→ 后台最终显示失败
因此,后台看到的错误并不是第一次单次调用的结果,而是第二套原生流程的结果。
这也解释了为什么:
- 单次正文请求记录显示成功;
- 数据库中已经存在更新后的英文文章;
- 页面却仍然显示
invalid_translation_runaway_output; - 错误之后还出现了几十次小请求。
七、问题出在 REST 接管 Hook
原来的自定义接管逻辑注册在:
rest_request_before_callbacks
这个位置可以在 REST callback 执行前检查或处理请求,但本次实现虽然返回了自定义翻译结果,却没有真正阻止 SlyTranslate 路由原有 callback 继续运行。
因此,同一个 REST 请求实际上执行了两套流程。
修复方法是把接管入口调整为:
rest_dispatch_request
概念上的调整如下:
// 原来的接管位置
rest_request_before_callbacks
// 修复后的接管位置
rest_dispatch_request
在新的调度入口中,只要自定义单次翻译返回非空结果,WordPress REST Server 就直接使用该结果,不再继续调用 SlyTranslate 原始 callback。
这次修改只调整了 REST 调度位置,没有改变:
- 正文单次调用逻辑;
- GLM-5.2 请求参数;
- 标题和摘要翻译;
- typed tokens 占位符;
- Code Block Pro 保护;
prepare_single();apply_single();- 文章保存方式。
安装修正版后,通过只读检查确认:
mu_class_loaded=yes
dispatch_override_registered=yes
old_before_callback_hook_removed=yes
http_tuning_registered=yes
single_request_guard_registered=yes
zhipu_api_called=no
database_modified=no
这说明:
- 新调度 Hook 已注册;
- 旧 Hook 已移除;
- GLM-5.2 参数调整仍然有效;
- 正文单请求保护仍然存在;
- 检查过程没有调用 API,也没有修改数据库。

八、英文文章其实已经保存成功
由于执行轨迹中已经出现:
apply_finished
因此没有立即再次点击 Translate now,而是先查询 Polylang 英文文章状态。
查询结果为:
source_id=19372
target_id=19426
status=publish
modified=2026-07-15 16:22:00
title=WordPress Polylang English Subdomain Migration Links Still Pointing to Chinese Site: Complete Fix for Category Dropdowns, Breadcrumbs, and Block Templates
content_chars=243100
excerpt_chars=813
swq_tokens=no
这进一步证明了:
后台虽然显示翻译失败,但单次调用生成的英文文章已经保存并发布。
这种情况在排查自动化任务时很容易误导判断。
如果只看前端错误提示,很可能会继续修改模型参数、输出比例或提示词,甚至覆盖已经生成成功的译文。

九、验证 Gutenberg 与 Code Block Pro 结构
文章保存成功并不代表结构一定安全。
接下来对中文原文和英文文章进行了数量对比:
========== source ==========
content_chars=232908
code_block_pro=108
textarea_tags=54
shiki_pre_tags=54
gutenberg_open_comments=304
gutenberg_close_comments=304
swq_tokens=no
========== target ==========
content_chars=243100
code_block_pro=108
textarea_tags=54
shiki_pre_tags=54
gutenberg_open_comments=304
gutenberg_close_comments=304
swq_tokens=no
code_block_pro=108 是因为每个 Code Block Pro 区块名称会分别出现在开始和结束注释中,因此实际区块数量仍然是 54 个。
结果表明:
- Gutenberg 开始注释数量一致;
- Gutenberg 结束注释数量一致;
- 54 个
textarea全部保留; - 54 个 Shiki
pre全部保留; - 没有 SWQ 占位符残留。
随后,又逐个提取中文和英文文章中的 Code Block Pro 区块,并使用序列化内容进行精确比较:
source_code_blocks=54
target_code_blocks=54
exact_matches=54
mismatch_count=0
mismatch_indexes=none
也就是说,54 个代码块全部逐字一致。
代码、命令、路径、URL、JSON 属性、HTML 标签以及 Shiki 高亮内容均未被修改。

十、检查是否存在漏译
最后移除全部 Code Block Pro 内容,再检查标题、摘要和普通正文中的中文字符。
结果为:
title_chinese_chars=0
excerpt_chinese_chars=0
content_chinese_chars_outside_code=0
========== Chinese snippets outside code ==========
none
这表明:
- 英文标题没有残留中文;
- 英文摘要没有残留中文;
- 代码块外正文没有漏译;
- 代码块中的中文说明则按照保护规则原样保留。
至此,已经能够确认本次单次调用在技术层面完成了三个目标:
- 正文只调用模型一次;
- Gutenberg 与代码块结构完整;
- 普通正文没有中文漏译。
十一、翻译质量初步评估
随后对生成的完整英文正文进行了人工评估。
本次单次调用的综合评分约为:
85 / 100
此前 SlyTranslate 原生分块翻译的评分约为:
76 / 100
本次最明显的提升是上下文理解和技术术语区分。
例如,中文文章中的“固定链接”在不同位置代表不同含义。
涉及 WordPress URL 规则时,模型翻译为:
permalink rules
涉及已经写入导航或区块中的固定地址时,模型则翻译为:
hardcoded links
这比把所有“固定链接”机械地统一翻译成 permalink 更准确。
当前译文也能够持续区分:
- 动态生成的链接;
- 保存到区块中的硬编码链接;
- Blog Homepage 模板;
- All Archives 模板;
- Cloudflare 页面缓存;
- W3 Total Cache Object Cache;
- 源站响应与浏览器实际交互。
这说明正文单次调用确实改善了跨章节术语一致性和上下文连贯性。译文中已经能够根据具体语境分别使用 permalink rules 和 hardcoded links。
不过,目前仍然存在一些需要后续优化的细节。
例如部分句子仍带有中文段落切分痕迹:
Ultimately returning a 404.
以及:
Which further redirects to the official category archive:
And returns a 200 normally.
这些内容可以理解,但对于海外技术博客来说,更自然的方式应该是把它们合并为完整英文句子。
另外,部分图片 alt 属性中的英文双引号没有被转义:
alt="【Figure 1, Categories List block and "Display as dropdown" setting in the theme editor】"
这可能影响 HTML 属性结构。该问题没有破坏 Gutenberg 区块数量,却说明图片属性也需要像代码块一样进行更严格的保护。
这些属于下一阶段的翻译自然度和 HTML 属性保护问题,不影响本次对“正文单次调用是否可行”的判断。
十二、这次排查得到的几个结论
1. 错误码不一定来自当前看到的那次模型调用
当一个 REST 请求中存在多套执行路径时,最终返回的错误可能来自第二套流程。
不能只根据:
invalid_translation_runaway_output
就断定第一次 GLM-5.2 调用失控。
必须结合:
- API 请求次数;
- 模型调用阶段;
apply_single()结果;- 数据库文章状态;
- REST callback 执行顺序。
2. “正文只调用一次”必须在 HTTP 层验证
代码设计上打算调用一次,并不代表实际运行时真的只有一次。
本次只有通过:
full_article_request_counted | request_count=1
以及后续大量 active=no 请求,才区分出:
- 自定义正文单次调用;
- SlyTranslate 原始分块调用。
3. 后台显示失败,不代表数据库没有保存
这次英文文章已经成功写入并发布,但第二套流程的错误覆盖了前面的成功结果。
因此,在重新翻译或覆盖文章前,应该先检查:
- Polylang 对应英文文章 ID;
- 修改时间;
- 标题和正文长度;
- 是否残留占位符。
4. 翻译质量与结构安全是两个独立问题
这次结构验证结果非常好:
54 个代码块全部逐字一致
304 组 Gutenberg 开始和结束注释完全对应
代码块外中文残留为 0
但英文自然度仍有提升空间。
因此,后续优化应分别处理:
- 翻译质量;
- HTML 属性安全;
- 标题和摘要自然度;
- Gutenberg 与代码块完整性。
不能因为区块结构正确,就认为英文语言质量已经达到最终标准。
十三、当前阶段的结论
经过这次排查,可以确认:
SlyTranslate + GLM-5.2 的 WordPress 长文正文单次调用方案是可行的。
本次已经实现:
- 23 万字符 WordPress 技术文章处理;
- 54 个 Code Block Pro 区块完整保护;
- 正文 GLM-5.2 单次调用;
- 英文文章正常保存到 Polylang;
- Gutenberg 结构数量一致;
- 代码块逐字一致;
- 普通正文无中文漏译;
- 翻译质量明显高于原生分块流程。
此前的 invalid_translation_runaway_output 并不是正文单次调用失败,而是 REST 接管位置不正确,导致自定义流程完成后,SlyTranslate 原始 callback 又执行了一遍。
目前调度入口已经从:
rest_request_before_callbacks
调整为:
rest_dispatch_request
并通过 Hook 注册检查。
接下来还需要使用其他文章进行一次完整回归测试,确认后台可以从点击 Translate now 一直到返回成功提示,而不会再次进入原生分块流程。
至于英文句式自然度、标题压缩、摘要优化和图片属性保护,可以作为下一阶段继续完善。
至少到这里,最关键的一步已经完成:
不再为了稳定性退回几十次分块调用,而是保留完整上下文,用一次正文请求换取更好的英文翻译质量。
WordPress 网站维护、性能优化与博客运营咨询
本站已持续运营超过 10 年,累计发布 1000+ 篇原创技术文章,长期实践 WordPress 网站建设、CDN / Cloudflare 配置、缓存优化、Google SEO、广告变现和多语言网站运营。
如果你的 WordPress 网站遇到访问慢、缓存异常、插件冲突、广告不显示、SEO 基础结构混乱、CDN 配置不确定等问题,可以联系我做一次远程技术排查。
适合以下用户:
✅ 个人博客站长
✅ WordPress 网站运营者
✅ 独立开发者与内容创作者
✅ SaaS 产品官网运营团队
✅ 希望优化网站速度与稳定性的站点
服务内容:
✅ WordPress 速度优化
✅ Cloudflare / CDN / 缓存配置排查
✅ 插件冲突与页面异常排查
✅ AdSense 广告显示问题排查
✅ SEO 基础结构检查
✅ 博客运营与商业化咨询
如需了解方案或交流相关问题,请直接联系我,并注明:WordPress 维护咨询。
联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com

发表回复