v0.247.0: i18n spike — the dashboard can speak English, Hungarian byte-identical
gates / gates (push) Successful in 19s
gates / gates (push) Successful in 19s
Message bundles (internal/i18n) expanded into templates before parsing, one template set per language. Launcher, /backups, /apps/<slug> and the layout converted; household language setting, POST /settings/language, ?lang= override, report field. Parity test against fixtures captured from unconverted templates; copy gates read templates expanded; new i18n_missing_gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -46,6 +46,7 @@ backups, monitoring and notifications. All Proxmox/disk operations are delegated
|
||||
- [App-to-App Integrations](#15-app-to-app-integrations)
|
||||
- [Network File Sharing — SMB](#16-network-file-sharing--smb-megosztás-v01440-r-7-slice-1)
|
||||
- [Async-job feedback](#17-async-job-feedback-v0147x-feedback-slice-1)
|
||||
- [Dashboard language (i18n)](#18-dashboard-language-i18n-v02470-spike)
|
||||
- [Repository Layout](#repository-layout)
|
||||
- [Configuration](#configuration)
|
||||
- [REST API](#rest-api)
|
||||
@@ -3613,6 +3614,46 @@ without reaching `Images()`.
|
||||
|
||||
---
|
||||
|
||||
### 18. Dashboard language (i18n) (v0.247.0, spike)
|
||||
|
||||
Design: `felhom.eu/documentation/architecture/10-localisation.md`. Inventory:
|
||||
`felhom.eu/documentation/audits/I18N-INVENTORY-2026-09-17.md`.
|
||||
|
||||
**What a household sees:** nothing new. Hungarian is the default and the only language shown unless
|
||||
the language is switched. English covers the **launcher**, the **backups overview** (`/backups`), the
|
||||
**app page** (`/apps/<slug>`) and the shared **layout** (menu, banners, the layout's modals) — other
|
||||
pages keep their Hungarian body inside an English menu.
|
||||
|
||||
- **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
|
||||
markup (`<strong>`), which are the message's parameters. English plurals are `key.one` / `key.other`
|
||||
(`Bundle.Plural`); Hungarian has one form.
|
||||
- **Templates:** a converted template carries `{{T "key"}}` markers. `loadTemplates` parses **one
|
||||
template set per language**, expanding the markers textually **before** `html/template` parses the
|
||||
file — so the Hungarian set is parsed from byte-identical source in identical escaping contexts.
|
||||
An undefined key is left in place and the set fails to load ("function T not defined").
|
||||
- **Fallback:** a key missing from English shows the Hungarian text and is counted (startup INFO line
|
||||
`i18n: en template set shows Hungarian for N markers`); never a key, never a blank.
|
||||
- **Language of a request:** `?lang=hu|en` (testing override, never persisted) → `settings.json`
|
||||
`language` → `hu`. `executeTemplate` renders from that language's set. Pages rendered outside
|
||||
`executeTemplate` (login, claim, recovery, the guest share pages) stay Hungarian.
|
||||
- **The switch:** `POST /settings/language` (`lang`, `back`; session CSRF like every form) saves the
|
||||
language and redirects to `back` without its query. Shown in the sidebar footer **only when the page
|
||||
is not Hungarian or the request carries `?lang=`** — deliberate for this release (three pages are
|
||||
English); the way back is always on screen.
|
||||
- **Go-side copy:** a handler names its title key (`data["TitleKey"]`); non-Hungarian sets override
|
||||
the copy-producing template funcs (`stateLabel`, `timeAgo`, `timeAgoStr`, `nextRunLabel`,
|
||||
`statusText`) from the bundle. The Hungarian funcs are untouched.
|
||||
- **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
|
||||
exist, no orphans, the English gap and the formal „ön" count only shrink (ratchets), no pleading
|
||||
English. The emoji, native-confirm, secret-in-markup and retrieval-promise gates read templates
|
||||
**expanded** through `scripts/i18n_bundle.py`; mojibake scans the bundles.
|
||||
- **Release gate:** `TestI18nParity` compares the Hungarian render of 14 fixture states with HTML
|
||||
captured from the unconverted templates (`internal/web/testdata/i18n_parity/`). Fixtures are never
|
||||
regenerated to make a conversion pass.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user