diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2018-08-16 21:48:09 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2018-08-16 21:48:09 +1000 |
commit | 563efc41c749af43b4aba5286fd5b5d2e4a51c7a (patch) | |
tree | 6aba61b5e5083b93583d4cc90d9eb203d514c3b3 /vendor/github.com/nicksnyder | |
parent | 090537a1f175f4320885486199752dc9508faca4 (diff) |
bump dependencies for i18n
Diffstat (limited to 'vendor/github.com/nicksnyder')
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 |