summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/nicksnyder
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/nicksnyder')
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/.gitignore6
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/.travis.yml8
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/CHANGELOG5
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/LICENSE19
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/README.md130
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/doc.go59
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/gendoc.sh10
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/goi18n.go82
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/merge.go127
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/merge_test.go74
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/en-us.yaml30
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.all.json65
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.untranslated.json50
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.all.json45
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.untranslated.json1
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.all.json45
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.untranslated.json45
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.one.json54
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.two.json54
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.one.json30
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.two.json26
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/fr-fr.json0
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.one.json54
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.two.json54
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.one.yaml19
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.two.json26
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/fr-fr.json0
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go315
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle_test.go289
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/example_test.go63
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/exampletemplate_test.go63
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/exampleyaml_test.go62
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go152
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/generate.sh5
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/main.go132
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/plurals.xml230
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/xml.go143
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go99
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/language_test.go85
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go119
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/operands_test.go45
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go40
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/plural_test.go28
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go74
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go567
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen_test.go645
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go733
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go78
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go308
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go57
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go61
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go146
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go83
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go17
54 files changed, 5757 insertions, 0 deletions
diff --git a/vendor/github.com/nicksnyder/go-i18n/.gitignore b/vendor/github.com/nicksnyder/go-i18n/.gitignore
new file mode 100644
index 000000000..20e1aa8ff
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/.gitignore
@@ -0,0 +1,6 @@
+*.a
+_*
+output/
+.DS_Store
+*.test
+*.swp
diff --git a/vendor/github.com/nicksnyder/go-i18n/.travis.yml b/vendor/github.com/nicksnyder/go-i18n/.travis.yml
new file mode 100644
index 000000000..8558bb44f
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+sudo: false
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - tip
diff --git a/vendor/github.com/nicksnyder/go-i18n/CHANGELOG b/vendor/github.com/nicksnyder/go-i18n/CHANGELOG
new file mode 100644
index 000000000..c7949b143
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/CHANGELOG
@@ -0,0 +1,5 @@
+Feb 24, 2015
+- Add Korean
+
+Feb 18, 2015
+- Added ParseTranslationFileBytes so translation files may be loaded from an arbitrary serialization format, such as go-bindata.
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/README.md b/vendor/github.com/nicksnyder/go-i18n/README.md
new file mode 100644
index 000000000..5aef540b3
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/README.md
@@ -0,0 +1,130 @@
+go-i18n [![Build Status](https://secure.travis-ci.org/nicksnyder/go-i18n.png?branch=master)](http://travis-ci.org/nicksnyder/go-i18n)
+=======
+
+go-i18n is a Go [package](#i18n-package) and a [command](#goi18n-command) that helps you translate Go programs into multiple languages.
+* Supports [pluralized strings](http://cldr.unicode.org/index/cldr-spec/plural-rules) for all 200+ languages in the [Unicode Common Locale Data Repository (CLDR)](http://www.unicode.org/cldr/charts/28/supplemental/language_plural_rules.html).
+ * Code and tests are [automatically generated](https://github.com/nicksnyder/go-i18n/tree/master/i18n/language/codegen) from [CLDR data](http://cldr.unicode.org/index/downloads)
+* Supports strings with named variables using [text/template](http://golang.org/pkg/text/template/) syntax.
+* Translation files are simple JSON or YAML.
+* [Documented](http://godoc.org/github.com/nicksnyder/go-i18n) and [tested](https://travis-ci.org/nicksnyder/go-i18n)!
+
+Package i18n [![GoDoc](http://godoc.org/github.com/nicksnyder/go-i18n?status.png)](http://godoc.org/github.com/nicksnyder/go-i18n/i18n)
+------------
+
+The i18n package provides runtime APIs for fetching translated strings.
+
+Command goi18n [![GoDoc](http://godoc.org/github.com/nicksnyder/go-i18n?status.png)](http://godoc.org/github.com/nicksnyder/go-i18n/goi18n)
+--------------
+
+The goi18n command provides functionality for managing the translation process.
+
+Installation
+------------
+
+Make sure you have [setup GOPATH](http://golang.org/doc/code.html#GOPATH).
+
+ go get -u github.com/nicksnyder/go-i18n/goi18n
+ goi18n -help
+
+Workflow
+--------
+
+A typical workflow looks like this:
+
+1. Add a new string to your source code.
+
+ ```go
+ T("settings_title")
+ ```
+
+2. Add the string to en-US.all.json
+
+ ```json
+ [
+ {
+ "id": "settings_title",
+ "translation": "Settings"
+ }
+ ]
+ ```
+
+3. Run goi18n
+
+ ```
+ goi18n path/to/*.all.json
+ ```
+
+4. Send `path/to/*.untranslated.json` to get translated.
+5. Run goi18n again to merge the translations
+
+ ```sh
+ goi18n path/to/*.all.json path/to/*.untranslated.json
+ ```
+
+Translation files
+-----------------
+
+A translation file stores translated and untranslated strings.
+
+Example:
+
+```json
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "one": "{{.Count}} day",
+ "other": "{{.Count}} days"
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "one": "I am {{.Count}} meter tall.",
+ "other": "I am {{.Count}} meters tall."
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "Hello {{.Person}}"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email.",
+ "other": "{{.Person}} has {{.Count}} unread emails."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}.",
+ "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": "Hello world"
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "one": "You have {{.Count}} unread email.",
+ "other": "You have {{.Count}} unread emails."
+ }
+ }
+]
+```
+
+Contributions
+-------------
+
+If you would like to submit a pull request, please
+
+1. Write tests
+2. Format code with [goimports](https://github.com/bradfitz/goimports).
+3. Read the [common code review comments](https://github.com/golang/go/wiki/CodeReviewComments).
+
+License
+-------
+go-i18n is available under the MIT license. See the [LICENSE](LICENSE) file for more info.
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/doc.go b/vendor/github.com/nicksnyder/go-i18n/goi18n/doc.go
new file mode 100644
index 000000000..10d244217
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/doc.go
@@ -0,0 +1,59 @@
+// The goi18n command formats and merges translation files.
+//
+// go get -u github.com/nicksnyder/go-i18n/goi18n
+// goi18n -help
+//
+// Help documentation:
+//
+// goi18n formats and merges translation files.
+//
+// Usage:
+//
+// goi18n [options] [files...]
+//
+// Translation files:
+//
+// A translation file contains the strings and translations for a single language.
+//
+// Translation file names must have a suffix of a supported format (e.g. .json) and
+// contain a valid language tag as defined by RFC 5646 (e.g. en-us, fr, zh-hant, etc.).
+//
+// For each language represented by at least one input translation file, goi18n will produce 2 output files:
+//
+// xx-yy.all.format
+// This file contains all strings for the language (translated and untranslated).
+// Use this file when loading strings at runtime.
+//
+// xx-yy.untranslated.format
+// This file contains the strings that have not been translated for this language.
+// The translations for the strings in this file will be extracted from the source language.
+// After they are translated, merge them back into xx-yy.all.format using goi18n.
+//
+// Merging:
+//
+// goi18n will merge multiple translation files for the same language.
+// Duplicate translations will be merged into the existing translation.
+// Non-empty fields in the duplicate translation will overwrite those fields in the existing translation.
+// Empty fields in the duplicate translation are ignored.
+//
+// Adding a new language:
+//
+// To produce translation files for a new language, create an empty translation file with the
+// appropriate name and pass it in to goi18n.
+//
+// Options:
+//
+// -sourceLanguage tag
+// goi18n uses the strings from this language to seed the translations for other languages.
+// Default: en-us
+//
+// -outdir directory
+// goi18n writes the output translation files to this directory.
+// Default: .
+//
+// -format format
+// goi18n encodes the output translation files in this format.
+// Supported formats: json, yaml
+// Default: json
+//
+package main
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/gendoc.sh b/vendor/github.com/nicksnyder/go-i18n/goi18n/gendoc.sh
new file mode 100644
index 000000000..094f479a5
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/gendoc.sh
@@ -0,0 +1,10 @@
+go install
+echo "// The goi18n command formats and merges translation files." > doc.go
+echo "//" >> doc.go
+echo "// go get -u github.com/nicksnyder/go-i18n/goi18n" >> doc.go
+echo "// goi18n -help" >> doc.go
+echo "//" >> doc.go
+echo "// Help documentation:" >> doc.go
+echo "//" >> doc.go
+goi18n -help | sed -e 's/^/\/\/ /' >> doc.go
+echo "package main" >> doc.go
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/goi18n.go b/vendor/github.com/nicksnyder/go-i18n/goi18n/goi18n.go
new file mode 100644
index 000000000..f57ea8175
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/goi18n.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+)
+
+func usage() {
+ fmt.Printf(`goi18n formats and merges translation files.
+
+Usage:
+
+ goi18n [options] [files...]
+
+Translation files:
+
+ A translation file contains the strings and translations for a single language.
+
+ Translation file names must have a suffix of a supported format (e.g. .json) and
+ contain a valid language tag as defined by RFC 5646 (e.g. en-us, fr, zh-hant, etc.).
+
+ For each language represented by at least one input translation file, goi18n will produce 2 output files:
+
+ xx-yy.all.format
+ This file contains all strings for the language (translated and untranslated).
+ Use this file when loading strings at runtime.
+
+ xx-yy.untranslated.format
+ This file contains the strings that have not been translated for this language.
+ The translations for the strings in this file will be extracted from the source language.
+ After they are translated, merge them back into xx-yy.all.format using goi18n.
+
+Merging:
+
+ goi18n will merge multiple translation files for the same language.
+ Duplicate translations will be merged into the existing translation.
+ Non-empty fields in the duplicate translation will overwrite those fields in the existing translation.
+ Empty fields in the duplicate translation are ignored.
+
+Adding a new language:
+
+ To produce translation files for a new language, create an empty translation file with the
+ appropriate name and pass it in to goi18n.
+
+Options:
+
+ -sourceLanguage tag
+ goi18n uses the strings from this language to seed the translations for other languages.
+ Default: en-us
+
+ -outdir directory
+ goi18n writes the output translation files to this directory.
+ Default: .
+
+ -format format
+ goi18n encodes the output translation files in this format.
+ Supported formats: json, yaml
+ Default: json
+
+`)
+ os.Exit(1)
+}
+
+func main() {
+ flag.Usage = usage
+ sourceLanguage := flag.String("sourceLanguage", "en-us", "")
+ outdir := flag.String("outdir", ".", "")
+ format := flag.String("format", "json", "")
+ flag.Parse()
+
+ mc := &mergeCommand{
+ translationFiles: flag.Args(),
+ sourceLanguageTag: *sourceLanguage,
+ outdir: *outdir,
+ format: *format,
+ }
+ if err := mc.execute(); err != nil {
+ fmt.Println(err.Error())
+ os.Exit(1)
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/merge.go b/vendor/github.com/nicksnyder/go-i18n/goi18n/merge.go
new file mode 100644
index 000000000..1317fe958
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/merge.go
@@ -0,0 +1,127 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "gopkg.in/yaml.v2"
+ "io/ioutil"
+ "path/filepath"
+ "reflect"
+ "sort"
+
+ "github.com/nicksnyder/go-i18n/i18n/bundle"
+ "github.com/nicksnyder/go-i18n/i18n/language"
+ "github.com/nicksnyder/go-i18n/i18n/translation"
+)
+
+type mergeCommand struct {
+ translationFiles []string
+ sourceLanguageTag string
+ outdir string
+ format string
+}
+
+func (mc *mergeCommand) execute() error {
+ if len(mc.translationFiles) < 1 {
+ return fmt.Errorf("need at least one translation file to parse")
+ }
+
+ if lang := language.Parse(mc.sourceLanguageTag); lang == nil {
+ return fmt.Errorf("invalid source locale: %s", mc.sourceLanguageTag)
+ }
+
+ marshal, err := newMarshalFunc(mc.format)
+ if err != nil {
+ return err
+ }
+
+ bundle := bundle.New()
+ for _, tf := range mc.translationFiles {
+ if err := bundle.LoadTranslationFile(tf); err != nil {
+ return fmt.Errorf("failed to load translation file %s because %s\n", tf, err)
+ }
+ }
+
+ translations := bundle.Translations()
+ sourceLanguageTag := language.NormalizeTag(mc.sourceLanguageTag)
+ sourceTranslations := translations[sourceLanguageTag]
+ if sourceTranslations == nil {
+ return fmt.Errorf("no translations found for source locale %s", sourceLanguageTag)
+ }
+ for translationID, src := range sourceTranslations {
+ for _, localeTranslations := range translations {
+ if dst := localeTranslations[translationID]; dst == nil || reflect.TypeOf(src) != reflect.TypeOf(dst) {
+ localeTranslations[translationID] = src.UntranslatedCopy()
+ }
+ }
+ }
+
+ for localeID, localeTranslations := range translations {
+ lang := language.MustParse(localeID)[0]
+ all := filter(localeTranslations, func(t translation.Translation) translation.Translation {
+ return t.Normalize(lang)
+ })
+ if err := mc.writeFile("all", all, localeID, marshal); err != nil {
+ return err
+ }
+
+ untranslated := filter(localeTranslations, func(t translation.Translation) translation.Translation {
+ if t.Incomplete(lang) {
+ return t.Normalize(lang).Backfill(sourceTranslations[t.ID()])
+ }
+ return nil
+ })
+ if err := mc.writeFile("untranslated", untranslated, localeID, marshal); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type marshalFunc func(interface{}) ([]byte, error)
+
+func (mc *mergeCommand) writeFile(label string, translations []translation.Translation, localeID string, marshal marshalFunc) error {
+ sort.Sort(translation.SortableByID(translations))
+ buf, err := marshal(marshalInterface(translations))
+ if err != nil {
+ return fmt.Errorf("failed to marshal %s strings to %s because %s", localeID, mc.format, err)
+ }
+ filename := filepath.Join(mc.outdir, fmt.Sprintf("%s.%s.%s", localeID, label, mc.format))
+ if err := ioutil.WriteFile(filename, buf, 0666); err != nil {
+ return fmt.Errorf("failed to write %s because %s", filename, err)
+ }
+ return nil
+}
+
+func filter(translations map[string]translation.Translation, filter func(translation.Translation) translation.Translation) []translation.Translation {
+ filtered := make([]translation.Translation, 0, len(translations))
+ for _, translation := range translations {
+ if t := filter(translation); t != nil {
+ filtered = append(filtered, t)
+ }
+ }
+ return filtered
+
+}
+
+func newMarshalFunc(format string) (marshalFunc, error) {
+ switch format {
+ case "json":
+ return func(v interface{}) ([]byte, error) {
+ return json.MarshalIndent(v, "", " ")
+ }, nil
+ case "yaml":
+ return func(v interface{}) ([]byte, error) {
+ return yaml.Marshal(v)
+ }, nil
+ }
+ return nil, fmt.Errorf("unsupported format: %s\n", format)
+}
+
+func marshalInterface(translations []translation.Translation) []interface{} {
+ mi := make([]interface{}, len(translations))
+ for i, translation := range translations {
+ mi[i] = translation.MarshalInterface()
+ }
+ return mi
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/merge_test.go b/vendor/github.com/nicksnyder/go-i18n/goi18n/merge_test.go
new file mode 100644
index 000000000..f0d0d47a1
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/merge_test.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func TestMergeExecuteJSON(t *testing.T) {
+ files := []string{
+ "testdata/input/en-us.one.json",
+ "testdata/input/en-us.two.json",
+ "testdata/input/fr-fr.json",
+ "testdata/input/ar-ar.one.json",
+ "testdata/input/ar-ar.two.json",
+ }
+ testMergeExecute(t, files)
+}
+
+func TestMergeExecuteYAML(t *testing.T) {
+ files := []string{
+ "testdata/input/yaml/en-us.one.yaml",
+ "testdata/input/yaml/en-us.two.json",
+ "testdata/input/yaml/fr-fr.json",
+ "testdata/input/yaml/ar-ar.one.json",
+ "testdata/input/yaml/ar-ar.two.json",
+ }
+ testMergeExecute(t, files)
+}
+
+func testMergeExecute(t *testing.T, files []string) {
+ resetDir(t, "testdata/output")
+
+ mc := &mergeCommand{
+ translationFiles: files,
+ sourceLanguageTag: "en-us",
+ outdir: "testdata/output",
+ format: "json",
+ }
+ if err := mc.execute(); err != nil {
+ t.Fatal(err)
+ }
+
+ expectEqualFiles(t, "testdata/output/en-us.all.json", "testdata/expected/en-us.all.json")
+ expectEqualFiles(t, "testdata/output/ar-ar.all.json", "testdata/expected/ar-ar.all.json")
+ expectEqualFiles(t, "testdata/output/fr-fr.all.json", "testdata/expected/fr-fr.all.json")
+ expectEqualFiles(t, "testdata/output/en-us.untranslated.json", "testdata/expected/en-us.untranslated.json")
+ expectEqualFiles(t, "testdata/output/ar-ar.untranslated.json", "testdata/expected/ar-ar.untranslated.json")
+ expectEqualFiles(t, "testdata/output/fr-fr.untranslated.json", "testdata/expected/fr-fr.untranslated.json")
+}
+
+func resetDir(t *testing.T, dir string) {
+ if err := os.RemoveAll(dir); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Mkdir(dir, 0777); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func expectEqualFiles(t *testing.T, expectedName, actualName string) {
+ actual, err := ioutil.ReadFile(actualName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expected, err := ioutil.ReadFile(expectedName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(actual, expected) {
+ t.Fatalf("contents of files did not match: %s, %s", expectedName, actualName)
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/en-us.yaml b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/en-us.yaml
new file mode 100644
index 000000000..cdf9c8dc5
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/en-us.yaml
@@ -0,0 +1,30 @@
+- id: program_greeting
+ translation: "Hello world"
+
+- id: person_greeting
+ translation: "Hello {{.Person}}"
+
+- id: my_height_in_meters
+ translation:
+ one: "I am {{.Count}} meter tall."
+ other: "I am {{.Count}} meters tall."
+
+- id: your_unread_email_count
+ translation:
+ one: "You have {{.Count}} unread email."
+ other: "You have {{.Count}} unread emails."
+
+- id: person_unread_email_count
+ translation:
+ one: "{{.Person}} has {{.Count}} unread email."
+ other: "{{.Person}} has {{.Count}} unread emails."
+
+- id: person_unread_email_count_timeframe
+ translation:
+ one: "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}."
+ other: "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+
+- id: d_days
+ translation:
+ one: "{{.Count}} day"
+ other: "{{.Count}} days"
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.all.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.all.json
new file mode 100644
index 000000000..26a72ff30
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.all.json
@@ -0,0 +1,65 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "few": "new arabic few translation of d_days",
+ "many": "arabic many translation of d_days",
+ "one": "arabic one translation of d_days",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "new arabic translation of person_greeting"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "few": "arabic few translation of person_unread_email_count",
+ "many": "arabic many translation of person_unread_email_count",
+ "one": "arabic one translation of person_unread_email_count",
+ "other": "arabic other translation of person_unread_email_count",
+ "two": "arabic two translation of person_unread_email_count",
+ "zero": "arabic zero translation of person_unread_email_count"
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": ""
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ }
+] \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.untranslated.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.untranslated.json
new file mode 100644
index 000000000..a19fa0bee
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.untranslated.json
@@ -0,0 +1,50 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "few": "new arabic few translation of d_days",
+ "many": "arabic many translation of d_days",
+ "one": "arabic one translation of d_days",
+ "other": "{{.Count}} days",
+ "two": "{{.Count}} days",
+ "zero": "{{.Count}} days"
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "few": "I am {{.Count}} meters tall.",
+ "many": "I am {{.Count}} meters tall.",
+ "one": "I am {{.Count}} meters tall.",
+ "other": "I am {{.Count}} meters tall.",
+ "two": "I am {{.Count}} meters tall.",
+ "zero": "I am {{.Count}} meters tall."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "few": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
+ "many": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
+ "one": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
+ "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
+ "two": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
+ "zero": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": "Hello world"
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "few": "You have {{.Count}} unread emails.",
+ "many": "You have {{.Count}} unread emails.",
+ "one": "You have {{.Count}} unread emails.",
+ "other": "You have {{.Count}} unread emails.",
+ "two": "You have {{.Count}} unread emails.",
+ "zero": "You have {{.Count}} unread emails."
+ }
+ }
+] \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.all.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.all.json
new file mode 100644
index 000000000..5aedc235a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.all.json
@@ -0,0 +1,45 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "one": "{{.Count}} day",
+ "other": "{{.Count}} days"
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "one": "I am {{.Count}} meter tall.",
+ "other": "I am {{.Count}} meters tall."
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "Hello {{.Person}}"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email.",
+ "other": "{{.Person}} has {{.Count}} unread emails."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}.",
+ "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": "Hello world"
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "one": "You have {{.Count}} unread email.",
+ "other": "You have {{.Count}} unread emails."
+ }
+ }
+] \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.untranslated.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.untranslated.json
new file mode 100644
index 000000000..0637a088a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.untranslated.json
@@ -0,0 +1 @@
+[] \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.all.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.all.json
new file mode 100644
index 000000000..20e0b8736
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.all.json
@@ -0,0 +1,45 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "one": "",
+ "other": ""
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "one": "",
+ "other": ""
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": ""
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "one": "",
+ "other": ""
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "one": "",
+ "other": ""
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": ""
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "one": "",
+ "other": ""
+ }
+ }
+] \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.untranslated.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.untranslated.json
new file mode 100644
index 000000000..e2d3967c5
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.untranslated.json
@@ -0,0 +1,45 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "one": "{{.Count}} days",
+ "other": "{{.Count}} days"
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "one": "I am {{.Count}} meters tall.",
+ "other": "I am {{.Count}} meters tall."
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "Hello {{.Person}}"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread emails.",
+ "other": "{{.Person}} has {{.Count}} unread emails."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
+ "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": "Hello world"
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "one": "You have {{.Count}} unread emails.",
+ "other": "You have {{.Count}} unread emails."
+ }
+ }
+] \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.one.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.one.json
new file mode 100644
index 000000000..f5af1d6f4
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.one.json
@@ -0,0 +1,54 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "few": "arabic few translation of d_days",
+ "many": "arabic many translation of d_days",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "arabic translation of person_greeting"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "few": "arabic few translation of person_unread_email_count",
+ "many": "arabic many translation of person_unread_email_count",
+ "one": "arabic one translation of person_unread_email_count",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": ""
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.two.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.two.json
new file mode 100644
index 000000000..e98d7e9b2
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.two.json
@@ -0,0 +1,54 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "few": "new arabic few translation of d_days",
+ "many": "",
+ "one": "arabic one translation of d_days",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "new arabic translation of person_greeting"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "arabic other translation of person_unread_email_count",
+ "two": "arabic two translation of person_unread_email_count",
+ "zero": "arabic zero translation of person_unread_email_count"
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": ""
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.one.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.one.json
new file mode 100644
index 000000000..63a9d6ffb
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.one.json
@@ -0,0 +1,30 @@
+[
+ {
+ "id": "program_greeting",
+ "translation": "Hello world"
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "one": "You have {{.Count}} unread email.",
+ "other": "You have {{.Count}} unread emails."
+ }
+ },
+ {
+ "id": "my_height_in_meters",
+ "translation": {
+ "one": "I am {{.Count}} meter tall.",
+ "other": "I am {{.Count}} meters tall."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "d_days",
+ "translation": "this should get overwritten"
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.two.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.two.json
new file mode 100644
index 000000000..dcc715c43
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.two.json
@@ -0,0 +1,26 @@
+[
+ {
+ "id": "person_greeting",
+ "translation": "Hello {{.Person}}"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email.",
+ "other": "{{.Person}} has {{.Count}} unread emails."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "d_days",
+ "translation": {
+ "one": "{{.Count}} day",
+ "other": "{{.Count}} days"
+ }
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/fr-fr.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/fr-fr.json
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/fr-fr.json
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.one.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.one.json
new file mode 100644
index 000000000..f5af1d6f4
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.one.json
@@ -0,0 +1,54 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "few": "arabic few translation of d_days",
+ "many": "arabic many translation of d_days",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "arabic translation of person_greeting"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "few": "arabic few translation of person_unread_email_count",
+ "many": "arabic many translation of person_unread_email_count",
+ "one": "arabic one translation of person_unread_email_count",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": ""
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.two.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.two.json
new file mode 100644
index 000000000..e98d7e9b2
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.two.json
@@ -0,0 +1,54 @@
+[
+ {
+ "id": "d_days",
+ "translation": {
+ "few": "new arabic few translation of d_days",
+ "many": "",
+ "one": "arabic one translation of d_days",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "person_greeting",
+ "translation": "new arabic translation of person_greeting"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "arabic other translation of person_unread_email_count",
+ "two": "arabic two translation of person_unread_email_count",
+ "zero": "arabic zero translation of person_unread_email_count"
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ },
+ {
+ "id": "program_greeting",
+ "translation": ""
+ },
+ {
+ "id": "your_unread_email_count",
+ "translation": {
+ "few": "",
+ "many": "",
+ "one": "",
+ "other": "",
+ "two": "",
+ "zero": ""
+ }
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.one.yaml b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.one.yaml
new file mode 100644
index 000000000..3ca8e380d
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.one.yaml
@@ -0,0 +1,19 @@
+- id: program_greeting
+ translation: Hello world
+
+- id: your_unread_email_count
+ translation:
+ one: You have {{.Count}} unread email.
+ other: You have {{.Count}} unread emails.
+
+- id: my_height_in_meters
+ translation:
+ one: I am {{.Count}} meter tall.
+ other: I am {{.Count}} meters tall.
+
+- id: person_unread_email_count_timeframe
+ translation:
+ one: "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}."
+
+- id: d_days
+ translation: this should get overwritten \ No newline at end of file
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.two.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.two.json
new file mode 100644
index 000000000..dcc715c43
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.two.json
@@ -0,0 +1,26 @@
+[
+ {
+ "id": "person_greeting",
+ "translation": "Hello {{.Person}}"
+ },
+ {
+ "id": "person_unread_email_count",
+ "translation": {
+ "one": "{{.Person}} has {{.Count}} unread email.",
+ "other": "{{.Person}} has {{.Count}} unread emails."
+ }
+ },
+ {
+ "id": "person_unread_email_count_timeframe",
+ "translation": {
+ "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
+ }
+ },
+ {
+ "id": "d_days",
+ "translation": {
+ "one": "{{.Count}} day",
+ "other": "{{.Count}} days"
+ }
+ }
+]
diff --git a/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/fr-fr.json b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/fr-fr.json
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/fr-fr.json
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go
new file mode 100644
index 000000000..e93db95d7
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go
@@ -0,0 +1,315 @@
+// Package bundle manages translations for multiple languages.
+package bundle
+
+import (
+ "encoding/json"
+ "fmt"
+ "gopkg.in/yaml.v2"
+ "io/ioutil"
+ "reflect"
+
+ "path/filepath"
+
+ "github.com/nicksnyder/go-i18n/i18n/language"
+ "github.com/nicksnyder/go-i18n/i18n/translation"
+)
+
+// TranslateFunc is a copy of i18n.TranslateFunc to avoid a circular dependency.
+type TranslateFunc func(translationID string, args ...interface{}) string
+
+// Bundle stores the translations for multiple languages.
+type Bundle struct {
+ // The primary translations for a language tag and translation id.
+ translations map[string]map[string]translation.Translation
+
+ // Translations that can be used when an exact language match is not possible.
+ fallbackTranslations map[string]map[string]translation.Translation
+}
+
+// New returns an empty bundle.
+func New() *Bundle {
+ return &Bundle{
+ translations: make(map[string]map[string]translation.Translation),
+ fallbackTranslations: make(map[string]map[string]translation.Translation),
+ }
+}
+
+// MustLoadTranslationFile is similar to LoadTranslationFile
+// except it panics if an error happens.
+func (b *Bundle) MustLoadTranslationFile(filename string) {
+ if err := b.LoadTranslationFile(filename); err != nil {
+ panic(err)
+ }
+}
+
+// LoadTranslationFile loads the translations from filename into memory.
+//
+// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
+//
+// Generally you should load translation files once during your program's initialization.
+func (b *Bundle) LoadTranslationFile(filename string) error {
+ buf, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+ return b.ParseTranslationFileBytes(filename, buf)
+}
+
+// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
+//
+// It is useful for parsing translation files embedded with go-bindata.
+func (b *Bundle) ParseTranslationFileBytes(filename string, buf []byte) error {
+ basename := filepath.Base(filename)
+ langs := language.Parse(basename)
+ switch l := len(langs); {
+ case l == 0:
+ return fmt.Errorf("no language found in %q", basename)
+ case l > 1:
+ return fmt.Errorf("multiple languages found in filename %q: %v; expected one", basename, langs)
+ }
+ translations, err := parseTranslations(filename, buf)
+ if err != nil {
+ return err
+ }
+ b.AddTranslation(langs[0], translations...)
+ return nil
+}
+
+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)
+ }
+
+ var translationsData []map[string]interface{}
+ if len(buf) > 0 {
+ if err := unmarshalFunc(buf, &translationsData); err != nil {
+ return nil, err
+ }
+ }
+
+ translations := make([]translation.Translation, 0, len(translationsData))
+ for i, translationData := range translationsData {
+ 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)
+ }
+ translations = append(translations, t)
+ }
+ return translations, nil
+}
+
+// AddTranslation adds translations for a language.
+//
+// It is useful if your translations are in a format not supported by LoadTranslationFile.
+func (b *Bundle) AddTranslation(lang *language.Language, translations ...translation.Translation) {
+ if b.translations[lang.Tag] == nil {
+ b.translations[lang.Tag] = make(map[string]translation.Translation, len(translations))
+ }
+ currentTranslations := b.translations[lang.Tag]
+ for _, newTranslation := range translations {
+ if currentTranslation := currentTranslations[newTranslation.ID()]; currentTranslation != nil {
+ currentTranslations[newTranslation.ID()] = currentTranslation.Merge(newTranslation)
+ } else {
+ currentTranslations[newTranslation.ID()] = newTranslation
+ }
+ }
+
+ // lang can provide translations for less specific language tags.
+ for _, tag := range lang.MatchingTags() {
+ b.fallbackTranslations[tag] = currentTranslations
+ }
+}
+
+// Translations returns all translations in the bundle.
+func (b *Bundle) Translations() map[string]map[string]translation.Translation {
+ return b.translations
+}
+
+// LanguageTags returns the tags of all languages that that have been added.
+func (b *Bundle) LanguageTags() []string {
+ var tags []string
+ for k := range b.translations {
+ tags = append(tags, k)
+ }
+ return tags
+}
+
+// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
+func (b *Bundle) LanguageTranslationIDs(languageTag string) []string {
+ var ids []string
+ for id := range b.translations[languageTag] {
+ ids = append(ids, id)
+ }
+ return ids
+}
+
+// MustTfunc is similar to Tfunc except it panics if an error happens.
+func (b *Bundle) MustTfunc(pref string, prefs ...string) TranslateFunc {
+ tfunc, err := b.Tfunc(pref, prefs...)
+ if err != nil {
+ panic(err)
+ }
+ return tfunc
+}
+
+// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
+func (b *Bundle) MustTfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language) {
+ tfunc, language, err := b.TfuncAndLanguage(pref, prefs...)
+ if err != nil {
+ panic(err)
+ }
+ return tfunc, language
+}
+
+// Tfunc is similar to TfuncAndLanguage except is doesn't return the Language.
+func (b *Bundle) Tfunc(pref string, prefs ...string) (TranslateFunc, error) {
+ tfunc, _, err := b.TfuncAndLanguage(pref, prefs...)
+ return tfunc, err
+}
+
+// TfuncAndLanguage returns a TranslateFunc for the first Language that
+// has a non-zero number of translations in the bundle.
+//
+// The returned Language matches the the first language preference that could be satisfied,
+// but this may not strictly match the language of the translations used to satisfy that preference.
+//
+// For example, the user may request "zh". If there are no translations for "zh" but there are translations
+// for "zh-cn", then the translations for "zh-cn" will be used but the returned Language will be "zh".
+//
+// It can parse languages from Accept-Language headers (RFC 2616),
+// but it assumes weights are monotonically decreasing.
+func (b *Bundle) TfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language, error) {
+ lang := b.supportedLanguage(pref, prefs...)
+ var err error
+ if lang == nil {
+ err = fmt.Errorf("no supported languages found %#v", append(prefs, pref))
+ }
+ return func(translationID string, args ...interface{}) string {
+ return b.translate(lang, translationID, args...)
+ }, lang, err
+}
+
+// supportedLanguage returns the first language which
+// has a non-zero number of translations in the bundle.
+func (b *Bundle) supportedLanguage(pref string, prefs ...string) *language.Language {
+ lang := b.translatedLanguage(pref)
+ if lang == nil {
+ for _, pref := range prefs {
+ lang = b.translatedLanguage(pref)
+ if lang != nil {
+ break
+ }
+ }
+ }
+ return lang
+}
+
+func (b *Bundle) translatedLanguage(src string) *language.Language {
+ langs := language.Parse(src)
+ for _, lang := range langs {
+ if len(b.translations[lang.Tag]) > 0 ||
+ len(b.fallbackTranslations[lang.Tag]) > 0 {
+ return lang
+ }
+ }
+ return nil
+}
+
+func (b *Bundle) translate(lang *language.Language, translationID string, args ...interface{}) string {
+ if lang == nil {
+ return translationID
+ }
+
+ translations := b.translations[lang.Tag]
+ if translations == nil {
+ translations = b.fallbackTranslations[lang.Tag]
+ if translations == nil {
+ return translationID
+ }
+ }
+
+ translation := translations[translationID]
+ if translation == nil {
+ return translationID
+ }
+
+ var data interface{}
+ var count interface{}
+ if argc := len(args); argc > 0 {
+ if isNumber(args[0]) {
+ count = args[0]
+ if argc > 1 {
+ data = args[1]
+ }
+ } else {
+ data = args[0]
+ }
+ }
+
+ if count != nil {
+ if data == nil {
+ data = map[string]interface{}{"Count": count}
+ } else {
+ dataMap := toMap(data)
+ dataMap["Count"] = count
+ data = dataMap
+ }
+ }
+
+ p, _ := lang.Plural(count)
+ template := translation.Template(p)
+ if template == nil {
+ return translationID
+ }
+
+ s := template.Execute(data)
+ if s == "" {
+ return translationID
+ }
+ return s
+}
+
+func isNumber(n interface{}) bool {
+ switch n.(type) {
+ case int, int8, int16, int32, int64, string:
+ return true
+ }
+ return false
+}
+
+func toMap(input interface{}) map[string]interface{} {
+ if data, ok := input.(map[string]interface{}); ok {
+ return data
+ }
+ v := reflect.ValueOf(input)
+ switch v.Kind() {
+ case reflect.Ptr:
+ return toMap(v.Elem().Interface())
+ case reflect.Struct:
+ return structToMap(v)
+ default:
+ return nil
+ }
+}
+
+// Converts the top level of a struct to a map[string]interface{}.
+// Code inspired by github.com/fatih/structs.
+func structToMap(v reflect.Value) map[string]interface{} {
+ out := make(map[string]interface{})
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ if field.PkgPath != "" {
+ // unexported field. skip.
+ continue
+ }
+ out[field.Name] = v.FieldByName(field.Name).Interface()
+ }
+ return out
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle_test.go
new file mode 100644
index 000000000..b9c0a0593
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle_test.go
@@ -0,0 +1,289 @@
+package bundle
+
+import (
+ "fmt"
+ "testing"
+
+ "reflect"
+ "sort"
+
+ "github.com/nicksnyder/go-i18n/i18n/language"
+ "github.com/nicksnyder/go-i18n/i18n/translation"
+)
+
+func TestMustLoadTranslationFile(t *testing.T) {
+ t.Skipf("not implemented")
+}
+
+func TestLoadTranslationFile(t *testing.T) {
+ t.Skipf("not implemented")
+}
+
+func TestParseTranslationFileBytes(t *testing.T) {
+ t.Skipf("not implemented")
+}
+
+func TestAddTranslation(t *testing.T) {
+ t.Skipf("not implemented")
+}
+
+func TestMustTfunc(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("expected MustTfunc to panic")
+ }
+ }()
+ New().MustTfunc("invalid")
+}
+
+func TestLanguageTagsAndTranslationIDs(t *testing.T) {
+ b := New()
+ translationID := "translation_id"
+ englishLanguage := languageWithTag("en-US")
+ frenchLanguage := languageWithTag("fr-FR")
+ spanishLanguage := languageWithTag("es")
+ addFakeTranslation(t, b, englishLanguage, "English"+translationID)
+ addFakeTranslation(t, b, frenchLanguage, translationID)
+ addFakeTranslation(t, b, spanishLanguage, translationID)
+
+ tags := b.LanguageTags()
+ sort.Strings(tags)
+ compareTo := []string{englishLanguage.Tag, spanishLanguage.Tag, frenchLanguage.Tag}
+ if !reflect.DeepEqual(tags, compareTo) {
+ t.Errorf("LanguageTags() = %#v; expected: %#v", tags, compareTo)
+ }
+
+ ids := b.LanguageTranslationIDs(englishLanguage.Tag)
+ sort.Strings(ids)
+ compareTo = []string{"English" + translationID}
+ if !reflect.DeepEqual(ids, compareTo) {
+ t.Errorf("LanguageTranslationIDs() = %#v; expected: %#v", ids, compareTo)
+ }
+}
+
+func TestTfuncAndLanguage(t *testing.T) {
+ b := New()
+ translationID := "translation_id"
+ englishLanguage := languageWithTag("en-US")
+ frenchLanguage := languageWithTag("fr-FR")
+ spanishLanguage := languageWithTag("es")
+ chineseLanguage := languageWithTag("zh-hans-cn")
+ englishTranslation := addFakeTranslation(t, b, englishLanguage, translationID)
+ frenchTranslation := addFakeTranslation(t, b, frenchLanguage, translationID)
+ spanishTranslation := addFakeTranslation(t, b, spanishLanguage, translationID)
+ chineseTranslation := addFakeTranslation(t, b, chineseLanguage, translationID)
+
+ tests := []struct {
+ languageIDs []string
+ result string
+ expectedLanguage *language.Language
+ }{
+ {
+ []string{"invalid"},
+ translationID,
+ nil,
+ },
+ {
+ []string{"invalid", "invalid2"},
+ translationID,
+ nil,
+ },
+ {
+ []string{"invalid", "en-US"},
+ englishTranslation,
+ englishLanguage,
+ },
+ {
+ []string{"en-US", "invalid"},
+ englishTranslation,
+ englishLanguage,
+ },
+ {
+ []string{"en-US", "fr-FR"},
+ englishTranslation,
+ englishLanguage,
+ },
+ {
+ []string{"invalid", "es"},
+ spanishTranslation,
+ spanishLanguage,
+ },
+ {
+ []string{"zh-CN,fr-XX,es"},
+ spanishTranslation,
+ spanishLanguage,
+ },
+ {
+ []string{"fr"},
+ frenchTranslation,
+
+ // The language is still "fr" even though the translation is provided by "fr-FR"
+ languageWithTag("fr"),
+ },
+ {
+ []string{"zh"},
+ chineseTranslation,
+
+ // The language is still "zh" even though the translation is provided by "zh-hans-cn"
+ languageWithTag("zh"),
+ },
+ {
+ []string{"zh-hans"},
+ chineseTranslation,
+
+ // The language is still "zh-hans" even though the translation is provided by "zh-hans-cn"
+ languageWithTag("zh-hans"),
+ },
+ {
+ []string{"zh-hans-cn"},
+ chineseTranslation,
+ languageWithTag("zh-hans-cn"),
+ },
+ }
+
+ for i, test := range tests {
+ tf, lang, err := b.TfuncAndLanguage(test.languageIDs[0], test.languageIDs[1:]...)
+ if err != nil && test.expectedLanguage != nil {
+ t.Errorf("Tfunc(%v) = error{%q}; expected no error", test.languageIDs, err)
+ }
+ if err == nil && test.expectedLanguage == nil {
+ t.Errorf("Tfunc(%v) = nil error; expected error", test.languageIDs)
+ }
+ if result := tf(translationID); result != test.result {
+ t.Errorf("translation %d was %s; expected %s", i, result, test.result)
+ }
+ if (lang == nil && test.expectedLanguage != nil) ||
+ (lang != nil && test.expectedLanguage == nil) ||
+ (lang != nil && test.expectedLanguage != nil && lang.String() != test.expectedLanguage.String()) {
+ t.Errorf("lang %d was %s; expected %s", i, lang, test.expectedLanguage)
+ }
+ }
+}
+
+func addFakeTranslation(t *testing.T, b *Bundle, lang *language.Language, translationID string) string {
+ translation := fakeTranslation(lang, translationID)
+ b.AddTranslation(lang, testNewTranslation(t, map[string]interface{}{
+ "id": translationID,
+ "translation": translation,
+ }))
+ return translation
+}
+
+func fakeTranslation(lang *language.Language, translationID string) string {
+ return fmt.Sprintf("%s(%s)", lang.Tag, translationID)
+}
+
+func testNewTranslation(t *testing.T, data map[string]interface{}) translation.Translation {
+ translation, err := translation.NewTranslation(data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return translation
+}
+
+func languageWithTag(tag string) *language.Language {
+ return language.MustParse(tag)[0]
+}
+
+func createBenchmarkTranslateFunc(b *testing.B, translationTemplate interface{}, count interface{}, expected string) func(data interface{}) {
+ bundle := New()
+ lang := "en-US"
+ translationID := "translation_id"
+ translation, err := translation.NewTranslation(map[string]interface{}{
+ "id": translationID,
+ "translation": translationTemplate,
+ })
+ if err != nil {
+ b.Fatal(err)
+ }
+ bundle.AddTranslation(languageWithTag(lang), translation)
+ tf, err := bundle.Tfunc(lang)
+ if err != nil {
+ b.Fatal(err)
+ }
+ return func(data interface{}) {
+ var result string
+ if count == nil {
+ result = tf(translationID, data)
+ } else {
+ result = tf(translationID, count, data)
+ }
+ if result != expected {
+ b.Fatalf("expected %q, got %q", expected, result)
+ }
+ }
+}
+
+func createBenchmarkPluralTranslateFunc(b *testing.B) func(data interface{}) {
+ translationTemplate := map[string]interface{}{
+ "one": "{{.Person}} is {{.Count}} year old.",
+ "other": "{{.Person}} is {{.Count}} years old.",
+ }
+ count := 26
+ expected := "Bob is 26 years old."
+ return createBenchmarkTranslateFunc(b, translationTemplate, count, expected)
+}
+
+func createBenchmarkNonPluralTranslateFunc(b *testing.B) func(data interface{}) {
+ translationTemplate := "Hi {{.Person}}!"
+ expected := "Hi Bob!"
+ return createBenchmarkTranslateFunc(b, translationTemplate, nil, expected)
+}
+
+func BenchmarkTranslateNonPluralWithMap(b *testing.B) {
+ data := map[string]interface{}{
+ "Person": "Bob",
+ }
+ tf := createBenchmarkNonPluralTranslateFunc(b)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tf(data)
+ }
+}
+
+func BenchmarkTranslateNonPluralWithStruct(b *testing.B) {
+ data := struct{ Person string }{Person: "Bob"}
+ tf := createBenchmarkNonPluralTranslateFunc(b)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tf(data)
+ }
+}
+
+func BenchmarkTranslateNonPluralWithStructPointer(b *testing.B) {
+ data := &struct{ Person string }{Person: "Bob"}
+ tf := createBenchmarkNonPluralTranslateFunc(b)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tf(data)
+ }
+}
+
+func BenchmarkTranslatePluralWithMap(b *testing.B) {
+ data := map[string]interface{}{
+ "Person": "Bob",
+ }
+ tf := createBenchmarkPluralTranslateFunc(b)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tf(data)
+ }
+}
+
+func BenchmarkTranslatePluralWithStruct(b *testing.B) {
+ data := struct{ Person string }{Person: "Bob"}
+ tf := createBenchmarkPluralTranslateFunc(b)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tf(data)
+ }
+}
+
+func BenchmarkTranslatePluralWithStructPointer(b *testing.B) {
+ data := &struct{ Person string }{Person: "Bob"}
+ tf := createBenchmarkPluralTranslateFunc(b)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tf(data)
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/example_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/example_test.go
new file mode 100644
index 000000000..d2d9706a7
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/example_test.go
@@ -0,0 +1,63 @@
+package i18n_test
+
+import (
+ "fmt"
+
+ "github.com/nicksnyder/go-i18n/i18n"
+)
+
+func Example() {
+ i18n.MustLoadTranslationFile("../goi18n/testdata/expected/en-us.all.json")
+
+ T, _ := i18n.Tfunc("en-US")
+
+ bobMap := map[string]interface{}{"Person": "Bob"}
+ bobStruct := struct{ Person string }{Person: "Bob"}
+
+ fmt.Println(T("program_greeting"))
+ fmt.Println(T("person_greeting", bobMap))
+ fmt.Println(T("person_greeting", bobStruct))
+
+ fmt.Println(T("your_unread_email_count", 0))
+ fmt.Println(T("your_unread_email_count", 1))
+ fmt.Println(T("your_unread_email_count", 2))
+ fmt.Println(T("my_height_in_meters", "1.7"))
+
+ fmt.Println(T("person_unread_email_count", 0, bobMap))
+ fmt.Println(T("person_unread_email_count", 1, bobMap))
+ fmt.Println(T("person_unread_email_count", 2, bobMap))
+ fmt.Println(T("person_unread_email_count", 0, bobStruct))
+ fmt.Println(T("person_unread_email_count", 1, bobStruct))
+ fmt.Println(T("person_unread_email_count", 2, bobStruct))
+
+ fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 0),
+ }))
+ fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 1),
+ }))
+ fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 2),
+ }))
+
+ // Output:
+ // Hello world
+ // Hello Bob
+ // Hello Bob
+ // You have 0 unread emails.
+ // You have 1 unread email.
+ // You have 2 unread emails.
+ // I am 1.7 meters tall.
+ // Bob has 0 unread emails.
+ // Bob has 1 unread email.
+ // Bob has 2 unread emails.
+ // Bob has 0 unread emails.
+ // Bob has 1 unread email.
+ // Bob has 2 unread emails.
+ // Bob has 3 unread emails in the past 0 days.
+ // Bob has 3 unread emails in the past 1 day.
+ // Bob has 3 unread emails in the past 2 days.
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/exampletemplate_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/exampletemplate_test.go
new file mode 100644
index 000000000..962936610
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/exampletemplate_test.go
@@ -0,0 +1,63 @@
+package i18n_test
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n"
+ "os"
+ "text/template"
+)
+
+var funcMap = map[string]interface{}{
+ "T": i18n.IdentityTfunc,
+}
+
+var tmpl = template.Must(template.New("").Funcs(funcMap).Parse(`
+{{T "program_greeting"}}
+{{T "person_greeting" .}}
+{{T "your_unread_email_count" 0}}
+{{T "your_unread_email_count" 1}}
+{{T "your_unread_email_count" 2}}
+{{T "person_unread_email_count" 0 .}}
+{{T "person_unread_email_count" 1 .}}
+{{T "person_unread_email_count" 2 .}}
+`))
+
+func Example_template() {
+ i18n.MustLoadTranslationFile("../goi18n/testdata/expected/en-us.all.json")
+
+ T, _ := i18n.Tfunc("en-US")
+ tmpl.Funcs(map[string]interface{}{
+ "T": T,
+ })
+
+ tmpl.Execute(os.Stdout, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 1),
+ })
+
+ tmpl.Execute(os.Stdout, struct {
+ Person string
+ Timeframe string
+ }{
+ Person: "Bob",
+ Timeframe: T("d_days", 1),
+ })
+
+ // Output:
+ // Hello world
+ // Hello Bob
+ // You have 0 unread emails.
+ // You have 1 unread email.
+ // You have 2 unread emails.
+ // Bob has 0 unread emails.
+ // Bob has 1 unread email.
+ // Bob has 2 unread emails.
+ //
+ // Hello world
+ // Hello Bob
+ // You have 0 unread emails.
+ // You have 1 unread email.
+ // You have 2 unread emails.
+ // Bob has 0 unread emails.
+ // Bob has 1 unread email.
+ // Bob has 2 unread emails.
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/exampleyaml_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/exampleyaml_test.go
new file mode 100644
index 000000000..b2e7bdcfe
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/exampleyaml_test.go
@@ -0,0 +1,62 @@
+package i18n_test
+
+import (
+ "fmt"
+ "github.com/nicksnyder/go-i18n/i18n"
+)
+
+func ExampleYAML() {
+ i18n.MustLoadTranslationFile("../goi18n/testdata/en-us.yaml")
+
+ T, _ := i18n.Tfunc("en-US")
+
+ bobMap := map[string]interface{}{"Person": "Bob"}
+ bobStruct := struct{ Person string }{Person: "Bob"}
+
+ fmt.Println(T("program_greeting"))
+ fmt.Println(T("person_greeting", bobMap))
+ fmt.Println(T("person_greeting", bobStruct))
+
+ fmt.Println(T("your_unread_email_count", 0))
+ fmt.Println(T("your_unread_email_count", 1))
+ fmt.Println(T("your_unread_email_count", 2))
+ fmt.Println(T("my_height_in_meters", "1.7"))
+
+ fmt.Println(T("person_unread_email_count", 0, bobMap))
+ fmt.Println(T("person_unread_email_count", 1, bobMap))
+ fmt.Println(T("person_unread_email_count", 2, bobMap))
+ fmt.Println(T("person_unread_email_count", 0, bobStruct))
+ fmt.Println(T("person_unread_email_count", 1, bobStruct))
+ fmt.Println(T("person_unread_email_count", 2, bobStruct))
+
+ fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 0),
+ }))
+ fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 1),
+ }))
+ fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
+ "Person": "Bob",
+ "Timeframe": T("d_days", 2),
+ }))
+
+ // Output:
+ // Hello world
+ // Hello Bob
+ // Hello Bob
+ // You have 0 unread emails.
+ // You have 1 unread email.
+ // You have 2 unread emails.
+ // I am 1.7 meters tall.
+ // Bob has 0 unread emails.
+ // Bob has 1 unread email.
+ // Bob has 2 unread emails.
+ // Bob has 0 unread emails.
+ // Bob has 1 unread email.
+ // Bob has 2 unread emails.
+ // Bob has 3 unread emails in the past 0 days.
+ // Bob has 3 unread emails in the past 1 day.
+ // Bob has 3 unread emails in the past 2 days.
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go b/vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go
new file mode 100644
index 000000000..f96842966
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go
@@ -0,0 +1,152 @@
+// Package i18n supports string translations with variable substitution and CLDR pluralization.
+// It is intended to be used in conjunction with the goi18n command, although that is not strictly required.
+//
+// Initialization
+//
+// Your Go program should load translations during its initialization.
+// i18n.MustLoadTranslationFile("path/to/fr-FR.all.json")
+// If your translations are in a file format not supported by (Must)?LoadTranslationFile,
+// then you can use the AddTranslation function to manually add translations.
+//
+// Fetching a translation
+//
+// Use Tfunc or MustTfunc to fetch a TranslateFunc that will return the translated string for a specific language.
+// func handleRequest(w http.ResponseWriter, r *http.Request) {
+// cookieLang := r.Cookie("lang")
+// acceptLang := r.Header.Get("Accept-Language")
+// defaultLang = "en-US" // known valid language
+// T, err := i18n.Tfunc(cookieLang, acceptLang, defaultLang)
+// fmt.Println(T("Hello world"))
+// }
+//
+// Usually it is a good idea to identify strings by a generic id rather than the English translation,
+// but the rest of this documentation will continue to use the English translation for readability.
+// T("Hello world") // ok
+// T("programGreeting") // better!
+//
+// Variables
+//
+// TranslateFunc supports strings that have variables using the text/template syntax.
+// T("Hello {{.Person}}", map[string]interface{}{
+// "Person": "Bob",
+// })
+//
+// Pluralization
+//
+// TranslateFunc supports the pluralization of strings using the CLDR pluralization rules defined here:
+// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
+// T("You have {{.Count}} unread emails.", 2)
+// T("I am {{.Count}} meters tall.", "1.7")
+//
+// Plural strings may also have variables.
+// T("{{.Person}} has {{.Count}} unread emails", 2, map[string]interface{}{
+// "Person": "Bob",
+// })
+//
+// Sentences with multiple plural components can be supported with nesting.
+// T("{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.", 3, map[string]interface{}{
+// "Person": "Bob",
+// "Timeframe": T("{{.Count}} days", 2),
+// })
+//
+// Templates
+//
+// You can use the .Funcs() method of a text/template or html/template to register a TranslateFunc
+// for usage inside of that template.
+package i18n
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/bundle"
+ "github.com/nicksnyder/go-i18n/i18n/language"
+ "github.com/nicksnyder/go-i18n/i18n/translation"
+)
+
+// TranslateFunc returns the translation of the string identified by translationID.
+//
+// If there is no translation for translationID, then the translationID itself is returned.
+// This makes it easy to identify missing translations in your app.
+//
+// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
+// or struct that contains template data.
+//
+// If translationID is a plural form, then the first variadic argument must be an integer type
+// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
+// The second variadic argument may be a map[string]interface{} or struct that contains template data.
+type TranslateFunc func(translationID string, args ...interface{}) string
+
+// IdentityTfunc returns a TranslateFunc that always returns the translationID passed to it.
+//
+// It is a useful placeholder when parsing a text/template or html/template
+// before the actual Tfunc is available.
+func IdentityTfunc() TranslateFunc {
+ return func(translationID string, args ...interface{}) string {
+ return translationID
+ }
+}
+
+var defaultBundle = bundle.New()
+
+// MustLoadTranslationFile is similar to LoadTranslationFile
+// except it panics if an error happens.
+func MustLoadTranslationFile(filename string) {
+ defaultBundle.MustLoadTranslationFile(filename)
+}
+
+// LoadTranslationFile loads the translations from filename into memory.
+//
+// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
+//
+// Generally you should load translation files once during your program's initialization.
+func LoadTranslationFile(filename string) error {
+ return defaultBundle.LoadTranslationFile(filename)
+}
+
+// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
+//
+// It is useful for parsing translation files embedded with go-bindata.
+func ParseTranslationFileBytes(filename string, buf []byte) error {
+ return defaultBundle.ParseTranslationFileBytes(filename, buf)
+}
+
+// AddTranslation adds translations for a language.
+//
+// It is useful if your translations are in a format not supported by LoadTranslationFile.
+func AddTranslation(lang *language.Language, translations ...translation.Translation) {
+ defaultBundle.AddTranslation(lang, translations...)
+}
+
+// LanguageTags returns the tags of all languages that have been added.
+func LanguageTags() []string {
+ return defaultBundle.LanguageTags()
+}
+
+// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
+func LanguageTranslationIDs(languageTag string) []string {
+ return defaultBundle.LanguageTranslationIDs(languageTag)
+}
+
+// MustTfunc is similar to Tfunc except it panics if an error happens.
+func MustTfunc(languageSource string, languageSources ...string) TranslateFunc {
+ return TranslateFunc(defaultBundle.MustTfunc(languageSource, languageSources...))
+}
+
+// Tfunc returns a TranslateFunc that will be bound to the first language which
+// has a non-zero number of translations.
+//
+// It can parse languages from Accept-Language headers (RFC 2616).
+func Tfunc(languageSource string, languageSources ...string) (TranslateFunc, error) {
+ tfunc, err := defaultBundle.Tfunc(languageSource, languageSources...)
+ return TranslateFunc(tfunc), err
+}
+
+// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
+func MustTfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language) {
+ tfunc, lang := defaultBundle.MustTfuncAndLanguage(languageSource, languageSources...)
+ return TranslateFunc(tfunc), lang
+}
+
+// TfuncAndLanguage is similar to Tfunc except it also returns the language which TranslateFunc is bound to.
+func TfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language, error) {
+ tfunc, lang, err := defaultBundle.TfuncAndLanguage(languageSource, languageSources...)
+ return TranslateFunc(tfunc), lang, err
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/generate.sh b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/generate.sh
new file mode 100644
index 000000000..a9fae846a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/generate.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+go build && ./codegen -cout ../pluralspec_gen.go -tout ../pluralspec_gen_test.go && \
+ gofmt -w=true ../pluralspec_gen.go && \
+ gofmt -w=true ../pluralspec_gen_test.go && \
+ rm codegen
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/main.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/main.go
new file mode 100644
index 000000000..5d6b6ad4f
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/main.go
@@ -0,0 +1,132 @@
+package main
+
+import (
+ "encoding/xml"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "text/template"
+)
+
+var usage = `%[1]s generates Go code to support CLDR plural rules.
+
+Usage: %[1]s [options]
+
+Options:
+
+`
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, usage, os.Args[0])
+ flag.PrintDefaults()
+ }
+ var in, cout, tout string
+ flag.StringVar(&in, "i", "plurals.xml", "the input XML file containing CLDR plural rules")
+ flag.StringVar(&cout, "cout", "", "the code output file")
+ flag.StringVar(&tout, "tout", "", "the test output file")
+ flag.BoolVar(&verbose, "v", false, "verbose output")
+ flag.Parse()
+
+ buf, err := ioutil.ReadFile(in)
+ if err != nil {
+ fatalf("failed to read file: %s", err)
+ }
+
+ var data SupplementalData
+ if err := xml.Unmarshal(buf, &data); err != nil {
+ fatalf("failed to unmarshal xml: %s", err)
+ }
+
+ count := 0
+ for _, pg := range data.PluralGroups {
+ count += len(pg.SplitLocales())
+ }
+ infof("parsed %d locales", count)
+
+ if cout != "" {
+ file := openWritableFile(cout)
+ if err := codeTemplate.Execute(file, data); err != nil {
+ fatalf("unable to execute code template because %s", err)
+ } else {
+ infof("generated %s", cout)
+ }
+ } else {
+ infof("not generating code file (use -cout)")
+ }
+
+ if tout != "" {
+ file := openWritableFile(tout)
+ if err := testTemplate.Execute(file, data); err != nil {
+ fatalf("unable to execute test template because %s", err)
+ } else {
+ infof("generated %s", tout)
+ }
+ } else {
+ infof("not generating test file (use -tout)")
+ }
+}
+
+func openWritableFile(name string) *os.File {
+ file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+ if err != nil {
+ fatalf("failed to write file %s because %s", name, err)
+ }
+ return file
+}
+
+var codeTemplate = template.Must(template.New("spec").Parse(`package language
+// This file is generated by i18n/language/codegen/generate.sh
+
+func init() {
+{{range .PluralGroups}}
+ registerPluralSpec({{printf "%#v" .SplitLocales}}, &PluralSpec{
+ Plurals: newPluralSet({{range $i, $e := .PluralRules}}{{if $i}}, {{end}}{{$e.CountTitle}}{{end}}),
+ PluralFunc: func(ops *operands) Plural { {{range .PluralRules}}{{if .GoCondition}}
+ // {{.Condition}}
+ if {{.GoCondition}} {
+ return {{.CountTitle}}
+ }{{end}}{{end}}
+ return Other
+ },
+ }){{end}}
+}
+`))
+
+var testTemplate = template.Must(template.New("spec").Parse(`package language
+// This file is generated by i18n/language/codegen/generate.sh
+
+import "testing"
+
+{{range .PluralGroups}}
+func Test{{.Name}}(t *testing.T) {
+ var tests []pluralTest
+ {{range .PluralRules}}
+ {{if .IntegerExamples}}tests = appendIntegerTests(tests, {{.CountTitle}}, {{printf "%#v" .IntegerExamples}}){{end}}
+ {{if .DecimalExamples}}tests = appendDecimalTests(tests, {{.CountTitle}}, {{printf "%#v" .DecimalExamples}}){{end}}
+ {{end}}
+ locales := {{printf "%#v" .SplitLocales}}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+{{end}}
+`))
+
+func infof(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, format+"\n", args...)
+}
+
+var verbose bool
+
+func verbosef(format string, args ...interface{}) {
+ if verbose {
+ infof(format, args...)
+ }
+}
+
+func fatalf(format string, args ...interface{}) {
+ infof("fatal: "+format+"\n", args...)
+ os.Exit(1)
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/plurals.xml b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/plurals.xml
new file mode 100644
index 000000000..cdd0b5296
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/plurals.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
+<!--
+Copyright © 1991-2015 Unicode, Inc.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+For terms of use, see http://www.unicode.org/copyright.html
+-->
+<supplementalData>
+ <version number="$Revision: 12002 $"/>
+ <plurals type="cardinal">
+ <!-- For a canonicalized list, use GeneratedPluralSamples -->
+
+ <!-- 1: other -->
+
+ <pluralRules locales="bm bo dz id ig ii in ja jbo jv jw kde kea km ko lkt lo ms my nqo root sah ses sg th to vi wo yo zh">
+ <pluralRule count="other"> @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 2: one,other -->
+
+ <pluralRules locales="am as bn fa gu hi kn mr zu">
+ <pluralRule count="one">i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ff fr hy kab">
+ <pluralRule count="one">i = 0,1 @integer 0, 1 @decimal 0.0~1.5</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ast ca de en et fi fy gl it ji nl sv sw ur yi">
+ <pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
+ <pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="si">
+ <pluralRule count="one">n = 0,1 or i = 0 and f = 1 @integer 0, 1 @decimal 0.0, 0.1, 1.0, 0.00, 0.01, 1.00, 0.000, 0.001, 1.000, 0.0000, 0.0001, 1.0000</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.2~0.9, 1.1~1.8, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ak bh guw ln mg nso pa ti wa">
+ <pluralRule count="one">n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="tzm">
+ <pluralRule count="one">n = 0..1 or n = 11..99 @integer 0, 1, 11~24 @decimal 0.0, 1.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0</pluralRule>
+ <pluralRule count="other"> @integer 2~10, 100~106, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="pt">
+ <pluralRule count="one">n = 0..2 and n != 2 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="af asa az bem bez bg brx ce cgg chr ckb dv ee el eo es eu fo fur gsw ha haw hu jgo jmc ka kaj kcg kk kkj kl ks ksb ku ky lb lg mas mgo ml mn nah nb nd ne nn nnh no nr ny nyn om or os pap ps rm rof rwk saq sdh seh sn so sq ss ssy st syr ta te teo tig tk tn tr ts ug uz ve vo vun wae xh xog">
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="pt_PT">
+ <pluralRule count="one">n = 1 and v = 0 @integer 1</pluralRule>
+ <pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="da">
+ <pluralRule count="one">n = 1 or t != 0 and i = 0,1 @integer 1 @decimal 0.1~1.6</pluralRule>
+ <pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0~3.4, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="is">
+ <pluralRule count="one">t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1~1.6, 10.1, 100.1, 1000.1, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="mk">
+ <pluralRule count="one">v = 0 and i % 10 = 1 or f % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 2~10, 12~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.0, 1.2~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="fil tl">
+ <pluralRule count="one">v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, 1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="other"> @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6, 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …</pluralRule>
+ </pluralRules>
+
+ <!-- 3: zero,one,other -->
+
+ <pluralRules locales="lv prg">
+ <pluralRule count="zero">n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="one">n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.0, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
+ <pluralRule count="other"> @integer 2~9, 22~29, 102, 1002, … @decimal 0.2~0.9, 1.2~1.9, 10.2, 100.2, 1000.2, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="lag">
+ <pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
+ <pluralRule count="one">i = 0,1 and n != 0 @integer 1 @decimal 0.1~1.6</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ksh">
+ <pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 3: one,two,other -->
+
+ <pluralRules locales="iu kw naq se sma smi smj smn sms">
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
+ <pluralRule count="other"> @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 3: one,few,other -->
+
+ <pluralRules locales="shi">
+ <pluralRule count="one">i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04</pluralRule>
+ <pluralRule count="few">n = 2..10 @integer 2~10 @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 2.00, 3.00, 4.00, 5.00, 6.00, 7.00, 8.00</pluralRule>
+ <pluralRule count="other"> @integer 11~26, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~1.9, 2.1~2.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="mo ro">
+ <pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
+ <pluralRule count="few">v != 0 or n = 0 or n != 1 and n % 100 = 1..19 @integer 0, 2~16, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="other"> @integer 20~35, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="bs hr sh sr">
+ <pluralRule count="one">v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
+ <pluralRule count="few">v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 4: one,two,few,other -->
+
+ <pluralRules locales="gd">
+ <pluralRule count="one">n = 1,11 @integer 1, 11 @decimal 1.0, 11.0, 1.00, 11.00, 1.000, 11.000, 1.0000</pluralRule>
+ <pluralRule count="two">n = 2,12 @integer 2, 12 @decimal 2.0, 12.0, 2.00, 12.00, 2.000, 12.000, 2.0000</pluralRule>
+ <pluralRule count="few">n = 3..10,13..19 @integer 3~10, 13~19 @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 3.00</pluralRule>
+ <pluralRule count="other"> @integer 0, 20~34, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="sl">
+ <pluralRule count="one">v = 0 and i % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, …</pluralRule>
+ <pluralRule count="two">v = 0 and i % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, …</pluralRule>
+ <pluralRule count="few">v = 0 and i % 100 = 3..4 or v != 0 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="dsb hsb">
+ <pluralRule count="one">v = 0 and i % 100 = 1 or f % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
+ <pluralRule count="two">v = 0 and i % 100 = 2 or f % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, … @decimal 0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 10.2, 100.2, 1000.2, …</pluralRule>
+ <pluralRule count="few">v = 0 and i % 100 = 3..4 or f % 100 = 3..4 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.3, 0.4, 1.3, 1.4, 2.3, 2.4, 3.3, 3.4, 4.3, 4.4, 5.3, 5.4, 6.3, 6.4, 7.3, 7.4, 10.3, 100.3, 1000.3, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 4: one,two,many,other -->
+
+ <pluralRules locales="he iw">
+ <pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
+ <pluralRule count="two">i = 2 and v = 0 @integer 2</pluralRule>
+ <pluralRule count="many">v = 0 and n != 0..10 and n % 10 = 0 @integer 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 3~17, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 4: one,few,many,other -->
+
+ <pluralRules locales="cs sk">
+ <pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
+ <pluralRule count="few">i = 2..4 and v = 0 @integer 2~4</pluralRule>
+ <pluralRule count="many">v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="pl">
+ <pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
+ <pluralRule count="few">v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …</pluralRule>
+ <pluralRule count="many">v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
+ <pluralRule count="other"> @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="be">
+ <pluralRule count="one">n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …</pluralRule>
+ <pluralRule count="few">n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, …</pluralRule>
+ <pluralRule count="many">n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="other"> @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="lt">
+ <pluralRule count="one">n % 10 = 1 and n % 100 != 11..19 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …</pluralRule>
+ <pluralRule count="few">n % 10 = 2..9 and n % 100 != 11..19 @integer 2~9, 22~29, 102, 1002, … @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 22.0, 102.0, 1002.0, …</pluralRule>
+ <pluralRule count="many">f != 0 @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="mt">
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="few">n = 0 or n % 100 = 2..10 @integer 0, 2~10, 102~107, 1002, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 102.0, 1002.0, …</pluralRule>
+ <pluralRule count="many">n % 100 = 11..19 @integer 11~19, 111~117, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …</pluralRule>
+ <pluralRule count="other"> @integer 20~35, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ru uk">
+ <pluralRule count="one">v = 0 and i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …</pluralRule>
+ <pluralRule count="few">v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …</pluralRule>
+ <pluralRule count="many">v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
+ <pluralRule count="other"> @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+
+ <!-- 5: one,two,few,many,other -->
+
+ <pluralRules locales="br">
+ <pluralRule count="one">n % 10 = 1 and n % 100 != 11,71,91 @integer 1, 21, 31, 41, 51, 61, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 81.0, 101.0, 1001.0, …</pluralRule>
+ <pluralRule count="two">n % 10 = 2 and n % 100 != 12,72,92 @integer 2, 22, 32, 42, 52, 62, 82, 102, 1002, … @decimal 2.0, 22.0, 32.0, 42.0, 52.0, 62.0, 82.0, 102.0, 1002.0, …</pluralRule>
+ <pluralRule count="few">n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99 @integer 3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 103, 1003, … @decimal 3.0, 4.0, 9.0, 23.0, 24.0, 29.0, 33.0, 34.0, 103.0, 1003.0, …</pluralRule>
+ <pluralRule count="many">n != 0 and n % 1000000 = 0 @integer 1000000, … @decimal 1000000.0, 1000000.00, 1000000.000, …</pluralRule>
+ <pluralRule count="other"> @integer 0, 5~8, 10~20, 100, 1000, 10000, 100000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="ga">
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
+ <pluralRule count="few">n = 3..6 @integer 3~6 @decimal 3.0, 4.0, 5.0, 6.0, 3.00, 4.00, 5.00, 6.00, 3.000, 4.000, 5.000, 6.000, 3.0000, 4.0000, 5.0000, 6.0000</pluralRule>
+ <pluralRule count="many">n = 7..10 @integer 7~10 @decimal 7.0, 8.0, 9.0, 10.0, 7.00, 8.00, 9.00, 10.00, 7.000, 8.000, 9.000, 10.000, 7.0000, 8.0000, 9.0000, 10.0000</pluralRule>
+ <pluralRule count="other"> @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="gv">
+ <pluralRule count="one">v = 0 and i % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, …</pluralRule>
+ <pluralRule count="two">v = 0 and i % 10 = 2 @integer 2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, …</pluralRule>
+ <pluralRule count="few">v = 0 and i % 100 = 0,20,40,60,80 @integer 0, 20, 40, 60, 80, 100, 120, 140, 1000, 10000, 100000, 1000000, …</pluralRule>
+ <pluralRule count="many">v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ <pluralRule count="other"> @integer 3~10, 13~19, 23, 103, 1003, …</pluralRule>
+ </pluralRules>
+
+ <!-- 6: zero,one,two,few,many,other -->
+
+ <pluralRules locales="ar">
+ <pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
+ <pluralRule count="few">n % 100 = 3..10 @integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, …</pluralRule>
+ <pluralRule count="many">n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …</pluralRule>
+ <pluralRule count="other"> @integer 100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ <pluralRules locales="cy">
+ <pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
+ <pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
+ <pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
+ <pluralRule count="few">n = 3 @integer 3 @decimal 3.0, 3.00, 3.000, 3.0000</pluralRule>
+ <pluralRule count="many">n = 6 @integer 6 @decimal 6.0, 6.00, 6.000, 6.0000</pluralRule>
+ <pluralRule count="other"> @integer 4, 5, 7~20, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
+ </pluralRules>
+ </plurals>
+</supplementalData>
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/xml.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/xml.go
new file mode 100644
index 000000000..9d39053c2
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/xml.go
@@ -0,0 +1,143 @@
+package main
+
+import (
+ "encoding/xml"
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+// SupplementalData is the top level struct of plural.xml
+type SupplementalData struct {
+ XMLName xml.Name `xml:"supplementalData"`
+ PluralGroups []PluralGroup `xml:"plurals>pluralRules"`
+}
+
+// PluralGroup is a group of locales with the same plural rules.
+type PluralGroup struct {
+ Locales string `xml:"locales,attr"`
+ PluralRules []PluralRule `xml:"pluralRule"`
+}
+
+// Name returns a unique name for this plural group.
+func (pg *PluralGroup) Name() string {
+ n := strings.Title(pg.Locales)
+ return strings.Replace(n, " ", "", -1)
+}
+
+// SplitLocales returns all the locales in the PluralGroup as a slice.
+func (pg *PluralGroup) SplitLocales() []string {
+ return strings.Split(pg.Locales, " ")
+}
+
+// PluralRule is the rule for a single plural form.
+type PluralRule struct {
+ Count string `xml:"count,attr"`
+ Rule string `xml:",innerxml"`
+}
+
+// CountTitle returns the title case of the PluralRule's count.
+func (pr *PluralRule) CountTitle() string {
+ return strings.Title(pr.Count)
+}
+
+// Condition returns the condition where the PluralRule applies.
+func (pr *PluralRule) Condition() string {
+ i := strings.Index(pr.Rule, "@")
+ return pr.Rule[:i]
+}
+
+// Examples returns the integer and decimal exmaples for the PLuralRule.
+func (pr *PluralRule) Examples() (integer []string, decimal []string) {
+ ex := strings.Replace(pr.Rule, ", …", "", -1)
+ ddelim := "@decimal"
+ if i := strings.Index(ex, ddelim); i > 0 {
+ dex := strings.TrimSpace(ex[i+len(ddelim):])
+ decimal = strings.Split(dex, ", ")
+ ex = ex[:i]
+ }
+ idelim := "@integer"
+ if i := strings.Index(ex, idelim); i > 0 {
+ iex := strings.TrimSpace(ex[i+len(idelim):])
+ integer = strings.Split(iex, ", ")
+ }
+ return integer, decimal
+}
+
+// IntegerExamples returns the integer exmaples for the PLuralRule.
+func (pr *PluralRule) IntegerExamples() []string {
+ integer, _ := pr.Examples()
+ return integer
+}
+
+// DecimalExamples returns the decimal exmaples for the PLuralRule.
+func (pr *PluralRule) DecimalExamples() []string {
+ _, decimal := pr.Examples()
+ return decimal
+}
+
+var relationRegexp = regexp.MustCompile("([niftvw])(?: % ([0-9]+))? (!=|=)(.*)")
+
+// GoCondition converts the XML condition to valid Go code.
+func (pr *PluralRule) GoCondition() string {
+ var ors []string
+ for _, and := range strings.Split(pr.Condition(), "or") {
+ var ands []string
+ for _, relation := range strings.Split(and, "and") {
+ parts := relationRegexp.FindStringSubmatch(relation)
+ if parts == nil {
+ continue
+ }
+ lvar, lmod, op, rhs := strings.Title(parts[1]), parts[2], parts[3], strings.TrimSpace(parts[4])
+ if op == "=" {
+ op = "=="
+ }
+ lvar = "ops." + lvar
+ var rhor []string
+ var rany []string
+ for _, rh := range strings.Split(rhs, ",") {
+ if parts := strings.Split(rh, ".."); len(parts) == 2 {
+ from, to := parts[0], parts[1]
+ if lvar == "ops.N" {
+ if lmod != "" {
+ rhor = append(rhor, fmt.Sprintf("ops.NmodInRange(%s, %s, %s)", lmod, from, to))
+ } else {
+ rhor = append(rhor, fmt.Sprintf("ops.NinRange(%s, %s)", from, to))
+ }
+ } else if lmod != "" {
+ rhor = append(rhor, fmt.Sprintf("intInRange(%s %% %s, %s, %s)", lvar, lmod, from, to))
+ } else {
+ rhor = append(rhor, fmt.Sprintf("intInRange(%s, %s, %s)", lvar, from, to))
+ }
+ } else {
+ rany = append(rany, rh)
+ }
+ }
+
+ if len(rany) > 0 {
+ rh := strings.Join(rany, ",")
+ if lvar == "ops.N" {
+ if lmod != "" {
+ rhor = append(rhor, fmt.Sprintf("ops.NmodEqualsAny(%s, %s)", lmod, rh))
+ } else {
+ rhor = append(rhor, fmt.Sprintf("ops.NequalsAny(%s)", rh))
+ }
+ } else if lmod != "" {
+ rhor = append(rhor, fmt.Sprintf("intEqualsAny(%s %% %s, %s)", lvar, lmod, rh))
+ } else {
+ rhor = append(rhor, fmt.Sprintf("intEqualsAny(%s, %s)", lvar, rh))
+ }
+ }
+ r := strings.Join(rhor, " || ")
+ if len(rhor) > 1 {
+ r = "(" + r + ")"
+ }
+ if op == "!=" {
+ r = "!" + r
+ }
+ ands = append(ands, r)
+ }
+ ors = append(ors, strings.Join(ands, " && "))
+ }
+ return strings.Join(ors, " ||\n")
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go
new file mode 100644
index 000000000..9a155efc5
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go
@@ -0,0 +1,99 @@
+// Package language defines languages that implement CLDR pluralization.
+package language
+
+import (
+ "fmt"
+ "strings"
+)
+
+// Language is a written human language.
+type Language struct {
+ // Tag uniquely identifies the language as defined by RFC 5646.
+ //
+ // Most language tags are a two character language code (ISO 639-1)
+ // optionally followed by a dash and a two character country code (ISO 3166-1).
+ // (e.g. en, pt-br)
+ Tag string
+ *PluralSpec
+}
+
+func (l *Language) String() string {
+ return l.Tag
+}
+
+// MatchingTags returns the set of language tags that map to this Language.
+// e.g. "zh-hans-cn" yields {"zh", "zh-hans", "zh-hans-cn"}
+// BUG: This should be computed once and stored as a field on Language for efficiency,
+// but this would require changing how Languages are constructed.
+func (l *Language) MatchingTags() []string {
+ parts := strings.Split(l.Tag, "-")
+ var prefix, matches []string
+ for _, part := range parts {
+ prefix = append(prefix, part)
+ match := strings.Join(prefix, "-")
+ matches = append(matches, match)
+ }
+ return matches
+}
+
+// Parse returns a slice of supported languages found in src or nil if none are found.
+// It can parse language tags and Accept-Language headers.
+func Parse(src string) []*Language {
+ var langs []*Language
+ start := 0
+ for end, chr := range src {
+ switch chr {
+ case ',', ';', '.':
+ tag := strings.TrimSpace(src[start:end])
+ if spec := getPluralSpec(tag); spec != nil {
+ langs = append(langs, &Language{NormalizeTag(tag), spec})
+ }
+ start = end + 1
+ }
+ }
+ if start > 0 {
+ tag := strings.TrimSpace(src[start:])
+ if spec := getPluralSpec(tag); spec != nil {
+ langs = append(langs, &Language{NormalizeTag(tag), spec})
+ }
+ return dedupe(langs)
+ }
+ if spec := getPluralSpec(src); spec != nil {
+ langs = append(langs, &Language{NormalizeTag(src), spec})
+ }
+ return langs
+}
+
+func dedupe(langs []*Language) []*Language {
+ found := make(map[string]struct{}, len(langs))
+ deduped := make([]*Language, 0, len(langs))
+ for _, lang := range langs {
+ if _, ok := found[lang.Tag]; !ok {
+ found[lang.Tag] = struct{}{}
+ deduped = append(deduped, lang)
+ }
+ }
+ return deduped
+}
+
+// MustParse is similar to Parse except it panics instead of retuning a nil Language.
+func MustParse(src string) []*Language {
+ langs := Parse(src)
+ if len(langs) == 0 {
+ panic(fmt.Errorf("unable to parse language from %q", src))
+ }
+ return langs
+}
+
+// Add adds support for a new language.
+func Add(l *Language) {
+ tag := NormalizeTag(l.Tag)
+ pluralSpecs[tag] = l.PluralSpec
+}
+
+// NormalizeTag returns a language tag with all lower-case characters
+// and dashes "-" instead of underscores "_"
+func NormalizeTag(tag string) string {
+ tag = strings.ToLower(tag)
+ return strings.Replace(tag, "_", "-", -1)
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/language_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language_test.go
new file mode 100644
index 000000000..2949bfe4a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language_test.go
@@ -0,0 +1,85 @@
+package language
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestParse(t *testing.T) {
+ tests := []struct {
+ src string
+ lang []*Language
+ }{
+ {"en", []*Language{{"en", pluralSpecs["en"]}}},
+ {"en-US", []*Language{{"en-us", pluralSpecs["en"]}}},
+ {"en_US", []*Language{{"en-us", pluralSpecs["en"]}}},
+ {"en-GB", []*Language{{"en-gb", pluralSpecs["en"]}}},
+ {"zh-CN", []*Language{{"zh-cn", pluralSpecs["zh"]}}},
+ {"zh-TW", []*Language{{"zh-tw", pluralSpecs["zh"]}}},
+ {"pt-BR", []*Language{{"pt-br", pluralSpecs["pt"]}}},
+ {"pt_BR", []*Language{{"pt-br", pluralSpecs["pt"]}}},
+ {"pt-PT", []*Language{{"pt-pt", pluralSpecs["pt-pt"]}}},
+ {"pt_PT", []*Language{{"pt-pt", pluralSpecs["pt-pt"]}}},
+ {"zh-Hans-CN", []*Language{{"zh-hans-cn", pluralSpecs["zh"]}}},
+ {"zh-Hant-TW", []*Language{{"zh-hant-tw", pluralSpecs["zh"]}}},
+ {"en-US-en-US", []*Language{{"en-us-en-us", pluralSpecs["en"]}}},
+ {".en-US..en-US.", []*Language{{"en-us", pluralSpecs["en"]}}},
+ {
+ "it, xx-zz, xx-ZZ, zh, en-gb;q=0.8, en;q=0.7, es-ES;q=0.6, de-xx",
+ []*Language{
+ {"it", pluralSpecs["it"]},
+ {"zh", pluralSpecs["zh"]},
+ {"en-gb", pluralSpecs["en"]},
+ {"en", pluralSpecs["en"]},
+ {"es-es", pluralSpecs["es"]},
+ {"de-xx", pluralSpecs["de"]},
+ },
+ },
+ {
+ "it-qq,xx,xx-zz,xx-ZZ,zh,en-gb;q=0.8,en;q=0.7,es-ES;q=0.6,de-xx",
+ []*Language{
+ {"it-qq", pluralSpecs["it"]},
+ {"zh", pluralSpecs["zh"]},
+ {"en-gb", pluralSpecs["en"]},
+ {"en", pluralSpecs["en"]},
+ {"es-es", pluralSpecs["es"]},
+ {"de-xx", pluralSpecs["de"]},
+ },
+ },
+ {"en.json", []*Language{{"en", pluralSpecs["en"]}}},
+ {"en-US.json", []*Language{{"en-us", pluralSpecs["en"]}}},
+ {"en-us.json", []*Language{{"en-us", pluralSpecs["en"]}}},
+ {"en-xx.json", []*Language{{"en-xx", pluralSpecs["en"]}}},
+ {"xx-Yyen-US", nil},
+ {"en US", nil},
+ {"", nil},
+ {"-", nil},
+ {"_", nil},
+ {"-en", nil},
+ {"_en", nil},
+ {"-en-", nil},
+ {"_en_", nil},
+ {"xx", nil},
+ }
+ for _, test := range tests {
+ lang := Parse(test.src)
+ if !reflect.DeepEqual(lang, test.lang) {
+ t.Errorf("Parse(%q) = %s expected %s", test.src, lang, test.lang)
+ }
+ }
+}
+
+func TestMatchingTags(t *testing.T) {
+ tests := []struct {
+ lang *Language
+ matches []string
+ }{
+ {&Language{"zh-hans-cn", nil}, []string{"zh", "zh-hans", "zh-hans-cn"}},
+ {&Language{"foo", nil}, []string{"foo"}},
+ }
+ for _, test := range tests {
+ if actual := test.lang.MatchingTags(); !reflect.DeepEqual(test.matches, actual) {
+ t.Errorf("matchingTags(%q) = %q expected %q", test.lang.Tag, actual, test.matches)
+ }
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
new file mode 100644
index 000000000..877bcc89d
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
@@ -0,0 +1,119 @@
+package language
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// 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
+}
+
+// NmodEqualAny 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
+}
+
+// NmodEqualAny 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
+}
+
+// NmodInRange 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
+}
+
+func newOperands(v interface{}) (*operands, error) {
+ switch v := v.(type) {
+ case int:
+ return newOperandsInt64(int64(v)), nil
+ case int8:
+ return newOperandsInt64(int64(v)), nil
+ case int16:
+ return newOperandsInt64(int64(v)), nil
+ case int32:
+ return newOperandsInt64(int64(v)), nil
+ case int64:
+ return newOperandsInt64(v), nil
+ case string:
+ return newOperandsString(v)
+ case float32, float64:
+ return nil, fmt.Errorf("floats should be formatted into a string")
+ default:
+ return nil, fmt.Errorf("invalid type %T; expected integer or string", v)
+ }
+}
+
+func newOperandsInt64(i int64) *operands {
+ if i < 0 {
+ i = -i
+ }
+ return &operands{float64(i), i, 0, 0, 0, 0}
+}
+
+func newOperandsString(s string) (*operands, error) {
+ if s[0] == '-' {
+ s = s[1:]
+ }
+ n, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return nil, err
+ }
+ ops := &operands{N: n}
+ parts := strings.SplitN(s, ".", 2)
+ ops.I, err = strconv.ParseInt(parts[0], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ if len(parts) == 1 {
+ return ops, nil
+ }
+ fraction := parts[1]
+ ops.V = int64(len(fraction))
+ for i := ops.V - 1; i >= 0; i-- {
+ if fraction[i] != '0' {
+ ops.W = i + 1
+ break
+ }
+ }
+ if ops.V > 0 {
+ f, err := strconv.ParseInt(fraction, 10, 0)
+ if err != nil {
+ return nil, err
+ }
+ ops.F = f
+ }
+ if ops.W > 0 {
+ t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
+ if err != nil {
+ return nil, err
+ }
+ ops.T = t
+ }
+ return ops, nil
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands_test.go
new file mode 100644
index 000000000..29030876a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands_test.go
@@ -0,0 +1,45 @@
+package language
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestNewOperands(t *testing.T) {
+ tests := []struct {
+ input interface{}
+ ops *operands
+ err bool
+ }{
+ {int64(0), &operands{0.0, 0, 0, 0, 0, 0}, false},
+ {int64(1), &operands{1.0, 1, 0, 0, 0, 0}, false},
+ {"0", &operands{0.0, 0, 0, 0, 0, 0}, false},
+ {"1", &operands{1.0, 1, 0, 0, 0, 0}, false},
+ {"1.0", &operands{1.0, 1, 1, 0, 0, 0}, false},
+ {"1.00", &operands{1.0, 1, 2, 0, 0, 0}, false},
+ {"1.3", &operands{1.3, 1, 1, 1, 3, 3}, false},
+ {"1.30", &operands{1.3, 1, 2, 1, 30, 3}, false},
+ {"1.03", &operands{1.03, 1, 2, 2, 3, 3}, false},
+ {"1.230", &operands{1.23, 1, 3, 2, 230, 23}, false},
+ {"20.0230", &operands{20.023, 20, 4, 3, 230, 23}, false},
+ {20.0230, nil, true},
+ }
+ for _, test := range tests {
+ ops, err := newOperands(test.input)
+ if err != nil && !test.err {
+ t.Errorf("newOperands(%#v) unexpected error: %s", test.input, err)
+ } else if err == nil && test.err {
+ t.Errorf("newOperands(%#v) returned %#v; expected error", test.input, ops)
+ } else if !reflect.DeepEqual(ops, test.ops) {
+ t.Errorf("newOperands(%#v) returned %#v; expected %#v", test.input, ops, test.ops)
+ }
+ }
+}
+
+func BenchmarkNewOperand(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if _, err := newOperands("1234.56780000"); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go
new file mode 100644
index 000000000..1f3ea5c69
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go
@@ -0,0 +1,40 @@
+package language
+
+import (
+ "fmt"
+)
+
+// Plural represents a language pluralization form as defined here:
+// http://cldr.unicode.org/index/cldr-spec/plural-rules
+type Plural string
+
+// All defined plural categories.
+const (
+ Invalid Plural = "invalid"
+ Zero = "zero"
+ One = "one"
+ Two = "two"
+ Few = "few"
+ Many = "many"
+ Other = "other"
+)
+
+// NewPlural returns src as a Plural
+// or Invalid and a non-nil error if src is not a valid Plural.
+func NewPlural(src string) (Plural, error) {
+ switch src {
+ case "zero":
+ return Zero, nil
+ case "one":
+ return One, nil
+ case "two":
+ return Two, nil
+ case "few":
+ return Few, nil
+ case "many":
+ return Many, nil
+ case "other":
+ return Other, nil
+ }
+ return Invalid, fmt.Errorf("invalid plural category %s", src)
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural_test.go
new file mode 100644
index 000000000..6336d29b2
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural_test.go
@@ -0,0 +1,28 @@
+package language
+
+import (
+ "testing"
+)
+
+func TestNewPlural(t *testing.T) {
+ tests := []struct {
+ src string
+ plural Plural
+ err bool
+ }{
+ {"zero", Zero, false},
+ {"one", One, false},
+ {"two", Two, false},
+ {"few", Few, false},
+ {"many", Many, false},
+ {"other", Other, false},
+ {"asdf", Invalid, true},
+ }
+ for _, test := range tests {
+ plural, err := NewPlural(test.src)
+ wrongErr := (err != nil && !test.err) || (err == nil && test.err)
+ if plural != test.plural || wrongErr {
+ t.Errorf("NewPlural(%#v) returned %#v,%#v; expected %#v", test.src, plural, err, test.plural)
+ }
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go
new file mode 100644
index 000000000..fc3522682
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go
@@ -0,0 +1,74 @@
+package language
+
+import "strings"
+
+// PluralSpec defines the CLDR plural rules for a language.
+// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
+// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
+type PluralSpec struct {
+ Plurals map[Plural]struct{}
+ PluralFunc func(*operands) Plural
+}
+
+var pluralSpecs = make(map[string]*PluralSpec)
+
+func normalizePluralSpecID(id string) string {
+ id = strings.Replace(id, "_", "-", -1)
+ id = strings.ToLower(id)
+ return id
+}
+
+func registerPluralSpec(ids []string, ps *PluralSpec) {
+ for _, id := range ids {
+ id = normalizePluralSpecID(id)
+ pluralSpecs[id] = ps
+ }
+}
+
+// Plural returns the plural category for number as defined by
+// the language's CLDR plural rules.
+func (ps *PluralSpec) Plural(number interface{}) (Plural, error) {
+ ops, err := newOperands(number)
+ if err != nil {
+ return Invalid, err
+ }
+ return ps.PluralFunc(ops), nil
+}
+
+// getPluralSpec returns the PluralSpec that matches the longest prefix of tag.
+// It returns nil if no PluralSpec matches tag.
+func getPluralSpec(tag string) *PluralSpec {
+ tag = NormalizeTag(tag)
+ subtag := tag
+ for {
+ if spec := pluralSpecs[subtag]; spec != nil {
+ return spec
+ }
+ end := strings.LastIndex(subtag, "-")
+ if end == -1 {
+ return nil
+ }
+ subtag = subtag[:end]
+ }
+}
+
+func newPluralSet(plurals ...Plural) map[Plural]struct{} {
+ set := make(map[Plural]struct{}, len(plurals))
+ for _, plural := range plurals {
+ set[plural] = struct{}{}
+ }
+ return set
+}
+
+func intInRange(i, from, to int64) bool {
+ return from <= i && i <= to
+}
+
+func intEqualsAny(i int64, any ...int64) bool {
+ for _, a := range any {
+ if i == a {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go
new file mode 100644
index 000000000..c9b4f2667
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go
@@ -0,0 +1,567 @@
+package language
+
+// This file is generated by i18n/language/codegen/generate.sh
+
+func init() {
+
+ registerPluralSpec([]string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "zh"}, &PluralSpec{
+ Plurals: newPluralSet(Other),
+ PluralFunc: func(ops *operands) Plural {
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"am", "as", "bn", "fa", "gu", "hi", "kn", "mr", "zu"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 0 or n = 1
+ if intEqualsAny(ops.I, 0) ||
+ ops.NequalsAny(1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ff", "fr", "hy", "kab"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 0,1
+ if intEqualsAny(ops.I, 0, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "it", "ji", "nl", "sv", "sw", "ur", "yi"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"si"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0,1 or i = 0 and f = 1
+ if ops.NequalsAny(0, 1) ||
+ intEqualsAny(ops.I, 0) && intEqualsAny(ops.F, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0..1
+ if ops.NinRange(0, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"tzm"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0..1 or n = 11..99
+ if ops.NinRange(0, 1) ||
+ ops.NinRange(11, 99) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"pt"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0..2 and n != 2
+ if ops.NinRange(0, 2) && !ops.NequalsAny(2) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"pt_PT"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1 and v = 0
+ if ops.NequalsAny(1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"da"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1 or t != 0 and i = 0,1
+ if ops.NequalsAny(1) ||
+ !intEqualsAny(ops.T, 0) && intEqualsAny(ops.I, 0, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"is"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0
+ if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
+ !intEqualsAny(ops.T, 0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"mk"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1 or f % 10 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) ||
+ intEqualsAny(ops.F%10, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"fil", "tl"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I, 1, 2, 3) ||
+ intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I%10, 4, 6, 9) ||
+ !intEqualsAny(ops.V, 0) && !intEqualsAny(ops.F%10, 4, 6, 9) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"lv", "prg"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19
+ if ops.NmodEqualsAny(10, 0) ||
+ ops.NmodInRange(100, 11, 19) ||
+ intEqualsAny(ops.V, 2) && intInRange(ops.F%100, 11, 19) {
+ return Zero
+ }
+ // n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) ||
+ intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) ||
+ !intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"lag"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // i = 0,1 and n != 0
+ if intEqualsAny(ops.I, 0, 1) && !ops.NequalsAny(0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ksh"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"iu", "kw", "naq", "se", "sma", "smi", "smj", "smn", "sms"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"shi"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 0 or n = 1
+ if intEqualsAny(ops.I, 0) ||
+ ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2..10
+ if ops.NinRange(2, 10) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"mo", "ro"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // v != 0 or n = 0 or n != 1 and n % 100 = 1..19
+ if !intEqualsAny(ops.V, 0) ||
+ ops.NequalsAny(0) ||
+ !ops.NequalsAny(1) && ops.NmodInRange(100, 1, 19) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"bs", "hr", "sh", "sr"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
+ intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) {
+ return One
+ }
+ // v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) ||
+ intInRange(ops.F%10, 2, 4) && !intInRange(ops.F%100, 12, 14) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"gd"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1,11
+ if ops.NequalsAny(1, 11) {
+ return One
+ }
+ // n = 2,12
+ if ops.NequalsAny(2, 12) {
+ return Two
+ }
+ // n = 3..10,13..19
+ if ops.NinRange(3, 10) || ops.NinRange(13, 19) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"sl"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 100 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) {
+ return One
+ }
+ // v = 0 and i % 100 = 2
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) {
+ return Two
+ }
+ // v = 0 and i % 100 = 3..4 or v != 0
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
+ !intEqualsAny(ops.V, 0) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"dsb", "hsb"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 100 = 1 or f % 100 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) ||
+ intEqualsAny(ops.F%100, 1) {
+ return One
+ }
+ // v = 0 and i % 100 = 2 or f % 100 = 2
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) ||
+ intEqualsAny(ops.F%100, 2) {
+ return Two
+ }
+ // v = 0 and i % 100 = 3..4 or f % 100 = 3..4
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
+ intInRange(ops.F%100, 3, 4) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"he", "iw"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // i = 2 and v = 0
+ if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) {
+ return Two
+ }
+ // v = 0 and n != 0..10 and n % 10 = 0
+ if intEqualsAny(ops.V, 0) && !ops.NinRange(0, 10) && ops.NmodEqualsAny(10, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"cs", "sk"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // i = 2..4 and v = 0
+ if intInRange(ops.I, 2, 4) && intEqualsAny(ops.V, 0) {
+ return Few
+ }
+ // v != 0
+ if !intEqualsAny(ops.V, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"pl"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // v = 0 and i % 10 = 2..4 and i % 100 != 12..14
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
+ return Few
+ }
+ // v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14
+ if intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I, 1) && intInRange(ops.I%10, 0, 1) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 12, 14) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"be"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 1 and n % 100 != 11
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) {
+ return One
+ }
+ // n % 10 = 2..4 and n % 100 != 12..14
+ if ops.NmodInRange(10, 2, 4) && !ops.NmodInRange(100, 12, 14) {
+ return Few
+ }
+ // n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14
+ if ops.NmodEqualsAny(10, 0) ||
+ ops.NmodInRange(10, 5, 9) ||
+ ops.NmodInRange(100, 11, 14) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"lt"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 1 and n % 100 != 11..19
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodInRange(100, 11, 19) {
+ return One
+ }
+ // n % 10 = 2..9 and n % 100 != 11..19
+ if ops.NmodInRange(10, 2, 9) && !ops.NmodInRange(100, 11, 19) {
+ return Few
+ }
+ // f != 0
+ if !intEqualsAny(ops.F, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"mt"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 0 or n % 100 = 2..10
+ if ops.NequalsAny(0) ||
+ ops.NmodInRange(100, 2, 10) {
+ return Few
+ }
+ // n % 100 = 11..19
+ if ops.NmodInRange(100, 11, 19) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ru", "uk"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1 and i % 100 != 11
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) {
+ return One
+ }
+ // v = 0 and i % 10 = 2..4 and i % 100 != 12..14
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
+ return Few
+ }
+ // v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 0) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 11, 14) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"br"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 1 and n % 100 != 11,71,91
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11, 71, 91) {
+ return One
+ }
+ // n % 10 = 2 and n % 100 != 12,72,92
+ if ops.NmodEqualsAny(10, 2) && !ops.NmodEqualsAny(100, 12, 72, 92) {
+ return Two
+ }
+ // n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99
+ if (ops.NmodInRange(10, 3, 4) || ops.NmodEqualsAny(10, 9)) && !(ops.NmodInRange(100, 10, 19) || ops.NmodInRange(100, 70, 79) || ops.NmodInRange(100, 90, 99)) {
+ return Few
+ }
+ // n != 0 and n % 1000000 = 0
+ if !ops.NequalsAny(0) && ops.NmodEqualsAny(1000000, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ga"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ // n = 3..6
+ if ops.NinRange(3, 6) {
+ return Few
+ }
+ // n = 7..10
+ if ops.NinRange(7, 10) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"gv"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) {
+ return One
+ }
+ // v = 0 and i % 10 = 2
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 2) {
+ return Two
+ }
+ // v = 0 and i % 100 = 0,20,40,60,80
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 0, 20, 40, 60, 80) {
+ return Few
+ }
+ // v != 0
+ if !intEqualsAny(ops.V, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ar"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ // n % 100 = 3..10
+ if ops.NmodInRange(100, 3, 10) {
+ return Few
+ }
+ // n % 100 = 11..99
+ if ops.NmodInRange(100, 11, 99) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"cy"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ // n = 3
+ if ops.NequalsAny(3) {
+ return Few
+ }
+ // n = 6
+ if ops.NequalsAny(6) {
+ return Many
+ }
+ return Other
+ },
+ })
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen_test.go
new file mode 100644
index 000000000..c8ec41fd4
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen_test.go
@@ -0,0 +1,645 @@
+package language
+
+// This file is generated by i18n/language/codegen/generate.sh
+
+import "testing"
+
+func TestBmBoDzIdIgIiInJaJboJvJwKdeKeaKmKoLktLoMsMyNqoRootSahSesSgThToViWoYoZh(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, Other, []string{"0~15", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "zh"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestAmAsBnFaGuHiKnMrZu(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1"})
+ tests = appendDecimalTests(tests, One, []string{"0.0~1.0", "0.00~0.04"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"1.1~2.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"am", "as", "bn", "fa", "gu", "hi", "kn", "mr", "zu"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestFfFrHyKab(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1"})
+ tests = appendDecimalTests(tests, One, []string{"0.0~1.5"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"2.0~3.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ff", "fr", "hy", "kab"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestAstCaDeEnEtFiFyGlItJiNlSvSwUrYi(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "it", "ji", "nl", "sv", "sw", "ur", "yi"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestSi(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1"})
+ tests = appendDecimalTests(tests, One, []string{"0.0", "0.1", "1.0", "0.00", "0.01", "1.00", "0.000", "0.001", "1.000", "0.0000", "0.0001", "1.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.2~0.9", "1.1~1.8", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"si"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestAkBhGuwLnMgNsoPaTiWa(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1"})
+ tests = appendDecimalTests(tests, One, []string{"0.0", "1.0", "0.00", "1.00", "0.000", "1.000", "0.0000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestTzm(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1", "11~24"})
+ tests = appendDecimalTests(tests, One, []string{"0.0", "1.0", "11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "19.0", "20.0", "21.0", "22.0", "23.0", "24.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~10", "100~106", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"tzm"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestPt(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1"})
+ tests = appendDecimalTests(tests, One, []string{"0.0", "1.0", "0.00", "1.00", "0.000", "1.000", "0.0000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"pt"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestAfAsaAzBemBezBgBrxCeCggChrCkbDvEeElEoEsEuFoFurGswHaHawHuJgoJmcKaKajKcgKkKkjKlKsKsbKuKyLbLgMasMgoMlMnNahNbNdNeNnNnhNoNrNyNynOmOrOsPapPsRmRofRwkSaqSdhSehSnSoSqSsSsyStSyrTaTeTeoTigTkTnTrTsUgUzVeVoVunWaeXhXog(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestPt_PT(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"pt_PT"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestDa(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"0.1~1.6"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0", "2.0~3.4", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"da"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestIs(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"0.1~1.6", "10.1", "100.1", "1000.1"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"is"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestMk(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "11", "21", "31", "41", "51", "61", "71", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"0.1", "1.1", "2.1", "3.1", "4.1", "5.1", "6.1", "7.1", "10.1", "100.1", "1000.1"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "2~10", "12~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0", "0.2~1.0", "1.2~1.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"mk"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestFilTl(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0~3", "5", "7", "8", "10~13", "15", "17", "18", "20", "21", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, One, []string{"0.0~0.3", "0.5", "0.7", "0.8", "1.0~1.3", "1.5", "1.7", "1.8", "2.0", "2.1", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"4", "6", "9", "14", "16", "19", "24", "26", "104", "1004"})
+ tests = appendDecimalTests(tests, Other, []string{"0.4", "0.6", "0.9", "1.4", "1.6", "1.9", "2.4", "2.6", "10.4", "100.4", "1000.4"})
+
+ locales := []string{"fil", "tl"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestLvPrg(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, Zero, []string{"0", "10~20", "30", "40", "50", "60", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Zero, []string{"0.0", "10.0", "11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"0.1", "1.0", "1.1", "2.1", "3.1", "4.1", "5.1", "6.1", "7.1", "10.1", "100.1", "1000.1"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~9", "22~29", "102", "1002"})
+ tests = appendDecimalTests(tests, Other, []string{"0.2~0.9", "1.2~1.9", "10.2", "100.2", "1000.2"})
+
+ locales := []string{"lv", "prg"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestLag(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, Zero, []string{"0"})
+ tests = appendDecimalTests(tests, Zero, []string{"0.0", "0.00", "0.000", "0.0000"})
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"0.1~1.6"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"2.0~3.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"lag"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestKsh(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, Zero, []string{"0"})
+ tests = appendDecimalTests(tests, Zero, []string{"0.0", "0.00", "0.000", "0.0000"})
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ksh"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestIuKwNaqSeSmaSmiSmjSmnSms(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2"})
+ tests = appendDecimalTests(tests, Two, []string{"2.0", "2.00", "2.000", "2.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "3~17", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"iu", "kw", "naq", "se", "sma", "smi", "smj", "smn", "sms"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestShi(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"0", "1"})
+ tests = appendDecimalTests(tests, One, []string{"0.0~1.0", "0.00~0.04"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~10"})
+ tests = appendDecimalTests(tests, Few, []string{"2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "2.00", "3.00", "4.00", "5.00", "6.00", "7.00", "8.00"})
+
+ tests = appendIntegerTests(tests, Other, []string{"11~26", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"1.1~1.9", "2.1~2.7", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"shi"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestMoRo(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+
+ tests = appendIntegerTests(tests, Few, []string{"0", "2~16", "101", "1001"})
+ tests = appendDecimalTests(tests, Few, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"20~35", "100", "1000", "10000", "100000", "1000000"})
+
+ locales := []string{"mo", "ro"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestBsHrShSr(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"0.1", "1.1", "2.1", "3.1", "4.1", "5.1", "6.1", "7.1", "10.1", "100.1", "1000.1"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~4", "22~24", "32~34", "42~44", "52~54", "62", "102", "1002"})
+ tests = appendDecimalTests(tests, Few, []string{"0.2~0.4", "1.2~1.4", "2.2~2.4", "3.2~3.4", "4.2~4.4", "5.2", "10.2", "100.2", "1000.2"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0", "0.5~1.0", "1.5~2.0", "2.5~2.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"bs", "hr", "sh", "sr"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestGd(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "11"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "11.0", "1.00", "11.00", "1.000", "11.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2", "12"})
+ tests = appendDecimalTests(tests, Two, []string{"2.0", "12.0", "2.00", "12.00", "2.000", "12.000", "2.0000"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3~10", "13~19"})
+ tests = appendDecimalTests(tests, Few, []string{"3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "19.0", "3.00"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "20~34", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"gd"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestSl(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "101", "201", "301", "401", "501", "601", "701", "1001"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2", "102", "202", "302", "402", "502", "602", "702", "1002"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3", "4", "103", "104", "203", "204", "303", "304", "403", "404", "503", "504", "603", "604", "703", "704", "1003"})
+ tests = appendDecimalTests(tests, Few, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+
+ locales := []string{"sl"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestDsbHsb(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "101", "201", "301", "401", "501", "601", "701", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"0.1", "1.1", "2.1", "3.1", "4.1", "5.1", "6.1", "7.1", "10.1", "100.1", "1000.1"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2", "102", "202", "302", "402", "502", "602", "702", "1002"})
+ tests = appendDecimalTests(tests, Two, []string{"0.2", "1.2", "2.2", "3.2", "4.2", "5.2", "6.2", "7.2", "10.2", "100.2", "1000.2"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3", "4", "103", "104", "203", "204", "303", "304", "403", "404", "503", "504", "603", "604", "703", "704", "1003"})
+ tests = appendDecimalTests(tests, Few, []string{"0.3", "0.4", "1.3", "1.4", "2.3", "2.4", "3.3", "3.4", "4.3", "4.4", "5.3", "5.4", "6.3", "6.4", "7.3", "7.4", "10.3", "100.3", "1000.3"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0", "0.5~1.0", "1.5~2.0", "2.5~2.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"dsb", "hsb"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestHeIw(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2"})
+
+ tests = appendIntegerTests(tests, Many, []string{"20", "30", "40", "50", "60", "70", "80", "90", "100", "1000", "10000", "100000", "1000000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "3~17", "101", "1001"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"he", "iw"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestCsSk(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~4"})
+
+ tests = appendDecimalTests(tests, Many, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+
+ locales := []string{"cs", "sk"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestPl(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~4", "22~24", "32~34", "42~44", "52~54", "62", "102", "1002"})
+
+ tests = appendIntegerTests(tests, Many, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+
+ tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"pl"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestBe(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "21.0", "31.0", "41.0", "51.0", "61.0", "71.0", "81.0", "101.0", "1001.0"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~4", "22~24", "32~34", "42~44", "52~54", "62", "102", "1002"})
+ tests = appendDecimalTests(tests, Few, []string{"2.0", "3.0", "4.0", "22.0", "23.0", "24.0", "32.0", "33.0", "102.0", "1002.0"})
+
+ tests = appendIntegerTests(tests, Many, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Many, []string{"0.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "11.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.1", "100.1", "1000.1"})
+
+ locales := []string{"be"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestLt(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "21.0", "31.0", "41.0", "51.0", "61.0", "71.0", "81.0", "101.0", "1001.0"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~9", "22~29", "102", "1002"})
+ tests = appendDecimalTests(tests, Few, []string{"2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "22.0", "102.0", "1002.0"})
+
+ tests = appendDecimalTests(tests, Many, []string{"0.1~0.9", "1.1~1.7", "10.1", "100.1", "1000.1"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "10~20", "30", "40", "50", "60", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0", "10.0", "11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"lt"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestMt(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Few, []string{"0", "2~10", "102~107", "1002"})
+ tests = appendDecimalTests(tests, Few, []string{"0.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "10.0", "102.0", "1002.0"})
+
+ tests = appendIntegerTests(tests, Many, []string{"11~19", "111~117", "1011"})
+ tests = appendDecimalTests(tests, Many, []string{"11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "111.0", "1011.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"20~35", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"mt"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestRuUk(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"})
+
+ tests = appendIntegerTests(tests, Few, []string{"2~4", "22~24", "32~34", "42~44", "52~54", "62", "102", "1002"})
+
+ tests = appendIntegerTests(tests, Many, []string{"0", "5~19", "100", "1000", "10000", "100000", "1000000"})
+
+ tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ru", "uk"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestBr(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "81", "101", "1001"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "21.0", "31.0", "41.0", "51.0", "61.0", "81.0", "101.0", "1001.0"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2", "22", "32", "42", "52", "62", "82", "102", "1002"})
+ tests = appendDecimalTests(tests, Two, []string{"2.0", "22.0", "32.0", "42.0", "52.0", "62.0", "82.0", "102.0", "1002.0"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3", "4", "9", "23", "24", "29", "33", "34", "39", "43", "44", "49", "103", "1003"})
+ tests = appendDecimalTests(tests, Few, []string{"3.0", "4.0", "9.0", "23.0", "24.0", "29.0", "33.0", "34.0", "103.0", "1003.0"})
+
+ tests = appendIntegerTests(tests, Many, []string{"1000000"})
+ tests = appendDecimalTests(tests, Many, []string{"1000000.0", "1000000.00", "1000000.000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "5~8", "10~20", "100", "1000", "10000", "100000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0"})
+
+ locales := []string{"br"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestGa(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2"})
+ tests = appendDecimalTests(tests, Two, []string{"2.0", "2.00", "2.000", "2.0000"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3~6"})
+ tests = appendDecimalTests(tests, Few, []string{"3.0", "4.0", "5.0", "6.0", "3.00", "4.00", "5.00", "6.00", "3.000", "4.000", "5.000", "6.000", "3.0000", "4.0000", "5.0000", "6.0000"})
+
+ tests = appendIntegerTests(tests, Many, []string{"7~10"})
+ tests = appendDecimalTests(tests, Many, []string{"7.0", "8.0", "9.0", "10.0", "7.00", "8.00", "9.00", "10.00", "7.000", "8.000", "9.000", "10.000", "7.0000", "8.0000", "9.0000", "10.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"0", "11~25", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ga"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestGv(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, One, []string{"1", "11", "21", "31", "41", "51", "61", "71", "101", "1001"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2", "12", "22", "32", "42", "52", "62", "72", "102", "1002"})
+
+ tests = appendIntegerTests(tests, Few, []string{"0", "20", "40", "60", "80", "100", "120", "140", "1000", "10000", "100000", "1000000"})
+
+ tests = appendDecimalTests(tests, Many, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"3~10", "13~19", "23", "103", "1003"})
+
+ locales := []string{"gv"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestAr(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, Zero, []string{"0"})
+ tests = appendDecimalTests(tests, Zero, []string{"0.0", "0.00", "0.000", "0.0000"})
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2"})
+ tests = appendDecimalTests(tests, Two, []string{"2.0", "2.00", "2.000", "2.0000"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3~10", "103~110", "1003"})
+ tests = appendDecimalTests(tests, Few, []string{"3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "103.0", "1003.0"})
+
+ tests = appendIntegerTests(tests, Many, []string{"11~26", "111", "1011"})
+ tests = appendDecimalTests(tests, Many, []string{"11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "111.0", "1011.0"})
+
+ tests = appendIntegerTests(tests, Other, []string{"100~102", "200~202", "300~302", "400~402", "500~502", "600", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"ar"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
+
+func TestCy(t *testing.T) {
+ var tests []pluralTest
+
+ tests = appendIntegerTests(tests, Zero, []string{"0"})
+ tests = appendDecimalTests(tests, Zero, []string{"0.0", "0.00", "0.000", "0.0000"})
+
+ tests = appendIntegerTests(tests, One, []string{"1"})
+ tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"})
+
+ tests = appendIntegerTests(tests, Two, []string{"2"})
+ tests = appendDecimalTests(tests, Two, []string{"2.0", "2.00", "2.000", "2.0000"})
+
+ tests = appendIntegerTests(tests, Few, []string{"3"})
+ tests = appendDecimalTests(tests, Few, []string{"3.0", "3.00", "3.000", "3.0000"})
+
+ tests = appendIntegerTests(tests, Many, []string{"6"})
+ tests = appendDecimalTests(tests, Many, []string{"6.0", "6.00", "6.000", "6.0000"})
+
+ tests = appendIntegerTests(tests, Other, []string{"4", "5", "7~20", "100", "1000", "10000", "100000", "1000000"})
+ tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"})
+
+ locales := []string{"cy"}
+ for _, locale := range locales {
+ runTests(t, locale, tests)
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go
new file mode 100644
index 000000000..34931b7bb
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go
@@ -0,0 +1,733 @@
+package language
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+const onePlusEpsilon = "1.00000000000000000000000000000001"
+
+func TestGetPluralSpec(t *testing.T) {
+ tests := []struct {
+ src string
+ spec *PluralSpec
+ }{
+ {"pl", pluralSpecs["pl"]},
+ {"en", pluralSpecs["en"]},
+ {"en-US", pluralSpecs["en"]},
+ {"en_US", pluralSpecs["en"]},
+ {"en-GB", pluralSpecs["en"]},
+ {"zh-CN", pluralSpecs["zh"]},
+ {"zh-TW", pluralSpecs["zh"]},
+ {"pt-BR", pluralSpecs["pt"]},
+ {"pt_BR", pluralSpecs["pt"]},
+ {"pt-PT", pluralSpecs["pt-pt"]},
+ {"pt_PT", pluralSpecs["pt-pt"]},
+ {"zh-Hans-CN", pluralSpecs["zh"]},
+ {"zh-Hant-TW", pluralSpecs["zh"]},
+ {"zh-CN", pluralSpecs["zh"]},
+ {"zh-TW", pluralSpecs["zh"]},
+ {"zh-Hans", pluralSpecs["zh"]},
+ {"zh-Hant", pluralSpecs["zh"]},
+ {"ko-KR", pluralSpecs["ko"]},
+ {"ko_KR", pluralSpecs["ko"]},
+ {"ko-KP", pluralSpecs["ko"]},
+ {"ko_KP", pluralSpecs["ko"]},
+ {"en-US-en-US", pluralSpecs["en"]},
+ {"th", pluralSpecs["th"]},
+ {"th-TH", pluralSpecs["th"]},
+ {"hr", pluralSpecs["hr"]},
+ {"bs", pluralSpecs["bs"]},
+ {"sr", pluralSpecs["sr"]},
+ {"ti", pluralSpecs["ti"]},
+ {"vi", pluralSpecs["vi"]},
+ {"vi-VN", pluralSpecs["vi"]},
+ {"mk", pluralSpecs["mk"]},
+ {"mk-MK", pluralSpecs["mk"]},
+ {"lv", pluralSpecs["lv"]},
+ {"lv-LV", pluralSpecs["lv"]},
+ {".en-US..en-US.", nil},
+ {"zh, en-gb;q=0.8, en;q=0.7", nil},
+ {"zh,en-gb;q=0.8,en;q=0.7", nil},
+ {"xx, en-gb;q=0.8, en;q=0.7", nil},
+ {"xx,en-gb;q=0.8,en;q=0.7", nil},
+ {"xx-YY,xx;q=0.8,en-US,en;q=0.8,de;q=0.6,nl;q=0.4", nil},
+ {"/foo/es/en.json", nil},
+ {"xx-Yyen-US", nil},
+ {"en US", nil},
+ {"", nil},
+ {"-", nil},
+ {"_", nil},
+ {".", nil},
+ {"-en", nil},
+ {"_en", nil},
+ {"-en-", nil},
+ {"_en_", nil},
+ {"xx", nil},
+ }
+ for _, test := range tests {
+ spec := getPluralSpec(test.src)
+ if spec != test.spec {
+ t.Errorf("getPluralSpec(%q) = %q expected %q", test.src, spec, test.spec)
+ }
+ }
+}
+
+type pluralTest struct {
+ num interface{}
+ plural Plural
+}
+
+func appendIntegerTests(tests []pluralTest, plural Plural, examples []string) []pluralTest {
+ for _, ex := range expandExamples(examples) {
+ i, err := strconv.ParseInt(ex, 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ tests = append(tests, pluralTest{ex, plural}, pluralTest{i, plural})
+ }
+ return tests
+}
+
+func appendDecimalTests(tests []pluralTest, plural Plural, examples []string) []pluralTest {
+ for _, ex := range expandExamples(examples) {
+ tests = append(tests, pluralTest{ex, plural})
+ }
+ return tests
+}
+
+func expandExamples(examples []string) []string {
+ var expanded []string
+ for _, ex := range examples {
+ if parts := strings.Split(ex, "~"); len(parts) == 2 {
+ for ex := parts[0]; ; ex = increment(ex) {
+ expanded = append(expanded, ex)
+ if ex == parts[1] {
+ break
+ }
+ }
+ } else {
+ expanded = append(expanded, ex)
+ }
+ }
+ return expanded
+}
+
+func increment(dec string) string {
+ runes := []rune(dec)
+ carry := true
+ for i := len(runes) - 1; carry && i >= 0; i-- {
+ switch runes[i] {
+ case '.':
+ continue
+ case '9':
+ runes[i] = '0'
+ default:
+ runes[i]++
+ carry = false
+ }
+ }
+ if carry {
+ runes = append([]rune{'1'}, runes...)
+ }
+ return string(runes)
+}
+
+//
+// Below here are tests that were manually written before tests were automatically generated.
+// These are kept around as sanity checks for our code generation.
+//
+
+func TestArabic(t *testing.T) {
+ tests := []pluralTest{
+ {0, Zero},
+ {"0", Zero},
+ {"0.0", Zero},
+ {"0.00", Zero},
+ {1, One},
+ {"1", One},
+ {"1.0", One},
+ {"1.00", One},
+ {onePlusEpsilon, Other},
+ {2, Two},
+ {"2", Two},
+ {"2.0", Two},
+ {"2.00", Two},
+ {3, Few},
+ {"3", Few},
+ {"3.0", Few},
+ {"3.00", Few},
+ {10, Few},
+ {"10", Few},
+ {"10.0", Few},
+ {"10.00", Few},
+ {103, Few},
+ {"103", Few},
+ {"103.0", Few},
+ {"103.00", Few},
+ {110, Few},
+ {"110", Few},
+ {"110.0", Few},
+ {"110.00", Few},
+ {11, Many},
+ {"11", Many},
+ {"11.0", Many},
+ {"11.00", Many},
+ {99, Many},
+ {"99", Many},
+ {"99.0", Many},
+ {"99.00", Many},
+ {111, Many},
+ {"111", Many},
+ {"111.0", Many},
+ {"111.00", Many},
+ {199, Many},
+ {"199", Many},
+ {"199.0", Many},
+ {"199.00", Many},
+ {100, Other},
+ {"100", Other},
+ {"100.0", Other},
+ {"100.00", Other},
+ {102, Other},
+ {"102", Other},
+ {"102.0", Other},
+ {"102.00", Other},
+ {200, Other},
+ {"200", Other},
+ {"200.0", Other},
+ {"200.00", Other},
+ {202, Other},
+ {"202", Other},
+ {"202.0", Other},
+ {"202.00", Other},
+ }
+ tests = appendFloatTests(tests, 0.1, 0.9, Other)
+ tests = appendFloatTests(tests, 1.1, 1.9, Other)
+ tests = appendFloatTests(tests, 2.1, 2.9, Other)
+ tests = appendFloatTests(tests, 3.1, 3.9, Other)
+ tests = appendFloatTests(tests, 4.1, 4.9, Other)
+ runTests(t, "ar", tests)
+}
+
+func TestBelarusian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Many},
+ {1, One},
+ {2, Few},
+ {3, Few},
+ {4, Few},
+ {5, Many},
+ {19, Many},
+ {20, Many},
+ {21, One},
+ {11, Many},
+ {52, Few},
+ {101, One},
+ {"0.1", Other},
+ {"0.7", Other},
+ {"1.5", Other},
+ {"1.0", One},
+ {onePlusEpsilon, Other},
+ {"2.0", Few},
+ {"10.0", Many},
+ }
+ runTests(t, "be", tests)
+}
+
+func TestBurmese(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "my", tests)
+}
+
+func TestCatalan(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {"0", Other},
+ {1, One},
+ {"1", One},
+ {"1.0", Other},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ {"2", Other},
+ }
+ tests = appendIntTests(tests, 2, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "ca", tests)
+}
+
+func TestChinese(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "zh", tests)
+}
+
+func TestCzech(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {"0", Other},
+ {1, One},
+ {"1", One},
+ {onePlusEpsilon, Many},
+ {2, Few},
+ {"2", Few},
+ {3, Few},
+ {"3", Few},
+ {4, Few},
+ {"4", Few},
+ {5, Other},
+ {"5", Other},
+ }
+ tests = appendFloatTests(tests, 0, 10, Many)
+ runTests(t, "cs", tests)
+}
+
+func TestDanish(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {onePlusEpsilon, One},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.1, 1.9, One)
+ tests = appendFloatTests(tests, 2.0, 10.0, Other)
+ runTests(t, "da", tests)
+}
+
+func TestDutch(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 10.0, Other)
+ runTests(t, "nl", tests)
+}
+
+func TestEnglish(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 10.0, Other)
+ runTests(t, "en", tests)
+}
+
+func TestFrench(t *testing.T) {
+ tests := []pluralTest{
+ {0, One},
+ {1, One},
+ {onePlusEpsilon, One},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 1.9, One)
+ tests = appendFloatTests(tests, 2.0, 10.0, Other)
+ runTests(t, "fr", tests)
+}
+
+func TestGerman(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 10.0, Other)
+ runTests(t, "de", tests)
+}
+
+func TestIcelandic(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {2, Other},
+ {11, Other},
+ {21, One},
+ {111, Other},
+ {"0.0", Other},
+ {"0.1", One},
+ {"2.0", Other},
+ }
+ runTests(t, "is", tests)
+}
+
+func TestIndonesian(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "id", tests)
+}
+
+func TestItalian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 10.0, Other)
+ runTests(t, "it", tests)
+}
+
+func TestKorean(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "ko", tests)
+}
+
+func TestLatvian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Zero},
+ {"0", Zero},
+ {"0.1", One},
+ {1, One},
+ {"1", One},
+ {onePlusEpsilon, One},
+ {"10.0", Zero},
+ {"10.1", One},
+ {"10.2", Other},
+ {21, One},
+ }
+ tests = appendFloatTests(tests, 0.2, 0.9, Other)
+ tests = appendFloatTests(tests, 1.2, 1.9, Other)
+ tests = appendIntTests(tests, 2, 9, Other)
+ tests = appendIntTests(tests, 10, 20, Zero)
+ tests = appendIntTests(tests, 22, 29, Other)
+ runTests(t, "lv", tests)
+}
+
+func TestJapanese(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "ja", tests)
+}
+
+func TestLithuanian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {2, Few},
+ {3, Few},
+ {9, Few},
+ {10, Other},
+ {11, Other},
+ {"0.1", Many},
+ {"0.7", Many},
+ {"1.0", One},
+ {onePlusEpsilon, Many},
+ {"2.0", Few},
+ {"10.0", Other},
+ }
+ runTests(t, "lt", tests)
+}
+
+func TestMalay(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "ms", tests)
+}
+
+func TestPolish(t *testing.T) {
+ tests := []pluralTest{
+ {0, Many},
+ {1, One},
+ {2, Few},
+ {3, Few},
+ {4, Few},
+ {5, Many},
+ {19, Many},
+ {20, Many},
+ {10, Many},
+ {11, Many},
+ {52, Few},
+ {"0.1", Other},
+ {"0.7", Other},
+ {"1.5", Other},
+ {"1.0", Other},
+ {onePlusEpsilon, Other},
+ {"2.0", Other},
+ {"10.0", Other},
+ }
+ runTests(t, "pl", tests)
+}
+
+func TestPortuguese(t *testing.T) {
+ tests := []pluralTest{
+ {0, One},
+ {"0.0", One},
+ {1, One},
+ {"1.0", One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.1, 0.9, Other)
+ tests = appendFloatTests(tests, 1.1, 10.0, Other)
+ runTests(t, "pt", tests)
+}
+
+func TestMacedonian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {"1.1", One},
+ {"2.1", One},
+ {onePlusEpsilon, One},
+ {2, Other},
+ {"2.2", Other},
+ {11, One},
+ }
+ runTests(t, "mk", tests)
+}
+
+func TestPortugueseEuropean(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {"0.0", Other},
+ {"0.1", Other},
+ {"0.01", Other},
+ {1, One},
+ {"1", One},
+ {"1.1", Other},
+ {"1.01", Other},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 2.0, 10.0, Other)
+ runTests(t, "pt-pt", tests)
+}
+
+func TestRussian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Many},
+ {1, One},
+ {2, Few},
+ {3, Few},
+ {4, Few},
+ {5, Many},
+ {19, Many},
+ {20, Many},
+ {21, One},
+ {11, Many},
+ {52, Few},
+ {101, One},
+ {"0.1", Other},
+ {"0.7", Other},
+ {"1.5", Other},
+ {"1.0", Other},
+ {onePlusEpsilon, Other},
+ {"2.0", Other},
+ {"10.0", Other},
+ }
+ runTests(t, "ru", tests)
+}
+
+func TestSpanish(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {"1", One},
+ {"1.0", One},
+ {"1.00", One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 0.9, Other)
+ tests = appendFloatTests(tests, 1.1, 10.0, Other)
+ runTests(t, "es", tests)
+}
+
+func TestNorweigan(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {"1", One},
+ {"1.0", One},
+ {"1.00", One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 0.9, Other)
+ tests = appendFloatTests(tests, 1.1, 10.0, Other)
+ runTests(t, "no", tests)
+}
+
+func TestBulgarian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {2, Other},
+ {3, Other},
+ {9, Other},
+ {10, Other},
+ {11, Other},
+ {"0.1", Other},
+ {"0.7", Other},
+ {"1.0", One},
+ {"1.001", Other},
+ {onePlusEpsilon, Other},
+ {"1.1", Other},
+ {"2.0", Other},
+ {"10.0", Other},
+ }
+ runTests(t, "bg", tests)
+}
+
+func TestSwedish(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ }
+ tests = appendFloatTests(tests, 0.0, 10.0, Other)
+ runTests(t, "sv", tests)
+}
+
+func TestThai(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "th", tests)
+}
+
+func TestVietnamese(t *testing.T) {
+ tests := appendIntTests(nil, 0, 10, Other)
+ tests = appendFloatTests(tests, 0, 10, Other)
+ runTests(t, "vi", tests)
+}
+
+func TestTurkish(t *testing.T) {
+ tests := []pluralTest{
+ {0, Other},
+ {1, One},
+ {"1", One},
+ {"1.0", One},
+ {"1.00", One},
+ {"1.001", Other},
+ {"1.100", Other},
+ {"1.101", Other},
+ {onePlusEpsilon, Other},
+ {2, Other},
+ {"0.7", Other},
+ {"2.0", Other},
+ }
+ runTests(t, "tr", tests)
+}
+
+func TestUkrainian(t *testing.T) {
+ tests := []pluralTest{
+ {0, Many},
+ {1, One},
+ {2, Few},
+ {3, Few},
+ {4, Few},
+ {5, Many},
+ {19, Many},
+ {20, Many},
+ {21, One},
+ {11, Many},
+ {52, Few},
+ {101, One},
+ {"0.1", Other},
+ {"0.7", Other},
+ {"1.5", Other},
+ {"1.0", Other},
+ {onePlusEpsilon, Other},
+ {"2.0", Other},
+ {"10.0", Other},
+ }
+ runTests(t, "uk", tests)
+}
+
+func TestCroatian(t *testing.T) {
+ tests := makeCroatianBosnianSerbianTests()
+ runTests(t, "hr", tests)
+}
+
+func TestBosnian(t *testing.T) {
+ tests := makeCroatianBosnianSerbianTests()
+ runTests(t, "bs", tests)
+}
+
+func TestSerbian(t *testing.T) {
+ tests := makeCroatianBosnianSerbianTests()
+ runTests(t, "sr", tests)
+}
+
+func makeCroatianBosnianSerbianTests() []pluralTest {
+ return []pluralTest{
+ {1, One},
+ {"0.1", One},
+ {21, One},
+ {101, One},
+ {1001, One},
+ {51, One},
+ {"1.1", One},
+ {"5.1", One},
+ {"100.1", One},
+ {"1000.1", One},
+ {2, Few},
+ {"0.2", Few},
+ {22, Few},
+ {"1.2", Few},
+ {24, Few},
+ {"2.4", Few},
+ {102, Few},
+ {"100.2", Few},
+ {1002, Few},
+ {"1000.2", Few},
+ {5, Other},
+ {"0.5", Other},
+ {0, Other},
+ {100, Other},
+ {19, Other},
+ {"0.0", Other},
+ {"100.0", Other},
+ {"1000.0", Other},
+ }
+}
+
+func TestTigrinya(t *testing.T) {
+ tests := []pluralTest{
+ {0, One},
+ {1, One},
+ }
+ tests = appendIntTests(tests, 2, 10, Other)
+ tests = appendFloatTests(tests, 1.1, 10.0, Other)
+ runTests(t, "ti", tests)
+}
+
+func appendIntTests(tests []pluralTest, from, to int, p Plural) []pluralTest {
+ for i := from; i <= to; i++ {
+ tests = append(tests, pluralTest{i, p})
+ }
+ return tests
+}
+
+func appendFloatTests(tests []pluralTest, from, to float64, p Plural) []pluralTest {
+ stride := 0.1
+ format := "%.1f"
+ for f := from; f < to; f += stride {
+ tests = append(tests, pluralTest{fmt.Sprintf(format, f), p})
+ }
+ tests = append(tests, pluralTest{fmt.Sprintf(format, to), p})
+ return tests
+}
+
+func runTests(t *testing.T, pluralSpecID string, tests []pluralTest) {
+ pluralSpecID = normalizePluralSpecID(pluralSpecID)
+ if spec := pluralSpecs[pluralSpecID]; spec != nil {
+ for _, test := range tests {
+ if plural, err := spec.Plural(test.num); plural != test.plural {
+ t.Errorf("%s: PluralCategory(%#v) returned %s, %v; expected %s", pluralSpecID, test.num, plural, err, test.plural)
+ }
+ }
+ } else {
+ t.Errorf("could not find plural spec for locale %s", pluralSpecID)
+ }
+
+}
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
new file mode 100644
index 000000000..4f579d16a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go
@@ -0,0 +1,78 @@
+package translation
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+type pluralTranslation struct {
+ id string
+ templates map[language.Plural]*template
+}
+
+func (pt *pluralTranslation) MarshalInterface() interface{} {
+ return map[string]interface{}{
+ "id": pt.id,
+ "translation": pt.templates,
+ }
+}
+
+func (pt *pluralTranslation) ID() string {
+ return pt.id
+}
+
+func (pt *pluralTranslation) Template(pc language.Plural) *template {
+ return pt.templates[pc]
+}
+
+func (pt *pluralTranslation) UntranslatedCopy() Translation {
+ return &pluralTranslation{pt.id, make(map[language.Plural]*template)}
+}
+
+func (pt *pluralTranslation) Normalize(l *language.Language) Translation {
+ // Delete plural categories that don't belong to this language.
+ for pc := range pt.templates {
+ if _, ok := l.Plurals[pc]; !ok {
+ delete(pt.templates, pc)
+ }
+ }
+ // Create map entries for missing valid categories.
+ for pc := range l.Plurals {
+ if _, ok := pt.templates[pc]; !ok {
+ pt.templates[pc] = mustNewTemplate("")
+ }
+ }
+ return pt
+}
+
+func (pt *pluralTranslation) Backfill(src Translation) Translation {
+ for pc, t := range pt.templates {
+ if t == nil || t.src == "" {
+ pt.templates[pc] = src.Template(language.Other)
+ }
+ }
+ return pt
+}
+
+func (pt *pluralTranslation) Merge(t Translation) Translation {
+ other, ok := t.(*pluralTranslation)
+ if !ok || pt.ID() != t.ID() {
+ return t
+ }
+ for pluralCategory, template := range other.templates {
+ if template != nil && template.src != "" {
+ pt.templates[pluralCategory] = template
+ }
+ }
+ return pt
+}
+
+func (pt *pluralTranslation) Incomplete(l *language.Language) bool {
+ for pc := range l.Plurals {
+ if t := pt.templates[pc]; t == nil || t.src == "" {
+ return true
+ }
+ }
+ return false
+}
+
+var _ = Translation(&pluralTranslation{})
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go
new file mode 100644
index 000000000..ea7de7fd9
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go
@@ -0,0 +1,308 @@
+package translation
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+func mustTemplate(t *testing.T, src string) *template {
+ tmpl, err := newTemplate(src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return tmpl
+}
+
+func pluralTranslationFixture(t *testing.T, id string, pluralCategories ...language.Plural) *pluralTranslation {
+ templates := make(map[language.Plural]*template, len(pluralCategories))
+ for _, pc := range pluralCategories {
+ templates[pc] = mustTemplate(t, string(pc))
+ }
+ return &pluralTranslation{id, templates}
+}
+
+func verifyDeepEqual(t *testing.T, actual, expected interface{}) {
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("\n%#v\nnot equal to expected value\n%#v", actual, expected)
+ }
+}
+
+func TestPluralTranslationMerge(t *testing.T) {
+ pt := pluralTranslationFixture(t, "id", language.One, language.Other)
+ oneTemplate, otherTemplate := pt.templates[language.One], pt.templates[language.Other]
+
+ pt.Merge(pluralTranslationFixture(t, "id"))
+ verifyDeepEqual(t, pt.templates, map[language.Plural]*template{
+ language.One: oneTemplate,
+ language.Other: otherTemplate,
+ })
+
+ pt2 := pluralTranslationFixture(t, "id", language.One, language.Two)
+ pt.Merge(pt2)
+ verifyDeepEqual(t, pt.templates, map[language.Plural]*template{
+ language.One: pt2.templates[language.One],
+ language.Two: pt2.templates[language.Two],
+ language.Other: otherTemplate,
+ })
+}
+
+/* Test implementations from old idea
+
+func TestCopy(t *testing.T) {
+ ls := &LocalizedString{
+ ID: "id",
+ Translation: testingTemplate(t, "translation {{.Hello}}"),
+ Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, "plural {{.One}}"),
+ language.Other: testingTemplate(t, "plural {{.Other}}"),
+ },
+ }
+
+ c := ls.Copy()
+ delete(c.Translations, language.One)
+ if _, ok := ls.Translations[language.One]; !ok {
+ t.Errorf("deleting plural translation from copy deleted it from the original")
+ }
+ c.Translations[language.Two] = testingTemplate(t, "plural {{.Two}}")
+ if _, ok := ls.Translations[language.Two]; ok {
+ t.Errorf("adding plural translation to copy added it to the original")
+ }
+}
+
+func TestNormalize(t *testing.T) {
+ oneTemplate := testingTemplate(t, "one {{.One}}")
+ ls := &LocalizedString{
+ Translation: testingTemplate(t, "single {{.Single}}"),
+ Translations: map[language.Plural]*template{
+ language.One: oneTemplate,
+ language.Two: testingTemplate(t, "two {{.Two}}"),
+ },
+ }
+ ls.Normalize(LanguageWithCode("en"))
+ if ls.Translation != nil {
+ t.Errorf("ls.Translation is %#v; expected nil", ls.Translation)
+ }
+ if actual := ls.Translations[language.Two]; actual != nil {
+ t.Errorf("ls.Translation[language.Two] is %#v; expected nil", actual)
+ }
+ if actual := ls.Translations[language.One]; actual != oneTemplate {
+ t.Errorf("ls.Translations[language.One] is %#v; expected %#v", actual, oneTemplate)
+ }
+ if _, ok := ls.Translations[language.Other]; !ok {
+ t.Errorf("ls.Translations[language.Other] shouldn't be empty")
+ }
+}
+
+func TestMergeTranslation(t *testing.T) {
+ ls := &LocalizedString{}
+
+ translation := testingTemplate(t, "one {{.Hello}}")
+ ls.Merge(&LocalizedString{
+ Translation: translation,
+ })
+ if ls.Translation != translation {
+ t.Errorf("expected %#v; got %#v", translation, ls.Translation)
+ }
+
+ ls.Merge(&LocalizedString{})
+ if ls.Translation != translation {
+ t.Errorf("expected %#v; got %#v", translation, ls.Translation)
+ }
+
+ translation = testingTemplate(t, "two {{.Hello}}")
+ ls.Merge(&LocalizedString{
+ Translation: translation,
+ })
+ if ls.Translation != translation {
+ t.Errorf("expected %#v; got %#v", translation, ls.Translation)
+ }
+}
+
+func TestMergeTranslations(t *testing.T) {
+ ls := &LocalizedString{}
+
+ oneTemplate := testingTemplate(t, "one {{.One}}")
+ otherTemplate := testingTemplate(t, "other {{.Other}}")
+ ls.Merge(&LocalizedString{
+ Translations: map[language.Plural]*template{
+ language.One: oneTemplate,
+ language.Other: otherTemplate,
+ },
+ })
+ if actual := ls.Translations[language.One]; actual != oneTemplate {
+ t.Errorf("ls.Translations[language.One] expected %#v; got %#v", oneTemplate, actual)
+ }
+ if actual := ls.Translations[language.Other]; actual != otherTemplate {
+ t.Errorf("ls.Translations[language.Other] expected %#v; got %#v", otherTemplate, actual)
+ }
+
+ ls.Merge(&LocalizedString{
+ Translations: map[language.Plural]*template{},
+ })
+ if actual := ls.Translations[language.One]; actual != oneTemplate {
+ t.Errorf("ls.Translations[language.One] expected %#v; got %#v", oneTemplate, actual)
+ }
+ if actual := ls.Translations[language.Other]; actual != otherTemplate {
+ t.Errorf("ls.Translations[language.Other] expected %#v; got %#v", otherTemplate, actual)
+ }
+
+ twoTemplate := testingTemplate(t, "two {{.Two}}")
+ otherTemplate = testingTemplate(t, "second other {{.Other}}")
+ ls.Merge(&LocalizedString{
+ Translations: map[language.Plural]*template{
+ language.Two: twoTemplate,
+ language.Other: otherTemplate,
+ },
+ })
+ if actual := ls.Translations[language.One]; actual != oneTemplate {
+ t.Errorf("ls.Translations[language.One] expected %#v; got %#v", oneTemplate, actual)
+ }
+ if actual := ls.Translations[language.Two]; actual != twoTemplate {
+ t.Errorf("ls.Translations[language.Two] expected %#v; got %#v", twoTemplate, actual)
+ }
+ if actual := ls.Translations[language.Other]; actual != otherTemplate {
+ t.Errorf("ls.Translations[language.Other] expected %#v; got %#v", otherTemplate, actual)
+ }
+}
+
+func TestMissingTranslations(t *testing.T) {
+ en := LanguageWithCode("en")
+
+ tests := []struct {
+ localizedString *LocalizedString
+ language *Language
+ expected bool
+ }{
+ {
+ &LocalizedString{},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{Translation: testingTemplate(t, "single {{.Single}}")},
+ en,
+ false,
+ },
+ {
+ &LocalizedString{
+ Translation: testingTemplate(t, "single {{.Single}}"),
+ Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, "one {{.One}}"),
+ }},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, "one {{.One}}"),
+ }},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: nil,
+ language.Other: nil,
+ }},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, ""),
+ language.Other: testingTemplate(t, ""),
+ }},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, "one {{.One}}"),
+ language.Other: testingTemplate(t, "other {{.Other}}"),
+ }},
+ en,
+ false,
+ },
+ }
+
+ for _, tt := range tests {
+ if actual := tt.localizedString.MissingTranslations(tt.language); actual != tt.expected {
+ t.Errorf("expected %t got %t for %s, %#v",
+ tt.expected, actual, tt.language.code, tt.localizedString)
+ }
+ }
+}
+
+func TestHasTranslations(t *testing.T) {
+ en := LanguageWithCode("en")
+
+ tests := []struct {
+ localizedString *LocalizedString
+ language *Language
+ expected bool
+ }{
+ {
+ &LocalizedString{},
+ en,
+ false,
+ },
+ {
+ &LocalizedString{Translation: testingTemplate(t, "single {{.Single}}")},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{
+ Translation: testingTemplate(t, "single {{.Single}}"),
+ Translations: map[language.Plural]*template{}},
+ en,
+ false,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, "one {{.One}}"),
+ }},
+ en,
+ true,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.Two: testingTemplate(t, "two {{.Two}}"),
+ }},
+ en,
+ false,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: nil,
+ }},
+ en,
+ false,
+ },
+ {
+ &LocalizedString{Translations: map[language.Plural]*template{
+ language.One: testingTemplate(t, ""),
+ }},
+ en,
+ false,
+ },
+ }
+
+ for _, tt := range tests {
+ if actual := tt.localizedString.HasTranslations(tt.language); actual != tt.expected {
+ t.Errorf("expected %t got %t for %s, %#v",
+ tt.expected, actual, tt.language.code, tt.localizedString)
+ }
+ }
+}
+
+func testingTemplate(t *testing.T, src string) *template {
+ tmpl, err := newTemplate(src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return tmpl
+}
+*/
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
new file mode 100644
index 000000000..1010e5947
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go
@@ -0,0 +1,57 @@
+package translation
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+type singleTranslation struct {
+ id string
+ template *template
+}
+
+func (st *singleTranslation) MarshalInterface() interface{} {
+ return map[string]interface{}{
+ "id": st.id,
+ "translation": st.template,
+ }
+}
+
+func (st *singleTranslation) ID() string {
+ return st.id
+}
+
+func (st *singleTranslation) Template(pc language.Plural) *template {
+ return st.template
+}
+
+func (st *singleTranslation) UntranslatedCopy() Translation {
+ return &singleTranslation{st.id, mustNewTemplate("")}
+}
+
+func (st *singleTranslation) Normalize(language *language.Language) Translation {
+ return st
+}
+
+func (st *singleTranslation) Backfill(src Translation) Translation {
+ if st.template == nil || st.template.src == "" {
+ st.template = src.Template(language.Other)
+ }
+ return st
+}
+
+func (st *singleTranslation) Merge(t Translation) Translation {
+ other, ok := t.(*singleTranslation)
+ if !ok || st.ID() != t.ID() {
+ return t
+ }
+ if other.template != nil && other.template.src != "" {
+ st.template = other.template
+ }
+ return st
+}
+
+func (st *singleTranslation) Incomplete(l *language.Language) bool {
+ return st.template == nil || st.template.src == ""
+}
+
+var _ = Translation(&singleTranslation{})
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go
new file mode 100644
index 000000000..c8756fa4e
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go
@@ -0,0 +1,61 @@
+package translation
+
+import (
+ "bytes"
+ "encoding"
+ "strings"
+ gotemplate "text/template"
+)
+
+type template struct {
+ tmpl *gotemplate.Template
+ src string
+}
+
+func newTemplate(src string) (*template, error) {
+ var tmpl template
+ err := tmpl.parseTemplate(src)
+ return &tmpl, err
+}
+
+func mustNewTemplate(src string) *template {
+ t, err := newTemplate(src)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+func (t *template) String() string {
+ return t.src
+}
+
+func (t *template) Execute(args interface{}) string {
+ if t.tmpl == nil {
+ return t.src
+ }
+ var buf bytes.Buffer
+ if err := t.tmpl.Execute(&buf, args); err != nil {
+ return err.Error()
+ }
+ return buf.String()
+}
+
+func (t *template) MarshalText() ([]byte, error) {
+ return []byte(t.src), nil
+}
+
+func (t *template) UnmarshalText(src []byte) error {
+ return t.parseTemplate(string(src))
+}
+
+func (t *template) parseTemplate(src string) (err error) {
+ t.src = src
+ if strings.Contains(src, "{{") {
+ t.tmpl, err = gotemplate.New(src).Parse(src)
+ }
+ return
+}
+
+var _ = encoding.TextMarshaler(&template{})
+var _ = encoding.TextUnmarshaler(&template{})
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go
new file mode 100644
index 000000000..73a923404
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go
@@ -0,0 +1,146 @@
+package translation
+
+import (
+ "bytes"
+ "fmt"
+ //"launchpad.net/goyaml"
+ "testing"
+ gotemplate "text/template"
+)
+
+func TestNilTemplate(t *testing.T) {
+ expected := "hello"
+ tmpl := &template{
+ tmpl: nil,
+ src: expected,
+ }
+ if actual := tmpl.Execute(nil); actual != expected {
+ t.Errorf("Execute(nil) returned %s; expected %s", actual, expected)
+ }
+}
+
+func TestMarshalText(t *testing.T) {
+ tmpl := &template{
+ tmpl: gotemplate.Must(gotemplate.New("id").Parse("this is a {{.foo}} template")),
+ src: "boom",
+ }
+ expectedBuf := []byte(tmpl.src)
+ if buf, err := tmpl.MarshalText(); !bytes.Equal(buf, expectedBuf) || err != nil {
+ t.Errorf("MarshalText() returned %#v, %#v; expected %#v, nil", buf, err, expectedBuf)
+ }
+}
+
+func TestUnmarshalText(t *testing.T) {
+ tmpl := &template{}
+ tmpl.UnmarshalText([]byte("hello {{.World}}"))
+ result := tmpl.Execute(map[string]string{
+ "World": "world!",
+ })
+ expected := "hello world!"
+ if result != expected {
+ t.Errorf("expected %#v; got %#v", expected, result)
+ }
+}
+
+/*
+func TestYAMLMarshal(t *testing.T) {
+ src := "hello {{.World}}"
+ tmpl, err := newTemplate(src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ buf, err := goyaml.Marshal(tmpl)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(buf, []byte(src)) {
+ t.Fatalf(`expected "%s"; got "%s"`, src, buf)
+ }
+}
+
+func TestYAMLUnmarshal(t *testing.T) {
+ buf := []byte(`Tmpl: "hello"`)
+
+ var out struct {
+ Tmpl *template
+ }
+ var foo map[string]string
+ if err := goyaml.Unmarshal(buf, &foo); err != nil {
+ t.Fatal(err)
+ }
+ if out.Tmpl == nil {
+ t.Fatalf("out.Tmpl was nil")
+ }
+ if out.Tmpl.tmpl == nil {
+ t.Fatalf("out.Tmpl.tmpl was nil")
+ }
+ if expected := "hello {{.World}}"; out.Tmpl.src != expected {
+ t.Fatalf("expected %s; got %s", expected, out.Tmpl.src)
+ }
+}
+
+func TestGetYAML(t *testing.T) {
+ src := "hello"
+ tmpl := &template{
+ tmpl: nil,
+ src: src,
+ }
+ if tag, value := tmpl.GetYAML(); tag != "" || value != src {
+ t.Errorf("GetYAML() returned (%#v, %#v); expected (%#v, %#v)", tag, value, "", src)
+ }
+}
+
+func TestSetYAML(t *testing.T) {
+ tmpl := &template{}
+ tmpl.SetYAML("tagDoesntMatter", "hello {{.World}}")
+ result := tmpl.Execute(map[string]string{
+ "World": "world!",
+ })
+ expected := "hello world!"
+ if result != expected {
+ t.Errorf("expected %#v; got %#v", expected, result)
+ }
+}
+*/
+
+func BenchmarkExecuteNilTemplate(b *testing.B) {
+ template := &template{src: "hello world"}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ template.Execute(nil)
+ }
+}
+
+func BenchmarkExecuteHelloWorldTemplate(b *testing.B) {
+ template, err := newTemplate("hello world")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ template.Execute(nil)
+ }
+}
+
+// Executing a simple template like this is ~6x slower than Sprintf
+// but it is still only a few microseconds which should be sufficiently fast.
+// The benefit is that we have nice semantic tags in the translation.
+func BenchmarkExecuteHelloNameTemplate(b *testing.B) {
+ template, err := newTemplate("hello {{.Name}}")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ template.Execute(map[string]string{
+ "Name": "Nick",
+ })
+ }
+}
+
+func BenchmarkSprintf(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Sprintf("hello %s", "nick")
+ }
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go
new file mode 100644
index 000000000..fa93180b8
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go
@@ -0,0 +1,83 @@
+// Package translation defines the interface for a translation.
+package translation
+
+import (
+ "fmt"
+
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+// Translation is the interface that represents a translated string.
+type Translation interface {
+ // MarshalInterface returns the object that should be used
+ // to serialize the translation.
+ MarshalInterface() interface{}
+ ID() string
+ Template(language.Plural) *template
+ UntranslatedCopy() Translation
+ Normalize(language *language.Language) Translation
+ Backfill(src Translation) Translation
+ Merge(Translation) Translation
+ Incomplete(l *language.Language) bool
+}
+
+// SortableByID implements sort.Interface for a slice of translations.
+type SortableByID []Translation
+
+func (a SortableByID) Len() int { return len(a) }
+func (a SortableByID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a SortableByID) Less(i, j int) bool { return a[i].ID() < a[j].ID() }
+
+// NewTranslation reflects on data to create a new Translation.
+//
+// data["id"] must be a string and data["translation"] must be either a string
+// for a non-plural translation or a map[string]interface{} for a plural translation.
+func NewTranslation(data map[string]interface{}) (Translation, error) {
+ id, ok := data["id"].(string)
+ if !ok {
+ return nil, fmt.Errorf(`missing "id" key`)
+ }
+ var pluralObject map[string]interface{}
+ switch translation := data["translation"].(type) {
+ case string:
+ tmpl, err := newTemplate(translation)
+ if err != nil {
+ return nil, err
+ }
+ return &singleTranslation{id, tmpl}, nil
+ case map[interface{}]interface{}:
+ // The YAML parser uses interface{} keys so we first convert them to string keys.
+ pluralObject = make(map[string]interface{})
+ for k, v := range translation {
+ kStr, ok := k.(string)
+ if !ok {
+ return nil, fmt.Errorf(`invalid plural category type %T; expected string`, k)
+ }
+ pluralObject[kStr] = v
+ }
+ case map[string]interface{}:
+ pluralObject = translation
+ case nil:
+ return nil, fmt.Errorf(`missing "translation" key`)
+ default:
+ return nil, fmt.Errorf(`unsupported type for "translation" key %T`, translation)
+ }
+
+ templates := make(map[language.Plural]*template, len(pluralObject))
+ for k, v := range pluralObject {
+ pc, err := language.NewPlural(k)
+ if err != nil {
+ return nil, err
+ }
+ str, ok := v.(string)
+ if !ok {
+ return nil, fmt.Errorf(`plural category "%s" has value of type %T; expected string`, pc, v)
+ }
+ tmpl, err := newTemplate(str)
+ if err != nil {
+ return nil, err
+ }
+ templates[pc] = tmpl
+ }
+ return &pluralTranslation{id, templates}, nil
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go
new file mode 100644
index 000000000..7380d5a6f
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go
@@ -0,0 +1,17 @@
+package translation
+
+import (
+ "sort"
+ "testing"
+)
+
+// Check this here to avoid unnecessary import of sort package.
+var _ = sort.Interface(make(SortableByID, 0, 0))
+
+func TestNewSingleTranslation(t *testing.T) {
+ t.Skipf("not implemented")
+}
+
+func TestNewPluralTranslation(t *testing.T) {
+ t.Skipf("not implemented")
+}