diff --git a/cmd/wasm/main.go b/cmd/wasm/main.go index a3c0db2..4f2333d 100644 --- a/cmd/wasm/main.go +++ b/cmd/wasm/main.go @@ -21,7 +21,7 @@ func renderToString(_ js.Value, args []js.Value) any { ctx := html.NewContext() if len(args) >= 2 { - ctx.Locale = args[1].String() + ctx.SetLocale(args[1].String()) } layout := html.NewLayout(variant) diff --git a/context.go b/context.go index a2eef2f..3fb7828 100644 --- a/context.go +++ b/context.go @@ -1,6 +1,9 @@ package html -import i18n "dappco.re/go/core/i18n" +// Translator provides Text() lookups for a rendering context. +type Translator interface { + T(key string, args ...any) string +} // Context carries rendering state through the node tree. type Context struct { @@ -8,7 +11,24 @@ type Context struct { Locale string Entitlements func(feature string) bool Data map[string]any - service *i18n.Service + service Translator +} + +func applyLocaleToService(svc Translator, locale string) { + if svc == nil || locale == "" { + return + } + + 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 + } + } + _ = setter.SetLanguage(base) + } } // NewContext creates a new rendering context with sensible defaults. @@ -18,20 +38,37 @@ func NewContext(locale ...string) *Context { Data: make(map[string]any), } if len(locale) > 0 { - ctx.Locale = locale[0] + ctx.SetLocale(locale[0]) } return ctx } -// NewContextWithService creates a rendering context backed by a specific i18n service. +// NewContextWithService creates a rendering context backed by a specific translator. // An optional locale may be provided as the second argument. -func NewContextWithService(svc *i18n.Service, locale ...string) *Context { - ctx := &Context{ - Data: make(map[string]any), - service: svc, - } - if len(locale) > 0 { - ctx.Locale = locale[0] - } +func NewContextWithService(svc Translator, locale ...string) *Context { + ctx := NewContext(locale...) + ctx.SetService(svc) + return ctx +} + +// SetService swaps the translator used by the context. +func (ctx *Context) SetService(svc Translator) *Context { + if ctx == nil { + return nil + } + + ctx.service = svc + applyLocaleToService(svc, ctx.Locale) + return ctx +} + +// SetLocale updates the context locale and reapplies it to the active translator. +func (ctx *Context) SetLocale(locale string) *Context { + if ctx == nil { + return nil + } + + ctx.Locale = locale + applyLocaleToService(ctx.service, ctx.Locale) return ctx } diff --git a/context_test.go b/context_test.go new file mode 100644 index 0000000..272fe85 --- /dev/null +++ b/context_test.go @@ -0,0 +1,72 @@ +// SPDX-Licence-Identifier: EUPL-1.2 + +package html + +import "testing" + +type localeTranslator struct { + language string +} + +func (t *localeTranslator) T(key string, args ...any) string { + if key == "prompt.yes" && t.language == "fr" { + return "o" + } + if key == "prompt.yes" && t.language == "en" { + return "y" + } + return key +} + +func (t *localeTranslator) SetLanguage(language string) error { + t.language = language + return nil +} + +func TestContext_SetService_AppliesLocale(t *testing.T) { + svc := &localeTranslator{} + ctx := NewContext("fr-FR") + + if got := ctx.SetService(svc); got != ctx { + t.Fatal("SetService should return the same context for chaining") + } + + if svc.language != "fr" { + t.Fatalf("SetService should apply locale to translator, got %q", svc.language) + } + + if got := Text("prompt.yes").Render(ctx); got != "o" { + t.Fatalf("SetService locale translation = %q, want %q", got, "o") + } +} + +func TestContext_SetLocale_AppliesLocale(t *testing.T) { + svc := &localeTranslator{} + ctx := NewContextWithService(svc) + + if got := ctx.SetLocale("en-GB"); got != ctx { + t.Fatal("SetLocale should return the same context for chaining") + } + + if svc.language != "en" { + t.Fatalf("SetLocale should apply locale to translator, got %q", svc.language) + } + + if got := Text("prompt.yes").Render(ctx); got != "y" { + t.Fatalf("SetLocale translation = %q, want %q", got, "y") + } +} + +func TestContext_SetService_NilContext(t *testing.T) { + var ctx *Context + if got := ctx.SetService(nil); got != nil { + t.Fatal("SetService on nil context should return nil") + } +} + +func TestContext_SetLocale_NilContext(t *testing.T) { + var ctx *Context + if got := ctx.SetLocale("en-GB"); got != nil { + t.Fatal("SetLocale on nil context should return nil") + } +}