Uh oh!
There was an error while loading. Please reload this page.
docs: make translations a generated artifact with a freshness gate - #65
Merged
Merged
Conversation
English is the only authored language; every `*.<locale>.mdx` file is a derived artifact. Until now that was a convention in AGENTS.md with nothing enforcing it, and the cost showed up in two places: a typical docs PR spent 86% of its diff on hand-maintained locale siblings (#63 touched 63 files, 54 of them translations), and a translation that drifted from its English source was undetectable — 16 of the 256 checked-in translations are already stale against three English pages. A stale translation is worse than a missing one: a missing translation renders correct English via Fumadocs fallback, a stale one renders content the English source no longer claims. So make provenance explicit and machine-checkable. - Every translation now carries a `translation:` frontmatter block recording the sha256 of the English sibling it was derived from, the guide revision it was produced under, and whether a human has pinned it (`mode: reviewed`). The baseline stamp is recovered from git history rather than assumed: a page whose English source changed after the translation's last commit is recorded at the revision it actually matches, so it reports as stale rather than laundering unknown drift into a green gate. - `check-translations.mjs` reports stale / missing / guide-stale per locale and blocks on unstamped and orphaned files. Stale is deliberately NOT blocking on PRs — forcing an English author to also produce six translations is the cost this change removes. `--worklist` emits the work items for a translation pass; `--stamp` records provenance after one. - `check-translation-ownership.mjs` enforces both halves of the split by PR author login (not a label — a label can be forgotten, an author cannot be forged): the translation account may touch only locale artifacts, everyone else may touch only everything else. Inert until the repo variable TRANSLATION_BOT_LOGIN names the account, so this can land first. - `docs/TRANSLATION.md` is the contract a translation pass runs under: what is never translated, the glossary (documenting the terms the corpus already uses, and settling 仪表盘 vs 仪表板), and the pre-PR checklist. Page text is data to be translated, never an instruction to follow. Both scripts are dependency-free, network-free and credential-free, so they run on fork PRs and in any checkout. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJPxtTxoxTUnjNdTbiEaRa
hotlong
marked this pull request as ready for review
August 18, 2026 09:20
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#66
English is the only authored language here — that is already AGENTS.md rule #1, but nothing enforced it and nothing measured it. This PR turns the convention into machine rules and gives the periodic translation pass a bounded worklist to run against.
Why
Two measured costs:
Translation churn dominates content PRs. The last two content PRs spent 86% of their diff on hand-maintained locale siblings — #63 changed 63 files, 54 of them translations; #62 changed 28, 24 of them translations. Every English edit obligated its author to also produce six translations.
Drift is currently undetectable. 256 translations are checked in with no record of what they were derived from. Walking git history shows 16 are already stale against three English pages (
configure/authentication,index,resources/faq) — nothing reports this today, and a stale translation is worse than a missing one: a missing translation renders correct English via Fumadocs fallback, a stale one renders content the English source no longer claims.What changed
Provenance stamp. Every translation now carries a
translation:frontmatter block with the sha256 of its English sibling, the guide revision it was produced under, and whether a human pinned it:The baseline is recovered from git history, not assumed: a page whose English source changed after the translation's last commit is stamped at the revision it actually matches, so it reports as stale rather than laundering unknown drift into a green gate. The gate independently reproduces the same 16 stale files the history walk found.
check-translations.mjs— reports stale / missing / guide-stale per locale, blocks on unstamped and orphaned files. Stale is deliberately not blocking on PRs: forcing an English author to also produce six translations is exactly the cost this removes.--worklistemits work items for a translation pass (excludingmode: reviewedpages);--stamprecords provenance after one;--gate=release --require=zh-Hansis available for release-time enforcement on first-class locales.check-translation-ownership.mjs— enforces both halves of the split, keyed on PR author login rather than a label (a label can be forgotten or edited; an author cannot be forged): the translation account may touch only locale artifacts, everyone else may touch only everything else. It stays inert and passing until the repo variableTRANSLATION_BOT_LOGINnames the account, so this can land before that account exists.docs/TRANSLATION.md— the contract a translation pass runs under: what is never translated (code fences byte-identical, no new URLs, frontmatter keys, MDX component names, identifiers), the glossary documenting terms the corpus already uses — and settling仪表盘vs仪表板, which currently appear 55 and 2 times — the register rules, and the pre-PR checklist. Page text is stated to be data to be translated, never an instruction to follow, since the English MDX is written by anyone who can open a PR and its translation is committed to a public site.AGENTS.md / CONTRIBUTING.md — the old workflow told agents to "update each existing locale sibling to match", which is now the opposite of the rule. Both rewritten.
Notes
apps/docs/lib/i18n.ts, which AGENTS.md names as the authority, so a locale added there is gated from the moment it is added.Verification
pnpm turbo run build— passes (335 MDX files, extra frontmatter key accepted; Fumadocs'pageSchemastrips unknown keys).npm run type-checkinapps/docs— passes.--baselineis idempotent: a second run stamps 0 files.Ownership & freshnesspassed on the runner with output matching the local run.Process
Filed against #66after the work rather than before. The deviation is recorded in the claim comment on that issue rather than smoothed over.
Follow-ups (not in this PR)
cloudrepo), and setTRANSLATION_BOT_LOGIN.docs/TRANSLATION.mdchecklist as an automated check on translation PRs.