diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-05-12 17:12:07 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-05-12 17:12:07 +0200 |
| commit | 19cfae39af4e7cbb8a0719e64d619ba0248f9d76 (patch) | |
| tree | af289634fac81e78ebe92029062eb13016622b8a /ki18n/ki18n_test.go | |
| parent | 783028348a17803ba2e0d2745229d57c0626a464 (diff) | |
ki18n: derive language from request context via strategy chain
Replace Tr(lang, ...) with Tr(ctx, ...) — language is now resolved once
per request by LangMiddleware and stored in context. Callers no longer
need to thread a lang string through to templates.
Adds Strategy interface with CookieStrategy and AcceptLanguageStrategy
built-ins; middleware chains them in order, falling back to en-GB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'ki18n/ki18n_test.go')
| -rw-r--r-- | ki18n/ki18n_test.go | 93 |
1 files changed, 90 insertions, 3 deletions
diff --git a/ki18n/ki18n_test.go b/ki18n/ki18n_test.go index d2e7e16..fa1f226 100644 --- a/ki18n/ki18n_test.go +++ b/ki18n/ki18n_test.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "fmt" + "net/http" + "net/http/httptest" "strings" "testing" "testing/fstest" @@ -17,8 +19,9 @@ func TestSimpleText(t *testing.T) { initFrenchLocale(map[string]string{ "Hello you": "Bonjour toi", }) + ctx := context.WithValue(context.Background(), contextKey{}, "fr-FR") - result := render(t, Tr("fr-FR", "Hello you")) + result := render(t, Tr(ctx, "Hello you")) assert.Equal(t, "Bonjour toi", result) } @@ -26,8 +29,9 @@ func TestVariables(t *testing.T) { initFrenchLocale(map[string]string{ "Hello %s": "Bonjour %s", }) + ctx := context.WithValue(context.Background(), contextKey{}, "fr-FR") - result := render(t, Tr("fr-FR", "Hello %s", "John")) + result := render(t, Tr(ctx, "Hello %s", "John")) assert.Equal(t, "Bonjour John", result) } @@ -35,11 +39,89 @@ func TestSpanClass(t *testing.T) { initFrenchLocale(map[string]string{ "Hello <span class=\"text-bold\">%s</span>": "Bonjour <span class=\"text-bold\">%s</span>", }) + ctx := context.WithValue(context.Background(), contextKey{}, "fr-FR") - result := render(t, Tr("fr-FR", "Hello <span class=\"text-bold\">%s</span>", "John")) + result := render(t, Tr(ctx, "Hello <span class=\"text-bold\">%s</span>", "John")) assert.Equal(t, "Bonjour <span class=\"text-bold\">John</span>", result) } +func TestTr_withoutLangInContext(t *testing.T) { + initFrenchLocale(map[string]string{ + "Hello": "Bonjour", + }) + + result := render(t, Tr(context.Background(), "Hello")) + assert.Equal(t, "Hello", result) +} + +func TestCookieStrategy_found(t *testing.T) { + r := httptest.NewRequest(http.MethodGet, "/", nil) + r.AddCookie(&http.Cookie{Name: "lang", Value: "fr-FR"}) + + s := CookieStrategy("lang") + assert.Equal(t, "fr-FR", s.Detect(r)) +} + +func TestCookieStrategy_missing(t *testing.T) { + r := httptest.NewRequest(http.MethodGet, "/", nil) + + s := CookieStrategy("lang") + assert.Equal(t, "", s.Detect(r)) +} + +func TestAcceptLanguageStrategy_found(t *testing.T) { + initFrenchLocale(map[string]string{}) + r := httptest.NewRequest(http.MethodGet, "/", nil) + r.Header.Set("Accept-Language", "fr-FR,fr;q=0.9") + + assert.Equal(t, "fr-FR", AcceptLanguageStrategy.Detect(r)) +} + +func TestAcceptLanguageStrategy_missing(t *testing.T) { + initFrenchLocale(map[string]string{}) + r := httptest.NewRequest(http.MethodGet, "/", nil) + + assert.Equal(t, "", AcceptLanguageStrategy.Detect(r)) +} + +func TestLangMiddleware_firstStrategyWins(t *testing.T) { + first := strategyFunc(func(_ *http.Request) string { return "fr-FR" }) + second := strategyFunc(func(_ *http.Request) string { return "de-DE" }) + + var capturedLang string + handler := LangMiddleware(first, second)(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + capturedLang, _ = r.Context().Value(contextKey{}).(string) + })) + + handler.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, "fr-FR", capturedLang) +} + +func TestLangMiddleware_fallsThrough(t *testing.T) { + first := strategyFunc(func(_ *http.Request) string { return "" }) + second := strategyFunc(func(_ *http.Request) string { return "fr-FR" }) + + var capturedLang string + handler := LangMiddleware(first, second)(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + capturedLang, _ = r.Context().Value(contextKey{}).(string) + })) + + handler.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, "fr-FR", capturedLang) +} + +func TestLangMiddleware_fallback(t *testing.T) { + first := strategyFunc(func(_ *http.Request) string { return "" }) + + var capturedLang string + handler := LangMiddleware(first)(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + capturedLang, _ = r.Context().Value(contextKey{}).(string) + })) + + handler.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)) + assert.Equal(t, "en-GB", capturedLang) +} + // render is a test helper that renders a component and returns the result as a string func render(t *testing.T, c templ.Component) string { t.Helper() @@ -63,3 +145,8 @@ func initFrenchLocale(translations map[string]string) { } kcore.Expect(Init(fs), "failed to init fs") } + +// strategyFunc is a test helper to create a Strategy from a plain function +type strategyFunc func(*http.Request) string + +func (f strategyFunc) Detect(r *http.Request) string { return f(r) } |
