2026-02-20 02:09:08 +00:00
|
|
|
package i18n
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
2026-04-02 06:26:29 +00:00
|
|
|
"golang.org/x/text/language"
|
2026-02-20 02:09:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// --- Formality.String() ---
|
|
|
|
|
|
|
|
|
|
func TestFormality_String_Good(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
f Formality
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{"neutral", FormalityNeutral, "neutral"},
|
|
|
|
|
{"informal", FormalityInformal, "informal"},
|
|
|
|
|
{"formal", FormalityFormal, "formal"},
|
|
|
|
|
{"unknown", Formality(99), "neutral"},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
assert.Equal(t, tt.want, tt.f.String())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- TextDirection.String() ---
|
|
|
|
|
|
|
|
|
|
func TestTextDirection_String_Good(t *testing.T) {
|
|
|
|
|
assert.Equal(t, "ltr", DirLTR.String())
|
|
|
|
|
assert.Equal(t, "rtl", DirRTL.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- PluralCategory.String() ---
|
|
|
|
|
|
|
|
|
|
func TestPluralCategory_String_Good(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
cat PluralCategory
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{"zero", PluralZero, "zero"},
|
|
|
|
|
{"one", PluralOne, "one"},
|
|
|
|
|
{"two", PluralTwo, "two"},
|
|
|
|
|
{"few", PluralFew, "few"},
|
|
|
|
|
{"many", PluralMany, "many"},
|
|
|
|
|
{"other", PluralOther, "other"},
|
|
|
|
|
{"unknown", PluralCategory(99), "other"},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
assert.Equal(t, tt.want, tt.cat.String())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- GrammaticalGender.String() ---
|
|
|
|
|
|
|
|
|
|
func TestGrammaticalGender_String_Good(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
g GrammaticalGender
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{"neuter", GenderNeuter, "neuter"},
|
|
|
|
|
{"masculine", GenderMasculine, "masculine"},
|
|
|
|
|
{"feminine", GenderFeminine, "feminine"},
|
|
|
|
|
{"common", GenderCommon, "common"},
|
|
|
|
|
{"unknown", GrammaticalGender(99), "neuter"},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
assert.Equal(t, tt.want, tt.g.String())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- IsRTLLanguage ---
|
|
|
|
|
|
|
|
|
|
func TestIsRTLLanguage_Good(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
lang string
|
|
|
|
|
want bool
|
|
|
|
|
}{
|
|
|
|
|
{"arabic", "ar", true},
|
|
|
|
|
{"arabic_sa", "ar-SA", true},
|
2026-04-02 03:11:15 +00:00
|
|
|
{"arabic_sa_underscore", "ar_EG", true},
|
2026-02-20 02:09:08 +00:00
|
|
|
{"hebrew", "he", true},
|
|
|
|
|
{"farsi", "fa", true},
|
|
|
|
|
{"urdu", "ur", true},
|
|
|
|
|
{"english", "en", false},
|
|
|
|
|
{"german", "de", false},
|
|
|
|
|
{"french", "fr", false},
|
|
|
|
|
{"unknown", "xx", false},
|
2026-04-01 23:56:57 +00:00
|
|
|
{"arabic_variant", "ar-EG-extra", true}, // len > 2 prefix check
|
2026-02-20 02:09:08 +00:00
|
|
|
{"english_variant", "en-US-extra", false}, // len > 2, not RTL
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
assert.Equal(t, tt.want, IsRTLLanguage(tt.lang))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Package-level SetFormality ---
|
|
|
|
|
|
|
|
|
|
func TestSetFormality_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
SetFormality(FormalityFormal)
|
|
|
|
|
assert.Equal(t, FormalityFormal, svc.Formality())
|
|
|
|
|
|
|
|
|
|
SetFormality(FormalityNeutral)
|
|
|
|
|
assert.Equal(t, FormalityNeutral, svc.Formality())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 04:20:04 +00:00
|
|
|
// --- Package-level SetFallback ---
|
|
|
|
|
|
|
|
|
|
func TestSetFallback_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
SetFallback("fr")
|
|
|
|
|
assert.Equal(t, "fr", svc.Fallback())
|
|
|
|
|
|
|
|
|
|
SetFallback("en")
|
|
|
|
|
assert.Equal(t, "en", svc.Fallback())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 23:56:57 +00:00
|
|
|
// --- Package-level CurrentFormality ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentFormality_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, FormalityNeutral, CurrentFormality())
|
|
|
|
|
|
|
|
|
|
SetFormality(FormalityFormal)
|
|
|
|
|
assert.Equal(t, FormalityFormal, CurrentFormality())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 04:20:04 +00:00
|
|
|
// --- Package-level CurrentFallback ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentFallback_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "en", CurrentFallback())
|
|
|
|
|
|
|
|
|
|
SetFallback("fr")
|
|
|
|
|
assert.Equal(t, "fr", CurrentFallback())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 00:03:23 +00:00
|
|
|
// --- Package-level SetLocation ---
|
|
|
|
|
|
|
|
|
|
func TestSetLocation_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
SetLocation("workspace")
|
|
|
|
|
assert.Equal(t, "workspace", svc.Location())
|
|
|
|
|
|
|
|
|
|
SetLocation("")
|
|
|
|
|
assert.Equal(t, "", svc.Location())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Package-level CurrentLocation ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentLocation_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "", CurrentLocation())
|
|
|
|
|
|
|
|
|
|
SetLocation("workspace")
|
|
|
|
|
assert.Equal(t, "workspace", CurrentLocation())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 11:13:09 +00:00
|
|
|
// --- Package-level Location ---
|
|
|
|
|
|
|
|
|
|
func TestLocation_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, CurrentLocation(), Location())
|
|
|
|
|
|
|
|
|
|
SetLocation("workspace")
|
|
|
|
|
assert.Equal(t, CurrentLocation(), Location())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 02:09:08 +00:00
|
|
|
// --- Package-level Direction ---
|
|
|
|
|
|
|
|
|
|
func TestDirection_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
dir := Direction()
|
|
|
|
|
assert.Equal(t, DirLTR, dir)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 04:23:18 +00:00
|
|
|
// --- Package-level CurrentDirection ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentDirection_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, DirLTR, CurrentDirection())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 12:10:30 +00:00
|
|
|
// --- Package-level CurrentTextDirection ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentTextDirection_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, CurrentDirection(), CurrentTextDirection())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 02:09:08 +00:00
|
|
|
// --- Package-level IsRTL ---
|
|
|
|
|
|
|
|
|
|
func TestIsRTL_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.False(t, IsRTL(), "English should not be RTL")
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 11:37:33 +00:00
|
|
|
// --- Package-level RTL ---
|
|
|
|
|
|
|
|
|
|
func TestRTL_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, IsRTL(), RTL())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 11:26:14 +00:00
|
|
|
// --- Package-level CurrentIsRTL ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentIsRTL_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.False(t, CurrentIsRTL(), "English should not be RTL")
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 11:37:33 +00:00
|
|
|
// --- Package-level CurrentRTL ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentRTL_Good(t *testing.T) {
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, CurrentIsRTL(), CurrentRTL())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 04:27:15 +00:00
|
|
|
// --- Package-level CurrentPluralCategory ---
|
|
|
|
|
|
|
|
|
|
func TestCurrentPluralCategory_Good(t *testing.T) {
|
|
|
|
|
prev := Default()
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
SetDefault(prev)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, PluralOther, CurrentPluralCategory(0))
|
|
|
|
|
assert.Equal(t, PluralOne, CurrentPluralCategory(1))
|
|
|
|
|
assert.Equal(t, PluralOther, CurrentPluralCategory(2))
|
|
|
|
|
|
|
|
|
|
require.NoError(t, SetLanguage("fr"))
|
|
|
|
|
assert.Equal(t, PluralOne, CurrentPluralCategory(0))
|
|
|
|
|
assert.Equal(t, PluralOne, CurrentPluralCategory(1))
|
|
|
|
|
assert.Equal(t, PluralOther, CurrentPluralCategory(2))
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 11:44:56 +00:00
|
|
|
// --- Package-level PluralCategoryOf ---
|
|
|
|
|
|
|
|
|
|
func TestPluralCategoryOf_Good(t *testing.T) {
|
|
|
|
|
prev := Default()
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
SetDefault(prev)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
svc, err := New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
SetDefault(svc)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, PluralOther, PluralCategoryOf(0))
|
|
|
|
|
assert.Equal(t, PluralOne, PluralCategoryOf(1))
|
|
|
|
|
assert.Equal(t, PluralOther, PluralCategoryOf(2))
|
|
|
|
|
|
|
|
|
|
require.NoError(t, SetLanguage("fr"))
|
|
|
|
|
assert.Equal(t, PluralOne, PluralCategoryOf(0))
|
|
|
|
|
assert.Equal(t, PluralOne, PluralCategoryOf(1))
|
|
|
|
|
assert.Equal(t, PluralOther, PluralCategoryOf(2))
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 11:05:51 +00:00
|
|
|
func TestCurrentPluralCategory_NoDefaultService(t *testing.T) {
|
|
|
|
|
prev := Default()
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
SetDefault(prev)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
SetDefault(nil)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, PluralOther, CurrentPluralCategory(2))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 02:09:08 +00:00
|
|
|
// --- detectLanguage ---
|
|
|
|
|
|
|
|
|
|
func TestDetectLanguage_Good(t *testing.T) {
|
|
|
|
|
// detectLanguage relies on env vars, which we can't easily set in tests
|
|
|
|
|
// but we can test with no supported languages
|
|
|
|
|
result := detectLanguage(nil)
|
|
|
|
|
assert.Equal(t, "", result, "should return empty with no supported languages")
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 06:26:29 +00:00
|
|
|
func TestDetectLanguage_PrefersLocaleOverrides(t *testing.T) {
|
|
|
|
|
t.Setenv("LANG", "en_US.UTF-8")
|
|
|
|
|
t.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
|
|
|
|
t.Setenv("LC_ALL", "de_DE.UTF-8")
|
|
|
|
|
|
|
|
|
|
supported := []language.Tag{
|
|
|
|
|
language.AmericanEnglish,
|
|
|
|
|
language.French,
|
|
|
|
|
language.German,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := detectLanguage(supported)
|
|
|
|
|
assert.Equal(t, "de", result, "LC_ALL should win over LANG and LC_MESSAGES")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDetectLanguage_SkipsInvalidHigherPriorityLocale(t *testing.T) {
|
|
|
|
|
t.Setenv("LANG", "en_US.UTF-8")
|
|
|
|
|
t.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
|
|
|
|
t.Setenv("LC_ALL", "not-a-locale")
|
|
|
|
|
|
|
|
|
|
supported := []language.Tag{
|
|
|
|
|
language.AmericanEnglish,
|
|
|
|
|
language.French,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := detectLanguage(supported)
|
|
|
|
|
assert.Equal(t, "fr", result, "invalid LC_ALL should not block a valid lower-priority locale")
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 07:04:54 +00:00
|
|
|
func TestDetectLanguage_PrefersLanguageList(t *testing.T) {
|
|
|
|
|
t.Setenv("LANGUAGE", "fr_FR.UTF-8:de_DE.UTF-8")
|
|
|
|
|
t.Setenv("LANG", "en_US.UTF-8")
|
|
|
|
|
|
|
|
|
|
supported := []language.Tag{
|
|
|
|
|
language.AmericanEnglish,
|
|
|
|
|
language.French,
|
|
|
|
|
language.German,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := detectLanguage(supported)
|
|
|
|
|
assert.Equal(t, "fr", result, "LANGUAGE should be considered before LANG")
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 02:09:08 +00:00
|
|
|
// --- Mode.String() ---
|
|
|
|
|
|
|
|
|
|
func TestMode_String_Good(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
m Mode
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{"normal", ModeNormal, "normal"},
|
|
|
|
|
{"strict", ModeStrict, "strict"},
|
|
|
|
|
{"collect", ModeCollect, "collect"},
|
|
|
|
|
{"unknown", Mode(99), "unknown"},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
assert.Equal(t, tt.want, tt.m.String())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|