# 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.