diff options
Diffstat (limited to 'vendor/github.com/nicksnyder/go-i18n/i18n')
6 files changed, 188 insertions, 15 deletions
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go index 155543bda..2ad1d7cc3 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go @@ -2,6 +2,7 @@ package bundle import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -11,6 +12,7 @@ import ( "github.com/nicksnyder/go-i18n/i18n/language" "github.com/nicksnyder/go-i18n/i18n/translation" + toml "github.com/pelletier/go-toml" "gopkg.in/yaml.v2" ) @@ -78,34 +80,103 @@ func (b *Bundle) ParseTranslationFileBytes(filename string, buf []byte) error { } func parseTranslations(filename string, buf []byte) ([]translation.Translation, error) { - var unmarshalFunc func([]byte, interface{}) error - switch format := filepath.Ext(filename); format { - case ".json": - unmarshalFunc = json.Unmarshal - case ".yaml": - unmarshalFunc = yaml.Unmarshal - default: - return nil, fmt.Errorf("unsupported file extension %s", format) + if len(buf) == 0 { + return []translation.Translation{}, nil } - var translationsData []map[string]interface{} - if len(buf) > 0 { - if err := unmarshalFunc(buf, &translationsData); err != nil { - return nil, fmt.Errorf("failed to load %s because %s", filename, err) + ext := filepath.Ext(filename) + + // `github.com/pelletier/go-toml` has an Unmarshal function, + // that can't unmarshal to maps, so we should parse TOML format separately. + if ext == ".toml" { + tree, err := toml.LoadReader(bytes.NewReader(buf)) + if err != nil { + return nil, err } + + m := make(map[string]map[string]interface{}) + for k, v := range tree.ToMap() { + m[k] = v.(map[string]interface{}) + } + + return parseFlatFormat(m) } - translations := make([]translation.Translation, 0, len(translationsData)) - for i, translationData := range translationsData { + // Then parse other formats. + if isStandardFormat(ext, buf) { + var standardFormat []map[string]interface{} + if err := unmarshal(ext, buf, &standardFormat); err != nil { + return nil, fmt.Errorf("failed to unmarshal %v: %v", filename, err) + } + return parseStandardFormat(standardFormat) + } else { + var flatFormat map[string]map[string]interface{} + if err := unmarshal(ext, buf, &flatFormat); err != nil { + return nil, fmt.Errorf("failed to unmarshal %v: %v", filename, err) + } + return parseFlatFormat(flatFormat) + } +} + +func isStandardFormat(ext string, buf []byte) bool { + firstRune := rune(buf[0]) + if (ext == ".json" && firstRune == '[') || (ext == ".yaml" && firstRune == '-') { + return true + } + return false +} + +// unmarshal finds an appropriate unmarshal function for ext +// (extension of filename) and unmarshals buf to out. out must be a pointer. +func unmarshal(ext string, buf []byte, out interface{}) error { + switch ext { + case ".json": + return json.Unmarshal(buf, out) + case ".yaml": + return yaml.Unmarshal(buf, out) + } + + return fmt.Errorf("unsupported file extension %v", ext) +} + +func parseStandardFormat(data []map[string]interface{}) ([]translation.Translation, error) { + translations := make([]translation.Translation, 0, len(data)) + for i, translationData := range data { t, err := translation.NewTranslation(translationData) if err != nil { - return nil, fmt.Errorf("unable to parse translation #%d in %s because %s\n%v", i, filename, err, translationData) + return nil, fmt.Errorf("unable to parse translation #%d because %s\n%v", i, err, translationData) } translations = append(translations, t) } return translations, nil } +// parseFlatFormat just converts data from flat format to standard format +// and passes it to parseStandardFormat. +// +// Flat format logic: +// key of data must be a string and data[key] must be always map[string]interface{}, +// but if there is only "other" key in it then it is non-plural, else plural. +func parseFlatFormat(data map[string]map[string]interface{}) ([]translation.Translation, error) { + var standardFormatData []map[string]interface{} + for id, translationData := range data { + dataObject := make(map[string]interface{}) + dataObject["id"] = id + if len(translationData) == 1 { // non-plural form + _, otherExists := translationData["other"] + if otherExists { + dataObject["translation"] = translationData["other"] + } + } else { // plural form + dataObject["translation"] = translationData + } + + standardFormatData = append(standardFormatData, dataObject) + } + + return parseStandardFormat(standardFormatData) +} + // AddTranslation adds translations for a language. // // It is useful if your translations are in a format not supported by LoadTranslationFile. diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go index 4f579d16a..5dd74b2f5 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go @@ -16,6 +16,10 @@ func (pt *pluralTranslation) MarshalInterface() interface{} { } } +func (pt *pluralTranslation) MarshalFlatInterface() interface{} { + return pt.templates +} + func (pt *pluralTranslation) ID() string { return pt.id } diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go index 1010e5947..9fcba5a18 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go @@ -16,6 +16,10 @@ func (st *singleTranslation) MarshalInterface() interface{} { } } +func (st *singleTranslation) MarshalFlatInterface() interface{} { + return map[string]interface{}{"other": st.template} +} + func (st *singleTranslation) ID() string { return st.id } diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go index c8756fa4e..3310150c0 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go @@ -13,6 +13,10 @@ type template struct { } func newTemplate(src string) (*template, error) { + if src == "" { + return new(template), nil + } + var tmpl template err := tmpl.parseTemplate(src) return &tmpl, err diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go index fa93180b8..197514623 100644 --- a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go @@ -12,6 +12,7 @@ type Translation interface { // MarshalInterface returns the object that should be used // to serialize the translation. MarshalInterface() interface{} + MarshalFlatInterface() interface{} ID() string Template(language.Plural) *template UntranslatedCopy() Translation diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translations_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translations_test.go new file mode 100644 index 000000000..86c580833 --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translations_test.go @@ -0,0 +1,89 @@ +package i18n + +import ( + "testing" + + "github.com/nicksnyder/go-i18n/i18n/bundle" +) + +var bobMap = map[string]interface{}{"Person": "Bob"} +var bobStruct = struct{ Person string }{Person: "Bob"} + +var testCases = []struct { + id string + arg interface{} + want string +}{ + {"program_greeting", nil, "Hello world"}, + {"person_greeting", bobMap, "Hello Bob"}, + {"person_greeting", bobStruct, "Hello Bob"}, + + {"your_unread_email_count", 0, "You have 0 unread emails."}, + {"your_unread_email_count", 1, "You have 1 unread email."}, + {"your_unread_email_count", 2, "You have 2 unread emails."}, + {"my_height_in_meters", "1.7", "I am 1.7 meters tall."}, + + {"person_unread_email_count", []interface{}{0, bobMap}, "Bob has 0 unread emails."}, + {"person_unread_email_count", []interface{}{1, bobMap}, "Bob has 1 unread email."}, + {"person_unread_email_count", []interface{}{2, bobMap}, "Bob has 2 unread emails."}, + {"person_unread_email_count", []interface{}{0, bobStruct}, "Bob has 0 unread emails."}, + {"person_unread_email_count", []interface{}{1, bobStruct}, "Bob has 1 unread email."}, + {"person_unread_email_count", []interface{}{2, bobStruct}, "Bob has 2 unread emails."}, + + {"person_unread_email_count_timeframe", []interface{}{3, map[string]interface{}{ + "Person": "Bob", + "Timeframe": "0 days", + }}, "Bob has 3 unread emails in the past 0 days."}, + {"person_unread_email_count_timeframe", []interface{}{3, map[string]interface{}{ + "Person": "Bob", + "Timeframe": "1 day", + }}, "Bob has 3 unread emails in the past 1 day."}, + {"person_unread_email_count_timeframe", []interface{}{3, map[string]interface{}{ + "Person": "Bob", + "Timeframe": "2 days", + }}, "Bob has 3 unread emails in the past 2 days."}, +} + +func testFile(t *testing.T, path string) { + b := bundle.New() + b.MustLoadTranslationFile(path) + + T, err := b.Tfunc("en-US") + if err != nil { + t.Fatal(err) + } + + for _, tc := range testCases { + var args []interface{} + if _, ok := tc.arg.([]interface{}); ok { + args = tc.arg.([]interface{}) + } else { + args = []interface{}{tc.arg} + } + + got := T(tc.id, args...) + if got != tc.want { + t.Error("got: %v; want: %v", got, tc.want) + } + } +} + +func TestJSONParse(t *testing.T) { + testFile(t, "../goi18n/testdata/expected/en-us.all.json") +} + +func TestYAMLParse(t *testing.T) { + testFile(t, "../goi18n/testdata/en-us.yaml") +} + +func TestJSONFlatParse(t *testing.T) { + testFile(t, "../goi18n/testdata/en-us.flat.json") +} + +func TestYAMLFlatParse(t *testing.T) { + testFile(t, "../goi18n/testdata/en-us.flat.yaml") +} + +func TestTOMLFlatParse(t *testing.T) { + testFile(t, "../goi18n/testdata/en-us.flat.toml") +} |