Files
felhom.eu/documentation/audits/I18N-INVENTORY-2026-09-17.md
T
admin 5b7b2b22f1
gates / gates (push) Successful in 23s
i18n starter: inventory (script + audit), rows R-553..R-562, language allowlisted in wire-contract gate
Phase 0 of the localisation starter: i18n_inventory.py counts every customer-visible
Hungarian string; the audit names six further claims in the prompt that live source
disproved. Rows for the compare-not-show sites, wizard deletion, the wire-contract comment
blind spot, and localisation slices 1-6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-17 14:50:29 +02:00

221 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# I18N INVENTORY — every Hungarian string a household meets, counted (2026-09-17)
**Run class:** spike, Phase 0 of the localisation starter. **Operator:** Claude Code on DooPlex.
**Baselines:** felhom-controller `89dd3e94b1de` (v0.246.0) · felhom-agent `d9864a94bf62` (v0.132.0) ·
felhom.eu `851198af7ff0` (hub v0.117.0) · app-catalog-felhom.eu `94bc5febaca2`.
**Instrument:** `scripts/i18n_inventory.py`, sha256
`be69b48d4d98ac47c0cec531cae63bee8ef009cacbbe2b5c45725cfb2fd40f0c`. Raw output and per-string JSON:
`audits/i18n-2026-09-17/inventory.md`, `inventory.json`.
**What was counted:** the controller at its baseline commit (a clean worktree of `origin/main`, so the
spike's own conversion is NOT in these numbers), the other three repos at their baselines.
**Design this feeds:** `architecture/10-localisation.md`.
---
## 0. Claims in the starter prompt that live source disproved
Named first, as the prompt asked. Four were already named in its §1; these are further ones.
1. **„`fmtTime`, `fmtBytes`, `timeAgo` — none of these exist. The FuncMap has one size helper,
`fmtMB`."** — **Wrong, and it is the claim that matters most for localisation.** All three exist
(`internal/web/funcmap.go` `timeAgo` L265, `fmtTime` L318, `fmtBytes` L403), and so do **nine more
helpers whose output is copy or locale-shaped**: `fmtGB`, `humanBytes`, `timeAgoStr`, `fmtTimeStr`,
`fmtTimeShort`, `fmtDuration`, `nextRunLabel` („ma"/„holnap"), `pruneLabel` („vasárnap"/„naponta"),
`nextPruneLabel`, `statusText`, and `stateLabel` (the app-state words). There are **four** size
helpers (`fmtMB`, `fmtGB`, `fmtBytes`, `humanBytes`), not one. `timeAgo` builds Hungarian with `%d`
(„%d perce") — the plural problem lives there.
2. **„`fmtMB`, used 10× in templates"** — **8×** (`grep -c` over templates, and the script agrees).
3. **„~13 `.Format(` calls in `internal/web/*.go` (`2006-01-02` ×6, `2006-01-02 15:04` ×4, `15:04` ×2,
`01-02 15:04` ×1)"** — at the baseline: `2006-01-02` **×4**, `2006-01-02 15:04` **×3**, `15:04` **×2**,
`01-02 15:04` ×1 = **10** layout literals, plus **8** `time.RFC3339` (machine, locale-free). The two
template layouts are as stated and do disagree (`2006. 01. 02. 15:04` vs `2006-01-02 15:04`).
4. **Template line count „1 613 (afternoon) / ~1 838 (morning)"** — **1 553**, by two independent methods
(this script, and `grep -cP` on the concatenated templates under `LC_ALL=C.UTF-8`). Neither prompt
figure was reproduced. Tried: adding typographic quotes „ ” (1 556), adding dashes and ellipsis
– — … (1 751), counting `style.css` too (1 559). The afternoon figure is **+60 (3.9 %)** and its method
is unknown; the morning figure is closest to the dash-inclusive variant. **Lines are the wrong unit
anyway** — see §1: the string count is 1 867.
5. **Go literal count „1 234 in 99 files (afternoon) / ~1 971 in 125 (morning)"** — **1 141 literals in
84 files** by a Go lexer (comments skipped), first-boot wizard excluded. A `grep` of quoted Hungarian
on non-comment lines gives 1 106 lines in 85 files, so the two methods agree. Counting lines that
hold an accented letter **including comments** gives 1 360 in 107 files; including the wizard and
`cmd/`, 1 375 in 108. The prompt's figures sit between the comment-free and comment-inclusive
counts; **comments explain most of the gap** (Hungarian quoted in Go comments is common here).
6. **Console banner „33 Hungarian lines"** — 33 by accented letter, **34** once ASCII-only Hungarian
words are counted. Not an error; a method difference, stated.
Confirmed as the prompt states: 36 page templates + `style.css`; 33 carry Hungarian; the second
template set and its one helper (`internal/setup/handlers.go` L79 `timeNow`); 8 wizard pages, **86**
Hungarian lines; `customerMessages` in the hub; 53 catalog files with `first_steps` under `app_info`;
no i18n library, no `Accept-Language`, no bundle anywhere.
---
## 1. Controls — the counts below mean something
Hungarian is matched by accented letter (the letters written as `\u` escapes, so the script itself is
ASCII) **or** by one of 46 ASCII-only Hungarian words („Fut", „Nincs", „Hiba", „Csak", „Megszakadt"…),
case-insensitive. Every surface prints a positive control (an ASCII fragment of a Hungarian word known
to be there, whose lines the matcher must flag) and a negative control (a fragment known absent; an
English sentence that must not be flagged).
| surface | positive fragment | lines holding it | flagged | negative hits | English line flagged | verdict |
|---|---|---|---|---|---|---|
| controller templates | `pult` | 20 | 20 | 0 | no | ok |
| controller Go | `ment` | 832 | 278 | 0 | no | ok |
| hub customer Go | `ment` | 35 | 18 | 0 | no | ok |
| installer banner | `Felhom` | 10 | 5 | 0 | no | ok |
| catalog | `Alkalmaz` | 5 | 5 | 0 | no | ok |
| guide | `Felhom` | 14 | 13 | 0 | no | ok |
**Measured limit of the ASCII word list.** The spike's conversion tool first used a 40-word list and
**missed eight Hungarian strings on the three pages** („Megszakadt:", „Elavult", „Csak x86",
„Pi kompatibilis", „Rendszermonitor", „Most nem", „sikertelen", „ismeretlen"); a by-eye review of what
was left found them, and they were added. **An ASCII-only Hungarian word that is not on the list is
still invisible to every count here.** The counts below are therefore floors, not totals.
---
## 2. The tables
### 2.1 Controller page templates (`internal/web/templates/`, 36 files)
**1 867 strings: 1 161 text runs + 88 attribute values + 618 inline-JS string literals.** 1 553 lines
carry an accented letter. A *run* is text up to the next block tag or control action — the unit a
translator needs.
| template | HU lines | text | attr | JS |
|---|---|---|---|---|
| deploy.html | 156 | 87 | 8 | 77 |
| storage.html | 110 | 43 | 9 | 98 |
| debug.html | 92 | 41 | 16 | 79 |
| layout.html (on every page) | 88 | 35 | 1 | 70 |
| backups_remote.html | 77 | 87 | 0 | 10 |
| settings_system.html | 84 | 58 | 0 | 36 |
| backups_apps.html | 80 | 79 | 7 | 6 |
| … 26 more | | | | |
Full per-file table: `audits/i18n-2026-09-17/inventory.md`. **One third of all template copy (618) is
inside `<script>`** — modal bodies and confirm questions built as JS strings, often with HTML inside
and with the sentence split around a `+ name +` concatenation. Most repeated: „Mentés" 10×, „Jelszó"
7×, „← Vissza" 7×, „Törlés", „Hiba", „Beállítás", „Mégsem", „Újraindítás", „Betöltés...", „Frissítés"
6× each.
### 2.2 Controller Go (non-test, wizard excluded)
**1 141 literals in 84 files.** Classified by the statement they sit in:
| kind | count | meaning |
|---|---|---|
| shown | 947 | assigned to page data, flash, alert, event text, struct fields |
| error | 184 | `fmt.Errorf` / `errors.New` — **reaches a page** wherever a handler prints `err.Error()` |
| compared | 7 | matched, not shown — §3 |
| log only | 3 | logs are English by convention; three are not |
Word-order hazards (non-log): **237** format strings with parameters, **111** concatenations, **42**
with a numeric `%d` (English needs a plural form; Hungarian does not inflect after a numeral).
Heaviest files: `web/handlers.go` 115, `cloudflare/countries.go` 113 (country names for the geo
filter), `web/offbox_handlers.go` 70, `web/storage_handlers.go` 45, `web/handler_debug.go` 39,
`backup/offbox_reconstitute.go` 38, `notify/notifier.go` 31. Most repeated: „érvénytelen kérés" 13×,
„A távoli mentési cél nincs beállítva." 9×, „Rendszermonitor" 7×.
**A classification caveat, measured:** a flash message travels to the page **inside the redirect URL**
(`?flash=<Hungarian text>`, e.g. the launcher's `ShareFlash`), so it is rendered in whatever language
the handler that redirected spoke. Slice 2 has to move these to keys, not only translate them.
### 2.3 Hub → customer
**65 literals in 4 files**: `notify/templates.go` 57 (the 39-entry `customerMessages` map, the 4
`severityLabels`, the body wrapper, and the claim / re-enroll / reset / claimed / self-bind mails),
`notify/dispatcher.go` 4, `web/customer_reset.go` 2, `web/selfbind.go` 2 (plus the public bind page's
19 Hungarian lines). 8 are multi-line raw blocks. One message (`app_start_failed`) is in the formal
„ön" register. For scale and **out of scope** (operator pages): all hub Go 83 literals; operator
templates 64 strings.
### 2.4 Installer
Console banner `scripts/iso/felhom-bootstrap.sh`: **34** lines, printed with `printf` blocks to the
console device (pairing, bound, `/etc/issue`, no-network screens). The `/etc/issue` text must stay
byte-identical to `iso/pkg/debian/postinst` and avoid ő/ű (the console font) — a constraint English
does not share but a second language on the same screen does. Download page `website/letoltes.html`:
**32** strings.
### 2.5 Catalog
**835 strings, ~4 984 words, in all 53 `.felhom.yml`.** No multi-language structure exists.
| field | strings |
|---|---|
| `app_info.use_cases[]` | 262 |
| `app_info.first_steps[]` | 233 |
| `deploy_fields[].description` | 79 |
| `deploy_fields[].label` | 68 |
| `app_info.prerequisites[]` | 60 |
| `description` | 56 |
| `app_info.tagline` | 53 |
| other (`label`, `help_text`, `default_creds`, `note`, `group`) | 24 |
The controller parses these with a plain `yaml.Unmarshal` that ignores unknown keys
(`internal/stacks/metadata.go` L317), so a sibling key per language is additive.
### 2.6 Guide
`runbooks/VOLUNTEER-first-hour.md`: 182 lines, **44 paragraphs (42 Hungarian), ~1 579 words.**
### 2.7 First-boot wizard — reported separately, never merged
`internal/setup/templates/` (8 pages): 86 Hungarian lines; **95 strings** (81 text, 12 attr, 2 JS).
Go: 16 literals. **When a household can still see it** (read from source): the controller runs
`bootstrap.MaybeIngest` (`cmd/controller/main.go` L314) and then `setup.NeedsSetup(cfg)` (L322), which
is true when `customer.id` is still empty **or** a `.needs-setup` marker exists in the data dir
(`internal/setup/setup.go` L17-25). Ingestion leaves `customer.id` empty — and the wizard therefore
starts — when `bootstrap.json` is missing, unreadable, not JSON, not schema `felhom.bootstrap/v2`, or
lacks `customer.id` / `hub.url` / `hub.retrieval_password` / the `local_api` block; when the hub pull
fails (auth and not-found at once; unreachable after retries at 2/4/8 s); or when the merge, the
atomic write or the reload fails (`internal/bootstrap/bootstrap.go` L109-163). **Operator decision
2026-09-17: out of scope, obsolete — a separate row to delete it.**
### 2.8 Dates, numbers, sizes
| where | layout | sites |
|---|---|---|
| internal/web Go | `2006-01-02` | 4 |
| internal/web Go | `2006-01-02 15:04` | 3 |
| internal/web Go | `15:04` | 2 |
| internal/web Go | `01-02 15:04` | 1 |
| template | `2006. 01. 02. 15:04` (Hungarian) | 1 |
| template | `2006-01-02 15:04` (ISO) | 1 |
Plus 25 `.Format("` calls elsewhere in the controller, and the helpers in §0.1. Sizes print `GB`/`MB`
with a **decimal point** (`%.1f`) — Hungarian convention is a decimal comma, so the Hungarian pages are
already un-Hungarian here; that is not a localisation regression and the spike does not change it.
---
## 3. Compared, not shown — each is a latent bug
A string matched by its Hungarian wording breaks the moment the wording is translated **or reworded**.
| where | what is matched | consequence if the text changes |
|---|---|---|
| `internal/api/router.go:478` | a deploy error contains „kötelező" or „memória" | a validation failure becomes **500 instead of 400** |
| `internal/backup/offbox.go:186` | a restic error contains „tárhelykeretet" | the off-site **quota** failure is no longer classified as quota |
| `internal/web/alerts.go:208` | a health warning contains „meghajtón" / „adattároló" / „meghajtó" | a disk warning moves from its inline place to the **top banner of every page** |
| `internal/web/handlers.go:927` via `offboxStaleWarningMarker` („nincs mentésre jelölt alkalmazás") | a **persisted** warning string | the „selection changed" line is no longer substituted — and a warning written in one language is compared in another |
| `internal/web/offbox_handlers.go:132` | — | **false positive** of the detector (`!= ""` belongs to `stageErr`, not the literal) |
Templates: **0** compares against Hungarian (a detector decoy with `eq .State "Leállítva"` was flagged,
so the zero is measured). JS: no `===` / `indexOf` / `includes` against a Hungarian literal.
---
## 4. What this inventory does not see
- ASCII-only Hungarian words outside the 46-word list (§1).
- Hungarian arriving at run time from outside the repos: app containers' own UIs, agent error strings
relayed verbatim, hub-pushed text.
- Whether a `shown` Go literal actually reaches a page (classification is by statement, not by data
flow) and whether an `error` literal is printed or swallowed.
- The **formal „ön" register** inside Hungarian copy (R-516) — counted only on the converted pages, by
the new `i18n_missing_gate.py` (6 occurrences).