From fb13048db290c28ac3c403ad22e734dab85203a3 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 15 Apr 2026 01:04:48 +0100 Subject: [PATCH] fix(html): share pluralisation args across builds Co-Authored-By: Virgil --- .gitignore | 1 + context.go | 23 +++++++++ context_test.go | 32 ++++++++++++ pipeline.go | 2 +- text_translate_args.go | 102 ++++++++++++++++++++++++++++++++++++++ text_translate_default.go | 97 ------------------------------------ text_translate_js.go | 4 -- 7 files changed, 159 insertions(+), 102 deletions(-) create mode 100644 text_translate_args.go diff --git a/.gitignore b/.gitignore index cdc6f76..509a6b1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode/ *.log .core/ +dist/ diff --git a/context.go b/context.go index 91167ef..1554f9c 100644 --- a/context.go +++ b/context.go @@ -85,3 +85,26 @@ func (ctx *Context) SetLocale(locale string) *Context { applyLocaleToService(ctx.service, ctx.Locale) return ctx } + +func cloneContext(ctx *Context) *Context { + if ctx == nil { + return nil + } + + clone := *ctx + clone.Data = cloneMetadataMap(ctx.Data) + clone.Metadata = cloneMetadataMap(ctx.Metadata) + return &clone +} + +func cloneMetadataMap(src map[string]any) map[string]any { + if len(src) == 0 { + return nil + } + + dst := make(map[string]any, len(src)) + for key, value := range src { + dst[key] = value + } + return dst +} diff --git a/context_test.go b/context_test.go index da36590..bd02baf 100644 --- a/context_test.go +++ b/context_test.go @@ -3,11 +3,23 @@ package html import ( + "reflect" "testing" i18n "dappco.re/go/core/i18n" ) +type recordingTranslator struct { + key string + args []any +} + +func (r *recordingTranslator) T(key string, args ...any) string { + r.key = key + r.args = append(r.args[:0], args...) + return "translated" +} + func TestNewContext_OptionalLocale_Good(t *testing.T) { ctx := NewContext("en-GB") @@ -61,6 +73,26 @@ func TestTextNode_UsesMetadataAliasWhenDataNil_Good(t *testing.T) { } } +func TestTextNode_CustomTranslatorReceivesCountArgs_Good(t *testing.T) { + ctx := NewContextWithService(&recordingTranslator{}) + ctx.Metadata["count"] = 3 + + got := Text("i18n.count.file", "ignored").Render(ctx) + if got != "translated" { + t.Fatalf("Text with custom translator = %q, want %q", got, "translated") + } + + svc := ctx.service.(*recordingTranslator) + if svc.key != "i18n.count.file" { + t.Fatalf("custom translator key = %q, want %q", svc.key, "i18n.count.file") + } + + wantArgs := []any{3, "ignored"} + if !reflect.DeepEqual(svc.args, wantArgs) { + t.Fatalf("custom translator args = %#v, want %#v", svc.args, wantArgs) + } +} + func TestContext_SetService_AppliesLocale_Good(t *testing.T) { svc, _ := i18n.New() ctx := NewContext("fr-FR") diff --git a/pipeline.go b/pipeline.go index 0e50703..146533a 100644 --- a/pipeline.go +++ b/pipeline.go @@ -82,7 +82,7 @@ func CompareVariants(r *Responsive, ctx *Context) map[string]float64 { if v.layout == nil { continue } - imp := Imprint(v.layout, ctx) + imp := Imprint(v.layout, cloneContext(ctx)) imprints = append(imprints, named{name: v.name, imp: imp}) } diff --git a/text_translate_args.go b/text_translate_args.go new file mode 100644 index 0000000..6fc6fc9 --- /dev/null +++ b/text_translate_args.go @@ -0,0 +1,102 @@ +// SPDX-Licence-Identifier: EUPL-1.2 + +package html + +import ( + "strconv" + "strings" +) + +func translationArgs(ctx *Context, key string, args []any) []any { + if ctx == nil { + return args + } + + count, ok := contextCount(ctx) + if !ok { + return args + } + + if len(args) == 0 { + return []any{count} + } + if strings.HasPrefix(key, "i18n.count.") && !isCountLike(args[0]) { + return append([]any{count}, args...) + } + return args +} + +func contextCount(ctx *Context) (int, bool) { + if ctx == nil { + return 0, false + } + + if n, ok := contextCountMap(ctx.Data); ok { + return n, true + } + if n, ok := contextCountMap(ctx.Metadata); ok { + return n, true + } + return 0, false +} + +func contextCountMap(data map[string]any) (int, bool) { + if len(data) == 0 { + return 0, false + } + + if v, ok := data["Count"]; ok { + if n, ok := countInt(v); ok { + return n, true + } + } + if v, ok := data["count"]; ok { + if n, ok := countInt(v); ok { + return n, true + } + } + return 0, false +} + +func countInt(v any) (int, bool) { + switch n := v.(type) { + case int: + return n, true + case int8: + return int(n), true + case int16: + return int(n), true + case int32: + return int(n), true + case int64: + return int(n), true + case uint: + return int(n), true + case uint8: + return int(n), true + case uint16: + return int(n), true + case uint32: + return int(n), true + case uint64: + return int(n), true + case float32: + return int(n), true + case float64: + return int(n), true + case string: + n = strings.TrimSpace(n) + if n == "" { + return 0, false + } + if parsed, err := strconv.Atoi(n); err == nil { + return parsed, true + } + } + return 0, false +} + +func isCountLike(v any) bool { + _, ok := countInt(v) + return ok +} diff --git a/text_translate_default.go b/text_translate_default.go index 90fa36e..afbdbec 100644 --- a/text_translate_default.go +++ b/text_translate_default.go @@ -5,106 +5,9 @@ package html import ( - "strconv" - "strings" - i18n "dappco.re/go/core/i18n" ) -func translationArgs(ctx *Context, key string, args []any) []any { - if ctx == nil { - return args - } - - count, ok := contextCount(ctx) - if !ok { - return args - } - - if len(args) == 0 { - return []any{count} - } - if strings.HasPrefix(key, "i18n.count.") && !isCountLike(args[0]) { - return append([]any{count}, args...) - } - return args -} - -func contextCount(ctx *Context) (int, bool) { - if ctx == nil { - return 0, false - } - - if n, ok := contextCountMap(ctx.Data); ok { - return n, true - } - if n, ok := contextCountMap(ctx.Metadata); ok { - return n, true - } - return 0, false -} - -func contextCountMap(data map[string]any) (int, bool) { - if len(data) == 0 { - return 0, false - } - - if v, ok := data["Count"]; ok { - if n, ok := countInt(v); ok { - return n, true - } - } - if v, ok := data["count"]; ok { - if n, ok := countInt(v); ok { - return n, true - } - } - return 0, false -} - -func countInt(v any) (int, bool) { - switch n := v.(type) { - case int: - return n, true - case int8: - return int(n), true - case int16: - return int(n), true - case int32: - return int(n), true - case int64: - return int(n), true - case uint: - return int(n), true - case uint8: - return int(n), true - case uint16: - return int(n), true - case uint32: - return int(n), true - case uint64: - return int(n), true - case float32: - return int(n), true - case float64: - return int(n), true - case string: - n = strings.TrimSpace(n) - if n == "" { - return 0, false - } - if parsed, err := strconv.Atoi(n); err == nil { - return parsed, true - } - } - return 0, false -} - -func isCountLike(v any) bool { - _, ok := countInt(v) - return ok -} - func translateDefault(key string, args ...any) string { return i18n.T(key, args...) } diff --git a/text_translate_js.go b/text_translate_js.go index 70e0a1a..692e4c9 100644 --- a/text_translate_js.go +++ b/text_translate_js.go @@ -4,10 +4,6 @@ package html -func translationArgs(_ *Context, _ string, args []any) []any { - return args -} - func translateDefault(key string, _ ...any) string { return key }