5b7b2b22f1
gates / gates (push) Successful in 23s
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
221 lines
13 KiB
Markdown
221 lines
13 KiB
Markdown
# 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).
|