feat(04-02): apply locale fallback and missing-key logging
- Resolve pl-PL then parent then app.fallback_locale then the raw key - Read towel.Locale from request context and default app.locale/fallback to en - Log each missing key once outside production and never log parameters Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
package phrasebook
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"git.golem15.com/golem15/summercms/backpack"
|
||||
"git.golem15.com/golem15/summercms/compass"
|
||||
"git.golem15.com/golem15/summercms/towel"
|
||||
)
|
||||
|
||||
func TestTranslationSmoke(t *testing.T) {
|
||||
@@ -189,3 +193,59 @@ func TestPluralSmoke(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocaleFallbackSmoke(t *testing.T) {
|
||||
fsys := fstest.MapFS{
|
||||
"lang/en/lang.yaml": {Data: []byte("greeting: Hello\nonlyEn: English only\n")},
|
||||
"lang/pl/lang.yaml": {Data: []byte("greeting: Cześć\n")},
|
||||
}
|
||||
cat := NewCatalog()
|
||||
if err := cat.Load("golem15.hello", fsys); err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
tr := NewTranslator(cat, Options{})
|
||||
if tr.locale != "en" || tr.fallback != "en" {
|
||||
t.Fatalf("defaults locale=%q fallback=%q, want en/en", tr.locale, tr.fallback)
|
||||
}
|
||||
if got := tr.GetIn("pl-PL", "golem15.hello::lang.greeting", nil); got != "Cześć" {
|
||||
t.Fatalf("pl-PL parent = %q", got)
|
||||
}
|
||||
if got := tr.GetIn("pl", "golem15.hello::lang.onlyEn", nil); got != "English only" {
|
||||
t.Fatalf("fallback en = %q", got)
|
||||
}
|
||||
const missing = "golem15.hello::lang.missing"
|
||||
if got := tr.GetIn("pl-PL", missing, nil); got != missing {
|
||||
t.Fatalf("raw key = %q", got)
|
||||
}
|
||||
ctx := towel.WithLocale(context.Background(), "pl")
|
||||
if got := tr.Get(ctx, "golem15.hello::lang.greeting", nil); got != "Cześć" {
|
||||
t.Fatalf("towel locale = %q", got)
|
||||
}
|
||||
if got := tr.Get(context.Background(), "golem15.hello::lang.greeting", nil); got != "Hello" {
|
||||
t.Fatalf("app locale default = %q", got)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
prev := slog.Default()
|
||||
slog.SetDefault(slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelWarn})))
|
||||
defer slog.SetDefault(prev)
|
||||
|
||||
logged := "golem15.hello::lang.loggedOnce"
|
||||
params := map[string]string{"secret": "nope"}
|
||||
tr.GetIn("en", logged, params)
|
||||
tr.GetIn("en", logged, params)
|
||||
log := buf.String()
|
||||
if strings.Count(log, logged) != 1 {
|
||||
t.Fatalf("missing key should log once, got %q", log)
|
||||
}
|
||||
if strings.Contains(log, "secret") || strings.Contains(log, "nope") {
|
||||
t.Fatalf("must not log parameters: %q", log)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
prod := NewTranslator(cat, Options{Production: true})
|
||||
prod.GetIn("en", "golem15.hello::lang.otherMissing", nil)
|
||||
if buf.Len() != 0 {
|
||||
t.Fatalf("production lookup must be silent, got %q", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user