v0.252.0 — the sentences the program builds follow the language (R-557 slice 2 release A)
gates / gates (push) Successful in 23s

Slice 1 translated the dashboard's markup. The sentences the program BUILDS were still
Hungarian literals in Go, so an English household clicked an English button and was
answered in Hungarian. 226 of them move into the bundle here.

A flash was the hard part: it travels inside the redirect URL and is rendered by a
DIFFERENT request, so it now carries a bundle key plus its parameters. A link minted by
an older controller carries prose and is shown verbatim — never a raw key, never dropped.

Also converted: page data and view-model text, the internal/api JSON answers, the alert
banners (Alert.MessageKey, rendered on the way out of GetAlerts), 237 country names at
display, and the four page titles built around an app name (R-566 closed).

Hungarian is byte-identical, and that is measured rather than read:
scripts/i18n_go_parity.py freezes every Go literal at the base commit (7 467) and refuses
a key whose Hungarian is not that text, byte for byte. Three decoys, each seen to convict.
Its own first version filtered the capture through an ASCII-Hungarian word list and missed
seven real literals — the R-565 class. The filter is gone.

Nothing on the wire moved, and wire goldens now hold it there: the report's health
warnings and every notify event message stay Hungarian, because the hub MAILS the
controller's sentence when it has no entry of its own. Slice 3 (R-558) owns those.

MinAgent: 0.131.0 (unchanged). No hub release needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-18 10:15:56 +02:00
parent 736f54b496
commit 5270bad76e
52 changed files with 10755 additions and 478 deletions
@@ -358,6 +358,13 @@ func TestHandlerTitleKeysMatchHungarianTitle(t *testing.T) {
// title, titleKey := "Title", "key"
regexp.MustCompile(`title, titleKey :?= "([^"]+)", "([^"]+)"`),
}
// R-566, v0.252.0 — the three titles built around an app name, plus the tier-2 one. Their
// Hungarian is a CONCATENATION, so there is no single literal to compare with; the pair is pinned
// instead by TestParameterisedPageTitles (the rendered title) and by scripts/i18n_go_parity.py
// (the key's text against the base-commit fragment). Collected here so the "every page.title.* key
// has a handler" half below still accounts for them.
withArgs := regexp.MustCompile(`data\["TitleKey"\], data\["TitleArgs"\] = (?:pageTitleKey|"([^"]+)")`)
argKeys := []string{"page.title.logs", "page.title.deploy", "page.title.app_settings", "page.title.tier2_config"}
files, _ := filepath.Glob("*.go")
seen := map[string]bool{}
for _, f := range files {
@@ -368,6 +375,11 @@ func TestHandlerTitleKeysMatchHungarianTitle(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for _, m := range withArgs.FindAllStringSubmatch(string(src), -1) {
if m[1] != "" {
seen[m[1]] = true
}
}
for _, re := range pairs {
for _, m := range re.FindAllStringSubmatch(string(src), -1) {
seen[m[2]] = true
@@ -377,6 +389,11 @@ func TestHandlerTitleKeysMatchHungarianTitle(t *testing.T) {
}
}
}
// The two keys a `pageTitleKey` variable carries cannot be read off the assignment line; they are
// named here and proven by TestParameterisedPageTitles.
for _, k := range argKeys {
seen[k] = true
}
for _, k := range b.Keys(i18n.Default) {
if strings.HasPrefix(k, "page.title.") && !seen[k] {
t.Errorf("%s has no handler setting it next to its Hungarian title", k)
@@ -499,3 +516,39 @@ func TestI18nDirectRenderPagesHaveNoAdminChrome(t *testing.T) {
}
}
}
// TestParameterisedPageTitles — R-566, v0.252.0. Three page titles are built in Go AROUND an app
// name („<app> — Naplók", „<app> — Telepítés" / „— Beállítások", „2. mentés beállítása — <app>").
// Slice 1 gave every STATIC title a key and left these three Hungarian in the browser tab while the
// page body was English, because one static message cannot hold a name.
//
// What this pins: rendering the key with the app name produces EXACTLY the string the handler's
// concatenation produced before, and English differs. The first half is the parity rule for a title;
// the second is the reason the row exists.
func TestParameterisedPageTitles(t *testing.T) {
b, err := i18n.Load()
if err != nil {
t.Fatal(err)
}
const app = "PrivateBin"
cases := []struct {
key, wantHU string
}{
{"page.title.logs", app + " — Naplók"},
{"page.title.deploy", app + " — Telepítés"},
{"page.title.app_settings", app + " — Beállítások"},
{"page.title.tier2_config", "2. mentés beállítása — " + app},
}
for _, c := range cases {
if got := b.Msgf(i18n.Default, c.key, app); got != c.wantHU {
t.Errorf("%s in Hungarian:\n got %q\n want %q (the concatenation this replaced)", c.key, got, c.wantHU)
}
en := b.Msgf("en", c.key, app)
if !strings.Contains(en, app) {
t.Errorf("%s in English lost the app name: %q", c.key, en)
}
if en == c.wantHU {
t.Errorf("%s was never translated — English still reads %q", c.key, en)
}
}
}