没有不值得去解决的问题,也没有不值得去学习的技术!

Adding Context-Aware Plaintext Handling to SlyTranslate’s GLM 5.2 Full-Article Translation and Fixing Invalid Code Block Pro Blocks

图3:清理英文 Host 下的对象与术语关系缓存后,英文文章重新显示 SEO Tools、Yoast SEO 和 Blog SEO Log

作者:

,

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:

  1. Should natural-language text inside Plaintext blocks be translated?
  2. Machine-readable content such as URLs, domains, and paths must not be changed.
  3. Exposing Plaintext to the model must not allow it to disrupt the Gutenberg block order.
  4. Code Block Pro blocks rendered correctly on the front end but were marked as invalid when the editor was reopened.
  5. 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:

Plaintext
https://en.shuijingwanwq.com/sitemap_index.xml
Plaintext
/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:

Plaintext
区块包含未预料的或无效的内容。

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:

Plaintext
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:

Plaintext
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:

Plaintext
在网域资源:
[域名代码块]

中提交:
[Sitemap URL 代码块]

To make the wording more natural in English, the model rewrote it as:

Plaintext
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:

Plaintext
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:

Plaintext
SWQSTRUCT000001END

Every Gutenberg opening and closing comment, for example:

HTML
<!-- 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:

Plaintext
350

The block signature also matched:

Plaintext
block_signature_identical=YES

The number of Chinese characters remaining outside code blocks fell to:

Plaintext
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.

Figure 1: Code Block Pro reports invalid content in the English article editor
Figure 1: Code Block Pro reports invalid content in the English article editor

My first suspicion was that the accessibility label on the copy button had been translated.

The Chinese code block stored:

HTML
aria-label="复制"

In the English article, it had become:

HTML
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:

Plaintext
source_bytes=2903
target_bytes=2241
byte_identical=NO

The first difference appeared in codeHTML.

The original block contained:

HTML
tabindex="0"

That attribute was missing from the English block.

A further comparison of a PHP block revealed another change:

Plaintext
\u003e

It had been re-escaped as:

Plaintext
\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:

  1. code inside the Gutenberg block JSON
  2. codeHTML inside the Gutenberg block JSON
  3. <textarea>
  4. <pre class="shiki">
  5. 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.

Figure 2: Code Block Pro renders normally after translation with SlyTranslate's native workflow and GLM 5.1
Figure 2: Code Block Pro renders normally after translation with SlyTranslate’s native workflow and GLM 5.1

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:

Plaintext
source_chars=377043
unit_chars=377043
whole_content_identical=YES

The Code Block Pro comparison produced:

Plaintext
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:

PHP
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:

Plaintext
https://en.shuijingwanwq.com/
Plaintext
/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:

PHP
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:

Plaintext
expected_hash

After reading the content back from the database, it calculates:

Plaintext
stored_hash

It then compares:

Plaintext
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:

Plaintext
中文文章 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:

Plaintext
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:

Plaintext
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:

Plaintext
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, and post_tag.

Only then did the front end show the correct information.

Figure 3: After clearing object and term relationship caches for the English Host, the article again displays SEO Tools, Yoast SEO, and Blog SEO Log
Figure 3: After clearing object and term relationship caches for the English Host, the article again displays SEO Tools, Yoast SEO, and Blog SEO Log

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:

Plaintext
当前没有部分编号
Figure 4: The English article appears first after being added to the Blog SEO Log series
Figure 4: The English article appears first after being added to the Blog SEO Log series
Figure 5: No Current Part number is set in the dashboard
Figure 5: No Current Part number is set in the dashboard

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:

Plaintext
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:

  1. Natural English.
  2. Accurate technical content.
  3. Unmodified code.
  4. Intact Gutenberg structure.
  5. Blocks that reopen normally in the editor.
  6. Verifiable database persistence.
  7. 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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理