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

Retrying a Page Five Times: Token and Present Structure Issues When Translating A Tour of Go With GLM-5.2

Figure 1: Local preview of the generics/1 Simplified Chinese page

作者:

在

, ,

Recently, I continued working on the Simplified Chinese translation for the go-tour-i18n project.

The goal of this project is not simply to replace the English text of A Tour of Go with Chinese, but to establish a complete workflow that can be maintained long-term, synchronized with the official upstream, extended to multiple languages, and capable of automated validation and publishing.

Currently, the project treats a complete lesson page displayed on the left side of the browser—namely, a top-level present.Section—as the minimal translation unit. Each time, GLM-5.2 translates an entire page, which then goes through structural comparison, syntax parsing, terminology checking, and an actual page preview.

After completing the Welcome chapter and the first three pages of Basics, I did not continue translating basics/4 directly in course order. Instead, I first selected six representative pages from different chapters to further calibrate the translation workflow.

The first one to be processed this time was:

Plaintext
generics/1 — Type parameters

Ultimately, this seemingly straightforward generics introductory page went through five development mode attempts before completing translation, validation, and browser preview.

The issues exposed during this process proved more valuable than the page translation itself.

1. Why choose generics/1 first

The English title of generics/1 is:

Plaintext
Type parameters

The page primarily introduces:

  • How Go functions use type parameters to handle multiple types;
  • The position of type parameters in a function declaration;
  • The relationship between T and the built-in constraint comparable;
  • Why comparable allows the use of == and !=;
  • How the generic Index function applies to all types that support comparison.

This page is of moderate length and has a fairly standard structure:

  • A title;
  • Three paragraphs of text;
  • A block of preformatted code;
  • Multiple legacy present inline code snippets;
  • A .play directive.

It contains no images, links, or complex exercise structures, making it well-suited as a starting point for a new round of calibration. When issues arise, it is also easier to determine whether they originate from the model’s translation, the prompts, structural protection, or the validator.

2. First attempt: not a translation failure, but a network permission failure

When executing the development mode translation for the first time, the GLM-5.2 API was not successfully called at all.

The error message was:

Plaintext
dial tcp: lookup open.bigmodel.cn on 127.0.0.53:53:
dial udp 127.0.0.53:53: socket: operation not permitted

This was an infrastructure failure caused by the Codex restricted sandbox denying network access.

The project still fully saved the following for this attempt:

  • request.json
  • response.json
  • validation.json

The page status remained pending.

This record was not deleted or overwritten. The purpose of the development phase is to expose real issues, so even without any model output, this attempt still held audit value.

For subsequent translation commands, I directly used an environment with external network access to avoid failing first in the restricted sandbox.

3. Second attempt: the model output a protection token twice

The second call to GLM-5.2 succeeded with an HTTP status of 200, and the model normally returned the complete Chinese content.

However, protected token validation failed.

The English original contained this sentence:

Plaintext
This declaration means that `s` is a slice of any type `T` that fulfills the
built-in constraint `comparable`.

The protected T corresponded to a unique token. To make the Chinese more natural, the model rewrote the sentence as:

Plaintext
任意类型 `T` 的切片,且 `T` 满足内置约束 `comparable`

The problem was that `T` appeared only once in the English source text, but the model used it twice in the Chinese translation.

As a result, 10 tokens were expected, but the model actually output 11. The token representing `T` appeared twice.

From a human reading perspective, the technical meaning of this sentence was correct; however, for the automated restoration process, a unique placeholder cannot be copied. Otherwise, residual tokens might remain after restoration, and it would be impossible to determine whether the extra positions were safe.

The validator therefore rejected this output.

4. Strengthening the protection token prompts

After analysis, I confirmed that the original system prompt already contained the English rule:

Plaintext
Preserve every protection token exactly once and in order.

That is, each token must appear exactly once and maintain its order.

However, for GLM-5.2, this rule was still not prominent enough. Especially when the model added explicit subjects to make the Chinese more natural, it tended to treat tokens as reusable technical terms rather than unique placeholders.

Therefore, I added a more explicit Chinese rule to the user prompt:

Plaintext
重要:下文每个形如 ⟪GTI18N_...⟫ 的保护 token 都是唯一占位符。
本页共有 10 个保护 token,输出中也必须恰好包含 10 个。
每个 token 必须原样输出且恰好输出一次,并严格保持输入顺序。
不得复制、不得复用、不得删除、不得改写或交换任何 token。
调整中文语序时,请使用“该类型”“该值”“前者”等普通中文承接,不要再次输出已有 token。

The token count here is not hardcoded; it is dynamically written based on the actual token map generated for the current page.

5. Third attempt: all tokens correct, but the present structure still failed

In the third attempt, all 10 tokens were:

  • Correct in count;
  • Each appearing only once;
  • Free of unknown tokens;
  • Completely correct in order.

The previous token duplication issue was resolved.

However, the restored Chinese contained:

Plaintext
`comparable`。`x`

The validator reported:

Plaintext
inline code mismatch at index 3:
expected "`comparable`",
actual "`comparable`。`x`"

This appeared to be simply a missing space after the Chinese period, but it actually involved the inline code parsing rules of legacy present.

6. Why legacy present depends on spaces

The legacy present font syntax currently used by A Tour of Go does not simply recognize every pair of backticks as independent inline code.

It first splits words by Unicode whitespace, then identifies font spans within each word.

The English original was:

Plaintext
`comparable`. `x`

There was an ASCII space after the period, so it was split into two words, ultimately yielding two independent code units:

Plaintext
`comparable`
`x`

After the model translated it into Chinese, it removed the space after the period following standard Chinese typography conventions:

Plaintext
`comparable`。`x`

This caused the entire content to fall within a single non-whitespace word. Present would recognize everything from the first backtick to the last backtick as a single unit, encompassing the Chinese period as well.

Similar issues could also occur with:

Plaintext
`s`是`T`
`==`和`!=`
`f`、`x`、`y`

These formulations look fine to humans, but for legacy present, multiple inline code snippets could be merged into a single span.

More troublingly, the project also contained this special formulation:

Plaintext
`package`rand`

This was supposed to be processed as a single complete code unit, with the semantic content being:

Plaintext
package rand

Therefore, we cannot simply scan for backticks and automatically split them after token restoration, or it would break the existing semantics of the official present tool.

7. Adding metadata-driven boundary normalization

The final adopted solution was:

After all strict token validations pass, but before restoring tokens, deterministically normalize the external boundaries of independent inline-code tokens based on their token type.

The project now records the type for protected tokens, for example:

  • inline code;
  • directive;
  • link target;
  • glossary or keep word;
  • other protection types.

The normalization logic only processes the external plain text of complete inline-code tokens; it does not inspect or modify the backticks inside the tokens.

For example, if the model output:

Plaintext
TOKEN_COMPARABLE。TOKEN_X

It would be normalized to:

Plaintext
TOKEN_COMPARABLE。 TOKEN_X

After restoration, this becomes:

Plaintext
`comparable`。 `x`

For:

Plaintext
TOKEN_S是TOKEN_T

It would be normalized to:

Plaintext
TOKEN_S 是 TOKEN_T

After restoration, this becomes:

Plaintext
`s` 是 `T`

Meanwhile, content like the following, which was originally a single legacy span:

Plaintext
`package`rand`

Always corresponds to a single token, and its internal content remains completely unchanged.

It should be specifically noted that normalization does not bypass strict token validation.

The processing order remains:

  1. Check total token count;
  2. Check for unknown tokens;
  3. Check that each token appears exactly once;
  4. Check token order;
  5. Only after all checks pass, perform boundary normalization;
  6. Restore tokens;
  7. Continue executing present and structural validation.

Missing, duplicated, unknown, or out-of-order tokens will not be automatically “fixed”.

8. Fourth attempt: the model swapped token order for natural Chinese

In the fourth attempt, all 10 tokens were present, and each appeared only once, but the tokens corresponding to `T` and `comparable` were swapped.

The English order was:

Plaintext
`T` → `comparable`

The model translated this as:

Plaintext
满足内置约束 `comparable` 的任意类型 `T`

Thus, the actual order became:

Plaintext
`comparable` → `T`

This Chinese sentence was natural and its technical meaning was mostly correct, but it violated the current global strict order requirement.

When I further inspected the prompts at that time, I realized that what had been referred to simply as the “Chinese system prompt” was actually:

English core rules + detailed Chinese translation rules

The user prompt and glossary control instructions also retained some English labels, such as:

Plaintext
Mandatory glossary rules:
Complete protected page:

This mixed English-Chinese formulation was not intentionally designed after comparative testing, but rather a historical result of the project’s incremental development:

  1. Initially establishing a minimal API contract in English;
  2. Later appending detailed Chinese requirements to improve Chinese quality;
  3. Then appending Chinese token rules based on actual failures.

9. Unifying fixed prompts into Simplified Chinese

To reduce variables, I did not immediately relax the token order, nor did I add page-specific examples for T and comparable.

This time, I made only one change:

Unified the fixed system prompt, user prompt labels, glossary control instructions, and previous failure prompts into Simplified Chinese.

The new system prompt begins with:

Plaintext
请将一个完整的《Go 语言之旅》present.Section 从英文翻译为中国大陆简体中文。

只返回完整且可由 present 解析的 .article 内容。必须保留每个保护 token,使其原样出现、恰好出现一次,并严格保持输入顺序。必须使用术语表中的强制译法;对应的、应当翻译的英文显示文本不得残留;不得简化、遗漏或改变原文含义。

The user prompt labels were also changed to:

Plaintext
强制术语表与译法规则:
需要翻译的完整受保护页面:

English sources in the glossary, present.Section, .article, tokens, directives, link targets, code, and paths were still preserved, as these are technical identifiers or source data that do not need to be forcibly translated for the sake of formal “all-Chinese” presentation.

10. The fifth attempt finally succeeded

The fifth attempt called the API only once, with no retries and no manual modifications to the model output.

The results were:

  • HTTP 200;
  • All 10 tokens present;
  • No missing, duplicated, or unknown tokens;
  • Token order completely correct;
  • `T` located before `comparable`;
  • Inline-code boundary normalization actually fixed one instance;
  • Present parsing passed;
  • Section structure passed;
  • Directive passed;
  • Inline code passed;
  • Preformatted code passed;
  • Glossary check passed;
  • Candidate successfully generated;
  • The page entered ready.

This time, the model still output:

Plaintext
TOKEN_COMPARABLE。TOKEN_X

Boundary normalization automatically adjusted it to:

Plaintext
TOKEN_COMPARABLE。 TOKEN_X

Finally, it was restored to:

Plaintext
`comparable`。 `x`

This proved that the newly added boundary normalization was not just passing unit tests, but also played a practical role in the actual GLM-5.2 output.

However, a single success cannot prove that localizing the fixed prompts into Chinese has completely eliminated the token reordering issue. The strict order strategy is currently retained, and further observation in other representative pages is still needed.

11. Final translation after manual polishing

After the model candidate passed automated validation, I performed some minor manual polishing.

The final page content was:

Plaintext
* 类型参数

Go 函数可以通过类型参数处理多种类型。函数的类型参数写在参数列表之前的方括号中。

  func Index[T comparable](s []T, x T) int

该声明表示,`s` 是元素类型为 `T` 的切片,而该类型满足内置约束 `comparable`。 `x` 也是该类型的值。

`comparable` 是一个很有用的约束,允许对该类型的值使用 `==` 和 `!=` 运算符。在本例中,我们用它将一个值与切片中的各个元素进行比较,直到找到匹配项。`Index` 函数适用于任何支持比较的类型。

.play generics/index.go

Where:

Plaintext
`comparable`。 `x`

The ASCII space after the Chinese period cannot be removed. It is not a typical typographical error, but rather the structural whitespace required by legacy present to distinguish two independent inline code spans.

After polishing, I re-ran the candidate validator, and the complete automated validation continued to pass.

12. Synchronizing and consolidating generics terminology

This time, three mandatory terms were also added to the zh-CN glossary:

Plaintext
constraint: 约束
type parameter: 类型参数
type parameters: 类型参数

Current glossary matching does not perform singular-plural normalization, so:

Plaintext
type parameter
type parameters

Need to be recorded separately.

The following were not added:

  • built-in constraint
  • comparable
  • supports comparison
  • slice element
  • generic function

Among these, comparable is Go’s predeclared constraint name, which is retained as code on the page; the rest can be naturally translated by the model based on context, making it unnecessary to write every common sentence pattern into the glossary.

13. Actual browser preview

After automated validation passed, I also started the local preview for generics/1.

The actual access address was:

Plaintext
http://127.0.0.1:3999/tour/generics/1

The preview used a temporary Tour content copy from /tmp, replacing only the target Chinese Section without modifying the repository’s official _content.

The page request returned HTTP 200, the index.go code on the right loaded normally, and there were no errors with static resources or templates.

In the actual HTML, the key content was:

HTML
该声明表示,<code>s</code> 是元素类型为 <code>T</code> 的切片,而该类型满足内置约束 <code>comparable</code>。 <code>x</code> 也是该类型的值。

It can be confirmed that:

  • comparable and x are two independent <code> elements;
  • The Chinese period was not included in the code font;
  • No residual protected tokens remained on the page;
  • The function declaration and the right-side example were complete;
  • The browser displayed everything normally.
Figure 1: Local preview of the generics/1 Simplified Chinese page
Figure 1: Local preview of the generics/1 Simplified Chinese page

14. Final commit

This round of changes was finalized in a single commit:

Plaintext
8f9f001 feat: 完成 generics/1 翻译校准并增强结构保护

The commit included:

  • Localization of the fixed translation prompts;
  • Protected token uniqueness and dynamic count rules;
  • Token kind metadata;
  • Legacy inline-code external boundary normalization;
  • Corresponding unit tests;
  • Records of five generics/1 attempts;
  • The manually polished candidate;
  • The generics glossary;
  • Status tests;
  • PROJECT_STATE.md updates.

The commit statistics were:

Plaintext
25 个文件,724 行新增,36 行删除

The commit has been pushed to the main branch on GitHub, and the working tree remains clean.

15. Current project status

As of August 5, 2026:

  • Officially published pages: 103;
  • ready: 9;
  • pending: 94;
  • blocked: 0;
  • Additionally, 2 separate #appengine: conditional source audit records were retained.

The pages completed so far include:

Plaintext
welcome/1~welcome/5
basics/1~basics/3
generics/1

This time, only the first page for representative page calibration was completed, which does not yet prove that the project possesses a stable batch translation capability.

Subsequent representative pages also include:

  • flowcontrol/8
  • methods/16
  • methods/20
  • concurrency/7
  • concurrency/10

These pages will continue to cover:

  • Lengthy exercise instructions;
  • Mathematical expressions;
  • methods and interfaces;
  • Special legacy present syntax;
  • .image;
  • concurrency.

Only after these representative pages are stable will a trial run of a batch of regular pending pages begin.

16. Insights gained from this calibration

The most important takeaway this time was not finally translating a generics page, but rather further clarifying the boundaries between several different types of issues in an automated translation system.

First, a human-readable correct translation is not necessarily a structurally publishable translation.

Duplicating a T or swapping T and comparable might make perfect sense to a human, but it breaks deterministic restoration and structural signatures.

Second, spaces in technical documentation can serve a syntactic role.

Chinese typography typically removes spaces around inline code, but legacy present relies on whitespace to delimit code units. The space here is not an aesthetic choice, but part of the structure.

Third, prompts can only reduce the probability of errors, not replace deterministic validation.

Even if the prompts explicitly forbid copying or swapping tokens, the model may still violate the constraints to rewrite for natural language. Therefore, token checks, present parsing, and structural comparisons remain indispensable.

Fourth, automated fixes must be built on explicit structural metadata.

The reason the inline-code space normalization was safe this time is that the system knew which tokens represented complete inline code. It did not guess backtick structures within restored plain strings, nor did it mistakenly damage legacy formulations like `package`rand`.

Fifth, a single success only proves that the current sample passed.

After the success of attempt-005, continued observation in other chapters and structural pages is still necessary. We should not relax the validator or declare the workflow stable based on a single success.

Conclusion

I initially chose generics/1 simply because its length was moderate, its structure was standard, and it was suitable as a calibration starting point for the generics chapter.

As the work progressed, however, this single page successively exposed issues such as network permissions, token duplication, legacy present whitespace boundaries, token reordering, and inconsistent prompt languages.

In retrospect, these five attempts were not simply repetitive consumption. Each failure drove the workflow to patch a step that had not previously been validated by a real page.

Now, generics/1 has completed translation, manual polishing, automated validation, terminology consolidation, and browser preview, but the entire project remains in the representative page calibration phase.

Whether we can proceed to trial runs of regular pages and batch translation will depend on whether subsequent types of high-risk pages can continue to pass the same workflow stably.

系列导航

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系