The per-app table, the unverified UI labels for the slice-6 walk, the judgement calls recorded rather than hidden, the one string deliberately left in Hungarian and why the ceiling's floor is 1 rather than 0, and the live proof: the English Apps list shows zero Hungarian app descriptions across all 53. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
9.2 KiB
REPORT — the app catalog speaks English (R-560 slice 5, Parts B and C)
2026-09-20 · catalog main at the batch-3 commit · needs controller v0.257.0, which is now the
fleet floor. All 53 apps translated; 1 031 of 1 032 strings.
For the operator, in one paragraph
Every app now describes itself in English. You read three of them and said go; the other fifty follow that same voice. The English app list on the demo box shows no Hungarian app text at all. The Hungarian pages are unchanged down to the byte. One single line is still Hungarian on purpose — it is wrong in Hungarian, and fixing Hungarian words is not something a translation release may do.
What shipped, in order
| commit | what | strings |
|---|---|---|
| the freeze, alone | scripts/copy_freeze/hu.json — every Hungarian copy string, captured before any translation existed |
1 032 frozen |
| the gate | scripts/check-copy-i18n.py, fifth row of catalog_gates.py, + 18 decoy cases |
— |
| the pilot | privatebin, paperless-ngx, romm | 89 |
| batch 1 | 17 apps | 319 |
| batch 2 | 17 apps | 317 |
| batch 3 | 16 apps | 306 |
EN_MISSING_CEILING: 1032 → 943 → 624 → 307 → 1.
No image, no pin, no catalog_since, no compose line changed in any of them. A translation is not
a version bump of an app.
The blocks are generated, not hand-written — and that is a decision
Fifty nested YAML blocks whose keys must match the Hungarian side exactly is fifty chances to mistype
an env_var. A mistyped key is INERT on the box rather than an error — the app simply keeps
showing Hungarian for that field and nobody learns. So each app's English is a flat
{path: english} map and a generator builds the block from the same paths the freeze uses, which are
derived from the Hungarian file itself. A key that does not exist on the Hungarian side cannot be
written at all, and review happens on the map, where one line is one string.
The one string deliberately left in Hungarian
papra's deploy_fields[AUTH_SECRET].description reads „Az alkalmazás aldomainje" — the sentence that
belongs on SUBDOMAIN, sitting on a session-signing key. That is a Hungarian defect (R-593). A
localisation release may not change Hungarian bytes, and translating a wrong sentence faithfully would
ship the error in a second language, so it falls back.
The ceiling's floor is therefore 1, not 0, and that is written into the ceiling's own comment — so a later session cannot "reach zero" by quietly editing Hungarian.
Per-app table
| app | copy strings | English | push | live-checked |
|---|---|---|---|---|
| actualbudget | 15 | 15 | batch 3 | gate only |
| adventurelog | 17 | 17 | batch 1 | yes |
| audiobookshelf | 21 | 21 | batch 2 | gate only |
| bentopdf | 14 | 14 | batch 1 | yes |
| bookstack | 19 | 19 | batch 1 | yes |
| calcom | 21 | 21 | batch 1 | gate only |
| calibre-web | 24 | 24 | batch 1 | yes |
| claper | 17 | 17 | batch 3 | gate only |
| code-server | 20 | 20 | batch 2 | gate only |
| crafty-controller | 24 | 24 | batch 2 | gate only |
| docmost | 19 | 19 | batch 3 | gate only |
| emby | 22 | 22 | batch 3 | gate only |
| ghost | 16 | 16 | batch 1 | gate only |
| gitea | 16 | 16 | batch 3 | yes |
| glance | 15 | 15 | batch 2 | gate only |
| gokapi | 19 | 19 | batch 2 | gate only |
| grafana | 19 | 19 | batch 1 | gate only |
| gramps-web | 16 | 16 | batch 2 | gate only |
| home-assistant | 18 | 18 | batch 1 | gate only |
| homebox | 16 | 16 | batch 1 | gate only |
| homepage | 15 | 15 | batch 1 | gate only |
| immich | 24 | 24 | batch 3 | yes |
| jellyfin | 22 | 22 | batch 2 | gate only |
| kimai | 20 | 20 | batch 3 | gate only |
| komga | 21 | 21 | batch 3 | gate only |
| mealie | 17 | 17 | batch 2 | gate only |
| n8n | 17 | 17 | batch 1 | gate only |
| navidrome | 21 | 21 | batch 2 | gate only |
| nextcloud | 28 | 28 | batch 1 | gate only |
| onlyoffice | 24 | 24 | batch 3 | gate only |
| opengist | 15 | 15 | batch 3 | yes |
| outline | 21 | 21 | batch 1 | gate only |
| paperless-ngx | 33 | 33 | pilot | yes |
| papra | 14 | 13 (1 left — R-593) | batch 1 | gate only |
| plant-it | 16 | 16 | batch 3 | gate only |
| plex | 25 | 25 | batch 2 | gate only |
| privatebin | 14 | 14 | pilot | yes |
| radarr | 22 | 22 | batch 1 | gate only |
| rallly | 17 | 17 | batch 3 | gate only |
| recipe-importer | 19 | 19 | batch 3 | yes |
| romm | 42 | 42 | pilot | yes |
| seerr | 18 | 18 | batch 3 | gate only |
| sonarr | 22 | 22 | batch 2 | gate only |
| sparkyfitness | 20 | 20 | batch 1 | gate only |
| tandoor | 18 | 18 | batch 2 | gate only |
| termix | 11 | 11 | batch 2 | gate only |
| uptime-kuma | 16 | 16 | batch 2 | gate only |
| vaultwarden | 23 | 23 | batch 3 | yes |
| vikunja | 17 | 17 | batch 2 | gate only |
| wanderer | 19 | 19 | batch 1 | gate only |
| wger | 18 | 18 | batch 2 | gate only |
| wishlist | 15 | 15 | batch 2 | gate only |
| zipline | 20 | 20 | batch 3 | gate only |
| total | 1032 | 1031 | 12 apps checked live |
Unverified UI labels — for the slice-6 walk (R-561)
These English lines name a button or a menu inside the app's own interface. No gate and no endpoint can tell whether the app shows that word in English; somebody has to open it.
| app | the line | what to check |
|---|---|---|
| privatebin | "Type your text and select Send" | PrivateBin's send button |
| paperless-ngx | "Create tags and correspondents" | Paperless's English words for these two |
| romm | "in the Settings menu" / "Start a Scan from the menu on the left" | RomM's menu and button labels |
| grafana | "(Connections > Data Sources)" | Grafana's menu path in English |
| crafty-controller | "server.properties -> server-port" | a file key, not a UI label — but the port range is a claim worth re-checking |
| vaultwarden | 'the "Users" tab, "Invite User"', '"Create account"' | Vaultwarden's English admin labels |
| immich | "Administration -> External Libraries" | Immich's English menu path |
| recipe-importer | "Profile -> API Tokens" (Mealie), "Settings -> API Browser -> Auth Token" (Tandoor) | two other apps' English menu paths |
| komga | "Tachiyomi" | an app name, verbatim — no check needed, listed for completeness |
| calibre-web | "the import/calibre folder in the File manager (FileBrowser)" | Felhom's own File manager - right by construction unless the controller bundle changes |
Several apps' English also names Felhom's own interface (Settings, File manager, Automatically generated values). Those come from the controller's English bundle and are right by construction.
Judgement calls, recorded rather than hidden
- Jellyfin told the reader to pick Hungarian in the setup wizard. For an English household that is wrong advice, so the English reads "choose your language". The Hungarian is untouched.
- Mealie's „multilingual - Hungarian is available too" became "A multilingual interface - English and Hungarian among them": the same fact, seen from the reader's side.
- recipe-importer imports from six Hungarian recipe sites. Its English says exactly that and names the sites; it is not a general importer and the English must not pretend it is.
- Vaultwarden's two select options keep the recommendation on the same option: "No - by invitation only (recommended)".
The gate convicted two of my own sentences, and was half right
Vaultwarden's invite step and sign-up setting both ended "…can open an account". The English
retrieval-promise pattern reads can … open as the claim that sealed backups can be opened, so it
convicted. Opening an ACCOUNT is not that claim — a false positive — but the wording was also the
weaker wording, so both now read "can sign up", which is what a household would say.
The structural gap is that the gate can convict a promise but cannot REGISTER a true one, which is exactly what the shared vocabulary's design calls for. Filed as R-594. Rewording was honest today; the first genuinely true "can be restored" in catalog copy will need the mechanism.
Live proof
Endpoint level, from inside the guest, with the customer Host header and a real session — the same
handlers and templates a browser drives, with only the drawing skipped.
- The English Apps list on demo-hp shows ZERO Hungarian app descriptions across all 53 apps. The only Hungarian left on that page is the „Naprakész" badge (R-589, ten occurrences) and the language picker naming itself, which is correct.
- The Hungarian Apps list is identical to the capture taken before the pilot, once the per-session CSRF token and Docker's own „Up N hours" string are normalised.
- Twelve apps were opened individually in both languages across the pushes (the table's last column).
- The second demo box took the new controller by itself when the floor was raised, and then rendered the English tagline — so the mechanism works on a box nobody touched by hand.
Evidence: felhom.eu/documentation/audits/i18n-slice5-2026-09-20/.
Teardown
Nothing installed, nothing removed, no app deployed. Template files changed on both demo boxes, which
is what a catalog sync does. Every staged password file and capture script was shredded from both
Proxmox hosts and both guests; ls confirms. Both boxes left on Hungarian.