I have recently been working on improving the English translation workflow for Chinese WordPress technical articles.
The current setup is based on:
- WordPress + Polylang
- SlyTranslate 1.9.0
- Zhipu GLM 5.2
- A custom MU plugin
- One-pass, full-article body translation
- Preservation of Gutenberg blocks, HTML, shortcodes, and code blocks
Earlier tests showed that GLM 5.2 performs well in technical accuracy, natural English, and long-form stability. However, once I began handling Plaintext code blocks more carefully, several less obvious problems surfaced:
- Should natural-language text inside Plaintext blocks be translated?
- Machine-readable content such as URLs, domains, and paths must not be changed.
- Exposing Plaintext to the model must not allow it to disrupt the Gutenberg block order.
- Code Block Pro blocks rendered correctly on the front end but were marked as invalid when the editor was reopened.
- Categories, series, and tags were written to the database, but the front-end cache did not always update promptly.
This article documents the full investigation and repair process for this round of improvements.
1. Why Plaintext Code Blocks Need Separate Handling
Plaintext in technical articles does not always represent the same kind of content.
Some Plaintext blocks contain only a URL, domain, or filesystem path:
https://en.shuijingwanwq.com/sitemap_index.xml
/data/wwwroot/www.shuijingwanwq.com/
These values obviously must not be translated.
Other Plaintext blocks, however, contain interface messages, error text, or command results, for example:
区块包含未预料的或无效的内容。
Protecting the entire block would leave Chinese text in the English article.
The ideal solution, therefore, is not simply to declare:
Translate every Plaintext block.
Nor is it:
Never translate Plaintext blocks.
Instead, the model should decide from context:
- Keep URLs, domains, paths, commands, and machine-readable data unchanged.
- Translate natural-language explanations into English.
- Decide from context whether authentic interface evidence should remain in its original language.
This became one of the central goals of the current optimization.
2. How Native SlyTranslate Protects Code Blocks
SlyTranslate’s original workflow replaces each complete code block with a placeholder.
For example, Code Block Pro blocks containing Bash, PHP, or HTML are replaced with a token similar to:
SWQBLOCK000001END
The model cannot see the code inside the block, so it cannot accidentally alter code, commands, or HTML.
This approach is safe for genuine code, but it creates a problem when applied to Plaintext:
The model cannot see any content inside the Plaintext block and therefore cannot use the surrounding context to decide whether it should be translated.
I therefore introduced a different treatment for Plaintext in the custom MU plugin.
3. Exposing Plaintext to the Model with Start and End Markers
Instead of replacing an entire Plaintext block with one placeholder, the plugin exposes its text to the model:
SWQPLAINSTART000001END
这里是 Plaintext 中的内容
SWQPLAINSTOP000001END
The model can see both the text between the markers and the surrounding article context.
This allows it to handle sentences such as:
After this adjustment, submit the following Sitemap in the domain property
shuijingwanwq.com.
The domain and Sitemap URL remain unchanged, while the ordinary explanatory text is translated into English.
Genuine code blocks such as Bash, PHP, and HTML continue to use full-block placeholders, so their internal content is never exposed.
4. The First Problem: The Model Swapped Adjacent Code Blocks
While testing a long article containing many URLs, domains, and Plaintext blocks, the model returned exactly the correct number of placeholders, but reversed the order of two adjacent code blocks.
The source structure was roughly:
在网域资源:
[域名代码块]
中提交:
[Sitemap URL 代码块]
To make the wording more natural in English, the model rewrote it as:
Submitted:
[Sitemap URL 代码块]
to the domain property:
[域名代码块]
The English meaning was still correct, but the Gutenberg block order had changed.
The final validation result was:
expected_count=139
actual_count=139
fixed_order_valid=false
No placeholders were missing or duplicated; two adjacent blocks had simply exchanged positions.
This demonstrated that:
Checking only the number of placeholders is not enough. Their order must also be validated.
5. Adding Gutenberg Structure Placeholders
To prevent the model from merging paragraphs, removing block boundaries, or swapping code blocks, I added another placeholder type:
SWQSTRUCT000001END
Every Gutenberg opening and closing comment, for example:
<!-- wp:paragraph -->
<!-- /wp:paragraph -->
is first converted into a structure marker whose position is fixed.
The model must preserve all of the following:
- The number of markers must remain unchanged.
- The marker order must remain unchanged.
- Two Gutenberg blocks must not be merged.
- Code block boundaries must not be moved.
After the fix, another test showed that both the source and translated articles contained the same number of Gutenberg block markers:
350
The block signature also matched:
block_signature_identical=YES
The number of Chinese characters remaining outside code blocks fell to:
han_outside_code_blocks=0
From the perspective of article structure, this stage was successful.
6. The Front End Looked Correct, but the Editor Marked the Blocks as Invalid
After the English article was published, the front-end page rendered perfectly.
However, reopening the WordPress block editor caused every Code Block Pro block to display this warning:
The block contains unexpected or invalid content.

My first suspicion was that the accessibility label on the copy button had been translated.
The Chinese code block stored:
aria-label="复制"
In the English article, it had become:
aria-label="Copy"
I first restored all 79 occurrences of Copy to 复制.
The editor still reported the blocks as invalid, which showed that the problem was not limited to aria-label.
7. Byte-for-Byte Comparison Revealed That Code Block Pro Had Been Regenerated
I compared the first Code Block Pro block in the Chinese and English articles.
The result was:
source_bytes=2903
target_bytes=2241
byte_identical=NO
The first difference appeared in codeHTML.
The original block contained:
tabindex="0"
That attribute was missing from the English block.
A further comparison of a PHP block revealed another change:
\u003e
It had been re-escaped as:
\u0026gt;
In other words, the model had not modified the code. The block had been reserialized during restoration or saving.
Code Block Pro does not store a piece of content in only one place. The same content appears simultaneously in:
codeinside the Gutenberg block JSONcodeHTMLinside the Gutenberg block JSON<textarea><pre class="shiki">- The
<code>and<span>elements used for the rendered code
If the formatting, escaping, or attribute order changes in any one of these locations, Gutenberg may consider the block invalid.
8. A Control Test with GLM 5.1
To determine whether the problem came from native SlyTranslate or from my custom GLM 5.2 full-article workflow, I ran a control test.
I selected a Chinese draft and translated it with SlyTranslate’s native workflow using GLM 5.1.
The translated Plaintext and PHP blocks opened normally in the editor, with no invalid-block warning.

This control test largely confirmed that:
The problem was not in Code Block Pro or SlyTranslate’s native save logic. It was in the custom GLM 5.2 full-article translation workflow.
9. Confirming That SlyTranslate Did Not Modify Code Blocks During Input Preparation
I then inspected SlyTranslate’s prepare_single() method.
The source body and the prepared content unit produced:
source_chars=377043
unit_chars=377043
whole_content_identical=YES
The Code Block Pro comparison produced:
source_code_blocks=79
unit_code_blocks=79
code_blocks_identical=79
code_blocks_different=0
This proved that SlyTranslate had not modified any code block before the content was sent to the model.
The actual problem occurred during restoration and saving after the model returned its translation.
10. The Original Plaintext Restoration Method Was Risky
Previously, after Plaintext had been translated, the plugin called logic similar to:
rebuild_plaintext_code_block_pro()
to rebuild the entire Code Block Pro block.
That process included:
- Reparsing the Gutenberg JSON.
- Updating
code. - Rebuilding
codeHTML. - Rebuilding
<textarea>. - Rebuilding the Shiki
<pre>. - Reserializing the block attributes.
Although this approach could update the Plaintext content, it could also easily alter unrelated details in the original block.
The final solution needed to distinguish between two kinds of Plaintext.
1. Machine-Oriented Plaintext
Examples include:
https://en.shuijingwanwq.com/
/data/wwwroot/www.shuijingwanwq.com/
The model can see this content and use it to understand the article context.
During restoration, however, the plugin puts back the complete original Code Block Pro block without regenerating any JSON or HTML.
2. Natural-Language Plaintext
Examples include error messages, command output, and explanatory text.
This content is still passed to GLM 5.2 for contextual evaluation and translation.
This preserves context-aware Plaintext translation instead of reverting to a blanket rule that protects every Plaintext block.
11. The Final Problem Was in the WordPress Save Filters
SlyTranslate’s Polylang adapter ultimately uses:
wp_update_post( wp_slash( $update_data ) );
to save the English body.
When WordPress saves post content, it may pass through content_save_pre, KSES, or other filters.
Comments in SlyTranslate’s own source code also explicitly note that KSES may:
- Remove
tabindex. - Remove custom-block
data-*attributes. - Alter case-sensitive SVG attributes.
- Cause Gutenberg to classify Code Block Pro blocks as invalid.
Although the current user is an administrator, the body generated by the custom full-article workflow could still be altered during the complete save process.
Placeholder restoration alone was therefore not enough.
The workflow also had to guarantee that:
Once the final body had passed validation, the version written to the database had to remain byte-for-byte identical to the version before saving.
12. Adding an Exact Final Write-Back and SHA-256 Verification
In MU plugin version 2026.07.16.12, I adopted a two-stage save process.
Stage One: Let SlyTranslate Handle Its Normal Workflow
SlyTranslate continues to handle:
- Creating or overwriting the English article.
- Setting the article status.
- Assigning the Polylang language.
- Linking the Chinese and English articles.
- Copying categories, series, and tags.
- Saving metadata.
Stage Two: Write the Body Back Exactly
After obtaining the English article ID, the plugin writes the fully validated English body directly back to post_content.
Before the write-back, it calculates:
expected_hash
After reading the content back from the database, it calculates:
stored_hash
It then compares:
expected_hash === stored_hash
If the hashes differ, the plugin immediately returns an error and does not treat the translation as successful.
This bypasses any final-save filter that might rewrite Code Block Pro blocks, while preserving SlyTranslate’s existing article, language, and taxonomy workflow.
13. Final Verification Results
After the fix, I overwrote the translated article again:
中文文章 ID:19383
英文文章 ID:19494
Every Code Block Pro block in the English editor returned to normal, with no invalid-content warning.
The final block-by-block comparison produced:
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
The result was essentially what I wanted:
- All five Bash blocks were byte-for-byte identical.
- All three HTML blocks were byte-for-byte identical.
- All three PHP blocks were byte-for-byte identical.
- Of the 68 Plaintext blocks:
- 47 remained unchanged.
- 21 were translated according to context.
In other words:
Actual code, URLs, domains, paths, and machine-readable data remained unchanged, while natural language inside Plaintext was evaluated and translated contextually by GLM 5.2.
That was precisely the result this round of work was intended to achieve.
14. Categories, Series, and Tags Did Not Update Promptly on the Front End
The English article editor already showed the correct English categories, series, and tags.
Database checks also returned the expected values:
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
At first, however, the English front end still displayed:
Uncategorized
After adding a random query parameter to bypass the page cache, some tags updated, but the category still showed the old value.
I eventually cleared the following in the English Host context:
- The post object cache.
- The term relationship cache.
- The relationship caches for
category,series, andpost_tag.
Only then did the front end show the correct information.

This demonstrated that:
The categories, series, and tags had not been lost. The real problem was that the W3 Total Cache / Redis object cache for the English domain had not been invalidated promptly.
Running “Purge All Caches” in the dashboard, or clearing the page cache and object cache separately, does not necessarily clear the cache namespace associated with the English Host.
This is the same kind of multi-domain cache boundary that previously caused incorrect language attributes, Canonical URLs, and category links on the English site.
15. A New Series Ordering Issue
After the cache was cleared, the English article appeared in the correct Blog SEO Log series.
However, it appeared as the first article in the series rather than the last.
The “Current Part” field in PublishPress Series also showed:
当前没有部分编号


This issue is no longer part of the same problem as Code Block Pro, Plaintext translation, or body persistence.
At this point, I could confirm that:
- The series term relationship was correct.
- The English article belonged to the correct series.
- The article had no explicit series part number.
- PublishPress Series therefore displayed it first.
I left this issue for a separate investigation rather than continuing to mix it into the current translation-plugin optimization.
16. The Final Stable Version from This Round
The MU plugin version now in use is:
2026.07.16.12
This version has been verified to provide the following:
- GLM 5.2 can translate a complete long-form article in a single request.
- The number and order of Gutenberg blocks remain unchanged.
- The model does not merge block boundaries.
- Bash, PHP, and HTML remain fully byte-for-byte identical.
- Plaintext can be translated selectively according to context.
- URLs, domains, and paths remain unchanged.
- Code Block Pro no longer reports invalid content in the editor.
- The final body write includes SHA-256 verification.
- Categories, series, and tags are written correctly to the database.
The main remaining issues are no longer about translation quality itself, but rather:
- Object cache invalidation in a multi-domain environment.
- PublishPress Series article-order synchronization.
These two issues are better addressed separately in future work.
17. Final Takeaways
The most important lesson from this investigation is that front-end rendering alone cannot determine whether Gutenberg content is safe.
A page rendering correctly on the front end does not necessarily mean that:
- The block structure is still valid.
- The content can still be edited normally.
- Code Block Pro’s serialized content is unchanged.
- The JSON, HTML, and Shiki code remain identical.
A reliable automated translation workflow for a technical blog must satisfy all of the following at the same time:
- Natural English.
- Accurate technical content.
- Unmodified code.
- Intact Gutenberg structure.
- Blocks that reopen normally in the editor.
- Verifiable database persistence.
- Correctly refreshed category, tag, and series relationships.
Compared with making the English in one article marginally more natural, stability in structure and persistence has a much greater influence on whether an automated translation workflow can be used reliably in production over the long term.
需要长期技术维护或远程问题排查?
我是拥有 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


发表回复