From 94e6c2d5d7c24d2481e15a56c52a20a939d9c2fc Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 06:35:46 +0000 Subject: [PATCH] fix(i18n): canonicalise locale scans Co-Authored-By: Virgil --- loader.go | 12 +++++++++++- loader_test.go | 16 ++++++++++++++++ service.go | 10 +++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/loader.go b/loader.go index 8d131af..759b431 100644 --- a/loader.go +++ b/loader.go @@ -3,6 +3,7 @@ package i18n import ( "io/fs" "path" + "slices" "strings" "sync" @@ -71,14 +72,23 @@ func (l *FSLoader) Languages() []string { l.langErr = log.E("FSLoader.Languages", "read locale directory: "+l.dir, err) return } + seen := make(map[string]struct{}, len(entries)) for _, entry := range entries { if entry.IsDir() || !core.HasSuffix(entry.Name(), ".json") { continue } lang := core.TrimSuffix(entry.Name(), ".json") - lang = core.Replace(lang, "_", "-") + lang = normalizeLanguageTag(core.Replace(lang, "_", "-")) + if lang == "" { + continue + } + if _, ok := seen[lang]; ok { + continue + } + seen[lang] = struct{}{} l.languages = append(l.languages, lang) } + slices.Sort(l.languages) }) return l.languages } diff --git a/loader_test.go b/loader_test.go index f740dd7..fe3ab89 100644 --- a/loader_test.go +++ b/loader_test.go @@ -19,6 +19,22 @@ func TestFSLoaderLanguages(t *testing.T) { } } +func TestFSLoaderLanguagesCanonicalAndUnique(t *testing.T) { + fs := fstest.MapFS{ + "locales/en.json": &fstest.MapFile{Data: []byte(`{}`)}, + "locales/en_US.json": &fstest.MapFile{Data: []byte(`{}`)}, + "locales/es-MX.json": &fstest.MapFile{Data: []byte(`{}`)}, + "locales/fr.json": &fstest.MapFile{Data: []byte(`{}`)}, + } + + loader := NewFSLoader(fs, "locales") + langs := loader.Languages() + want := []string{"en", "en-US", "es-MX", "fr"} + if !slices.Equal(langs, want) { + t.Fatalf("Languages() = %v, want %v", langs, want) + } +} + func TestFSLoaderLoad(t *testing.T) { loader := NewFSLoader(localeFS, "locales") messages, grammar, err := loader.Load("en") diff --git a/service.go b/service.go index 7dc32b1..ec25ac8 100644 --- a/service.go +++ b/service.go @@ -936,6 +936,7 @@ func (s *Service) LoadFS(fsys fs.FS, dir string) error { if err != nil { return log.E("Service.LoadFS", "read locales directory", err) } + seen := make(map[string]struct{}, len(entries)) for _, entry := range entries { if entry.IsDir() || !core.HasSuffix(entry.Name(), ".json") { continue @@ -946,7 +947,14 @@ func (s *Service) LoadFS(fsys fs.FS, dir string) error { return log.E("Service.LoadFS", "read locale: "+entry.Name(), err) } lang := core.TrimSuffix(entry.Name(), ".json") - lang = core.Replace(lang, "_", "-") + lang = normalizeLanguageTag(core.Replace(lang, "_", "-")) + if lang == "" { + continue + } + if _, ok := seen[lang]; ok { + continue + } + seen[lang] = struct{}{} if err := s.loadJSON(lang, data); err != nil { return log.E("Service.LoadFS", "parse locale: "+entry.Name(), err) } -- 2.45.3