package ki18n import ( "bytes" "context" "fmt" "net/http" "net/http/httptest" "strings" "testing" "testing/fstest" "time" "github.com/a-h/templ" "github.com/martinlehoux/kagamigo/kcore" "github.com/stretchr/testify/assert" ) 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(ctx, "Hello you")) assert.Equal(t, "Bonjour toi", result) } 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(ctx, "Hello %s", "John")) assert.Equal(t, "Bonjour John", result) } func TestSpanClass(t *testing.T) { initFrenchLocale(map[string]string{ "Hello %s": "Bonjour %s", }) ctx := context.WithValue(context.Background(), contextKey{}, "fr-FR") result := render(t, Tr(ctx, "Hello %s", "John")) assert.Equal(t, "Bonjour John", 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 TestFormatTime_englishLocale(t *testing.T) { ctx := context.WithValue(context.Background(), contextKey{}, "en-GB") d := time.Date(2026, time.May, 12, 0, 0, 0, 0, time.UTC) assert.Equal(t, "12 May 2026", FormatTime(ctx, d)) } func TestFormatTime_frenchLocale(t *testing.T) { ctx := context.WithValue(context.Background(), contextKey{}, "fr-FR") d := time.Date(2026, time.May, 12, 0, 0, 0, 0, time.UTC) assert.Equal(t, "12 mai 2026", FormatTime(ctx, d)) } func TestFormatTime_unknownLocale(t *testing.T) { ctx := context.WithValue(context.Background(), contextKey{}, "de-DE") d := time.Date(2026, time.May, 12, 0, 0, 0, 0, time.UTC) assert.Equal(t, "12 May 2026", FormatTime(ctx, d)) } func TestFormatTime_extraLocale(t *testing.T) { spanishMonths := [...]string{ "enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre", } fs := fstest.MapFS{ "es-ES/index.yml": &fstest.MapFile{Data: []byte{}}, } kcore.Expect(Init(fs, Locale{ Lang: "es-ES", FormatTime: func(t time.Time) string { return fmt.Sprintf("%d de %s de %d", t.Day(), spanishMonths[t.Month()-1], t.Year()) }, }), "failed to init fs") ctx := context.WithValue(context.Background(), contextKey{}, "es-ES") d := time.Date(2026, time.May, 12, 0, 0, 0, 0, time.UTC) assert.Equal(t, "12 de mayo de 2026", FormatTime(ctx, d)) } func TestFormatTime_extraLocaleNoFormatter(t *testing.T) { fs := fstest.MapFS{ "es-ES/index.yml": &fstest.MapFile{Data: []byte{}}, } kcore.Expect(Init(fs, Locale{Lang: "es-ES"}), "failed to init fs") ctx := context.WithValue(context.Background(), contextKey{}, "es-ES") d := time.Date(2026, time.May, 12, 0, 0, 0, 0, time.UTC) assert.Equal(t, "12 May 2026", FormatTime(ctx, d)) } func TestFormatTime_noLocaleInContext(t *testing.T) { d := time.Date(2026, time.May, 12, 0, 0, 0, 0, time.UTC) assert.Equal(t, "12 May 2026", FormatTime(context.Background(), d)) } 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() w := &bytes.Buffer{} err := c.Render(context.Background(), w) assert.NoError(t, err) return w.String() } // initFrenchLocale creates a filesystem with fr-FR locale from a translation map func initFrenchLocale(translations map[string]string) { var sb strings.Builder for key, value := range translations { fmt.Fprintf(&sb, "%q: %q\n", key, value) } fs := fstest.MapFS{ "fr-FR/index.yml": &fstest.MapFile{ Data: []byte(sb.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) }