From de392cd7b85cdf77b2dcce9dbd35ff97818f2759 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sun, 20 Sep 2026 16:39:42 +0200 Subject: [PATCH] =?UTF-8?q?REPORT:=20the=20catalog=20speaks=20English=20?= =?UTF-8?q?=E2=80=94=20all=2053=20apps,=201=20031=20of=201=20032=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- REPORT.md | 297 +++++++++++++++++++++++++----------------------------- 1 file changed, 135 insertions(+), 162 deletions(-) diff --git a/REPORT.md b/REPORT.md index 2f94ba4..ce54011 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,203 +1,176 @@ -# REPORT — localisation slice 5, Part B: the copy freeze, the gate, and the English pilot (R-560) +# REPORT — the app catalog speaks English (R-560 slice 5, Parts B and C) -2026-09-20 · catalog `main` `e81d41e` · needs controller **v0.257.0** to render · Part C not started. +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 — the one thing you have to do +## For the operator, in one paragraph -**Read the English below (it is short) and say "go", or say what to change.** Fifty more apps follow -whatever you say. Ten minutes now against a day of rework later. Everything else in this report is -for the record. +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. --- -## The three pilot apps, in full, for your read +## What shipped, in order -### PrivateBin +| 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 | -> **What it is:** Encrypted note and text sharing -> **Tagline:** Encrypted text sharing - the server never sees the content +`EN_MISSING_CEILING`: 1032 → 943 → 624 → 307 → **1**. -**What it is for** -- Share sensitive text safely -- End-to-end encryption - the server cannot reach the content -- You choose how long it lasts (5 minutes to 1 year, or never) -- Optional: delete it automatically after it is read -- Password protection for extra safety +**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. -**First steps** -1. Open paste.DOMAIN in your browser -2. Type your text and select Send -3. Share the link you get - the encryption key is part of the URL +## The blocks are generated, not hand-written — and that is a decision -**Install settings:** Domain — "The server domain name" · Subdomain — "The subdomain this app -answers on" +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. -### Paperless-ngx +## The one string deliberately left in Hungarian -> **What it is:** Digitise your documents and keep them in order -> **Tagline:** A digital filing cabinet - scanning, OCR and automatic sorting +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. -**What it is for** -- Turn paper documents into files and keep them in order -- Automatic OCR text recognition on scanned documents -- Smart automatic sorting and tagging -- Full text search across every document -- Documents that arrive by e-mail are processed automatically - -**First steps** -1. Open paperless.DOMAIN in your browser -2. Sign in: user "admin". The password is on the Settings page, under Automatically generated values -3. Upload a document in the app, OR drop it into the import/paperless folder in the File manager - (FileBrowser) - it is read in and processed with OCR from there. The File manager sign-in is on - the File manager app page -4. You can upload many documents at once: they are processed one by one, and a larger batch takes a - few minutes -5. Create tags and correspondents so new documents sort themselves - -**Before you start** -- An external hard drive is recommended to keep the documents on -- At least 1 GB of free RAM (for the OCR work) - -**Install settings:** Database password · Encryption key · Admin user name · Admin password ("For -the first sign-in. You can change it in the app afterwards.") · Data storage path ("The path to the -external hard drive where the documents are kept") · OCR language (Hungarian / English / Hungarian + -English / German + English — "The language the text recognition reads") · Folder label: "Documents -to read in" - -### RomM - -> **What it is:** Retro game collection manager -> **Tagline:** Retro game collection manager, browser and player - -**What it is for** -- Sort and browse a retro game collection in your browser -- Game details download themselves - cover art, descriptions, ratings -- Filter and search the whole collection by platform -- Upload and download ROM files in your browser -- Several people in the household can have their own account - -**First steps** -1. Open arcade.DOMAIN in your browser -2. Sign in with the default admin / admin account -3. Change the password straight away, in the Settings menu -4. Upload the ROM files into the roms/ folder in the File manager (FileBrowser), sorted into - platform folders (for example roms/gba/, roms/snes/) -5. Start a Scan from the menu on the left to read the ROMs in -6. Optional: set up metadata providers so cover art and descriptions download themselves (see below) - -**Before you start** -- An external hard drive is needed to keep the ROM files and cover art on -- At least 1 GB of free RAM is recommended (MariaDB + Redis + RomM) -- ROM files sorted into platform folders (for example roms/gba/, roms/snes/) - -**Optional settings — "Metadata providers":** "So that cover art, descriptions and ratings download -themselves. IGDB is the one worth setting up first. All of them are free after you sign up." · six -key fields with their own sign-up instructions · Folder label: "ROM collection" - ---- +**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 | list-length exceptions | push | live-checked | -|---|---|---|---|---|---| -| privatebin | 14 | 14 | none | pilot | **yes** — app page, Apps list | -| paperless-ngx | 33 | 33 | none | pilot | **yes** — app page, deploy page, Apps list | -| romm | 42 | 42 | none | pilot | **yes** — app page, deploy page, Apps list | -| the other 50 | 943 | 0 | — | — | — | -| **total** | **1 032** | **89** | **none** | | | +| 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** | -No app needed a list-length exception: every English list has exactly as many entries as its -Hungarian twin, which is what the gate requires unless the file carries a stated reason. ## Unverified UI labels — for the slice-6 walk (R-561) -These English lines name a button or a menu in the app's OWN interface. No gate and no endpoint can -tell whether the app actually shows that word in English; somebody has to open it. +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 has to be checked | +| app | the line | what to check | |---|---|---| -| privatebin | "Type your text and select **Send**" | PrivateBin's send button's English label | -| paperless-ngx | "under **Automatically generated values**" | this is Felhom's own Settings page — taken from the controller's English bundle, so it is right by construction unless that bundle changes | +| 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 | "Change the password straight away, in the **Settings** menu" | RomM's English menu label | -| romm | "Start a **Scan** from the menu on the left" | RomM's English button label | +| 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 | -## What changed in this repo +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. -**Three commits, in this order, and the order is the point.** +## Judgement calls, recorded rather than hidden -1. `0c19b45` — **the freeze alone.** `scripts/copy_freeze/hu.json`: every customer-facing Hungarian - string in the catalog, 1 032 of them across 53 apps, captured from the commit before any - translation existed. Its own commit so that a baseline cannot be said to have arrived with the - checker that reads it. -2. `84e0584` — **the gate.** `scripts/check-copy-i18n.py`, fifth row of `catalog_gates.py`, static - and in the pre-push hook: the Hungarian freeze, the English block's structure, its language, its - credential tokens, and a coverage ratchet. 18 new decoy cases. -3. `e81d41e` — **the pilot.** Three apps, 89 strings, `EN_MISSING_CEILING` 1032 → 943. +- **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)". -**No image, no pin, no `catalog_since`, no compose line changed.** A translation is not a version -bump of an app. +## The gate convicted two of my own sentences, and was half right -## What the gate checks, and what it cannot +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. -| # | check | convicts on | -|---|---|---| -| 1 | FREEZE | any Hungarian copy string that differs from the freeze, is new, or has gone. Runs on all 53 apps whatever scope is named — a scoped push that quietly edits a neighbour is what a freeze is for. A new app must be admitted with `--add-app NAME --reason "…"` | -| 2 | STRUCTURE | a key the English block may not carry; an `env_var`, option `value`, `match_group`, `target` or `path` with no Hungarian twin (it would be INERT on the box and the translator would never learn); a list whose length differs from the Hungarian | -| 3 | LANGUAGE | an accented Hungarian letter; **ASCII-only Hungarian**; "please"/"kindly"; an English retrieval promise the Hungarian does not make; a dropped app name or „Felhom" | -| 4 | CREDENTIALS | a login token inside `default_creds` or an initial-credentials note that did not survive translation, matched on WORD boundaries | -| 5 | RATCHET | a coverage count above **or** below `EN_MISSING_CEILING`, counted over the whole catalog whatever scope is named | - -**It cannot judge whether the English says what the Hungarian says**, and it cannot judge whether an -app's first steps match that app's real screens. The first is your read above; the second is the -table before it. - -## Numbers that were wrong in the plan, corrected by measurement - -| | the plan said | measured on `94bc5febaca2` | -|---|---|---| -| copy strings | 835 | **1 032** (it omitted 13 `placeholder` values, and every ASCII-only label) | -| with a Hungarian letter | 832 | **832** ✔ | -| ASCII-only Hungarian | three — „Igen", „Nem", „Nincs" | **~120**, and those three do not occur in this catalog at all | - -The last row decides an instrument rather than a footnote. „Aldomain" appears 53 times and „A szerver -domain neve" 53 times; both are Hungarian with no accent in them. **A gate that scans for accents -passes every one of them inside an English block.** So check 3 folds the text and matches word-bounded -stems, and prints a positive and two negative controls on every run. - -## Three defects in the gate, each found by a decoy rather than by reading it (R-592) - -1. **Coverage was counted only for the apps named on the command line.** `check-copy-i18n.py - privatebin` reported 47 more untranslated strings than the same tree unscoped — a ratchet anybody - could loosen by naming one app. -2. **The credential check matched its token as a bare substring**, so „admin / adminadmin" translated - to "administrator / hunter2" passed: "admin" is inside "administrator". -3. **The ASCII-Hungarian stems matched as bare substrings too**, so „ird be" convicted "the third - best" and „angol" convicted "Angola". - -All three now have their own case. 33 decoy cases in total, every one seen to convict or to pass as -intended. +**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 the browser drives, only the drawing skipped. Said plainly because "the page -shows X" has to name how it was seen. +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. -- **demo-hp (controller 0.257.0):** the three English app pages, the Apps list and two deploy pages - show the English catalog text. The seven **Hungarian** pages are byte-identical before and after the - push apart from the per-session CSRF token. -- **demo-felhom (controller 0.255.0, deliberately old):** with the pilot synced onto the box — - confirmed positively, the sync named the three apps and the block is present in both the cache and - the stack copy — its pages hash identically before and after and its log carries no parse warning, - in a 93-line window that includes the sync's own lines. +- **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. Three template files changed on each demo box, -which is what a catalog sync does. The staged password file and the capture scripts were shredded from -both Proxmox hosts and both guests. Both boxes left on Hungarian. +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.