v0.254.0 — the saved notes follow the language, and the switch becomes a globe (R-557 slice 2 release C; SLICE 2 CLOSED)
gates / gates (push) Successful in 23s
gates / gates (push) Successful in 23s
The notes a background run SAVES — last night's backup line, the last error, the proof result, the restore outcome — are written in the BOX's language at the moment they are written. A household that switches sees the previous run's note in the old language until the next run rewrites it: the operator's §16 option 1, stated rather than hidden. EndRestoreOp no longer receives a Hungarian literal from anywhere. The language switch is a globe. Two text links wrapped in the sidebar footer and asked the reader to recognise "Magyar"/"English" as links; a globe is the one symbol every web user already reads as "language", so nobody has to read Hungarian to escape Hungarian. It is <details>/<summary> — a menu with no script, drawn inline because the icon sprite lives only in layout.html and the visitor pages have their own shell. Those visitor pages get the same globe, and a visitor's choice stays theirs: a display-only felhom_lang cookie that langFor reads ONLY when there is no session. A signed-in household can never inherit a language a previous visitor picked in the same browser. POST /lang is CSRF-exempt for a narrow reason written at the exemption — its only achievable effect is the language of the page the victim's own browser shows them — and safeBackPath refuses //evil.example as well as https://, because "starts with /" alone is not the test. §16 taken: a successful claim carries the cookie into the household's setting. TWO PARITY EXCEPTIONS, MEASURED: 106 fixtures compared with a real diff — exactly two change shapes (the dashboard footer, the globe in the shells) and 5 byte-identical, which are the three pages that must not change. I INTRODUCED A DEADLOCK AND THE SUITE CAUGHT IT BY HANGING. UpdateOffboxStatus holds the settings write lock while running its callback; boxLang() wants the read lock; sync.RWMutex is not reentrant. On a real box an off-site run would have hung forever HOLDING the settings lock. Fixed by resolving the language before the callback, and guarded by a test that names the file and line in a second instead of hanging for 25 minutes. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+24
-4
@@ -3614,7 +3614,7 @@ without reaching `Images()`.
|
||||
|
||||
---
|
||||
|
||||
### 18. Dashboard language (i18n) (v0.247.0–v0.253.0)
|
||||
### 18. Dashboard language (i18n) (v0.247.0–v0.254.0)
|
||||
|
||||
Design: `felhom.eu/documentation/architecture/10-localisation.md`. Inventory:
|
||||
`felhom.eu/documentation/audits/I18N-INVENTORY-2026-09-17.md`.
|
||||
@@ -3624,9 +3624,10 @@ sidebar footer of every dashboard page. Every template is converted (v0.247.0 th
|
||||
apps and settings, v0.249.0 backups, v0.250.0 storage, sharing, sign-in, claim, guest share, catch-all,
|
||||
debug). Since **v0.252.0** the sentences the program BUILDS follow the language too — flash lines, page
|
||||
data, the JSON the page's script reads, the alert banners, the country names, and the four page titles
|
||||
built around an app name. Since **v0.253.0** every error message carries its key too, so a refusal made deep in a package is
|
||||
rendered in the household's language by whoever prints it. Still Hungarian: text persisted by a
|
||||
background run (release C), catalog copy (slice 5), and **everything the hub reads** (see below).
|
||||
built around an app name. Since **v0.253.0** every error message carries its key too. Since **v0.254.0** the notes a background
|
||||
run SAVES follow the language as well, and the switch is a **globe** — on the dashboard and on the
|
||||
sign-in, claim and recovery pages. Still Hungarian: catalog copy (slice 5) and **everything the hub
|
||||
reads** (see below).
|
||||
|
||||
- **Bundles:** `internal/i18n/locales/hu.json` (authoritative, every key) and `en.json`, embedded.
|
||||
Flat `key → text`; a value may carry template actions (`{{.RecoveryAbandonDate}}`) and inline
|
||||
@@ -3680,6 +3681,25 @@ background run (release C), catalog copy (slice 5), and **everything the hub rea
|
||||
- **Plurals (v0.253.0):** a key that carries `.one`/`.other` in a language is a plural key and its FIRST
|
||||
parameter is the count (`i18n.Bundle.form`). Hungarian has one form at every count. `.one`/`.other`
|
||||
are RESERVED suffixes — `TestNoOrdinaryKeyEndsInAPluralSuffix`.
|
||||
- **Saved notes (v0.254.0):** a background run has no request and no reader, so a note it SAVES is
|
||||
written in the BOX's language at write time — `util.Text(lang, key, …)` via `m.note`/`s.note`
|
||||
(`m.noteErr`/`s.noteErr` for an error). Consequence, by operator ruling (slice 2 §16 option 1): a
|
||||
household that switches sees the previous run's note in the old language until the next run.
|
||||
**TRAP, and it cost a 25-minute hang:** `UpdateOffboxStatus` and its siblings hold the settings WRITE
|
||||
lock while running their callback, and `boxLang()` wants the READ lock — `sync.RWMutex` is not
|
||||
reentrant, so a note rendered inside such a callback DEADLOCKS while holding the settings lock.
|
||||
Resolve the language BEFORE the callback; `TestNoteHelpersAreNotCalledUnderTheSettingsLock` guards it.
|
||||
- **The switch is a globe (v0.254.0):** `templates/lang_globe.html`, a `<details>`/`<summary>` menu with
|
||||
no script, drawn inline (the sprite lives only in `layout.html`, so `<use>` would render nothing on
|
||||
the shells). Language names are shown in their own language and never translated.
|
||||
- **A visitor's language (v0.254.0):** the sign-in, claim and recovery pages carry the same globe, posting
|
||||
to `POST /lang`, which sets the display-only `felhom_lang` cookie. `langFor`'s order: `?lang=` →
|
||||
**household setting when a session exists** → cookie when none → setting → `hu`; a signed-in household
|
||||
never reads the cookie. `/lang` is CSRF-exempt because its only achievable effect is the language of
|
||||
the page the victim's own browser shows them — **if it gains a second effect it needs CSRF that day**.
|
||||
`safeBackPath` refuses a protocol-relative `//host` as well as an absolute URL. A successful CLAIM
|
||||
carries the cookie into the household setting (§16). **No globe on the guest share pages or the
|
||||
not-found page** — R-577.
|
||||
- **Report:** the hub report carries `"language"` (always present). No hub release reads it yet.
|
||||
- **Tools and gates:** `scripts/i18n_extract.py` converts a template (moves each Hungarian run into
|
||||
hu.json, leaves a marker); `scripts/i18n_missing_gate.py` (in `controller_gates.py`) checks keys
|
||||
|
||||
Reference in New Issue
Block a user