Skip to content

Every language of the site is derived from the English build and a translation memory

Every language of the site is derived from the English build and a translation memory

majordomus://adr/adr-0218accepted sitelanguagederivation

it references

  • put_in_force scripts/lib/site-i18n.mjs (file)
  • put_in_force scripts/site-build (file)
  • put_in_force scripts/site-translate (file)
  • put_in_force site/i18n/cs/edition.toml (file)
  • put_in_force site/i18n/languages.json (file)
  • put_in_force site/templates/partials/language-switch.html (file)
  • put_in_force adr-0077
  • put_in_force adr-0149
  • put_in_force project.english-only@4
  • put_in_force test/cases/3590_the_site_is_derived_in_every_language.sh (test)

referenced by

Nothing in the layer names it. Backlinks are derived, so an empty list is an answer rather than an omission.

218. Every language of the site is derived from the English build and a translation memory

Context

On 2026-10-19 the owner ordered that majordomus.dev be, from then on, always fully multilingual — Czech and English at the least — with a language switch, and that every language be derived, inferred and translated automatically: "I do not want to maintain two language versions; it must work automatically, derived, inferred, the prismatic way."

The site then had 2,137 pages and about 880,000 words, nearly all of them generated from English records (the plan, the documents, the rules, the decisions, the registry), and four hand-written Czech pages admitted by project.english-only v2 (ADR 0077). Three ways to make it bilingual were on the table:

  • A second content tree (*.cs.md beside every page, Zola's multilingual mode). Every generator would write twice, every template would need its strings keyed, and the data the templates read (site/data/generated/*.json) would still render in English. It is the "two language versions" the owner refused, written by machines instead of people.
  • Translation at build time, by asking a model during scripts/site-build. A build would stop being a pure function of the tree: two builds of one commit would differ, CI would need a credential, and a model outage would take the site down.
  • A pass over the built pages and a memory of translated segments — chosen.

Decision

  1. English is the only source. Nobody writes a page in another language. The four hand-written Czech pages stay as they are, and are carried into the Czech copy unchanged.

  2. Every other language is a pass over the built English site. After Zola, the accessibility pass and the diagrams, scripts/site-build runs scripts/lib/site-i18n.mjs over every page it wrote. A page is cut into segments: each run of inline content between block boundaries, and each attribute a reader meets (title, alt, aria-label, the page's description). Code, pre, kbd, SVG and anything marked translate="no" are never a segment. The copy under /<lang>/ is the English page with each segment the memory knows replaced, every internal link pointed at its counterpart, <html lang> and the canonical URL set, hreflang alternates added, and the machine-translation notice revealed. Because the pass reads the rendered HTML, it covers templates, generated data and content alike: there is nothing a template must do to be translated.

  3. The memory is keyed by the English, not by the page. site/i18n/<lang>/memory/*.jsonl holds {k, en, <lang>} per segment, k the hash of the English with inline markup replaced by numbered placeholders (<t1>…</t1>, <t2/> for code and images). The same words in two templates are one entry; an href that differs from page to page does not split them. A changed English segment has a new key, so a stale translation is never served — the segment falls back to English until it is translated again.

  4. A translation is admitted only with the structure of its source. admissible requires every placeholder exactly once, nested as in the English; scripts/site-translate import and run refuse anything else. A translator can reorder words, never drop a link or code.

  5. Translation is a separate, automatic step, outside the build. The build lists what the memory lacks in target/site-i18n/missing.json with the number of pages each segment is on and publishes its coverage at /<lang>/i18n.json. scripts/site-translate run --adapter asks a model through the advisor adapters of ADR 0098, and export/import carry batches to translators outside the process (a session's subagents). The glossary and style a translator is given live in site/i18n/<lang>/guide.json.

  6. The memory is content addressed to a reader, so project.english-only v4 admits site/i18n/<lang>/ as an edition: the directory says its language in edition.toml, and scripts/ci/english-only-allow.txt declares it with that language and a reason.

Consequences

  • A new language is one line of site/i18n/languages.json, a guide, an edition.toml and a translation run. No template, generator or page changes.
  • Coverage is measured, not asserted. It is reported on every build; the gate that refuses a regression, then anything below complete coverage, is the next step of this decision.
  • Every change to English prose costs a translation of the segments it changed. The cost is proportional to the change, because unchanged segments keep their keys.
  • The translation is machine translation and says so on every translated page, with a link to the original.
  • Search, the sitemap and the JavaScript strings of the site are not yet translated; they are outside the pages this pass reads.

Where this comes from

canonical file
.ai/repo/adrs/0218-every-language-of-the-site-is-derived-from-the-english-build-and-a-translation-memory.md
identity
adr-0218
also served as
MCP resource majordomus://adr/adr-0218 · majordomus entity show adr/adr-0218 · GET /api/v1/entity?uri=majordomus://adr/adr-0218
in the Cockpit
/cockpit/objects/adr/adr-0218

This page is generated from the file above by scripts/generate-site-data; editing the page changes nothing, editing the file changes every surface. The relations above are edges of docs/generated/graph.json, which the executable writes from the same index.

Searches the titles and descriptions of every page this site publishes.