summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/nicksnyder
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2018-08-16 21:48:09 +1000
committerJesse Duffield <jessedduffield@gmail.com>2018-08-16 21:48:09 +1000
commit563efc41c749af43b4aba5286fd5b5d2e4a51c7a (patch)
tree6aba61b5e5083b93583d4cc90d9eb203d514c3b3 /vendor/github.com/nicksnyder
parent090537a1f175f4320885486199752dc9508faca4 (diff)
bump dependencies for i18n
Diffstat (limited to 'vendor/github.com/nicksnyder')
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/LICENSE19
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go129
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go21
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go198
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go6
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/message.go164
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/message_template.go55
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/parse.go112
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go3
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go16
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go120
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule.go44
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule_gen.go561
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rules.go24
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/v2/internal/template.go26
15 files changed, 1498 insertions, 0 deletions
diff --git a/vendor/github.com/nicksnyder/go-i18n/LICENSE b/vendor/github.com/nicksnyder/go-i18n/LICENSE
new file mode 100644
index 000000000..609cce797
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go
new file mode 100644
index 000000000..2a8faaf23
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go
@@ -0,0 +1,129 @@
+package i18n
+
+import (
+ "fmt"
+ "io/ioutil"
+
+ "github.com/nicksnyder/go-i18n/v2/internal"
+ "github.com/nicksnyder/go-i18n/v2/internal/plural"
+
+ "golang.org/x/text/language"
+)
+
+// UnmarshalFunc unmarshals data into v.
+type UnmarshalFunc = internal.UnmarshalFunc
+
+// Bundle stores a set of messages and pluralization rules.
+// Most applications only need a single bundle
+// that is initialized early in the application's lifecycle.
+type Bundle struct {
+ // DefaultLanguage is the default language of the bundle.
+ DefaultLanguage language.Tag
+
+ // UnmarshalFuncs is a map of file extensions to UnmarshalFuncs.
+ UnmarshalFuncs map[string]UnmarshalFunc
+
+ messageTemplates map[language.Tag]map[string]*internal.MessageTemplate
+ pluralRules plural.Rules
+ tags []language.Tag
+ matcher language.Matcher
+}
+
+func (b *Bundle) init() {
+ if b.pluralRules == nil {
+ b.pluralRules = plural.DefaultRules()
+ }
+ b.addTag(b.DefaultLanguage)
+}
+
+// RegisterUnmarshalFunc registers an UnmarshalFunc for format.
+func (b *Bundle) RegisterUnmarshalFunc(format string, unmarshalFunc UnmarshalFunc) {
+ if b.UnmarshalFuncs == nil {
+ b.UnmarshalFuncs = make(map[string]UnmarshalFunc)
+ }
+ b.UnmarshalFuncs[format] = unmarshalFunc
+}
+
+// LoadMessageFile loads the bytes from path
+// and then calls ParseMessageFileBytes.
+func (b *Bundle) LoadMessageFile(path string) (*MessageFile, error) {
+ buf, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ return b.ParseMessageFileBytes(buf, path)
+}
+
+// MustLoadMessageFile is similar to LoadTranslationFile
+// except it panics if an error happens.
+func (b *Bundle) MustLoadMessageFile(path string) {
+ if _, err := b.LoadMessageFile(path); err != nil {
+ panic(err)
+ }
+}
+
+// MessageFile represents a parsed message file.
+type MessageFile = internal.MessageFile
+
+// ParseMessageFileBytes parses the bytes in buf to add translations to the bundle.
+//
+// The format of the file is everything after the last ".".
+//
+// The language tag of the file is everything after the second to last "." or after the last path separator, but before the format.
+func (b *Bundle) ParseMessageFileBytes(buf []byte, path string) (*MessageFile, error) {
+ messageFile, err := internal.ParseMessageFileBytes(buf, path, b.UnmarshalFuncs)
+ if err != nil {
+ return nil, err
+ }
+ if err := b.AddMessages(messageFile.Tag, messageFile.Messages...); err != nil {
+ return nil, err
+ }
+ return messageFile, nil
+}
+
+// MustParseMessageFileBytes is similar to ParseMessageFileBytes
+// except it panics if an error happens.
+func (b *Bundle) MustParseMessageFileBytes(buf []byte, path string) {
+ if _, err := b.ParseMessageFileBytes(buf, path); err != nil {
+ panic(err)
+ }
+}
+
+// AddMessages adds messages for a language.
+// It is useful if your messages are in a format not supported by ParseMessageFileBytes.
+func (b *Bundle) AddMessages(tag language.Tag, messages ...*Message) error {
+ b.init()
+ pluralRule := b.pluralRules.Rule(tag)
+ if pluralRule == nil {
+ return fmt.Errorf("no plural rule registered for %s", tag)
+ }
+ if b.messageTemplates == nil {
+ b.messageTemplates = map[language.Tag]map[string]*internal.MessageTemplate{}
+ }
+ if b.messageTemplates[tag] == nil {
+ b.messageTemplates[tag] = map[string]*internal.MessageTemplate{}
+ b.addTag(tag)
+ }
+ for _, m := range messages {
+ b.messageTemplates[tag][m.ID] = internal.NewMessageTemplate(m)
+ }
+ return nil
+}
+
+// MustAddMessages is similar to AddMessages except it panics if an error happens.
+func (b *Bundle) MustAddMessages(tag language.Tag, messages ...*Message) {
+ if err := b.AddMessages(tag, messages...); err != nil {
+ panic(err)
+ }
+}
+
+func (b *Bundle) addTag(tag language.Tag) {
+ for _, t := range b.tags {
+ if t == tag {
+ // Tag already exists
+ return
+ }
+ }
+ b.tags = append(b.tags, tag)
+ b.matcher = language.NewMatcher(b.tags)
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go
new file mode 100644
index 000000000..7b56a7172
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go
@@ -0,0 +1,21 @@
+// Package i18n provides support for looking up messages
+// according to a set of locale preferences.
+//
+// Create a Bundle to use for the lifetime of your application.
+// bundle := &i18n.Bundle{DefaultLanguage: language.English}
+//
+// Create a Localizer to use for a set of language preferences.
+// func(w http.ResponseWriter, r *http.Request) {
+// lang := r.FormValue("lang")
+// accept := r.Header.Get("Accept-Language")
+// localizer := i18n.NewLocalizer(bundle, lang, accept)
+// }
+//
+// Use the Localizer to lookup messages.
+// localizer.MustLocalize(&i18n.LocalizeConfig{
+// DefaultMessage: &i18n.Message{
+// ID: "HelloWorld",
+// Other: "Hello World!",
+// },
+// })
+package i18n
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go
new file mode 100644
index 000000000..f9d516fa6
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go
@@ -0,0 +1,198 @@
+package i18n
+
+import (
+ "fmt"
+
+ "text/template"
+
+ "github.com/nicksnyder/go-i18n/v2/internal"
+ "github.com/nicksnyder/go-i18n/v2/internal/plural"
+ "golang.org/x/text/language"
+)
+
+// Localizer provides Localize and MustLocalize methods that return localized messages.
+type Localizer struct {
+ // bundle contains the messages that can be returned by the Localizer.
+ bundle *Bundle
+
+ // tags is the list of language tags that the Localizer checks
+ // in order when localizing a message.
+ tags []language.Tag
+}
+
+// NewLocalizer returns a new Localizer that looks up messages
+// in the bundle according to the language preferences in langs.
+// It can parse Accept-Language headers as defined in http://www.ietf.org/rfc/rfc2616.txt.
+func NewLocalizer(bundle *Bundle, langs ...string) *Localizer {
+ bundle.init()
+ return &Localizer{
+ bundle: bundle,
+ tags: parseTags(langs),
+ }
+}
+
+func parseTags(langs []string) []language.Tag {
+ tags := []language.Tag{}
+ for _, lang := range langs {
+ t, _, err := language.ParseAcceptLanguage(lang)
+ if err != nil {
+ continue
+ }
+ tags = append(tags, t...)
+ }
+ return tags
+}
+
+// LocalizeConfig configures a call to the Localize method on Localizer.
+type LocalizeConfig struct {
+ // MessageID is the id of the message to lookup.
+ // This field is ignored if DefaultMessage is set.
+ MessageID string
+
+ // TemplateData is the data passed when executing the message's template.
+ // If TemplateData is nil and PluralCount is not nil, then the message template
+ // will be executed with data that contains the plural count.
+ TemplateData interface{}
+
+ // PluralCount determines which plural form of the message is used.
+ PluralCount interface{}
+
+ // DefaultMessage is used if the message is not found in any message files.
+ DefaultMessage *Message
+
+ // Funcs is used to extend the Go template engines built in functions
+ Funcs template.FuncMap
+}
+
+type invalidPluralCountErr struct {
+ messageID string
+ pluralCount interface{}
+ err error
+}
+
+func (e *invalidPluralCountErr) Error() string {
+ return fmt.Sprintf("invalid plural count %#v for message id %q: %s", e.pluralCount, e.messageID, e.err)
+}
+
+type messageNotFoundErr struct {
+ messageID string
+}
+
+func (e *messageNotFoundErr) Error() string {
+ return fmt.Sprintf("message %q not found", e.messageID)
+}
+
+type pluralizeErr struct {
+ messageID string
+ tag language.Tag
+}
+
+func (e *pluralizeErr) Error() string {
+ return fmt.Sprintf("unable to pluralize %q because there no plural rule for %q", e.messageID, e.tag)
+}
+
+// Localize returns a localized message.
+func (l *Localizer) Localize(lc *LocalizeConfig) (string, error) {
+ messageID := lc.MessageID
+ if lc.DefaultMessage != nil {
+ messageID = lc.DefaultMessage.ID
+ }
+
+ var operands *plural.Operands
+ templateData := lc.TemplateData
+ if lc.PluralCount != nil {
+ var err error
+ operands, err = plural.NewOperands(lc.PluralCount)
+ if err != nil {
+ return "", &invalidPluralCountErr{messageID: messageID, pluralCount: lc.PluralCount, err: err}
+ }
+ if templateData == nil {
+ templateData = map[string]interface{}{
+ "PluralCount": lc.PluralCount,
+ }
+ }
+ }
+ tag, template := l.getTemplate(messageID, lc.DefaultMessage)
+ if template == nil {
+ return "", &messageNotFoundErr{messageID: messageID}
+ }
+ pluralForm := l.pluralForm(tag, operands)
+ if pluralForm == plural.Invalid {
+ return "", &pluralizeErr{messageID: messageID, tag: tag}
+ }
+ return template.Execute(pluralForm, templateData, lc.Funcs)
+}
+
+func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *internal.MessageTemplate) {
+ // Fast path.
+ // Optimistically assume this message id is defined in each language.
+ fastTag, template := l.matchTemplate(id, l.bundle.matcher, l.bundle.tags)
+ if template != nil {
+ return fastTag, template
+ }
+ if fastTag == l.bundle.DefaultLanguage {
+ if defaultMessage == nil {
+ return fastTag, nil
+ }
+ return fastTag, internal.NewMessageTemplate(defaultMessage)
+ }
+ if len(l.bundle.tags) > 1 {
+ // 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))
+ if l.bundle.DefaultLanguage != fastTag {
+ foundTags = append(foundTags, l.bundle.DefaultLanguage)
+ }
+ for t, templates := range l.bundle.messageTemplates {
+ if t == fastTag {
+ // We already tried this tag in the fast path
+ continue
+ }
+ template := templates[id]
+ if template == nil || template.Other == "" {
+ continue
+ }
+ foundTags = append(foundTags, t)
+ }
+ tag, template := l.matchTemplate(id, language.NewMatcher(foundTags), foundTags)
+ if template != nil {
+ return tag, template
+ }
+ }
+ if defaultMessage == nil {
+ return l.bundle.DefaultLanguage, nil
+ }
+ return l.bundle.DefaultLanguage, internal.NewMessageTemplate(defaultMessage)
+}
+
+func (l *Localizer) matchTemplate(id string, matcher language.Matcher, tags []language.Tag) (language.Tag, *internal.MessageTemplate) {
+ _, i, _ := matcher.Match(l.tags...)
+ tag := tags[i]
+ templates := l.bundle.messageTemplates[tag]
+ if templates != nil && templates[id] != nil {
+ return tag, templates[id]
+ }
+ return tag, nil
+}
+
+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)
+}
+
+// MustLocalize is similar to Localize, except it panics if an error happens.
+func (l *Localizer) MustLocalize(lc *LocalizeConfig) string {
+ localized, err := l.Localize(lc)
+ if err != nil {
+ panic(err)
+ }
+ return localized
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go
new file mode 100644
index 000000000..7c881a6b4
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go
@@ -0,0 +1,6 @@
+package i18n
+
+import "github.com/nicksnyder/go-i18n/v2/internal"
+
+// Message is a string that can be localized.
+type Message = internal.Message
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/internal/message.go b/vendor/github.com/nicksnyder/go-i18n/v2/internal/message.go
new file mode 100644
index 000000000..592ec363e
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/internal/message.go
@@ -0,0 +1,164 @@
+package internal
+
+import (
+ "fmt"
+ "strings"
+)
+
+// Message is a string that can be localized.
+type Message struct {
+ // ID uniquely identifies the message.
+ ID string
+
+ // Hash uniquely identifies the content of the message
+ // that this message was translated from.
+ Hash string
+
+ // Description describes the message to give additional
+ // context to translators that may be relevant for translation.
+ Description string
+
+ // LeftDelim is the left Go template delimiter.
+ LeftDelim string
+
+ // RightDelim is the right Go template delimiter.``
+ RightDelim string
+
+ // Zero is the content of the message for the CLDR plural form "zero".
+ Zero string
+
+ // One is the content of the message for the CLDR plural form "one".
+ One string
+
+ // Two is the content of the message for the CLDR plural form "two".
+ Two string
+
+ // Few is the content of the message for the CLDR plural form "few".
+ Few string
+
+ // Many is the content of the message for the CLDR plural form "many".
+ Many string
+
+ // Other is the content of the message for the CLDR plural form "other".
+ Other string
+}
+
+// NewMessage parses data and returns a new message.
+func NewMessage(data interface{}) (*Message, error) {
+ m := &Message{}
+ if err := m.unmarshalInterface(data); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// MustNewMessage is similar to NewMessage except it panics if an error happens.
+func MustNewMessage(data interface{}) *Message {
+ m, err := NewMessage(data)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
+// unmarshalInterface unmarshals a message from data.
+func (m *Message) unmarshalInterface(v interface{}) error {
+ strdata, err := stringMap(v)
+ if err != nil {
+ return err
+ }
+ for k, v := range strdata {
+ switch strings.ToLower(k) {
+ case "id":
+ m.ID = v
+ case "description":
+ m.Description = v
+ case "hash":
+ m.Hash = v
+ case "leftDelim":
+ m.LeftDelim = v
+ case "rightDelim":
+ m.RightDelim = v
+ case "zero":
+ m.Zero = v
+ case "one":
+ m.One = v
+ case "two":
+ m.Two = v
+ case "few":
+ m.Few = v
+ case "many":
+ m.Many = v
+ case "other":
+ m.Other = v
+ }
+ }
+ return nil
+}
+
+func stringMap(v interface{}) (map[string]string, error) {
+ switch value := v.(type) {
+ case string:
+ return map[string]string{
+ "other": value,
+ }, nil
+ case map[string]string:
+ return value, nil
+ case map[string]interface{}:
+ strdata := map[string]string{}
+ for k, v := range value {
+ if k == "translation" {
+ switch vt := v.(type) {
+ case string:
+ strdata["other"] = vt
+ default:
+ v1Message, err := stringMap(v)
+ if err != nil {
+ return nil, err
+ }
+ for kk, vv := range v1Message {
+ strdata[kk] = vv
+ }
+ }
+ continue
+ }
+ vstr, ok := v.(string)
+ if !ok {
+ return nil, fmt.Errorf("expected value for key %q be a string but got %#v", k, v)
+ }
+ strdata[k] = vstr
+ }
+ return strdata, nil
+ case map[interface{}]interface{}:
+ strdata := map[string]string{}
+ for k, v := range value {
+ kstr, ok := k.(string)
+ if !ok {
+ return nil, fmt.Errorf("expected key to be a string but got %#v", k)
+ }
+ if kstr == "translation" {
+ switch vt := v.(type) {
+ case string:
+ strdata["other"] = vt
+ default:
+ v1Message, err := stringMap(v)
+ if err != nil {
+ return nil, err
+ }
+ for kk, vv := range v1Message {
+ strdata[kk] = vv
+ }
+ }
+ continue
+ }
+ vstr, ok := v.(string)
+ if !ok {
+ return nil, fmt.Errorf("expected value for key %q be a string but got %#v", k, v)
+ }
+ strdata[kstr] = vstr
+ }
+ return strdata, nil
+ default:
+ return nil, fmt.Errorf("unsupported type %#v", value)
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/internal/message_template.go b/vendor/github.com/nicksnyder/go-i18n/v2/internal/message_template.go
new file mode 100644
index 000000000..03aed6f39
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/internal/message_template.go
@@ -0,0 +1,55 @@
+package internal
+
+import (
+ "bytes"
+
+ "text/template"
+
+ "github.com/nicksnyder/go-i18n/v2/internal/plural"
+)
+
+// MessageTemplate is an executable template for a message.
+type MessageTemplate struct {
+ *Message
+ PluralTemplates map[plural.Form]*Template
+}
+
+// NewMessageTemplate returns a new message template.
+func NewMessageTemplate(m *Message) *MessageTemplate {
+ pluralTemplates := map[plural.Form]*Template{}
+ setPluralTemplate(pluralTemplates, plural.Zero, m.Zero)
+ setPluralTemplate(pluralTemplates, plural.One, m.One)
+ setPluralTemplate(pluralTemplates, plural.Two, m.Two)
+ setPluralTemplate(pluralTemplates, plural.Few, m.Few)
+ setPluralTemplate(pluralTemplates, plural.Many, m.Many)
+ setPluralTemplate(pluralTemplates, plural.Other, m.Other)
+ if len(pluralTemplates) == 0 {
+ return nil
+ }
+ return &MessageTemplate{
+ Message: m,
+ PluralTemplates: pluralTemplates,
+ }
+}
+
+func setPluralTemplate(pluralTemplates map[plural.Form]*Template, pluralForm plural.Form, src string) {
+ if src != "" {
+ pluralTemplates[pluralForm] = &Template{Src: src}
+ }
+}
+
+// Execute executes the template for the plural form and template data.
+func (mt *MessageTemplate) Execute(pluralForm plural.Form, data interface{}, funcs template.FuncMap) (string, error) {
+ t := mt.PluralTemplates[pluralForm]
+ if err := t.parse(mt.LeftDelim, mt.RightDelim, funcs); err != nil {
+ return "", err
+ }
+ if t.Template == nil {
+ return t.Src, nil
+ }
+ var buf bytes.Buffer
+ if err := t.Template.Execute(&buf, data); err != nil {
+ return "", err
+ }
+ return buf.String(), nil
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/internal/parse.go b/vendor/github.com/nicksnyder/go-i18n/v2/internal/parse.go
new file mode 100644
index 000000000..a5cee87c6
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/internal/parse.go
@@ -0,0 +1,112 @@
+package internal
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+
+ "golang.org/x/text/language"
+)
+
+// UnmarshalFunc unmarshals data into v.
+type UnmarshalFunc func(data []byte, v interface{}) error
+
+// MessageFile represents a parsed message file.
+type MessageFile struct {
+ Path string
+ Tag language.Tag
+ Format string
+ Messages []*Message
+}
+
+// ParseMessageFileBytes returns the messages parsed from file.
+func ParseMessageFileBytes(buf []byte, path string, unmarshalFuncs map[string]UnmarshalFunc) (*MessageFile, error) {
+ lang, format := parsePath(path)
+ tag := language.Make(lang)
+ messageFile := &MessageFile{
+ Path: path,
+ Tag: tag,
+ Format: format,
+ }
+ if len(buf) == 0 {
+ return messageFile, nil
+ }
+ unmarshalFunc := unmarshalFuncs[messageFile.Format]
+ if unmarshalFunc == nil {
+ if messageFile.Format == "json" {
+ unmarshalFunc = json.Unmarshal
+ } else {
+ return nil, fmt.Errorf("no unmarshaler registered for %s", messageFile.Format)
+ }
+ }
+ var raw interface{}
+ if err := unmarshalFunc(buf, &raw); err != nil {
+ return nil, err
+ }
+ switch data := raw.(type) {
+ case map[string]interface{}:
+ messageFile.Messages = make([]*Message, 0, len(data))
+ for id, data := range data {
+ m, err := NewMessage(data)
+ if err != nil {
+ return nil, err
+ }
+ m.ID = id
+ messageFile.Messages = append(messageFile.Messages, m)
+ }
+ case map[interface{}]interface{}:
+ messageFile.Messages = make([]*Message, 0, len(data))
+ for id, data := range data {
+ strid, ok := id.(string)
+ if !ok {
+ return nil, fmt.Errorf("expected key to be string but got %#v", id)
+ }
+ m, err := NewMessage(data)
+ if err != nil {
+ return nil, err
+ }
+ m.ID = strid
+ messageFile.Messages = append(messageFile.Messages, m)
+ }
+ case []interface{}:
+ // Backward compatibility for v1 file format.
+ messageFile.Messages = make([]*Message, 0, len(data))
+ for _, data := range data {
+ m, err := NewMessage(data)
+ if err != nil {
+ return nil, err
+ }
+ messageFile.Messages = append(messageFile.Messages, m)
+ }
+ default:
+ return nil, fmt.Errorf("unsupported file format %T", raw)
+ }
+ return messageFile, nil
+}
+
+func parsePath(path string) (langTag, format string) {
+ formatStartIdx := -1
+ for i := len(path) - 1; i >= 0; i-- {
+ c := path[i]
+ if os.IsPathSeparator(c) {
+ if formatStartIdx != -1 {
+ langTag = path[i+1 : formatStartIdx]
+ }
+ return
+ }
+ if path[i] == '.' {
+ if formatStartIdx != -1 {
+ langTag = path[i+1 : formatStartIdx]
+ return
+ }
+ if formatStartIdx == -1 {
+ format = path[i+1:]
+ formatStartIdx = i
+ }
+ }
+ }
+ if formatStartIdx != -1 {
+ langTag = path[:formatStartIdx]
+ }
+ return
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go b/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go
new file mode 100644
index 000000000..c2a71d53e
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go
@@ -0,0 +1,3 @@
+// Package plural provides support for pluralizing messages
+// according to CLDR rules http://cldr.unicode.org/index/cldr-spec/plural-rules
+package plural
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go b/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go
new file mode 100644
index 000000000..287a87f22
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go
@@ -0,0 +1,16 @@
+package plural
+
+// Form represents a language pluralization form as defined here:
+// http://cldr.unicode.org/index/cldr-spec/plural-rules
+type Form string
+
+// All defined plural forms.
+const (
+ Invalid Form = ""
+ Zero Form = "zero"
+ One Form = "one"
+ Two Form = "two"
+ Few Form = "few"
+ Many Form = "many"
+ Other Form = "other"
+)
diff --git a/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go b/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go
new file mode 100644
index 000000000..4d2972659
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go
@@ -0,0 +1,120 @@
+package plural
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// Operands is a representation of http://unicode.org/reports/tr35/tr35-numbers.html#Operands
+type Operands struct {
+ N float64 // absolute value of the source number (integer and decimals)
+ I int64 // integer digits of n
+ V int64 // number of visible fraction digits in n, with trailing zeros
+ W int64 // number of visible fraction digits in n, without trailing zeros
+ F int64 // visible fractional digits in n, with trailing zeros
+ T int64 // visible fractional digits in n, without trailing zeros
+}
+
+// NEqualsAny returns true if o represents an integer equal to any of the arguments.
+func (o *Operands) NEqualsAny(any ...int64) bool {
+ for _, i := range any {
+ if o.I == i && o.T == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// NModEqualsAny returns true if o represents an integer equal to any of the arguments modulo mod.
+func (o *Operands) NModEqualsAny(mod int64, any ...int64) bool {
+ modI := o.I % mod
+ for _, i := range any {
+ if modI == i && o.T == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// NInRange returns true if o represents an integer in the closed interval [from, to].
+func (o *Operands) NInRange(from, to int64) bool {
+ return o.T == 0 && from <= o.I && o.I <= to
+}
+
+// NModInRange returns true if o represents an integer in the closed interval [from, to] modulo mod.
+func (o *Operands) NModInRange(mod, from, to int64) bool {
+ modI := o.I % mod
+ return o.T == 0 && from <= modI && modI <= to
+}
+
+// NewOperands returns the operands for number.
+func NewOperands(number interface{}) (*Operands, error) {
+ switch number := number.(type) {
+ case int:
+ return newOperandsInt64(int64(number)), nil
+ case int8:
+ return newOperandsInt64(int64(number)), nil
+ case int16:
+ return newOperandsInt64(int64(number)), nil
+ case int32:
+ return newOperandsInt64(int64(nu