At the end of the previous article, I had finalized the plan for the next steps in the representative page calibration phase.
At that time, 4 representative pages had already been completed:
generics/1flowcontrol/8methods/16methods/20
And the final 3 representative pages were explicitly selected:
concurrency/7concurrency/11methods/24
The plan was also very clear:
If these 3 pages also pass stably, the total number of representative pages will reach 7.
Then the page-by-page calibration phase would end, and we would enter:
Automated trial runs for 10 regular
pendingpages.
So this time, it was not about reselecting representative pages or endlessly increasing the test sample size.
The goal was fixed from the very beginning:
Actually finish the last 3 representative pages left over from the previous article.
I originally thought that after continuously making corrections during the first 4 representative pages, the last 3 pages might mostly just verify the existing mechanisms.
The actual results proved once again that real pages often expose boundary issues more easily than artificially designed tests.
Rather than simply passing translation “in one go,” concurrency/7, concurrency/11, and methods/24 successively revealed:
- A gap in position validation for non-tail directives;
- Overly narrow scope in standalone conditional source projection;
- The common English verb
Gobeing mistakenly protected as a Go language name; - Incomplete protection of inline code within link display text;
- Potential lack of structural spaces required by legacy present between Chinese full-width punctuation and inline code.
After all these issues were resolved, the originally planned 7 representative pages were finally truly completed.
This article continues the plan from the previous one, recording how the last 3 representative pages were completed step by step.
1. After the first 4 pages were done, why insist on finishing the last 3?
After completing the first 4 representative pages, the entire translation pipeline was actually quite stable.
At that point, many key structures had already been covered in real translations, including:
- Generics and more complex technical explanations;
- Regular inline code and old-style program font snippets;
- Regular links;
- Rearranging protected content according to natural Chinese word order;
- Static preformatted code;
- Translatable instructional code comments and the identifiers within them;
- Emphasis and font style snippets;
- Exercise pages;
.play;- Section topology;
- Tail directives.
Moreover, a very important issue had just been resolved previously:
The global order of protected tokens is not a reliable multilingual structural invariant.
In other words, Chinese can reasonably change the order in which protected content appears, as long as the actual code, links, directives, and present structures are not broken.
At this point, if the goal was just to “translate a few more pages,” we could indeed have started batch processing.
But the previous article had already decided to use 3 different types of pages to round out the real coverage.
Looking back now, this decision was well worth it.
Because most of the issues discovered next would not have been easy to anticipate through ordinary unit tests.
2. concurrency/7: .image content unchanged, but position could quietly shift
The 5th representative page is:
concurrency/7
Corresponding to the course title:
Exercise: Equivalent Binary Trees
It was selected because the page contains:
.image /tour/static/img/tree.png
This is a very rare .image in the current official course pages.
And it is not placed at the end of the page, but sandwiched between two paragraphs of body text:
Body text A
.image /tour/static/img/tree.png
Body text B
Before starting the translation, I had Codex perform a read-only analysis.
The result showed that while the system at the time could strictly check:
- Whether
.imagewas lost; - Whether the image path changed;
- Whether the number of directives changed;
- Whether the directive content was modified;
- Whether the order between directives changed.
There was still one loophole:
If the model kept
.image /tour/static/img/tree.pngintact but simply moved it from the middle of the body text to the beginning or end of the page, the existing validation might still pass.
The reason was that a position constraint had already been established for tail directives, but .image was a non-tail directive on this page.
If there was only this one directive in the entire Section, moving its position would not change the number, content, or relative order of directives.
Thus, the 5th representative page first revealed:
Within the same Section, the structural position of non-tail directives was not truly protected.
3. Adding directiveLayout: Protecting structural relationships, not locking down Chinese paragraphs
This issue could not be solved simply by recording the absolute element index of .image.
Because the project consistently insisted on:
Translating whole course pages as a unit, rather than requiring a strict one-to-one paragraph correspondence between Chinese and English.
English might consist of two paragraphs, which could reasonably be merged into one after Chinese translation; the reverse could also happen.
If we required:
The 3rd present element must always remain the 3rd
It would easily regress back to paragraph-level structural binding.
The approach ultimately adopted was to establish a directiveLayout for Sections containing directives, which is a directive structural layout.
For example, a page could be abstracted into:
Body text area
→ Directive
→ Body text area
→ Preformatted code
→ Body text area
Consecutive regular natural language text would be collapsed into a single body text area, while:
- Preformatted code;
- Lists;
- Nested Sections;
.play;.image;- Other directives
would continue to exist as stable structural nodes.
This way:
Body text
→ .image
→ Body text
could not be arbitrarily changed into:
.image
→ Body text
or:
Body text
→ .image
At the same time, regular Chinese paragraphs could still be reasonably split, merged, and rephrased.
After the fix, concurrency/7 passed its first real -dev translation directly.
The final Chinese title was:
练习:等价二叉树
Here, .image remained in its original mixed position within the body text, and the JavaScript link target in the page:
javascript:click('.next-page')
was also fully preserved.
This page was ultimately committed as:
b74b3b36c513b516938d0fe4ea3e09b766aed54d
feat: 完成 concurrency/7 并增强非尾部 directive 位置校验
concurrency/7 Chinese page, .image correctly kept between preceding and following body text4. concurrency/11: The real problem first appeared in the translation source
The 6th representative page:
concurrency/11
Corresponding to the title:
Where to Go from here...
The previous article anticipated that this page would mainly be used to verify:
- Long, multi-paragraph body text;
- A large number of link targets;
- Cross-paragraph links;
slidesforced terminology;- Stability of long text when there is no code or directives.
However, before actually starting, a more fundamental issue was discovered.
In the fixed upstream, this page contains:
#appengine: You can get started by
#appengine: [[/doc/install/][installing Go]].
#appengine: Once you have Go installed, the
The
[[/doc/][Go Documentation]] is a great place to
#appengine: continue.
start.
The #appengine: lines are not the body text actually displayed by the current standalone Tour.
What the standalone page actually needs is:
The
[[/doc/][Go Documentation]] is a great place to
start.
The project had actually already implemented conditional source projection.
The problem was:
That processing only covered
welcome.article.
If other .article also contained #appengine:, the original conditional content could still enter the translation directory.
And the validator originally had a strict line of defense:
As long as
#appengine:still exists in the standalone source, the candidate translation must be rejected.
Therefore, in this case, even if GLM-5.2 translated it perfectly, it could not pass validation.
5. Not relaxing the validator, but fixing the standalone source projection
This issue was ultimately not resolved by deleting:
standalone source contains #appengine content
this validation check.
On the contrary, this check was kept fully intact.
Because the real problem was not that the validator was too strict, but rather:
The standalone source entering the translation pipeline should not contain these conditional lines in the first place.
Thus, the standalone conditional source projection, which previously only covered welcome.article, was generalized to other Tour .article.
In the fixed upstream, in addition to welcome.article, two other pages were found to be affected:
flowcontrol/10
concurrency/11
Both pages were still pending at the time, so it was only necessary to safely update the corresponding source hashes.
The source migration conditions were also specifically restricted here:
Only if the old source contained
#appengine:, and the new source exactly matched the result of deleting those conditional lines according to the rules, was it recognized as a safe migration.
Regular upstream content changes would still not be automatically allowed through.
This fixed the standalone translation source without relaxing the original source lock.
6. concurrency/11 failed twice in a row, but it turned out we protected the wrong thing
After the source projection was fixed, the real translation could finally begin.
The projected page had a total of:
15 protected link targets
12 Go tokens that must be retained
27 protected tokens in total
First attempt:
attempt-001
The API succeeded, and GLM-5.2 returned a complete page.
But validation failed:
protected token count = 26, want 27
token 1 occurrence count = 0, want 1
What was missing was the Go in the title:
Where to Go from here...
The model translated the title into:
从这里去往何处...
Second attempt:
attempt-002
The result was almost exactly the same.
It was still:
26 / 27
Still missing only the Go from the title.
Both times, the model considered that this should be translated into a common Chinese meaning like “where to go next.”
If we stopped here, it would be easy to conclude that GLM-5.2 had violated the rule that protected tokens must be fully retained twice in a row.
But after further analysis, the conclusion changed.
7. Go in Where to Go from here... is not necessarily the Go language
At the time, the project would protect all standalone capitalized:
Go
The reasoning was sound.
In A Tour of Go, the vast majority of Go explicitly refer to the Go language, for example:
Go has...
Go's...
in Go
Go Documentation
Go Blog
A Tour of Go
So a bare Go needed to be protected by default.
But:
Where to Go from here...
was different.
The Go here was capitalized likely only because it was in an English title.
Semantically in English, it is very natural: where to go next?
I also did a quick comparison using Google Translate, which yielded: where to go next…
I then checked about 66 standalone capitalized Go in the fixed upstream.
The vast majority indeed explicitly referred to the Go language.
Only a very few were not explicitly language names, for example:
Where to Go from here...
Go local
Go offline (optional)
Among them:
Where to Go from here...
had the clearest meaning as a common English verb.
This showed that the problem was not:
The model was unwilling to retain Go.
But more likely:
Our literal protection rule mistakenly identified the capitalized common verb
Go, resulting from title capitalization, as a Go language name.
8. Not removing Go protection, only allowing high-confidence common English verbs
The solution was not changed to:
Do not protect any Go
This would have caused a large number of actual Go language names to lose hard protection.
Nor was a special rule written for:
concurrency/11
or the full title.
Ultimately, a very conservative contextual judgment was adopted:
A bare
Gocontinues to be protected by default; only when it is in a high-confidencewhere + to + Gointerrogative infinitive structure is no protected token generated.
Therefore:
Where to Go from here...
Go was no longer protected.
While:
Go has...
Go's...
in Go
install Go
Go Documentation
A Tour of Go
remained protected.
Even:
migrate to Go from another language
could not be simply allowed through just because to Go from appeared.
Here, Go could still explicitly refer to the Go language.
As for:
Go local
Go offline
a conservative strategy was maintained for now, without attempting to solve all English word sense disambiguation at once just for this one issue.
9. Encountered network issues a 3rd time, succeeded on the 4th attempt
After fixing the false protection of Go, the protected token count for concurrency/11 decreased from:
27
to:
26
The single one removed was the incorrectly protected Go in the title.
The next new real translation attempt was supposed to verify this fix.
However:
attempt-003
encountered a DNS/network sandbox error, and GLM-5.2 never actually received the request.
This record was still preserved.
Because the current representative page calibration consistently used development mode -dev.
Development mode inherently allows continuous debugging for the same page, unaffected by the “three limited retries” constraint of future production runs.
After the network recovered, we continued with:
attempt-004
This time it passed smoothly.
All 26 protected tokens were intact.
All 15 link targets were retained.
The 3 slides link display texts were also translated according to the current forced terminology as:
页面
The final title:
接下来去哪……
no longer created awkward Chinese just to erroneously retain a Go.
This page was ultimately committed as:
652839a0e5c14488b1391a61303dad03e8260668
feat: 完成 concurrency/11 并修复条件源码投影与 Go 误保护

concurrency/11 final Chinese page, “Where to Go from here…” naturally translated as “接下来去哪……”This experience also illustrated a point:
A protected token failure does not necessarily mean the model made a mistake; it could also mean we protected the wrong thing.
10. The last representative page methods/24 encountered inline code inside a link
The previous article had already designated the last representative page as:
methods/24
The main purpose was to verify API documentation-style pages, longer static interface code, multiple API links, emphasized text, and the tail .play.
During actual analysis, another combination that had not been truly covered before was discovered:
[[/pkg/image/#Rectangle][`image.Rectangle`]]
This is a link.
But its display text is not regular natural language, but inline code:
`image.Rectangle`
Previously, the project could protect:
- Regular link targets;
- Inline code in regular body text.
But this was exactly:
A piece of inline code nested inside link display text.
The original presentInlineCodes would intentionally skip program font snippets inside links.
So:
image.Rectangle
would not become an inline code protected token.
At the same time, while the validator could know:
A program font snippet still existed here.
it did not validate whether the specific content of this program font snippet was still:
image.Rectangle
Theoretically, if the model changed it to:
somethingElse
as long as the backtick structure remained, it could slip past automated validation.
11. Protecting link targets and inline code within links separately
This time, the entire link display text was not simply locked down.
Because link display text could very well contain both:
- Natural language that can be freely translated;
- Technical code that must remain unchanged.
Ultimately, logic specifically handling program font snippets inside link display text was added.
For example:
[[/pkg/image/#Rectangle][`image.Rectangle`]]
will now separately protect:
Link target
and:
image.Rectangle
Conceptually, this is equivalent to:
[[⟪Link target token⟫][⟪Inline code token⟫]]
After restoration, it still yields:
[[/pkg/image/#Rectangle][`image.Rectangle`]]
The validator also no longer just checks “whether there is a program font snippet here.”
Instead, it checks:
Whether the program font content inside this specific link is still correct.
Therefore:
[[/pkg/image/#Rectangle][`image.Rectangle`]]
if changed to:
[[/pkg/image/#Rectangle][`somethingElse`]]
must fail.
At the same time, if multiple inline code snippets exist within the same link tag, they are still allowed to be reasonably adjusted according to target language word order.
The restriction that “all inline code across the entire page must maintain the original English order” was not reintroduced.
12. The first time all 15 protected tokens were correct, but the page still failed
After the fix was completed, running:
attempt-001
The API succeeded.
This page had a total of 15 protected tokens:
4 link targets
1 tail directive
1 static preformatted code block
9 inline code snippets
All 15 were intact.
The newly protected:
image.Rectangle
was also correctly retained in the original link.
But the final page still failed:
font span count mismatch: expected 10, actual 9
That is:
The source page should have had 10 font style snippets, but the restored candidate page only had 9.
Initially, I suspected that a necessary blank line was missing between the static code block and the following:
*Note*
If I had modified the preformatted code logic based on that judgment at the time, I would likely have fixed the wrong place.
13. Local replay proved: the static code block was actually fine
So, instead of immediately calling GLM-5.2 again, I directly used the saved:
attempt-001
original model response for a local replay.
The result showed that the static code block boundaries had actually been correctly restored:
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
*注意*:
This meant:
The protected content of the static preformatted code already included the blank space needed to close the code block.
The problem was not at this layer at all.
After further checking the font style snippets one by one, what was actually missing was:
Bounds
14. Bounds after : looked fine, but present did not recognize it
The structure generated by the model was similar to:
*注意*:⟪BOUNDS_TOKEN⟫ 方法
After restoration, visually it was:
*注意*:`Bounds` 方法
For normal Markdown reading, this looked perfectly fine.
But legacy present did not recognize the:
`Bounds`
here as an independent program font snippet.
The root cause was finally located:
Boundswas immediately followed by a full-width Chinese colon:, and legacy present still required an ASCII space as a structural delimiter in this case.
This issue was actually of the same class as the inline code boundary issue previously encountered in generics/1.
When writing naturally in Chinese, it is easy for us to write:
注意:`Bounds`
But legacy present is not a modern Markdown parser.
For certain combinations, it still relies on the ASCII space in:
注意: `Bounds`
to function.
15. This space is not a typographical habit, but present structure
Ultimately, a typographical rule to “add spaces after all Chinese punctuation” was not introduced.
Instead, the existing inline code boundary normalization was further extended.
The new processing still only applied to protected inline code in regular body text.
When the adjacent character preceding the inline code:
- Was not whitespace;
- And was not ASCII punctuation;
and legacy present genuinely required a structural boundary, an ASCII space was added.
Therefore:
*注意*:`Bounds` 方法
needed to be restored in the actual present source code as:
*注意*: `Bounds` 方法
Similar Chinese full-width punctuation includes:
:
,
。
(
But this did not mechanically add spaces before or after all punctuation.
For example, if present could already stably parse Chinese punctuation following inline code, no extra space was added just for “formatting uniformity.”
The principle here was clear:
These ASCII spaces are not Chinese typographical style, but structural delimiters required by legacy present.
This also meant that during subsequent manual polishing, one should not delete a space solely because:
“There should usually be no space after a Chinese colon”
and remove it.
16. The original model output from the first failure passed directly after fixing the program
The most valuable step in this issue was reusing the existing failure record.
Instead of immediately running attempt-002, I first took:
attempt-001
‘s original model output and re-ran restoration and validation.
Before the fix:
font span = 9
After the fix:
font span = 10
Bounds was correctly recognized again as a program font snippet.
At the same time:
[[/pkg/image/#Rectangle][`image.Rectangle`]]
was still retained in the original link.
The static Go code was preserved verbatim.
The tail:
.play methods/images.go
also remained unchanged.
Then, the full candidate translation validation was re-run on the model output that had failed the first time:
Passed.
This was very similar to the experience with methods/20 in the previous article.
It did not pass by chance after re-translating.
Rather:
The same original model output passed again after fixing the local protection, restoration, or validation logic.
This was extremely helpful for determining the true root cause.
17. attempt-002 real translation finally passed
After local replay verification, running:
attempt-002
This time it was a complete success.
All 9 inline code snippets on the page were correctly restored, including:
Image
Bounds
Rectangle
image.Rectangle
image
color.Color
color.Model
color.RGBA
color.RGBAModel
The final Chinese page was:
* 图片
[[/pkg/image/#Image][image 包]]定义了 `Image` 接口:
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
*注意*: `Bounds` 方法的 `Rectangle` 返回值实际上是
[[/pkg/image/#Rectangle][`image.Rectangle`]],因为该声明位于 `image` 包内。
(详见 [[/pkg/image/#Image][文档]]。)
`color.Color` 和 `color.Model` 类型也是接口,不过我们将使用预定义的实现 `color.RGBA` 和 `color.RGBAModel`,因此这里暂不考虑这一点。这些接口和类型由 [[/pkg/image/color/][image/color 包]]定义。
.play methods/images.go
Final commit:
db62b6331019cc7aad0d08c977e79d55343f9d21
feat: 完成 methods/24 并增强链接内代码与行内边界校验

methods/24 Chinese page, static code, Bounds inline code, and image.Rectangle inside the link are all rendered correctly18. The 7 representative pages planned in the previous article are now all complete
At this point, the plan set at the end of the previous article has been truly executed.
All 7 representative pages have been completed:
generics/1
flowcontrol/8
methods/16
methods/20
concurrency/7
concurrency/11
methods/24
The first 4 pages established the basic capabilities for whole-page translation, structural protection, and validation.
The last 3 pages continued to uncover issues in several different directions.
concurrency/7 proved:
Directives must not only keep their content unchanged, but must also maintain their proper structural position.
concurrency/11 proved:
Before translation, we must first ensure that the standalone source itself is correct published content.
It also proved:
We cannot unconditionally assume that a word written as
Gonecessarily refers to the Go language.
methods/24 proved:
Links can still nest technical code that requires independent protection.
It also reaffirmed:
The structural boundary between Chinese full-width punctuation and legacy present inline code needs to be reliably restored by local programs.
If these issues had been left until after batch translating dozens or hundreds of pages, the troubleshooting cost would have been significantly higher.
19. The last 3 pages prove once again: don’t blame the model first for failures
The last 3 representative pages made me even more certain of one thing:
Model output failing validation does not mean the model definitely translated incorrectly.
concurrency/11 was missing the Go protected token twice in a row.
On the surface:
GLM-5.2 was not obeying the hard constraint.
But after real analysis, it turned out:
We had protected a common English verb.
methods/24 had all 15 protected tokens intact the first time, but structural validation still failed.
On the surface, it was easy to think:
The model broke the code block or formatting.
After a real local replay, it was discovered:
The model output itself was valid; it was the local legacy present inline boundary normalization that was still missing coverage for one Chinese punctuation combination.
So now I am increasingly inclined to ask first after a failure:
Is this actually a model translation issue, or an issue with the translation system itself?
There are many layers where things could go wrong:
upstream source
→ standalone source projection
→ protected content identification
→ model whole-page translation
→ protected content restoration
→ present parsing
→ structural validation
→ page verification
Only after determining which layer the problem actually occurred in should the corresponding mechanism be modified.
20. Saving failure responses: local replay is becoming increasingly important
When handling methods/20 in the previous article, I had already realized the value of saving original failure responses.
This time, methods/24 proved this point once again.
The ideal troubleshooting approach has now gradually solidified into:
Real whole-page translation
→ Automated validation failure
→ Preserve original response
→ Local replay
→ Locate minimal difference
→ Modify local generic mechanism
→ Re-validate using the same response
→ Then decide whether the model needs to be called again
This approach has several obvious benefits.
First, it saves GLM-5.2 tokens.
Second, it helps distinguish:
Whether the model actually needs to re-translate.
Third, if the same original response can pass after the fix, the proof of the root cause is much more convincing.
Because it demonstrates:
The translation did not change; what changed was only our judgment of “what counts as structurally safe” or our restoration capability.
This will be especially important when expanding to other languages in the future.
21. The representative page calibration phase can now conclude
After the last representative page was completed, the project ran full validation again:
go test ./internal/i18n
go test ./...
All passed.
Course catalog validation:
catalog OK: 103 published pages, 2 conditional source records
Simplified Chinese status validation:
status OK: 103 pages for zh-CN
The last representative page commit:
db62b6331019cc7aad0d08c977e79d55343f9d21
feat: 完成 methods/24 并增强链接内代码与行内边界校验
After committing and pushing:
git status --short
was empty again.
At this point, the originally planned 7 representative pages have all been completed.
I believe there is no need to continue artificially hunting for an 8th, 9th, or 10th complex representative page just to pursue coverage.
Continuing to do so would yield diminishing returns.
It is more important to enter the next phase and let the system, which has undergone real calibration, face regular pages.
22. Next step: automated trial runs for 10 regular pending pages
This directly follows the next phase determined in the previous article.
The plan at that time was:
If the last 3 representative pages also passed stably, bringing the total to 7 representative pages, we would end page-by-page calibration and enter automated trial runs for 10 regular
pendingpages.
Now, this prerequisite has been met.
So next, we will no longer deliberately look for:
- Special
.image; - Special link combinations;
- Special code structures;
- Special conditional sources.
Instead, we will select 10 relatively ordinary course pages from the remaining pending pages.
The issues this batch aims to verify are already different.
The representative page phase asked:
Does the system have any obvious structural blind spots?
The next 10 regular trial runs will ask:
After these calibrations, can the system continuously and stably complete whole-page translation and automated validation when facing regular A Tour of Go pages?
The general workflow will be:
10 regular pending pages
→ GLM-5.2 full page translation
→ Automated restoration of protected content
→ present parsing
→ Structural and terminology validation
→ ready / pending / blocked
→ Summarize the actual results of the first batch of regular pages
If these 10 pages can also pass relatively stably, we can further reduce the need for page-by-page manual observation and enter the batch translation phase for the remaining pages.
Conclusion
At the end of the previous article, the next step I wrote down was:
Complete the last 3 representative pages.
Now, this goal is finally accomplished.
But what was truly gained was not just:
3 more Chinese pages.
concurrency/7, concurrency/11, and methods/24 helped this project discover issues with structural position, conditional source, word sense protection, code inside links, and Chinese inline boundaries, respectively.
Several of the failures were particularly representative.
Some issues looked on the surface like the model disobeying instructions, but were ultimately found to be incorrect protection rules.
Some issues looked on the surface like the model breaking formatting, but were ultimately found to be a missing boundary in the local present restoration logic.
This makes me even more certain that what the A Tour of Go multilingual translation project truly needs to establish is not a process of:
“Making the AI make as few mistakes as possible, and then re-translating when it fails”
Rather, it is an automated system capable of clearly distinguishing:
Which content allows natural translation, which content must be strictly protected, which changes are reasonable expressions in different languages, and which changes actually break code, links, directives, and present structures
The 7 representative pages have pushed this system to a new stage.
Next, we will no longer continue calibrating around a few special pages.
Instead, we will let it truly face the first batch of regular course pages:
Automated trial runs for 10 regular
pendingpages.
If this batch can run stably, then this project will truly begin to transition from:
Translation pipeline calibration
into:
Scaling generation of Simplified Chinese A Tour of Go content
.
