package locale import ( "testing" "github.com/stretchr/testify/assert" ) // Helper function to create test locale entries func createTestEntries() []LocaleEntry { return []LocaleEntry{ { LanguageName: "English", LanguageLocal: "English", LanguageCode: "en", LanguageCode2: "eng", LanguageCode3: "eng", Regions: []RegionEntry{ { Locale: "en-US", CountryName: "United States", CountryLocal: "United States", CountryCode: "US", Flag: "🇺🇸", }, { Locale: "en-GB", CountryName: "United Kingdom", CountryLocal: "United Kingdom", CountryCode: "GB", Flag: "🇬🇧", }, }, }, { LanguageName: "Japanese", LanguageLocal: "日本語", LanguageCode: "ja", LanguageCode2: "jpn", LanguageCode3: "jpn", Regions: []RegionEntry{ { Locale: "ja-JP", CountryName: "Japan", CountryLocal: "日本", CountryCode: "JP", Flag: "🇯🇵", }, }, }, { LanguageName: "Spanish", LanguageLocal: "Español", LanguageCode: "es", LanguageCode2: "spa", LanguageCode3: "spa", Regions: []RegionEntry{ { Locale: "es-ES", CountryName: "Spain", CountryLocal: "España", CountryCode: "ES", Flag: "🇪🇸", }, { Locale: "es-419", CountryName: "Latin America", CountryLocal: "América Latina", CountryCode: "419", Flag: "🌎", }, }, }, { LanguageName: "French", LanguageLocal: "Français", LanguageCode: "fr", LanguageCode2: "fra", LanguageCode3: "fra", Regions: []RegionEntry{ { Locale: "fr-FR", CountryName: "France", CountryLocal: "France", CountryCode: "FR", Flag: "🇫🇷", }, }, }, { LanguageName: "German", LanguageLocal: "Deutsch", LanguageCode: "de", LanguageCode2: "deu", LanguageCode3: "deu", Regions: []RegionEntry{ { Locale: "de-DE", CountryName: "Germany", CountryLocal: "Deutschland", CountryCode: "DE", Flag: "🇩🇪", }, }, }, { LanguageName: "Afar", LanguageLocal: "Afaraf", LanguageCode: "aa", LanguageCode2: "aar", LanguageCode3: "aar", Regions: []RegionEntry{}, }, { LanguageName: "Chinese", LanguageLocal: "中文", LanguageCode: "zh", LanguageCode2: "zho", LanguageCode3: "zho", Regions: []RegionEntry{ { Locale: "zh-CN", CountryName: "China", CountryLocal: "中国", CountryCode: "CN", Flag: "🇨🇳", }, }, }, // Antarctica entry for testing filtering { LanguageName: "English", LanguageLocal: "English", LanguageCode: "en", Regions: []RegionEntry{ { Locale: "en-AQ", CountryName: "Antarctica", CountryLocal: "Antarctica", CountryCode: "AQ", Flag: "🇦🇶", }, }, }, { LanguageName: "No Regions Language", LanguageLocal: "No Regions", LanguageCode: "xx", Regions: []RegionEntry{}, }, } } func TestSearchLanguages(t *testing.T) { testEntries := createTestEntries() tests := []struct { name string opts LanguageFilterOptions wantCount int wantFirstLang string wantFirstLocale string wantNoMatch bool }{ { name: "search by language name - full match", opts: LanguageFilterOptions{ SearchTerm: "English", }, wantCount: 2, // en-US, en-GB (Antarctica filtered out) wantFirstLang: "English", wantFirstLocale: "en-US", }, { name: "search by language name - partial match", opts: LanguageFilterOptions{ SearchTerm: "jap", }, wantCount: 1, wantFirstLang: "Japanese", wantFirstLocale: "ja-JP", }, { name: "search by language name - case insensitive", opts: LanguageFilterOptions{ SearchTerm: "SPANISH", }, wantCount: 2, wantFirstLang: "Spanish", wantFirstLocale: "es-ES", }, { name: "search by language local name", opts: LanguageFilterOptions{ SearchTerm: "日本語", }, wantCount: 1, wantFirstLang: "Japanese", wantFirstLocale: "ja-JP", }, { name: "search by language code", opts: LanguageFilterOptions{ SearchTerm: "fr", }, wantCount: 1, wantFirstLang: "French", wantFirstLocale: "fr-FR", }, { name: "search by country code", opts: LanguageFilterOptions{ SearchTerm: "US", }, wantCount: 2, // en-US and en-GB (matches "United" in "United Kingdom") wantFirstLang: "English", }, { name: "search by country code - case insensitive", opts: LanguageFilterOptions{ SearchTerm: "jp", }, wantCount: 1, wantFirstLang: "Japanese", wantFirstLocale: "ja-JP", }, { name: "search by country name", opts: LanguageFilterOptions{ SearchTerm: "France", }, wantCount: 1, wantFirstLang: "French", wantFirstLocale: "fr-FR", }, { name: "search by country name - partial", opts: LanguageFilterOptions{ SearchTerm: "United", }, wantCount: 2, // United States and United Kingdom wantFirstLang: "English", }, { name: "filter by CommonOnly - common language", opts: LanguageFilterOptions{ CommonOnly: true, SearchTerm: "English", }, wantCount: 2, // en-US, en-GB (Antarctica filtered out) wantFirstLang: "English", }, { name: "filter by CommonOnly - uncommon language excluded", opts: LanguageFilterOptions{ CommonOnly: true, SearchTerm: "Afar", }, wantCount: 0, wantNoMatch: true, }, { name: "filter by CountryCode", opts: LanguageFilterOptions{ CountryCode: "ES", }, wantCount: 1, wantFirstLang: "Spanish", wantFirstLocale: "es-ES", }, { name: "filter by CountryCode - case insensitive", opts: LanguageFilterOptions{ CountryCode: "es", }, wantCount: 1, wantFirstLang: "Spanish", wantFirstLocale: "es-ES", }, { name: "filter by CountryCode with search", opts: LanguageFilterOptions{ CountryCode: "US", SearchTerm: "English", }, wantCount: 1, wantFirstLang: "English", wantFirstLocale: "en-US", }, { name: "apply limit", opts: LanguageFilterOptions{ Limit: 2, }, wantCount: 2, wantFirstLang: "Afar", // Alphabetically first }, { name: "apply limit with search", opts: LanguageFilterOptions{ SearchTerm: "English", Limit: 1, }, wantCount: 1, wantFirstLang: "English", wantFirstLocale: "en-US", }, { name: "empty search term - returns all sorted", opts: LanguageFilterOptions{}, wantCount: 10, // All except Antarctica (includes languages without regions) wantFirstLang: "Afar", // Alphabetically first }, { name: "empty search term with CommonOnly", opts: LanguageFilterOptions{ CommonOnly: true, }, wantCount: 8, // zh-CN, en-US, en-GB, fr-FR, de-DE, ja-JP, es-ES, es-419 wantFirstLang: "Chinese", }, { name: "no matches found", opts: LanguageFilterOptions{ SearchTerm: "NonExistentLanguage", }, wantCount: 0, wantNoMatch: true, }, { name: "Antarctica filtered out", opts: LanguageFilterOptions{ SearchTerm: "Antarctica", }, wantCount: 0, wantNoMatch: true, }, { name: "no regions language included", opts: LanguageFilterOptions{ SearchTerm: "No Regions", }, wantCount: 1, wantFirstLang: "No Regions Language", wantFirstLocale: "xx", }, { name: "exact match prioritized in search", opts: LanguageFilterOptions{ SearchTerm: "Spanish", }, wantCount: 2, wantFirstLang: "Spanish", wantFirstLocale: "es-ES", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { results := SearchLanguages(testEntries, tt.opts) if tt.wantNoMatch { assert.Empty(t, results) } else { assert.Len(t, results, tt.wantCount) if tt.wantCount > 0 { assert.Equal(t, tt.wantFirstLang, results[0].LanguageName) if tt.wantFirstLocale != "" { assert.Equal(t, tt.wantFirstLocale, results[0].Locale) } } } }) } } func TestSearchLanguages_Sorting(t *testing.T) { testEntries := createTestEntries() t.Run("alphabetical sort without search term", func(t *testing.T) { opts := LanguageFilterOptions{ CommonOnly: true, } results := SearchLanguages(testEntries, opts) // Should be sorted alphabetically by language name languageNames := make([]string, len(results)) for i, r := range results { languageNames[i] = r.LanguageName } // Expected order: Chinese, English, English, French, German, Japanese, Spanish, Spanish expected := []string{"Chinese", "English", "English", "French", "German", "Japanese", "Spanish", "Spanish"} assert.Equal(t, expected, languageNames) }) t.Run("exact match priority in search", func(t *testing.T) { opts := LanguageFilterOptions{ SearchTerm: "English", } results := SearchLanguages(testEntries, opts) // English variants should come first (exact match on language name) assert.Len(t, results, 2) assert.Equal(t, "English", results[0].LanguageName) assert.Equal(t, "en-US", results[0].Locale) assert.Equal(t, "English", results[1].LanguageName) assert.Equal(t, "en-GB", results[1].Locale) }) } func TestIsCommonLanguage(t *testing.T) { tests := []struct { name string languageCode string wantCommon bool }{ { name: "English is common", languageCode: "en", wantCommon: true, }, { name: "Japanese is common", languageCode: "ja", wantCommon: true, }, { name: "Spanish is common", languageCode: "es", wantCommon: true, }, { name: "French is common", languageCode: "fr", wantCommon: true, }, { name: "German is common", languageCode: "de", wantCommon: true, }, { name: "Chinese is common", languageCode: "zh", wantCommon: true, }, { name: "Korean is common", languageCode: "ko", wantCommon: true, }, { name: "Italian is common", languageCode: "it", wantCommon: true, }, { name: "Portuguese is common", languageCode: "pt", wantCommon: true, }, { name: "Russian is common", languageCode: "ru", wantCommon: true, }, { name: "Afar is not common", languageCode: "aa", wantCommon: false, }, { name: "empty string", languageCode: "", wantCommon: false, }, { name: "locale with region extracts base code", languageCode: "en-US", wantCommon: true, }, { name: "locale with region extracts base code - ja-JP", languageCode: "ja-JP", wantCommon: true, }, { name: "locale with region extracts base code - es-ES", languageCode: "es-ES", wantCommon: true, }, { name: "locale with region extracts base code - uncommon", languageCode: "aa-ET", wantCommon: false, }, { name: "case insensitive - EN", languageCode: "EN", wantCommon: true, }, { name: "case insensitive - JA", languageCode: "JA", wantCommon: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := IsCommonLanguage(tt.languageCode) assert.Equal(t, tt.wantCommon, result) }) } } func TestGetCommonLanguageCodes(t *testing.T) { t.Run("returns copy of commonLanguageCodes", func(t *testing.T) { codes := GetCommonLanguageCodes() // Verify codes are returned (list has been expanded to 33 languages) assert.Greater(t, len(codes), 10) }) t.Run("modifying returned slice doesn't affect original", func(t *testing.T) { codes1 := GetCommonLanguageCodes() codes2 := GetCommonLanguageCodes() // Modify first slice codes1[0] = "modified" // Verify second slice is unchanged assert.NotEqual(t, codes1[0], codes2[0]) }) t.Run("can be modified independently", func(t *testing.T) { codes1 := GetCommonLanguageCodes() // Append to slice codes1 = append(codes1, "new-code") assert.Contains(t, codes1, "new-code") // Get a new copy codes2 := GetCommonLanguageCodes() // Verify new copy doesn't have appended code (but note that implementation returns a new copy each time) assert.Greater(t, len(codes2), 10) assert.NotContains(t, codes2, "new-code") }) } func TestLanguageOption_DisplayText(t *testing.T) { testEntries := createTestEntries() t.Run("display text format with region", func(t *testing.T) { opts := LanguageFilterOptions{ SearchTerm: "Japanese", } results := SearchLanguages(testEntries, opts) if len(results) > 0 { displayText := results[0].DisplayText // Should contain flag, country code, and language name assert.Contains(t, displayText, "🇯🇵") assert.Contains(t, displayText, "JP") assert.Contains(t, displayText, "日本語") assert.Contains(t, displayText, "Japanese") } }) t.Run("display text format without region", func(t *testing.T) { opts := LanguageFilterOptions{ SearchTerm: "No Regions", } results := SearchLanguages(testEntries, opts) if len(results) > 0 { displayText := results[0].DisplayText // Should contain flag and language name assert.Contains(t, displayText, "No Regions Language") } }) t.Run("display text format English with different local name", func(t *testing.T) { results := SearchLanguages(testEntries, LanguageFilterOptions{SearchTerm: "Japanese"}) if len(results) != 1 { t.Fatalf("expected Japanese region, got %v", results) } result := results[0] assert.Contains(t, result.DisplayText, "🇯🇵") assert.Contains(t, result.DisplayText, "JP") assert.Contains(t, result.DisplayText, "日本語") assert.Contains(t, result.DisplayText, "Japanese") }) } func TestLanguageFilterOptions(t *testing.T) { t.Run("default options", func(t *testing.T) { opts := LanguageFilterOptions{} assert.False(t, opts.CommonOnly) assert.Empty(t, opts.SearchTerm) assert.Equal(t, 0, opts.Limit) assert.Empty(t, opts.CountryCode) }) t.Run("with all options set", func(t *testing.T) { opts := LanguageFilterOptions{ CommonOnly: true, SearchTerm: "test", Limit: 10, CountryCode: "US", } assert.True(t, opts.CommonOnly) assert.Equal(t, "test", opts.SearchTerm) assert.Equal(t, 10, opts.Limit) assert.Equal(t, "US", opts.CountryCode) }) } func TestFullLocaleFormatting(t *testing.T) { testEntries := createTestEntries() t.Run("FullLocale set correctly", func(t *testing.T) { opts := LanguageFilterOptions{ SearchTerm: "Spanish", } results := SearchLanguages(testEntries, opts) // Check ES-419 formatting (should be ES-419) found419 := false for _, r := range results { if r.Locale == "es-419" { assert.Equal(t, "ES-419", r.FullLocale) found419 = true break } } assert.True(t, found419, "es-419 locale should be found with FullLocale") }) t.Run("FullLocale empty for non-regional locales", func(t *testing.T) { opts := LanguageFilterOptions{ SearchTerm: "No Regions", } results := SearchLanguages(testEntries, opts) if len(results) > 0 { assert.Empty(t, results[0].FullLocale, "Non-regional locale should have empty FullLocale") } }) }