Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/go-i18n.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Snyder <nickdsnyder@gmail.com>2020-09-29 02:06:02 +0300
committerGitHub <noreply@github.com>2020-09-29 02:06:02 +0300
commit66dee750b54fda381116a1c93336f24f67b2613f (patch)
tree0d8dc794a47bfab95c658ff60cfa526e20c3790a
parent663c0888ab79a5a2274b55f596ebefb07b8366f4 (diff)
simpler fallback behavior (#189)
-rw-r--r--v2/go.mod6
-rw-r--r--v2/go.sum19
-rw-r--r--v2/i18n/bundle.go8
-rw-r--r--v2/i18n/localizer.go86
-rw-r--r--v2/i18n/localizer_test.go84
5 files changed, 109 insertions, 94 deletions
diff --git a/v2/go.mod b/v2/go.mod
index bd92c7c..e8cb75f 100644
--- a/v2/go.mod
+++ b/v2/go.mod
@@ -3,11 +3,7 @@ module github.com/nicksnyder/go-i18n/v2
go 1.9
require (
- github.com/BurntSushi/toml v0.3.0
- golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284 // indirect
- golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c // indirect
- golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b // indirect
+ github.com/BurntSushi/toml v0.3.1
golang.org/x/text v0.3.2
- golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c // indirect
gopkg.in/yaml.v2 v2.2.1
)
diff --git a/v2/go.sum b/v2/go.sum
index 96207c2..ce639e9 100644
--- a/v2/go.sum
+++ b/v2/go.sum
@@ -1,21 +1,10 @@
-github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
-github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38 h1:yr7ItWHARpqySNZjEh5mPMHrw3xPR9tMnomFZVcO1mQ=
-golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDaCRQc=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/v2/i18n/bundle.go b/v2/i18n/bundle.go
index 6c6f5ce..513c127 100644
--- a/v2/i18n/bundle.go
+++ b/v2/i18n/bundle.go
@@ -134,3 +134,11 @@ func (b *Bundle) addTag(tag language.Tag) {
func (b *Bundle) LanguageTags() []language.Tag {
return b.tags
}
+
+func (b *Bundle) getMessageTemplate(tag language.Tag, id string) *MessageTemplate {
+ templates := b.messageTemplates[tag]
+ if templates == nil {
+ return nil
+ }
+ return templates[id]
+}
diff --git a/v2/i18n/localizer.go b/v2/i18n/localizer.go
index 5d3b0df..17261e2 100644
--- a/v2/i18n/localizer.go
+++ b/v2/i18n/localizer.go
@@ -2,7 +2,6 @@ package i18n
import (
"fmt"
-
"text/template"
"github.com/nicksnyder/go-i18n/v2/internal/plural"
@@ -74,11 +73,12 @@ func (e *invalidPluralCountErr) Error() string {
// MessageNotFoundErr is returned from Localize when a message could not be found.
type MessageNotFoundErr struct {
+ tag language.Tag
messageID string
}
func (e *MessageNotFoundErr) Error() string {
- return fmt.Sprintf("message %q not found", e.messageID)
+ return fmt.Sprintf("message %q not found in language %q", e.messageID, e.tag)
}
type pluralizeErr struct {
@@ -146,82 +146,62 @@ func (l *Localizer) LocalizeWithTag(lc *LocalizeConfig) (string, language.Tag, e
}
}
- tag, template := l.getTemplate(messageID, lc.DefaultMessage)
+ tag, template, err := l.getMessageTemplate(messageID, lc.DefaultMessage)
if template == nil {
- return "", language.Und, &MessageNotFoundErr{messageID: messageID}
+ return "", language.Und, err
}
pluralForm := l.pluralForm(tag, operands)
- if pluralForm == plural.Invalid {
- return "", language.Und, &pluralizeErr{messageID: messageID, tag: tag}
- }
+ msg, err2 := template.Execute(pluralForm, templateData, lc.Funcs)
+ if err2 != nil {
+ if err == nil {
+ err = err2
+ }
- msg, err := template.Execute(pluralForm, templateData, lc.Funcs)
- if err != nil {
// Attempt to fallback to "Other" pluralization in case translations are incomplete.
if pluralForm != plural.Other {
- msg2, err2 := template.Execute(plural.Other, templateData, lc.Funcs)
- if err2 == nil {
- return msg2, tag, err
+ msg2, err3 := template.Execute(plural.Other, templateData, lc.Funcs)
+ if err3 == nil {
+ msg = msg2
}
}
- return "", language.Und, err
}
- return msg, tag, nil
+ return msg, tag, err
}
-func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *MessageTemplate) {
- // Fast path.
- // Optimistically assume this message id is defined in each language.
- fastTag, template := l.matchTemplate(id, defaultMessage, l.bundle.matcher, l.bundle.tags)
- if template != nil {
- return fastTag, template
+func (l *Localizer) getMessageTemplate(id string, defaultMessage *Message) (language.Tag, *MessageTemplate, error) {
+ _, i, _ := l.bundle.matcher.Match(l.tags...)
+ tag := l.bundle.tags[i]
+ mt := l.bundle.getMessageTemplate(tag, id)
+ if mt != nil {
+ return tag, mt, nil
}
- if len(l.bundle.tags) <= 1 {
- return l.bundle.defaultLanguage, nil
- }
-
- // Slow path.
- // We didn't find a translation for the tag suggested by the default matcher
- // so we need to create a new matcher that contains only the tags in the bundle
- // that have this message.
- foundTags := make([]language.Tag, 0, len(l.bundle.messageTemplates)+1)
- foundTags = append(foundTags, l.bundle.defaultLanguage)
-
- for t, templates := range l.bundle.messageTemplates {
- template := templates[id]
- if template == nil || template.Other == "" {
- continue
+ if tag == l.bundle.defaultLanguage {
+ if defaultMessage == nil {
+ return language.Und, nil, &MessageNotFoundErr{tag: tag, messageID: id}
}
- foundTags = append(foundTags, t)
+ return tag, NewMessageTemplate(defaultMessage), nil
}
- return l.matchTemplate(id, defaultMessage, language.NewMatcher(foundTags), foundTags)
-}
-
-func (l *Localizer) matchTemplate(id string, defaultMessage *Message, matcher language.Matcher, tags []language.Tag) (language.Tag, *MessageTemplate) {
- _, i, _ := matcher.Match(l.tags...)
- tag := tags[i]
- templates := l.bundle.messageTemplates[tag]
- if templates != nil && templates[id] != nil {
- return tag, templates[id]
+ // Fallback to default language in bundle.
+ mt = l.bundle.getMessageTemplate(l.bundle.defaultLanguage, id)
+ if mt != nil {
+ return l.bundle.defaultLanguage, mt, &MessageNotFoundErr{tag: tag, messageID: id}
}
- if tag == l.bundle.defaultLanguage && defaultMessage != nil {
- return tag, NewMessageTemplate(defaultMessage)
+
+ // Fallback to default message.
+ if defaultMessage == nil {
+ return language.Und, nil, &MessageNotFoundErr{tag: tag, messageID: id}
}
- return tag, nil
+ return l.bundle.defaultLanguage, NewMessageTemplate(defaultMessage), &MessageNotFoundErr{tag: tag, messageID: id}
}
func (l *Localizer) pluralForm(tag language.Tag, operands *plural.Operands) plural.Form {
if operands == nil {
return plural.Other
}
- pluralRule := l.bundle.pluralRules.Rule(tag)
- if pluralRule == nil {
- return plural.Invalid
- }
- return pluralRule.PluralFormFunc(operands)
+ return l.bundle.pluralRules.Rule(tag).PluralFormFunc(operands)
}
// MustLocalize is similar to Localize, except it panics if an error happens.
diff --git a/v2/i18n/localizer_test.go b/v2/i18n/localizer_test.go
index 4194914..3dedfb6 100644
--- a/v2/i18n/localizer_test.go
+++ b/v2/i18n/localizer_test.go
@@ -1,6 +1,7 @@
package i18n
import (
+ "fmt"
"reflect"
"testing"
@@ -52,28 +53,19 @@ func localizerTests() []localizerTest {
defaultLanguage: language.English,
acceptLangs: []string{"en"},
conf: &LocalizeConfig{MessageID: "HelloWorld"},
- expectedErr: &MessageNotFoundErr{messageID: "HelloWorld"},
+ expectedErr: &MessageNotFoundErr{tag: language.English, messageID: "HelloWorld"},
expectedLocalized: "",
},
{
name: "empty translation without fallback",
defaultLanguage: language.English,
messages: map[language.Tag][]*Message{
- language.Spanish: {{ID: "HelloWorld"}},
- },
- acceptLangs: []string{"es"},
- conf: &LocalizeConfig{MessageID: "HelloWorld"},
- expectedErr: &MessageNotFoundErr{messageID: "HelloWorld"},
- },
- {
- name: "empty translation with fallback",
- defaultLanguage: language.English,
- messages: map[language.Tag][]*Message{
language.English: {{ID: "HelloWorld", Other: "Hello World!"}},
language.Spanish: {{ID: "HelloWorld"}},
},
acceptLangs: []string{"es"},
conf: &LocalizeConfig{MessageID: "HelloWorld"},
+ expectedErr: &MessageNotFoundErr{tag: language.Spanish, messageID: "HelloWorld"},
expectedLocalized: "Hello World!",
},
{
@@ -84,26 +76,38 @@ func localizerTests() []localizerTest {
},
acceptLangs: []string{"en"},
conf: &LocalizeConfig{MessageID: "HelloWorld"},
- expectedErr: &MessageNotFoundErr{messageID: "HelloWorld"},
+ expectedErr: &MessageNotFoundErr{tag: language.English, messageID: "HelloWorld"},
expectedLocalized: "",
},
{
- name: "missing translation from not default language",
+ name: "missing translations from not default language",
defaultLanguage: language.English,
acceptLangs: []string{"es"},
conf: &LocalizeConfig{MessageID: "HelloWorld"},
- expectedErr: &MessageNotFoundErr{messageID: "HelloWorld"},
+ expectedErr: &MessageNotFoundErr{tag: language.English, messageID: "HelloWorld"},
+ expectedLocalized: "",
+ },
+ {
+ name: "missing translation from not default language",
+ defaultLanguage: language.English,
+ messages: map[language.Tag][]*Message{
+ language.Spanish: {{ID: "SomethingElse", Other: "other"}},
+ },
+ acceptLangs: []string{"es"},
+ conf: &LocalizeConfig{MessageID: "HelloWorld"},
+ expectedErr: &MessageNotFoundErr{tag: language.Spanish, messageID: "HelloWorld"},
expectedLocalized: "",
},
{
name: "missing translation not default language with other translation",
defaultLanguage: language.English,
messages: map[language.Tag][]*Message{
- language.French: {{ID: "HelloWorld", Other: "other"}},
+ language.French: {{ID: "HelloWorld", Other: "other"}},
+ language.Spanish: {{ID: "SomethingElse", Other: "other"}},
},
acceptLangs: []string{"es"},
conf: &LocalizeConfig{MessageID: "HelloWorld"},
- expectedErr: &MessageNotFoundErr{messageID: "HelloWorld"},
+ expectedErr: &MessageNotFoundErr{tag: language.Spanish, messageID: "HelloWorld"},
expectedLocalized: "",
},
{
@@ -513,7 +517,7 @@ func localizerTests() []localizerTest {
expectedLocalized: "Nick has 2.5 cats",
},
{
- name: "test slow path",
+ name: "no fallback",
defaultLanguage: language.Spanish,
messages: map[language.Tag][]*Message{
language.English: {{
@@ -529,10 +533,10 @@ func localizerTests() []localizerTest {
conf: &LocalizeConfig{
MessageID: "Hello",
},
- expectedLocalized: "Hello!",
+ expectedErr: &MessageNotFoundErr{tag: language.AmericanEnglish, messageID: "Hello"},
},
{
- name: "test slow path default message",
+ name: "fallback default message",
defaultLanguage: language.Spanish,
messages: map[language.Tag][]*Message{
language.English: {{
@@ -552,9 +556,10 @@ func localizerTests() []localizerTest {
},
},
expectedLocalized: "Hola!",
+ expectedErr: &MessageNotFoundErr{tag: language.AmericanEnglish, messageID: "Hello"},
},
{
- name: "test slow path no message",
+ name: "no fallback default message",
defaultLanguage: language.Spanish,
messages: map[language.Tag][]*Message{
language.English: {{
@@ -570,7 +575,7 @@ func localizerTests() []localizerTest {
conf: &LocalizeConfig{
MessageID: "Hello",
},
- expectedErr: &MessageNotFoundErr{messageID: "Hello"},
+ expectedErr: &MessageNotFoundErr{tag: language.AmericanEnglish, messageID: "Hello"},
},
}
}
@@ -625,3 +630,40 @@ func BenchmarkLocalizer_Localize(b *testing.B) {
})
}
}
+
+func TestMessageNotFoundError(t *testing.T) {
+ actual := (&MessageNotFoundErr{tag: language.AmericanEnglish, messageID: "hello"}).Error()
+ expected := `message "hello" not found in language "en-US"`
+ if actual != expected {
+ t.Fatalf("expected %q; got %q", expected, actual)
+ }
+}
+
+func TestMessageIDMismatchError(t *testing.T) {
+ actual := (&messageIDMismatchErr{messageID: "hello", defaultMessageID: "world"}).Error()
+ expected := `message id "hello" does not match default message id "world"`
+ if actual != expected {
+ t.Fatalf("expected %q; got %q", expected, actual)
+ }
+}
+
+func TestInvalidPluralCountError(t *testing.T) {
+ actual := (&invalidPluralCountErr{messageID: "hello", pluralCount: "blah", err: fmt.Errorf("error")}).Error()
+ expected := `invalid plural count "blah" for message id "hello": error`
+ if actual != expected {
+ t.Fatalf("expected %q; got %q", expected, actual)
+ }
+}
+
+func TestMustLocalize(t *testing.T) {
+ defer func() {
+ if recover() == nil {
+ t.Fatalf("MustLocalize did not panic")
+ }
+ }()
+ bundle := NewBundle(language.English)
+ localizer := NewLocalizer(bundle)
+ localizer.MustLocalize(&LocalizeConfig{
+ MessageID: "hello",
+ })
+}