diff --git a/loader.go b/loader.go index 53c51a7..3dbe1fc 100644 --- a/loader.go +++ b/loader.go @@ -1,6 +1,7 @@ package i18n import ( + "errors" "io/fs" "path" "slices" @@ -30,14 +31,21 @@ func (l *FSLoader) Load(lang string) (map[string]Message, *GrammarData, error) { variants := localeFilenameCandidates(lang) var data []byte var err error + var firstNonMissingErr error for _, filename := range variants { filePath := path.Join(l.dir, filename) data, err = fs.ReadFile(l.fsys, filePath) if err == nil { break } + if firstNonMissingErr == nil && !errors.Is(err, fs.ErrNotExist) { + firstNonMissingErr = err + } } if err != nil { + if firstNonMissingErr != nil { + err = firstNonMissingErr + } return nil, nil, log.E("FSLoader.Load", "locale not found: "+lang, err) } @@ -70,11 +78,21 @@ func localeFilenameCandidates(lang string) []string { } variants = append(variants, candidate) } - addVariant(lang + ".json") - addVariant(core.Replace(lang, "-", "_") + ".json") - addVariant(core.Replace(lang, "_", "-") + ".json") - if base := baseLanguageTag(lang); base != "" && base != lang { - addVariant(base + ".json") + canonical := normalizeLanguageTag(lang) + addTag := func(tag string) { + if tag == "" { + return + } + addVariant(tag + ".json") + addVariant(core.Replace(tag, "-", "_") + ".json") + addVariant(core.Replace(tag, "_", "-") + ".json") + } + addTag(lang) + if canonical != "" && canonical != lang { + addTag(canonical) + } + if base := baseLanguageTag(canonical); base != "" && base != canonical { + addTag(base) } return variants } diff --git a/loader_test.go b/loader_test.go index 2a16f4f..c5d0ada 100644 --- a/loader_test.go +++ b/loader_test.go @@ -187,6 +187,33 @@ func TestLocaleFilenameCandidates(t *testing.T) { } } +func TestLocaleFilenameCandidatesNormalisesCase(t *testing.T) { + got := localeFilenameCandidates("en-us") + want := []string{"en-us.json", "en_us.json", "en-US.json", "en_US.json", "en.json"} + if !slices.Equal(got, want) { + t.Fatalf("localeFilenameCandidates(en-us) = %v, want %v", got, want) + } +} + +func TestFSLoaderLoadUsesCanonicalVariant(t *testing.T) { + fs := fstest.MapFS{ + "locales/en-US.json": &fstest.MapFile{ + Data: []byte(`{ + "greeting": "hello" + }`), + }, + } + + loader := NewFSLoader(fs, "locales") + messages, _, err := loader.Load("en-us") + if err != nil { + t.Fatalf("Load(en-us) error: %v", err) + } + if got := messages["greeting"].Text; got != "hello" { + t.Fatalf("Load(en-us) greeting = %q, want %q", got, "hello") + } +} + func TestFlattenWithGrammar(t *testing.T) { messages := make(map[string]Message) grammar := &GrammarData{