nuke/lib/localization/localization_test.go
lif 71147b4857
fix: respect Accept-Language quality factors in language detection (#1380)
The Accept-Language header parsing was not correctly handling quality
factors. When a browser sends "en-GB,de-DE;q=0.5", the expected behavior
is to prefer English (q=1.0 by default) over German (q=0.5).

The fix uses golang.org/x/text/language.ParseAcceptLanguage to properly
parse and sort language preferences by quality factor. It also adds base
language fallbacks (e.g., "en" for "en-GB") to ensure regional variants
match their parent languages when no exact match exists.

Fixes #1022

Signed-off-by: majiayu000 <1835304752@qq.com>
2026-01-02 08:01:43 -05:00

178 lines
4.3 KiB
Go

package localization
import (
"encoding/json"
"fmt"
"net/http/httptest"
"sort"
"testing"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
func TestLocalizationService(t *testing.T) {
service := NewLocalizationService()
loadingStrMap := map[string]string{
"de": "Ladevorgang...",
"en": "Loading...",
"es": "Cargando...",
"et": "Laadin...",
"fil": "Naglo-load...",
"fr": "Chargement...",
"ja": "ロード中...",
"is": "Hleður...",
"nb": "Laster inn...",
"nl": "Laden...",
"nn": "Lastar inn...",
"pl": "Ładowanie...",
"pt-BR": "Carregando...",
"tr": "Yükleniyor...",
"ru": "Загрузка...",
"uk": "Завантаження...",
"vi": "Đang nạp...",
"zh-CN": "加载中...",
"zh-TW": "載入中...",
"sv": "Laddar...",
}
var keys []string
for lang := range loadingStrMap {
keys = append(keys, lang)
}
sort.Strings(keys)
for _, lang := range keys {
expected := loadingStrMap[lang]
t.Run(fmt.Sprintf("%s localization", lang), func(t *testing.T) {
localizer := service.GetLocalizer(lang)
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
if result != expected {
t.Errorf("Expected '%s', got '%s'", expected, result)
}
})
}
// Test for requiredKeys localization
requiredKeys := []string{
"loading", "why_am_i_seeing", "protected_by", "protected_from", "made_with",
"mascot_design", "try_again", "go_home", "javascript_required",
}
for _, lang := range keys {
t.Run(fmt.Sprintf("All required keys exist in %s", lang), func(t *testing.T) {
loc := service.GetLocalizer(lang)
for _, key := range requiredKeys {
result := loc.MustLocalize(&i18n.LocalizeConfig{MessageID: key})
if result == "" {
t.Errorf("Key '%s' returned empty string", key)
}
}
})
}
}
type manifest struct {
SupportedLanguages []string `json:"supportedLanguages"`
}
func loadManifest(t *testing.T) manifest {
t.Helper()
fin, err := localeFS.Open("locales/manifest.json")
if err != nil {
t.Fatal(err)
}
defer fin.Close()
var result manifest
if err := json.NewDecoder(fin).Decode(&result); err != nil {
t.Fatal(err)
}
return result
}
func TestComprehensiveTranslations(t *testing.T) {
service := NewLocalizationService()
var translations = map[string]any{}
fin, err := localeFS.Open("locales/en.json")
if err != nil {
t.Fatal(err)
}
defer fin.Close()
if err := json.NewDecoder(fin).Decode(&translations); err != nil {
t.Fatal(err)
}
var keys []string
for k := range translations {
keys = append(keys, k)
}
sort.Strings(keys)
manifest := loadManifest(t)
if len(manifest.SupportedLanguages) == 0 {
t.Fatal("no languages loaded")
}
for _, lang := range loadManifest(t).SupportedLanguages {
t.Run(lang, func(t *testing.T) {
loc := service.GetLocalizer(lang)
sl := SimpleLocalizer{Localizer: loc}
service_lang := sl.GetLang()
if service_lang != lang {
t.Error("Localizer language not same as specified")
}
for _, key := range keys {
t.Run(key, func(t *testing.T) {
if result := sl.T(key); result == "" {
t.Error("key not defined")
}
})
}
})
}
}
func TestAcceptLanguageQualityFactors(t *testing.T) {
service := NewLocalizationService()
testCases := []struct {
name string
acceptLanguage string
expectedLang string
}{
{"simple_en", "en", "en"},
{"simple_de", "de", "de"},
{"en_GB_with_lower_priority_de", "en-GB,de-DE;q=0.5", "en"},
{"en_GB_only", "en-GB", "en"},
{"de_with_lower_priority_en", "de,en;q=0.5", "de"},
{"de_DE_with_lower_priority_en", "de-DE,en;q=0.5", "de"},
{"fr_with_lower_priority_de", "fr,de;q=0.5", "fr"},
{"zh_CN_regional", "zh-CN", "zh-CN"},
{"zh_TW_regional", "zh-TW", "zh-TW"},
{"pt_BR_regional", "pt-BR", "pt-BR"},
{"complex_header", "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,de;q=0.5", "fr"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("Accept-Language", tc.acceptLanguage)
localizer := service.GetLocalizerFromRequest(req)
sl := &SimpleLocalizer{Localizer: localizer}
gotLang := sl.GetLang()
if gotLang != tc.expectedLang {
t.Errorf("Accept-Language %q: expected %s, got %s", tc.acceptLanguage, tc.expectedLang, gotLang)
}
})
}
}