diff --git a/context.go b/context.go index 0ef7d71..6d9b9f2 100644 --- a/context.go +++ b/context.go @@ -31,17 +31,35 @@ func applyLocaleToService(svc Translator, locale string) { } if setter, ok := svc.(interface{ SetLanguage(string) error }); ok { - base := locale - for i := 0; i < len(base); i++ { - if base[i] == '-' || base[i] == '_' { - base = base[:i] - break - } + // The built-in i18n service matches best on a base language tag. + // Custom translators should receive the full locale string. + if serviceUsesBaseLanguage(svc) { + locale = baseLanguage(locale) } - _ = setter.SetLanguage(base) + _ = setter.SetLanguage(locale) } } +func serviceUsesBaseLanguage(svc Translator) bool { + t := reflect.TypeOf(svc) + for t != nil && t.Kind() == reflect.Pointer { + t = t.Elem() + } + if t == nil { + return false + } + return t.PkgPath() == "dappco.re/go/core/i18n" && t.Name() == "Service" +} + +func baseLanguage(locale string) string { + for i := 0; i < len(locale); i++ { + if locale[i] == '-' || locale[i] == '_' { + return locale[:i] + } + } + return locale +} + // NewContext creates a new rendering context with sensible defaults. // Usage example: html := Render(Text("welcome"), NewContext("en-GB")) func NewContext(locale ...string) *Context { diff --git a/context_test.go b/context_test.go index a58a42e..924b9d8 100644 --- a/context_test.go +++ b/context_test.go @@ -10,10 +10,16 @@ import ( ) type recordingTranslator struct { + lang string key string args []any } +func (r *recordingTranslator) SetLanguage(lang string) error { + r.lang = lang + return nil +} + func (r *recordingTranslator) T(key string, args ...any) string { r.key = key r.args = append(r.args[:0], args...) @@ -59,6 +65,18 @@ func TestNewContextWithService_AppliesLocaleToService_Good(t *testing.T) { } } +func TestNewContextWithService_ForwardsFullLocaleToTranslator_Good(t *testing.T) { + svc := &recordingTranslator{} + ctx := NewContextWithService(svc, "en-GB") + + if ctx == nil { + t.Fatal("NewContextWithService returned nil") + } + if svc.lang != "en-GB" { + t.Fatalf("NewContextWithService forwarded locale = %q, want %q", svc.lang, "en-GB") + } +} + func TestTextNode_UsesMetadataAliasWhenDataNil_Good(t *testing.T) { svc, _ := i18n.New() i18n.SetDefault(svc) @@ -143,6 +161,19 @@ func TestContext_SetLocale_AppliesLocale_Good(t *testing.T) { } } +func TestContext_SetLocale_ForwardsFullLocaleToTranslator_Good(t *testing.T) { + ctx := NewContextWithService(&recordingTranslator{}) + + if got := ctx.SetLocale("fr-FR"); got != ctx { + t.Fatal("SetLocale should return the same context for chaining") + } + + svc := ctx.service.(*recordingTranslator) + if svc.lang != "fr-FR" { + t.Fatalf("SetLocale forwarded locale = %q, want %q", svc.lang, "fr-FR") + } +} + func TestContext_SetLocale_NilContext_Ugly(t *testing.T) { var ctx *Context if got := ctx.SetLocale("en-GB"); got != nil {