Removed go-i18n

This commit is contained in:
Matthieu Grieger 2016-06-21 09:23:11 -07:00
parent 50c5e6cb61
commit cbc850da95
56 changed files with 21 additions and 5786 deletions

46
glide.lock generated
View file

@ -1,20 +1,20 @@
hash: 90b4d676820ec357d9eac7be23afe091897035f8e22579f32bef93f9e1971e42
updated: 2016-06-21T09:01:11.596780617-07:00
hash: b68f7c8a3b59d7dac3e12321ed6a2265b553c2856ae70e0ed5e960ba8412f8d8
updated: 2016-06-21T09:22:02.569941705-07:00
imports:
- name: github.com/antonholmquist/jason
version: c23cef7eaa75a6a5b8810120e167bd590d8fd2ab
version: ""
- name: github.com/BurntSushi/toml
version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
version: ""
- name: github.com/ChannelMeter/iso8601duration
version: 8da3af7a2a61a4eb5ae9bddec06bf637fa9593da
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/fsnotify/fsnotify
version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/golang/protobuf
version: 0c1f6d65b5a189c2250d10e71a5506f06f9fa0a0
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
subpackages:
- proto
- name: github.com/hashicorp/hcl
version: da486364306ed66c218be9b7953e19173447c18b
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
subpackages:
- hcl/ast
- hcl/parser
@ -25,9 +25,9 @@ imports:
- json/scanner
- json/token
- name: github.com/layeh/gopus
version: 867541549ca5f8b4db2b92fd1dded8711256a27d
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/layeh/gumble
version: b26208eca4b75d9efdacb068241c68912437bd69
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
subpackages:
- gumble
- gumbleffmpeg
@ -36,31 +36,27 @@ imports:
- gumble/MumbleProto
- gumble/varint
- name: github.com/magiconair/properties
version: c265cfa48dda6474e208715ca93e987829f572f8
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/mitchellh/mapstructure
version: d2dd0262208475919e1a362f675cfc0e7c10e905
- name: github.com/nicksnyder/go-i18n
version: 37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3
subpackages:
- i18n
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/Sirupsen/logrus
version: 4b6ea7319e214d98c938f12692336f7ca9348d6b
version: ""
- name: github.com/spf13/cast
version: 27b586b42e29bec072fe7379259cc719e1289da6
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/spf13/jwalterweatherman
version: 33c24e77fb80341fe7130ee7c594256ff08ccc46
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/spf13/pflag
version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/spf13/viper
version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
- name: github.com/stretchr/testify
version: f390dcf405f7b83c997eac1b06768bb9f44dec18
version: ""
- name: github.com/urfave/cli
version: 01857ac33766ce0c93856370626f9799281c14f4
version: ""
- name: golang.org/x/sys
version: 62bee037599929a6e9146f29d10dd5208c43507d
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
subpackages:
- unix
- name: gopkg.in/yaml.v2
version: a83829b6f1293c91addabc89d0571c246397bbf4
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94
devImports: []

View file

@ -24,7 +24,3 @@ import:
version: v1.1.3
- package: github.com/BurntSushi/toml
version: v0.2.0
- package: github.com/nicksnyder/go-i18n
version: v1.4.0
subpackages:
- i18n

View file

@ -1,6 +0,0 @@
*.a
_*
output/
.DS_Store
*.test
*.swp

View file

@ -1,8 +0,0 @@
language: go
sudo: false
go:
- 1.2
- 1.3
- 1.4
- 1.5
- tip

View file

@ -1,5 +0,0 @@
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.

View file

@ -1,19 +0,0 @@
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.

View file

@ -1,130 +0,0 @@
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.

View file

@ -1,59 +0,0 @@
// 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

View file

@ -1,10 +0,0 @@
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

View file

@ -1,82 +0,0 @@
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)
}
}

View file

@ -1,127 +0,0 @@
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
}

View file

@ -1,74 +0,0 @@
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)
}
}

View file

@ -1,30 +0,0 @@
- 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"

View file

@ -1,65 +0,0 @@
[
{
"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": ""
}
}
]

View file

@ -1,50 +0,0 @@
[
{
"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."
}
}
]

View file

@ -1,45 +0,0 @@
[
{
"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."
}
}
]

View file

@ -1,45 +0,0 @@
[
{
"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": ""
}
}
]

View file

@ -1,45 +0,0 @@
[
{
"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."
}
}
]

View file

@ -1,54 +0,0 @@
[
{
"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": ""
}
}
]

View file

@ -1,54 +0,0 @@
[
{
"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": ""
}
}
]

View file

@ -1,30 +0,0 @@
[
{
"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"
}
]

View file

@ -1,26 +0,0 @@
[
{
"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"
}
}
]

View file

@ -1,54 +0,0 @@
[
{
"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": ""
}
}
]

View file

@ -1,54 +0,0 @@
[
{
"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": ""
}
}
]

View file

@ -1,19 +0,0 @@
- 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

View file

@ -1,26 +0,0 @@
[
{
"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"
}
}
]

View file

@ -1,315 +0,0 @@
// 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
}

View file

@ -1,289 +0,0 @@
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)
}
}

View file

@ -1,63 +0,0 @@
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.
}

View file

@ -1,63 +0,0 @@
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.
}

View file

@ -1,62 +0,0 @@
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.
}

View file

@ -1,152 +0,0 @@
// 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
}

View file

@ -1,5 +0,0 @@
#!/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

View file

@ -1,132 +0,0 @@
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)
}

View file

@ -1,230 +0,0 @@
<?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>

View file

@ -1,143 +0,0 @@
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")
}

View file

@ -1,99 +0,0 @@
// 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)
}

View file

@ -1,85 +0,0 @@
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)
}
}
}

View file

@ -1,119 +0,0 @@
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
}

View file

@ -1,45 +0,0 @@
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)
}
}
}

View file

@ -1,40 +0,0 @@
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)
}

View file

@ -1,28 +0,0 @@
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)
}
}
}

View file

@ -1,74 +0,0 @@
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
}

View file

@ -1,567 +0,0 @@
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
},
})
}

View file

@ -1,645 +0,0 @@
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)
}
}

View file

@ -1,733 +0,0 @@
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)
}
}

View file

@ -1,78 +0,0 @@
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{})

View file

@ -1,308 +0,0 @@
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
}
*/

View file

@ -1,57 +0,0 @@
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{})

View file

@ -1,61 +0,0 @@
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{})

View file

@ -1,146 +0,0 @@
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")
}
}

View file

@ -1,83 +0,0 @@
// 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
}

View file

@ -1,17 +0,0 @@
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")
}