docs: localisation slice 2 release A (controller v0.252.0) — R-557 progress, R-566 closed, R-572..R-574
gates / gates (push) Successful in 21s

10-localisation.md gains §10.1: the measured numbers (1 120 base literals, not 1 141; 226
converted), the flash-as-key design and why a key must be resolved by the READER, word order
through Go's explicit argument indexes rather than a second placeholder syntax, and the parity
gate that makes "byte-identical" a measurement instead of a reading.

Two claims the plan carried that live source disproved, both about the wire, both recorded:
the country table is NOT on the wire (only codes are), and the hub does NOT always compose its
own customer mail — it falls back to the controller's event message, which is why those 31
sentences stay Hungarian until R-558. That survey is handed to R-558 as its input list.

R-566 CLOSED (four app-named page titles now carry a %s). New rows R-572 (two funcmap helpers
with no English form), R-573 (the two channel-health banners arrive as finished Hungarian),
R-574 (handler_debug.go mixes page copy with payload).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-18 10:16:24 +02:00
parent 2bd6fbfac9
commit ea4f6ab340
3 changed files with 98 additions and 4 deletions
+66 -1
View File
@@ -279,12 +279,77 @@ session). Each slice ends with the parity gate green for every page it touched.
|---|---|---|---|---|
| 0 (done) | — | mechanism, 3 pages + layout, setting, report field, gates | Hungarian unchanged by measurement; English reachable | spent |
| 1 (done, v0.248.0–v0.250.0) | R-556 | the other 31 dashboard templates (~1 500 strings, 600 of them JS), parity fixtures per page; switch shown to everyone; English retrieval stems; the extractor's ASCII word list reviewed per page | the whole dashboard in English with Hungarian byte-identical | 12–16 h, three releases |
| 2 | R-557 (after R-553) | Go-side customer strings: 947 shown + 184 errors; flash-in-URL → keys; country names; alert texts | a page's server messages follow the language | 16–20 h |
| 2 | R-557 (after R-553) | Go-side customer strings; flash-in-URL → keys; country names; alert texts | a page's server messages follow the language | 16–20 h, three releases · **A shipped 2026-09-18** |
| 3 | R-558 | hub: per-customer language at creation; `configgen` renders it; `customerMessages` (39), the 5 lifecycle mails and the self-bind page in English; dispatcher reads the reported language | a household's e-mails arrive in its language | 6–8 h, one hub + one controller release |
| 4 | R-559 | console banner (34 lines, console-font limits) and the download page | an English household meets English from the first boot screen (in scope — ruling 1b) | 4–6 h + an ISO train |
| 5 | R-560 | catalog: §7 format, controller reads it, 835 strings / ~5 000 words, catalog copy gates per language | an app card, its settings and its first steps in English | 12–16 h |
| 6 | R-561 | the volunteer guide in English (~1 600 words); **then a stranger's first hour in English**, the 2026-09-14 walk; closes R-516 against the inventory | a newcomer can set up and use a box in English | 6–8 h |
### 10.1 Slice 2, as measured (release A, controller v0.252.0, 2026-09-18)
**[FACT] The counts the plan carried were the inventory's, and they moved.** At slice 2's base commit
`736f54b49610` there were **1 120** Hungarian Go literals, not 1 141; `handlers.go` 120 (not 121),
`offbox_handlers.go` 70 (not 72), `storage_handlers.go` 45 (not 48), `api/router.go` 18 (not 19).
Release A converted **226** of them and added 237 country keys.
**[DESIGN] A flash is a KEY, because it is read by a different request than the one that wrote it.**
`?flash=<key>&fa=<param>…`, built by `flashQuery`, resolved by `s.flashFrom`. A value the bundle does
not know is shown **verbatim** — that is what keeps a link already in a customer's tab, a bookmark or
a back-forward cache reading correctly, and it is the same rule that makes a hand-typed `?flash=`
harmless (`TestFlashKeyRoundTrip`). The same shape one layer in: an **alert banner** is built by a
background health cycle and read minutes later, so `Alert` carries `MessageKey` + `MessageArgs` and
`GetAlerts(lang)` renders on the way out.
**[DESIGN] Word order is Go's explicit argument index, not a second placeholder syntax.** The plan
proposed a named-parameter (`{{.Name}}`) form for multi-parameter Go messages. English reorders with
`%[2]s`, which `fmt` already understands, so the Hungarian value stays **the format string the code
always had, byte for byte** — and that is precisely what the parity gate compares. A second syntax
would have needed an exception in the measurement, which is the thing this slice cannot afford.
`TestBundleParametersMatchAcrossLanguages` counts an indexed verb as one verb.
**[FACT] Parity for Go copy is measured, not read.** `controller/scripts/i18n_go_parity.py` freezes
every Go string literal at the base commit (**7 467**, `scripts/i18n_go_base.json`) and refuses a key
whose Hungarian is not that text byte for byte — reworded, re-punctuated, split or joined.
`scripts/i18n_go_keys.json` records what each key replaced (a string for one literal, an ordered list
for a join, `{"inside": …}` for the three sites where the Hungarian was already URL-escaped inside a
redirect target). In `controller_gates.py`; three decoys, each seen to convict.
**[FACT] The gate found a live defect in its own first version — the R-565 class again.** The capture
filtered literals through an ASCII-Hungarian word list and missed seven real ones („Naponta",
„5 percenkent", „Eletjel (Heartbeat)", „Adatbazis mentes", „Biztonsagi mentes", „Mentes integritas",
„Rendszer allapot"); the gate refused the keys citing them. **The filter is gone.** The index answers
one question — *did the base commit contain this text?* — and an unfiltered index answers it for every
string. **The lesson generalises: a word list is a list, and nobody knows which words are missing.**
**[FACT] Two claims in the plan that live source disproved, both about the wire.**
1. `cloudflare/countries.go` is **not** on the wire. `report/builder.go` carries country **codes**;
no name leaves the box, and `CountryName()` has no caller. The 237 names were therefore free to
translate, and are — at DISPLAY, in `api/geo.go`, re-sorted per language. The table stays: it
validates codes.
2. The hub does **not** compose every customer mail from the event kind. `FormatCustomerEmail`
(`hub/internal/notify/templates.go` L167-210) uses `customerMessages[eventType]` when it has one
and **falls back to the controller's message when it has none**, appending it as „- Üzenet: %s"
whenever the two differ; several event types carry no entry precisely so the controller's sentence
IS the mail. So `notify/notifier.go`'s 31 messages are customer-facing text on the wire. The
verdict (do not translate them here) was right; the reason was not, and the reason is what slice 3
has to act on.
**[DESIGN] On the wire = not translated, and now measurable.** `internal/monitor` and
`internal/notify` carry wire goldens capturing those producers' exact bytes at the base commit. A
later slice that translates one fails a test that prints both strings. R-558 is the row that unfreezes
them, by teaching the hub which language to compose in.
**[FACT] Still Hungarian after release A:** 894 literals — **176 of them `fmt.Errorf`/`errors.New`**
(release B), the text a background run persists (release C), the R-570 producer, `handler_debug.go`
(R-574), two copy-producing funcmap helpers (R-572) and the two channel-health banners (R-573).
**[DESIGN] Persisted text (release C) — the plan's §16 option 1, its own stated default.** A note is
written in the box's language **at the time it is written**, and a household that switches sees last
night's note in the old language until the next run rewrites it. The alternative (store a code, render
live) costs a dozen new persisted fields and a legacy path for each — the R-570 shape, a dozen times
over. Recorded here when release C ships.
---
## 11. Operator decisions