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
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user