diff --git a/v2/i18n/bundle.go b/v2/i18n/bundle.go index 513c127d..ac1bd039 100644 --- a/v2/i18n/bundle.go +++ b/v2/i18n/bundle.go @@ -26,9 +26,58 @@ type Bundle struct { matcher language.Matcher } +// The matcher in x/text/language does not handle artificial languages, +// see https://github.com/golang/go/issues/45749 +// This is a simplified matcher that delegates to the x/text/language matcher for +// the harder cases. +type matcher struct { + tags []language.Tag + defaultMatcher language.Matcher +} + +func newMatcher(tags []language.Tag) language.Matcher { + var hasArt bool + for _, tag := range tags { + base, _ := tag.Base() + hasArt = base == artTagBase + if hasArt { + break + } + } + + if !hasArt { + return language.NewMatcher(tags) + } + + return matcher{ + tags: tags, + defaultMatcher: language.NewMatcher(tags), + } +} + +func (m matcher) Match(t ...language.Tag) (language.Tag, int, language.Confidence) { + for _, candidate := range t { + base, _ := candidate.Base() + if base != artTagBase { + continue + } + + for i, tag := range m.tags { + if tag == candidate { + return candidate, i, language.Exact + } + } + } + + return m.defaultMatcher.Match(t...) +} + // artTag is the language tag used for artificial languages // https://en.wikipedia.org/wiki/Codes_for_constructed_languages -var artTag = language.MustParse("art") +var ( + artTag = language.MustParse("art") + artTagBase, _ = artTag.Base() +) // NewBundle returns a bundle with a default language and a default set of plural rules. func NewBundle(defaultLanguage language.Tag) *Bundle { @@ -126,7 +175,7 @@ func (b *Bundle) addTag(tag language.Tag) { } } b.tags = append(b.tags, tag) - b.matcher = language.NewMatcher(b.tags) + b.matcher = newMatcher(b.tags) } // LanguageTags returns the list of language tags diff --git a/v2/i18n/bundle_test.go b/v2/i18n/bundle_test.go index 12377899..e4f9280c 100644 --- a/v2/i18n/bundle_test.go +++ b/v2/i18n/bundle_test.go @@ -98,6 +98,31 @@ hello = "`+expected+`" } } +func TestPseudoLanguages(t *testing.T) { + bundle := NewBundle(language.English) + bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) + expected := "a2" + bundle.MustParseMessageFileBytes([]byte(` +hello = "a1" +`), "art-x-a1.toml") + bundle.MustParseMessageFileBytes([]byte(` +hello = "a2" +`), "art-x-a2.toml") + bundle.MustParseMessageFileBytes([]byte(` +hello = "a3" +`), "art-x-a3.toml") + + { + localized, err := NewLocalizer(bundle, "art-x-a2").Localize(&LocalizeConfig{MessageID: "hello"}) + if err != nil { + t.Fatal(err) + } + if localized != expected { + t.Fatalf("expected %q\ngot %q", expected, localized) + } + } +} + func TestJSON(t *testing.T) { bundle := NewBundle(language.English) bundle.MustParseMessageFileBytes([]byte(`{