In the previous article, I completed the design freeze for the Chinese version of A Tour of Go, focusing on issues such as page count, translation units, state workflows, and command-line architecture. (Shuijing Wanwq)
After the design freeze, I did not immediately call GLM-5.2 to start translating.
Once I actually entered the implementation phase, I first completed three more fundamental tasks:
- Investigate the official Tour and multiple existing translation projects;
- Extract an independently runnable English baseline from a fixed official source code commit;
- Build a machine-readable table of contents, persistent identities, and upstream change previews for the 101 lesson pages.
During this phase, I also specifically verified how the Go example code on the right side runs.
A Tour of Go is not just a set of static lesson articles. Readers can directly modify the example code, format the code, and click Run. How to retain this capability without running untrusted code on an ordinary web server is an unavoidable problem for this project.
This article does not repeat the full-page translation principles already explained in the previous article. Instead, it records what these designs ultimately became once they entered the code implementation phase.
1. Before starting development, I investigated six translation projects
Before importing the official source code, I conducted a read-only investigation of six representative A Tour of Go translation projects:
Go-zh/websiteGo-zh/touratotto/go-tour-jpgolang-id/tourgolangbr/go-tour-brsuzusime/go-tour-jp
These projects cover several common approaches:
| Approach | Representative form |
|---|---|
Full copy of golang/website | Chinese website project |
| Copy of legacy single-language Tour | Chinese, Japanese, Indonesian, and Brazilian Portuguese projects |
| Generate static pages from legacy Tour | Japanese static project |
| Independent deployment for each language | Most community translation sites |
These projects are not without value.
On the contrary, they proved that basic directions are feasible: full .article translation, separating lesson text from the public UI, using /socket for local execution, and the official website calling a remote Playground.
However, after years of maintenance, several common problems gradually emerged.
2. Single-language copying enables a quick launch, but incurs high long-term maintenance costs
The approach adopted by most legacy translation projects can be summarized as follows:
Copy a Tour server
→ Copy templates and static assets
→ Copy all Go examples
→ Translate lesson text
→ Deploy a separate language site
The advantages of this approach are obvious.
When a project starts, maintainers only need to copy the official code, modify .article and UI copy, and they can quickly get a runnable translation site.
The problem is that every added language introduces an independently maintained set of server code, templates, JavaScript, example code, and deployment configurations.
After the official Tour updates, every translation project needs to sync separately:
- Server code;
- Frontend assets;
- Lesson pages;
- Go examples;
- Third-party dependencies;
- Deployment runtime.
If a language project stops being maintained, it often does not immediately fail completely.
A more common scenario is that the site remains temporarily accessible, but the lesson content, dependencies, and deployment methods gradually fall behind.
Therefore, this project chose not to copy a permanently independent Tour for Simplified Chinese, nor does it plan to copy more servers for Japanese, German, or other languages in the future.
The currently established principles are:
- Share the Go server as much as possible;
- Share the base template structure and static assets as much as possible;
- Maintain only a single official baseline for Go examples;
- Independently maintain only the lesson translations, UI copy, glossary, and page status for each language.
3. Copying the entire website creates another set of problems
The official A Tour of Go is currently located in the golang/website repository.
The most straightforward approach seems to be forking the entire website and then modifying the Tour inside it.
But golang/website contains not only the Tour, but also:
- The official Go website service;
- Blog and documentation content;
- Download pages;
- Package documentation logic;
- Official build and deployment configurations;
- App Engine and Cloud Build files;
- A large amount of static assets and tools unrelated to the Tour.
If the goal is only to maintain a multilingual Tour, copying the entire website would mix a large number of irrelevant changes into every upstream sync.
Therefore, I ultimately adopted a different approach:
Extract the minimal source code closure required for the Tour to run independently from a fixed official commit.
The currently fixed official baseline commit is:
e11dacba76c5aae474746e9eedee19693f492803
The initial import involved 199 upstream source files:
- 192 files remain byte-for-byte identical;
- 7 files underwent minimal modifications to adapt to the standalone repository.
The project uses UPSTREAM_MANIFEST.tsv to record the following for each source file:
- Upstream path;
- Local path;
- File type;
- Whether it is kept as-is;
- SHA-256;
- Necessary adaptation notes.
This avoids both copying the entire website and the network dependencies and non-reproducibility caused by temporarily downloading source code during every build.
4. Long-term problems observed in other translation projects
Beyond redundant server maintenance, this investigation also exposed several more noteworthy issues.
1. Many projects lack a clear upstream version
Having a lesson translation in a Git repository does not directly indicate which official commit it corresponds to.
Recent commits do not guarantee that the lesson text has been synced.
Therefore, this project simultaneously records:
- Fixed upstream commit;
- File-level SHA-256;
- Page-level
source_sha256; - Page identity and current path;
- Categorized results of future upstream changes.
2. Deployment platform retirement can take an entire language site offline
Multiple legacy translation projects are deeply tied to the App Engine runtime.
When the legacy runtime was retired, a batch of community translation sites went offline simultaneously. Although the official Go project provided entry points for these languages on its pages, these sites were owned by community maintainers, and the official project could not directly restore deployments on the maintainers’ behalf.
This demonstrates that:
Source code still existing does not equate to the online service being able to run long-term.
Therefore, the current project does not treat a specific cloud platform’s proprietary deployment files as core architecture, nor does it write personal project IDs, account configurations, or fixed runtimes into the general workflow.
3. Without automated validation, errors rely primarily on readers to be discovered
Existing translation projects typically have basic Go tests, but rarely possess:
- Structural comparison between English pages and translations;
.playpath integrity checks;- Link target checks;
- Inline code checks;
- Page identity checks;
- Translation status gates;
- Upstream change previews;
- Blocking publication of broken translations.
This means that even if a translation breaks the lesson structure, it might not be noticed until a maintainer or reader stumbles upon it.
4. Example code easily diverges across languages
Some translation projects modified comments or strings in the example code.
In the short term, this makes the code appear more localized; in the long term, it leads to different example baselines for different languages.
After the official code is modified, maintainers must not only sync the code but also judge whether the modifications in each language version still apply.
Therefore, the first phase decided:
- Go examples referenced by
.playremain exactly as the official original; - Variable names, function names, and output strings are not translated;
- A different set of examples is not copied for each language;
- Hashes and tests are used continuously to confirm that no unintended changes occur in the examples.
5. Purely static pages are easier to deploy, but lose full interactivity
The static Japanese project proved that lesson text can be pre-generated into ordinary HTML.
This approach is easy to migrate and does not depend on a continuously running Go service.
However, static pages usually cannot fully preserve:
- Editor;
- Run;
- Format;
- Kill;
- Interaction with the Playground.
Therefore, static pages can serve as a read-only backup or a graceful degradation product in the future, but they are not suitable as the sole official form for the current project.
5. The English Tour baseline can now run independently
After completing the minimal source code import, the project no longer depends on the full golang/website repository to run the Tour.
The current English baseline has completed the following verifications:
- Go Module resolves correctly;
- All Go tests pass;
- Seven
.articlecan be parsed bypresent; - Example code completes the original build and run tests;
- Tour HTTP service starts normally;
- Pages, CSS, JavaScript, Logo, and favicon are accessible;
/_/fmtformats code correctly;- Local
/socketcompletes the WebSocket handshake; - No listening ports or temporary files remain after the process exits.
The current source code structure statistics are:
| Item | Count |
|---|---|
Top-level .article | 7 |
| Standalone regular pages | 101 |
Regular .play references | 92 |
.image references | 1 |
#appengine: conditional pages | 2 |
This set of 7 / 101 / 92 / 1 data has become part of the current fixed English baseline.
6. Why the previous article had 103 pages, but the current status file only has 101
The previous article counted 103 pages from the official integrated production projection perspective.
The two extra pages were:
Go offline (optional)The Go Playground
They come from the #appengine: conditional content in welcome.article.
Upon actually starting the standalone Tour import, I decided to first establish the first-phase status using the 101 regular pages in the current standalone runtime mode, while separately recording the two conditional pages in:
data/tour-conditional-pages.tsv
Therefore, the current structure does not ignore those two pages; rather:
101 standalone regular pages
→ Enter zh-CN page status
2 appengine conditional pages
→ Registered separately
→ Not entering first-phase translation status for now
This allows the first-phase page table of contents to remain consistent with the current standalone runtime baseline, while preserving the source, title, and hash of the conditional pages.
When actually implementing the production projection or equivalent pages later, I will explicitly decide how to handle these two pages, rather than mixing the standalone and App Engine runtime modes into the same status file now.
7. Where exactly does the Go example code on the right run
One of the most important features of A Tour of Go is that readers can directly modify the code on the right and click Run.
However, “clicking Run” does not mean that all environments run the code the same way.
Local development mode
When starting the Tour locally, the execution flow is roughly:
Code editor in browser
→ WebSocket /socket
→ Local Tour process
→ Local Go toolchain
→ Return execution result
This approach is suitable for local learning and development on personal computers.
After importing the English baseline, the current project also retains this upstream behavior and actually verified that /socket can complete the WebSocket 101 Switching Protocols handshake.
However, /socket calls the Go toolchain on the machine hosting the Tour to execute code, so it can only be used in a trusted local development environment.
It cannot directly serve as a code execution interface for a public website.
Official production mode
The current official Tour production flow is not the browser running Go locally, nor is it the go.dev ordinary web process directly executing user code.
The actual flow is:
Browser
→ Same-origin /_/compile
→ go.dev server proxy
→ play.golang.org/compile
→ Go Playground sandbox
→ Return execution result
Go Playground compiles and runs code in a restricted sandbox, limiting execution time, CPU, memory, and external access. The official project also allows third-party services beneficial to the Go community to use the Playground, but requires callers to contact them in advance and provide a recognizable, unique User-Agent. (Go)
Approach planned for the current project in the future
The official website still plans to keep Run, but will not expose the public /socket to the internet.
The planned official flow is:
Browser
→ Project same-origin lightweight proxy
→ Official Go Playground
→ Return execution result
The “proxy” here is only responsible for forwarding restricted requests.
It does not mean that user code runs on the project’s web server.
This retains the interactive learning experience while avoiding the need to take on malicious code isolation, system call restrictions, and full sandbox operations ourselves.
However, this part is currently still just the established official plan; the proxy has not yet been implemented and is not publicly online.
8. The 101 lesson pages now have a machine-readable table of contents
After completing the English baseline, the project did not immediately start translating, but first generated:
data/tour-pages.tsv
Each page records:
page_id- Parent article;
- Current Section number;
- Current route;
- English title;
- Full page
source_sha256; .playcount;.imagecount.
For example, a page can have the following relationships:
page_id: welcome/1
article: welcome.article
section_number: 1
route: /welcome/1
source_sha256: 491d9386ffb917083a1e556bc9849d57894dd8b29bd243ac855cd9e99f701893
The hash here is not generated based solely on the title, nor is it generated from an abstract syntax tree.
It corresponds to the full top-level page source text exported from the English article, including:
- Page title;
- Regular body text;
- present directives;
- Code references;
- Trailing newline.
When the official project modifies a page later, this project can accurately determine whether the English source has changed, rather than relying solely on file modification times or manual visual comparison.
9. Pages can be exported individually by full Section
The current maintenance tool can already export a complete English page based on page_id:
go run -mod=readonly ./cmd/tour-i18n page export \
--id welcome/1 \
--output /tmp/welcome-1.article
Export results:
- Contain only one complete top-level Section;
- Do not include other pages from the same article;
- Maintain the original present format;
- Can be re-parsed;
- SHA-256 matches the page table of contents;
- Do not overwrite existing files by default.
In the future, GLM-5.2 will receive such complete pages, rather than a few sentences or JSON fields extracted from a page.
This design principle was already detailed in the previous article and will not be repeated here.
10. The 101 zh-CN pages are currently all pending
The project has created an independent Simplified Chinese language space:
locales/zh-CN/
It separately maintains:
- Locale metadata;
- Page status;
- Future full page translations;
- UI copy boundaries;
- Glossary.
Currently, status.tsv has exactly 101 records:
pending:101
candidate:0
ready:0
blocked:0
published:0
This means the project knows which pages need to be processed, but has not yet generated any official Chinese translations.
No English content is masquerading as Chinese pages, nor were empty .article files created prematurely just to show progress.
11. Structural validation no longer locks down regular paragraphs
During the design phase in the previous article, I leaned towards strictly preserving the count, order, and position of regular paragraphs.
After actually analyzing the 101 pages and implementing the candidate validator, I adjusted this rule.
The principle is now:
Only strictly protect content that is genuinely dangerous or untranslatable; do not restrict normal Chinese expression just to match the form of English paragraphs.
Content that must currently be preserved includes:
.playdirectives and target paths;.imagedirectives and target paths;- Link target URLs;
- JavaScript link targets;
- Inline code;
- Preformatted code;
- present directives;
- The count and order of protected content;
- The count of top-level Sections.
Content allowed to change includes:
- Page titles;
- Regular body text;
- Chinese word order;
- Sentence count;
- Regular paragraph count;
- Regular text line breaks;
- Link display text.
For example, the link display text below can be translated:
[[/cmd/gofmt/][gofmt tool]]
However, the link target /cmd/gofmt/ cannot be modified by the model.
Similarly, .play welcome/hello.go can appear in the translated page, but the paths inside must remain exactly as they are.
This preserves the context needed for full-page translation while avoiding awkward Chinese phrasing caused by pursuing mechanical structural consistency.
12. page_id cannot always equal the page position
When the page table of contents was initially generated, page_id looked completely identical to the current route:
page_id: basics/5
route: /basics/5
But if the official project later inserts a page before Basics page 5, the original page 5 will move to page 6.
If IDs continue to be regenerated based on position, all subsequent pages will change:
basics/5 → basics/6
basics/6 → basics/7
basics/7 → basics/8
This would further cause:
- Translation file path changes;
- Loss of association in status records;
- Inability to match historical Candidates;
- Identity drift in published pages;
- A large number of meaningless changes in sync results.
Therefore, the current 101 page_id have been officially frozen as persistent identities.
The fields now have different responsibilities:
| Field | Meaning |
|---|---|
page_id | Internal persistent project identity |
route | Current access path |
section_number | Current upstream location |
source_title | Identification and diagnostic information |
source_sha256 | Current English source version |
In the future, even if a page moves, route and section_number can change, while page_id remains unchanged.
New pages will not insert a number and reshuffle old pages; instead, they will be explicitly assigned a never-before-used new ID.
13. Upstream changes can now be previewed first
The project added a read-only upstream change preview command:
go run -mod=readonly ./cmd/tour-i18n upstream preview \
--source-root /path/to/website
The preview results categorize pages into six types:
| Type | Meaning |
|---|---|
unchanged | Page content and position are unchanged |
content_changed | Can be confirmed as the same page, but English content has been updated |
moved | Full source content is identical, but position or route has changed |
added | New page added upstream |
removed | Page removed upstream |
ambiguous | Insufficient evidence, cannot be safely matched automatically |
The matching strategy remains conservative.
If the full source hash is uniquely identical, a page move can be confirmed.
If the current route still exists and the protected structure remains compatible, a change in regular content can be confirmed.
However, the following situations will not be automatically migrated:
- A page moves while simultaneously undergoing significant structural changes;
.playpath changes;- Multiple pages share duplicate characteristics;
- Matching can only be determined by identical titles;
- Matching can only be determined by similar regular body text;
- Moving across articles without unique evidence.
These cases will enter ambiguous, awaiting explicit handling.
The preview command will not:
- Modify the official table of contents;
- Modify
status.tsv; - Delete language status;
- Create new page IDs;
- Move Candidates;
- Automatically overwrite translations.
14. Preview results for the current fixed baseline
Running the preview twice using the current fixed upstream yields completely identical output:
普通页面:101
条件页面:2
unchanged:101
content_changed:0
moved:0
added:0
removed:0
ambiguous:0
conditional unchanged:2
This indicates that the official page table of contents is completely consistent with the current English source code, and also proves that the preview output is deterministic.
At the same time:
- 101
page_idhave not changed; status.tsvremains byte-for-byte unchanged;- The two conditional page directories have not changed;
- English articles have not changed;
- Go examples have not changed.
15. Multilingual domain names have also shifted from a path scheme to subdomains
During the design phase, using paths under the same domain was considered:
/ja/
/de/
After actually considering CDN and deployment, I switched to using subdomains by language.
The current plan is:
go-tour.<actual-domain>
→ Default Simplified Chinese
→ EdgeOne
en.go-tour.<actual-domain>
→ English
→ Cloudflare
ja.go-tour.<actual-domain>
→ Future Japanese
→ Cloudflare
The default site directly provides zh-CN, without creating zh. or zh-cn. subdomains.
Project introduction, language selection, upstream baseline, and GitHub entry will be placed on the /about/ page of the default site.
This way, different languages can independently choose in the future:
- CDN;
- Deployment region;
- Caching strategy;
- Release time;
- Failure handling method.
Currently, these are just recorded domain and deployment plans; no actual DNS, certificates, or CDN configurations have been created.
16. This phase still has not translated any official pages
As of now, the project has completed:
- Minimal English Tour source code closure import;
- 199 source file tracking;
- Independent English Tour execution;
- Full parsing of seven articles;
- 101 regular page table of contents;
- Two conditional page table of contents;
- 92
.playreference validations; - Example code build and run tests;
- Single-page full export;
- zh-CN status file;
- Candidate structural validation;
- 101 persistent
page_id; - Upstream change preview;
- Multilingual domain and CDN boundary planning.
But the current status is still:
正式中文页面:0
Candidate:0
Ready:0
Blocked:0
Published:0
This is not project progress stagnating.
On the contrary, it means that before the first translation appears, the project has already established:
- Reproducible English sources;
- Traceable page identities;
- Verifiable structural boundaries;
- Previewable upstream changes;
- A status foundation that does not rely on human memory.
17. The next step is where GLM-5.2 is actually called
The next phase will enter the official translation pipeline:
Export full English page based on page_id
→ Protect dangerous structures
→ Call GLM-5.2 for full-page translation
→ Save Candidate
→ present re-parsing
→ protected structure comparison
→ Update attempt count and page status
This phase still will not immediately batch process all 101 pages.
A more prudent approach is:
- First implement the single-page translation executor;
- Complete automated testing using a mock service;
- Select a shorter page for the first real translation;
- Verify Candidate persistence and failure recovery;
- Verify
readyandblockedstate transitions; - Finally decide when to start batch translation.
Only then will this project actually generate the first Simplified Chinese lesson page.
And the work completed now ensures that after the first translation appears, it will not become an isolated text whose source cannot be confirmed, whose expiration cannot be judged, and whose structure cannot be automatically validated.
