From cdc396269cd5d65bf6855be9fc265abcaf10bc0b Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 12:52:13 +0000 Subject: [PATCH] fix(i18n): accept prefixed prompt and lang keys Co-Authored-By: Virgil --- i18n.go | 27 +++++++++++++++++++++++---- i18n_test.go | 3 +++ service.go | 8 ++++---- service_test.go | 6 ++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/i18n.go b/i18n.go index 043a123..a5e820b 100644 --- a/i18n.go +++ b/i18n.go @@ -3,6 +3,7 @@ package i18n import ( "bytes" "io/fs" + "strings" "text/template" "dappco.re/go/core" @@ -257,7 +258,7 @@ func Prompt(key string) string { if key == "" { return "" } - return T("prompt." + key) + return T(namespaceLookupKey("prompt", key)) } // CurrentPrompt is a short alias for Prompt. @@ -281,23 +282,41 @@ func Lang(key string) string { if key == "" { return "" } - if got := T("lang." + key); got != "lang."+key { + if got := T(namespaceLookupKey("lang", key)); got != namespaceLookupKey("lang", key) { return got } if idx := indexAny(key, "-_"); idx > 0 { if base := key[:idx]; base != "" { - if got := T("lang." + base); got != "lang."+base { + if got := T(namespaceLookupKey("lang", base)); got != namespaceLookupKey("lang", base) { return got } } } - return "lang." + key + return namespaceLookupKey("lang", key) } func normalizeLookupKey(key string) string { return core.Lower(core.Trim(key)) } +func namespaceLookupKey(namespace, key string) string { + key = normalizeLookupKey(key) + namespace = normalizeLookupKey(namespace) + if key == "" { + return namespace + } + if namespace != "" && key == namespace { + return key + } + if namespace != "" && strings.HasPrefix(key, namespace+".") { + return key + } + if namespace == "" { + return key + } + return namespace + "." + key +} + // AddHandler appends one or more handlers to the default service's handler chain. // // Example: diff --git a/i18n_test.go b/i18n_test.go index 865631c..a01598c 100644 --- a/i18n_test.go +++ b/i18n_test.go @@ -356,7 +356,9 @@ func TestPrompt_Good(t *testing.T) { }{ {"yes", "yes", "y"}, {"yes_trimmed", " yes ", "y"}, + {"yes_prefixed", "prompt.yes", "y"}, {"confirm", "confirm", "Are you sure?"}, + {"confirm_prefixed", "prompt.confirm", "Are you sure?"}, {"empty", "", ""}, } for _, tt := range tests { @@ -392,6 +394,7 @@ func TestLang_Good(t *testing.T) { {"de", "de", "German"}, {"fr", "fr", "French"}, {"fr_ca", "fr_CA", "French"}, + {"fr_prefixed", "lang.fr", "French"}, {"empty", "", ""}, } for _, tt := range tests { diff --git a/service.go b/service.go index 3a4ff47..ae4c414 100644 --- a/service.go +++ b/service.go @@ -364,7 +364,7 @@ func (s *Service) Prompt(key string) string { if key == "" { return "" } - return s.T("prompt." + key) + return s.T(namespaceLookupKey("prompt", key)) } // CurrentPrompt is a short alias for Prompt. @@ -378,17 +378,17 @@ func (s *Service) Lang(key string) string { if key == "" { return "" } - if got := s.T("lang." + key); got != "lang."+key { + if got := s.T(namespaceLookupKey("lang", key)); got != namespaceLookupKey("lang", key) { return got } if idx := indexAny(key, "-_"); idx > 0 { if base := key[:idx]; base != "" { - if got := s.T("lang." + base); got != "lang."+base { + if got := s.T(namespaceLookupKey("lang", base)); got != namespaceLookupKey("lang", base) { return got } } } - return "lang." + key + return namespaceLookupKey("lang", key) } func (s *Service) AvailableLanguages() []string { diff --git a/service_test.go b/service_test.go index c8c268c..a514c0d 100644 --- a/service_test.go +++ b/service_test.go @@ -384,12 +384,18 @@ func TestServicePromptAndLang(t *testing.T) { if got, want := svc.Prompt("confirm"), "Are you sure?"; got != want { t.Fatalf("Prompt(confirm) = %q, want %q", got, want) } + if got, want := svc.Prompt("prompt.confirm"), "Are you sure?"; got != want { + t.Fatalf("Prompt(prompt.confirm) = %q, want %q", got, want) + } if got, want := svc.CurrentPrompt("confirm"), "Are you sure?"; got != want { t.Fatalf("CurrentPrompt(confirm) = %q, want %q", got, want) } if got, want := svc.Lang("fr"), "French"; got != want { t.Fatalf("Lang(fr) = %q, want %q", got, want) } + if got, want := svc.Lang("lang.fr"), "French"; got != want { + t.Fatalf("Lang(lang.fr) = %q, want %q", got, want) + } if got, want := svc.Lang("fr_CA"), "French"; got != want { t.Fatalf("Lang(fr_CA) = %q, want %q", got, want) }