package i18n import ( "regexp" "strings" "testing" ) // EnMissingCeiling is the number of Hungarian keys allowed to be missing from English. It is zero // and it stays zero: a fallback that renders Hungarian into an English household's mail is the // failure this bundle exists to prevent, so it must never be reachable in a shipped build. const EnMissingCeiling = 0 func TestEveryLanguageCarriesEveryKey(t *testing.T) { b := Shared() for _, lang := range Supported { missing := b.MissingKeys(lang) if lang == Default && len(missing) != 0 { t.Fatalf("%s is the source and cannot be missing keys: %v", lang, missing) } if len(missing) > EnMissingCeiling { t.Errorf("%s is missing %d key(s) (ceiling %d): %v", lang, len(missing), EnMissingCeiling, missing) } } } // verbRe matches a Go format verb, including an explicit argument index (%[2]s). var verbRe = regexp.MustCompile(`%(\[\d+\])?[-+# 0]*\d*(\.\d+)?[a-zA-Z%]`) // TestFormatVerbsMatchAcrossLanguages is the test that stops a translation corrupting a mail. // // Msgf formats the TRANSLATED value with the arguments the Hungarian call site passes. A translation // that drops a `%s` silently loses a customer's server name; one that adds a `%s` renders // `%!s(MISSING)` into a real e-mail. Neither is visible to a reviewer reading the JSON, and neither // would fail any other test here — the mail still sends. // // Explicit argument indexes (`%[2]s`) are how English reorders what Hungarian does not, so the check // is on the MULTISET of verbs, not on their order. func TestFormatVerbsMatchAcrossLanguages(t *testing.T) { b := Shared() for _, key := range b.Keys(Default) { huVerbs := verbCounts(b.Msg(Default, key)) for _, lang := range Supported { if lang == Default || !b.Has(lang, key) { continue } got := verbCounts(b.Msg(lang, key)) if len(got) != len(huVerbs) { t.Errorf("%s/%s has %d format verb(s), hu has %d — the mail would render wrong", lang, key, len(got), len(huVerbs)) continue } for verb, n := range huVerbs { if got[verb] != n { t.Errorf("%s/%s uses %q %d time(s), hu uses it %d time(s)", lang, key, verb, got[verb], n) } } } } } // verbCounts counts each format verb, ignoring the escaped %% which formats nothing. func verbCounts(s string) map[string]int { out := map[string]int{} for _, m := range verbRe.FindAllString(s, -1) { if m == "%%" { continue } // An indexed verb counts as its bare form: %[2]s and %s consume the same argument type. if i := strings.Index(m, "]"); i >= 0 { m = "%" + m[i+1:] } out[m]++ } return out } func TestNormalizeAndIsSupported(t *testing.T) { for in, want := range map[string]string{ "hu": "hu", "en": "en", "EN": "en", " en ": "en", "": "hu", "de": "hu", "en-GB": "hu", "garbage": "hu", } { if got := Normalize(in); got != want { t.Errorf("Normalize(%q) = %q, want %q", in, got, want) } } // IsSupported REFUSES where Normalize coerces — that difference is the whole reason both exist. for _, bad := range []string{"", "EN", " en ", "de", "en-GB"} { if IsSupported(bad) { t.Errorf("IsSupported(%q) is true — a bad input would be stored as a real language", bad) } } for _, good := range []string{"hu", "en"} { if !IsSupported(good) { t.Errorf("IsSupported(%q) is false", good) } } } // A key that exists nowhere must be LOUD, never blank: an empty string in a mail body is a hole // nobody notices. func TestUnknownKeyIsVisible(t *testing.T) { got := Shared().Msg("en", "mail.event.no_such_thing_exists") if got == "" { t.Fatal("an unknown key rendered as an empty string — the mail would have a silent hole") } if !strings.Contains(got, "no_such_thing_exists") { t.Errorf("an unknown key rendered as %q — it does not name the key", got) } } // An English key falls back to Hungarian rather than to nothing. Proven with a key deliberately // absent from en.json would be circular (there are none), so it is proven on the mechanism: a // language that carries no file at all. func TestFallbackIsHungarianNotBlank(t *testing.T) { b := Shared() if got := b.Msg("de", "mail.event.backup_completed"); got != b.Msg(Default, "mail.event.backup_completed") { t.Errorf("an unsupported language did not fall back to Hungarian: %q", got) } } // The English bundle must not be a copy of the Hungarian one. A translation pass that silently // copied the source would pass every other test in this file. func TestEnglishIsActuallyTranslated(t *testing.T) { // The keys that are IDENTICAL IN BOTH LANGUAGES ON PURPOSE, each with the reason. An allowlist // rather than a count: a count lets the next untranslated key hide inside the budget, and the // question "why is this one the same?" then has no answer written down anywhere. sameOnPurpose := map[string]string{ "mail.customer.subject": `"[Felhom] %s: %s" — a format with no words in it; both halves are themselves translated`, "bind.placeholder.pairing": `"ABC-234" — a sample of a code the box prints, not prose`, } b := Shared() for _, key := range b.Keys(Default) { hu, en := b.Msg(Default, key), b.Msg("en", key) if hu != en { if _, listed := sameOnPurpose[key]; listed { t.Errorf("%s is allowlisted as identical but the two languages now differ — "+ "remove it from sameOnPurpose", key) } continue } if _, ok := sameOnPurpose[key]; !ok { t.Errorf("%s is identical in hu and en — untranslated text would ship: %q", key, hu) } } }