fix(i18n): accept prefixed prompt and lang keys
All checks were successful
Security Scan / security (push) Successful in 15s
Test / test (push) Successful in 2m20s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 12:52:13 +00:00
parent 9d4af96d3d
commit cdc396269c
4 changed files with 36 additions and 8 deletions

27
i18n.go
View file

@ -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:

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}