diff options
Diffstat (limited to 'vendor/github.com/nicksnyder/go-i18n/i18n/translation')
7 files changed, 750 insertions, 0 deletions
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go new file mode 100644 index 000000000..4f579d16a --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go @@ -0,0 +1,78 @@ +package translation + +import ( + "github.com/nicksnyder/go-i18n/i18n/language" +) + +type pluralTranslation struct { + id string + templates map[language.Plural]*template +} + +func (pt *pluralTranslation) MarshalInterface() interface{} { + return map[string]interface{}{ + "id": pt.id, + "translation": pt.templates, + } +} + +func (pt *pluralTranslation) ID() string { + return pt.id +} + +func (pt *pluralTranslation) Template(pc language.Plural) *template { + return pt.templates[pc] +} + +func (pt *pluralTranslation) UntranslatedCopy() Translation { + return &pluralTranslation{pt.id, make(map[language.Plural]*template)} +} + +func (pt *pluralTranslation) Normalize(l *language.Language) Translation { + // Delete plural categories that don't belong to this language. + for pc := range pt.templates { + if _, ok := l.Plurals[pc]; !ok { + delete(pt.templates, pc) + } + } + // Create map entries for missing valid categories. + for pc := range l.Plurals { + if _, ok := pt.templates[pc]; !ok { + pt.templates[pc] = mustNewTemplate("") + } + } + return pt +} + +func (pt *pluralTranslation) Backfill(src Translation) Translation { + for pc, t := range pt.templates { + if t == nil || t.src == "" { + pt.templates[pc] = src.Template(language.Other) + } + } + return pt +} + +func (pt *pluralTranslation) Merge(t Translation) Translation { + other, ok := t.(*pluralTranslation) + if !ok || pt.ID() != t.ID() { + return t + } + for pluralCategory, template := range other.templates { + if template != nil && template.src != "" { + pt.templates[pluralCategory] = template + } + } + return pt +} + +func (pt *pluralTranslation) Incomplete(l *language.Language) bool { + for pc := range l.Plurals { + if t := pt.templates[pc]; t == nil || t.src == "" { + return true + } + } + return false +} + +var _ = Translation(&pluralTranslation{}) diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go new file mode 100644 index 000000000..ea7de7fd9 --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go @@ -0,0 +1,308 @@ +package translation + +import ( + "reflect" + "testing" + + "github.com/nicksnyder/go-i18n/i18n/language" +) + +func mustTemplate(t *testing.T, src string) *template { + tmpl, err := newTemplate(src) + if err != nil { + t.Fatal(err) + } + return tmpl +} + +func pluralTranslationFixture(t *testing.T, id string, pluralCategories ...language.Plural) *pluralTranslation { + templates := make(map[language.Plural]*template, len(pluralCategories)) + for _, pc := range pluralCategories { + templates[pc] = mustTemplate(t, string(pc)) + } + return &pluralTranslation{id, templates} +} + +func verifyDeepEqual(t *testing.T, actual, expected interface{}) { + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("\n%#v\nnot equal to expected value\n%#v", actual, expected) + } +} + +func TestPluralTranslationMerge(t *testing.T) { + pt := pluralTranslationFixture(t, "id", language.One, language.Other) + oneTemplate, otherTemplate := pt.templates[language.One], pt.templates[language.Other] + + pt.Merge(pluralTranslationFixture(t, "id")) + verifyDeepEqual(t, pt.templates, map[language.Plural]*template{ + language.One: oneTemplate, + language.Other: otherTemplate, + }) + + pt2 := pluralTranslationFixture(t, "id", language.One, language.Two) + pt.Merge(pt2) + verifyDeepEqual(t, pt.templates, map[language.Plural]*template{ + language.One: pt2.templates[language.One], + language.Two: pt2.templates[language.Two], + language.Other: otherTemplate, + }) +} + +/* Test implementations from old idea + +func TestCopy(t *testing.T) { + ls := &LocalizedString{ + ID: "id", + Translation: testingTemplate(t, "translation {{.Hello}}"), + Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, "plural {{.One}}"), + language.Other: testingTemplate(t, "plural {{.Other}}"), + }, + } + + c := ls.Copy() + delete(c.Translations, language.One) + if _, ok := ls.Translations[language.One]; !ok { + t.Errorf("deleting plural translation from copy deleted it from the original") + } + c.Translations[language.Two] = testingTemplate(t, "plural {{.Two}}") + if _, ok := ls.Translations[language.Two]; ok { + t.Errorf("adding plural translation to copy added it to the original") + } +} + +func TestNormalize(t *testing.T) { + oneTemplate := testingTemplate(t, "one {{.One}}") + ls := &LocalizedString{ + Translation: testingTemplate(t, "single {{.Single}}"), + Translations: map[language.Plural]*template{ + language.One: oneTemplate, + language.Two: testingTemplate(t, "two {{.Two}}"), + }, + } + ls.Normalize(LanguageWithCode("en")) + if ls.Translation != nil { + t.Errorf("ls.Translation is %#v; expected nil", ls.Translation) + } + if actual := ls.Translations[language.Two]; actual != nil { + t.Errorf("ls.Translation[language.Two] is %#v; expected nil", actual) + } + if actual := ls.Translations[language.One]; actual != oneTemplate { + t.Errorf("ls.Translations[language.One] is %#v; expected %#v", actual, oneTemplate) + } + if _, ok := ls.Translations[language.Other]; !ok { + t.Errorf("ls.Translations[language.Other] shouldn't be empty") + } +} + +func TestMergeTranslation(t *testing.T) { + ls := &LocalizedString{} + + translation := testingTemplate(t, "one {{.Hello}}") + ls.Merge(&LocalizedString{ + Translation: translation, + }) + if ls.Translation != translation { + t.Errorf("expected %#v; got %#v", translation, ls.Translation) + } + + ls.Merge(&LocalizedString{}) + if ls.Translation != translation { + t.Errorf("expected %#v; got %#v", translation, ls.Translation) + } + + translation = testingTemplate(t, "two {{.Hello}}") + ls.Merge(&LocalizedString{ + Translation: translation, + }) + if ls.Translation != translation { + t.Errorf("expected %#v; got %#v", translation, ls.Translation) + } +} + +func TestMergeTranslations(t *testing.T) { + ls := &LocalizedString{} + + oneTemplate := testingTemplate(t, "one {{.One}}") + otherTemplate := testingTemplate(t, "other {{.Other}}") + ls.Merge(&LocalizedString{ + Translations: map[language.Plural]*template{ + language.One: oneTemplate, + language.Other: otherTemplate, + }, + }) + if actual := ls.Translations[language.One]; actual != oneTemplate { + t.Errorf("ls.Translations[language.One] expected %#v; got %#v", oneTemplate, actual) + } + if actual := ls.Translations[language.Other]; actual != otherTemplate { + t.Errorf("ls.Translations[language.Other] expected %#v; got %#v", otherTemplate, actual) + } + + ls.Merge(&LocalizedString{ + Translations: map[language.Plural]*template{}, + }) + if actual := ls.Translations[language.One]; actual != oneTemplate { + t.Errorf("ls.Translations[language.One] expected %#v; got %#v", oneTemplate, actual) + } + if actual := ls.Translations[language.Other]; actual != otherTemplate { + t.Errorf("ls.Translations[language.Other] expected %#v; got %#v", otherTemplate, actual) + } + + twoTemplate := testingTemplate(t, "two {{.Two}}") + otherTemplate = testingTemplate(t, "second other {{.Other}}") + ls.Merge(&LocalizedString{ + Translations: map[language.Plural]*template{ + language.Two: twoTemplate, + language.Other: otherTemplate, + }, + }) + if actual := ls.Translations[language.One]; actual != oneTemplate { + t.Errorf("ls.Translations[language.One] expected %#v; got %#v", oneTemplate, actual) + } + if actual := ls.Translations[language.Two]; actual != twoTemplate { + t.Errorf("ls.Translations[language.Two] expected %#v; got %#v", twoTemplate, actual) + } + if actual := ls.Translations[language.Other]; actual != otherTemplate { + t.Errorf("ls.Translations[language.Other] expected %#v; got %#v", otherTemplate, actual) + } +} + +func TestMissingTranslations(t *testing.T) { + en := LanguageWithCode("en") + + tests := []struct { + localizedString *LocalizedString + language *Language + expected bool + }{ + { + &LocalizedString{}, + en, + true, + }, + { + &LocalizedString{Translation: testingTemplate(t, "single {{.Single}}")}, + en, + false, + }, + { + &LocalizedString{ + Translation: testingTemplate(t, "single {{.Single}}"), + Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, "one {{.One}}"), + }}, + en, + true, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, "one {{.One}}"), + }}, + en, + true, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: nil, + language.Other: nil, + }}, + en, + true, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, ""), + language.Other: testingTemplate(t, ""), + }}, + en, + true, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, "one {{.One}}"), + language.Other: testingTemplate(t, "other {{.Other}}"), + }}, + en, + false, + }, + } + + for _, tt := range tests { + if actual := tt.localizedString.MissingTranslations(tt.language); actual != tt.expected { + t.Errorf("expected %t got %t for %s, %#v", + tt.expected, actual, tt.language.code, tt.localizedString) + } + } +} + +func TestHasTranslations(t *testing.T) { + en := LanguageWithCode("en") + + tests := []struct { + localizedString *LocalizedString + language *Language + expected bool + }{ + { + &LocalizedString{}, + en, + false, + }, + { + &LocalizedString{Translation: testingTemplate(t, "single {{.Single}}")}, + en, + true, + }, + { + &LocalizedString{ + Translation: testingTemplate(t, "single {{.Single}}"), + Translations: map[language.Plural]*template{}}, + en, + false, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, "one {{.One}}"), + }}, + en, + true, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.Two: testingTemplate(t, "two {{.Two}}"), + }}, + en, + false, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: nil, + }}, + en, + false, + }, + { + &LocalizedString{Translations: map[language.Plural]*template{ + language.One: testingTemplate(t, ""), + }}, + en, + false, + }, + } + + for _, tt := range tests { + if actual := tt.localizedString.HasTranslations(tt.language); actual != tt.expected { + t.Errorf("expected %t got %t for %s, %#v", + tt.expected, actual, tt.language.code, tt.localizedString) + } + } +} + +func testingTemplate(t *testing.T, src string) *template { + tmpl, err := newTemplate(src) + if err != nil { + t.Fatal(err) + } + return tmpl +} +*/ diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go new file mode 100644 index 000000000..1010e5947 --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go @@ -0,0 +1,57 @@ +package translation + +import ( + "github.com/nicksnyder/go-i18n/i18n/language" +) + +type singleTranslation struct { + id string + template *template +} + +func (st *singleTranslation) MarshalInterface() interface{} { + return map[string]interface{}{ + "id": st.id, + "translation": st.template, + } +} + +func (st *singleTranslation) ID() string { + return st.id +} + +func (st *singleTranslation) Template(pc language.Plural) *template { + return st.template +} + +func (st *singleTranslation) UntranslatedCopy() Translation { + return &singleTranslation{st.id, mustNewTemplate("")} +} + +func (st *singleTranslation) Normalize(language *language.Language) Translation { + return st +} + +func (st *singleTranslation) Backfill(src Translation) Translation { + if st.template == nil || st.template.src == "" { + st.template = src.Template(language.Other) + } + return st +} + +func (st *singleTranslation) Merge(t Translation) Translation { + other, ok := t.(*singleTranslation) + if !ok || st.ID() != t.ID() { + return t + } + if other.template != nil && other.template.src != "" { + st.template = other.template + } + return st +} + +func (st *singleTranslation) Incomplete(l *language.Language) bool { + return st.template == nil || st.template.src == "" +} + +var _ = Translation(&singleTranslation{}) diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go new file mode 100644 index 000000000..c8756fa4e --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go @@ -0,0 +1,61 @@ +package translation + +import ( + "bytes" + "encoding" + "strings" + gotemplate "text/template" +) + +type template struct { + tmpl *gotemplate.Template + src string +} + +func newTemplate(src string) (*template, error) { + var tmpl template + err := tmpl.parseTemplate(src) + return &tmpl, err +} + +func mustNewTemplate(src string) *template { + t, err := newTemplate(src) + if err != nil { + panic(err) + } + return t +} + +func (t *template) String() string { + return t.src +} + +func (t *template) Execute(args interface{}) string { + if t.tmpl == nil { + return t.src + } + var buf bytes.Buffer + if err := t.tmpl.Execute(&buf, args); err != nil { + return err.Error() + } + return buf.String() +} + +func (t *template) MarshalText() ([]byte, error) { + return []byte(t.src), nil +} + +func (t *template) UnmarshalText(src []byte) error { + return t.parseTemplate(string(src)) +} + +func (t *template) parseTemplate(src string) (err error) { + t.src = src + if strings.Contains(src, "{{") { + t.tmpl, err = gotemplate.New(src).Parse(src) + } + return +} + +var _ = encoding.TextMarshaler(&template{}) +var _ = encoding.TextUnmarshaler(&template{}) diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go new file mode 100644 index 000000000..73a923404 --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go @@ -0,0 +1,146 @@ +package translation + +import ( + "bytes" + "fmt" + //"launchpad.net/goyaml" + "testing" + gotemplate "text/template" +) + +func TestNilTemplate(t *testing.T) { + expected := "hello" + tmpl := &template{ + tmpl: nil, + src: expected, + } + if actual := tmpl.Execute(nil); actual != expected { + t.Errorf("Execute(nil) returned %s; expected %s", actual, expected) + } +} + +func TestMarshalText(t *testing.T) { + tmpl := &template{ + tmpl: gotemplate.Must(gotemplate.New("id").Parse("this is a {{.foo}} template")), + src: "boom", + } + expectedBuf := []byte(tmpl.src) + if buf, err := tmpl.MarshalText(); !bytes.Equal(buf, expectedBuf) || err != nil { + t.Errorf("MarshalText() returned %#v, %#v; expected %#v, nil", buf, err, expectedBuf) + } +} + +func TestUnmarshalText(t *testing.T) { + tmpl := &template{} + tmpl.UnmarshalText([]byte("hello {{.World}}")) + result := tmpl.Execute(map[string]string{ + "World": "world!", + }) + expected := "hello world!" + if result != expected { + t.Errorf("expected %#v; got %#v", expected, result) + } +} + +/* +func TestYAMLMarshal(t *testing.T) { + src := "hello {{.World}}" + tmpl, err := newTemplate(src) + if err != nil { + t.Fatal(err) + } + buf, err := goyaml.Marshal(tmpl) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(buf, []byte(src)) { + t.Fatalf(`expected "%s"; got "%s"`, src, buf) + } +} + +func TestYAMLUnmarshal(t *testing.T) { + buf := []byte(`Tmpl: "hello"`) + + var out struct { + Tmpl *template + } + var foo map[string]string + if err := goyaml.Unmarshal(buf, &foo); err != nil { + t.Fatal(err) + } + if out.Tmpl == nil { + t.Fatalf("out.Tmpl was nil") + } + if out.Tmpl.tmpl == nil { + t.Fatalf("out.Tmpl.tmpl was nil") + } + if expected := "hello {{.World}}"; out.Tmpl.src != expected { + t.Fatalf("expected %s; got %s", expected, out.Tmpl.src) + } +} + +func TestGetYAML(t *testing.T) { + src := "hello" + tmpl := &template{ + tmpl: nil, + src: src, + } + if tag, value := tmpl.GetYAML(); tag != "" || value != src { + t.Errorf("GetYAML() returned (%#v, %#v); expected (%#v, %#v)", tag, value, "", src) + } +} + +func TestSetYAML(t *testing.T) { + tmpl := &template{} + tmpl.SetYAML("tagDoesntMatter", "hello {{.World}}") + result := tmpl.Execute(map[string]string{ + "World": "world!", + }) + expected := "hello world!" + if result != expected { + t.Errorf("expected %#v; got %#v", expected, result) + } +} +*/ + +func BenchmarkExecuteNilTemplate(b *testing.B) { + template := &template{src: "hello world"} + b.ResetTimer() + for i := 0; i < b.N; i++ { + template.Execute(nil) + } +} + +func BenchmarkExecuteHelloWorldTemplate(b *testing.B) { + template, err := newTemplate("hello world") + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + template.Execute(nil) + } +} + +// Executing a simple template like this is ~6x slower than Sprintf +// but it is still only a few microseconds which should be sufficiently fast. +// The benefit is that we have nice semantic tags in the translation. +func BenchmarkExecuteHelloNameTemplate(b *testing.B) { + template, err := newTemplate("hello {{.Name}}") + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + template.Execute(map[string]string{ + "Name": "Nick", + }) + } +} + +func BenchmarkSprintf(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + fmt.Sprintf("hello %s", "nick") + } +} diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go new file mode 100644 index 000000000..fa93180b8 --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go @@ -0,0 +1,83 @@ +// Package translation defines the interface for a translation. +package translation + +import ( + "fmt" + + "github.com/nicksnyder/go-i18n/i18n/language" +) + +// Translation is the interface that represents a translated string. +type Translation interface { + // MarshalInterface returns the object that should be used + // to serialize the translation. + MarshalInterface() interface{} + ID() string + Template(language.Plural) *template + UntranslatedCopy() Translation + Normalize(language *language.Language) Translation + Backfill(src Translation) Translation + Merge(Translation) Translation + Incomplete(l *language.Language) bool +} + +// SortableByID implements sort.Interface for a slice of translations. +type SortableByID []Translation + +func (a SortableByID) Len() int { return len(a) } +func (a SortableByID) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a SortableByID) Less(i, j int) bool { return a[i].ID() < a[j].ID() } + +// NewTranslation reflects on data to create a new Translation. +// +// data["id"] must be a string and data["translation"] must be either a string +// for a non-plural translation or a map[string]interface{} for a plural translation. +func NewTranslation(data map[string]interface{}) (Translation, error) { + id, ok := data["id"].(string) + if !ok { + return nil, fmt.Errorf(`missing "id" key`) + } + var pluralObject map[string]interface{} + switch translation := data["translation"].(type) { + case string: + tmpl, err := newTemplate(translation) + if err != nil { + return nil, err + } + return &singleTranslation{id, tmpl}, nil + case map[interface{}]interface{}: + // The YAML parser uses interface{} keys so we first convert them to string keys. + pluralObject = make(map[string]interface{}) + for k, v := range translation { + kStr, ok := k.(string) + if !ok { + return nil, fmt.Errorf(`invalid plural category type %T; expected string`, k) + } + pluralObject[kStr] = v + } + case map[string]interface{}: + pluralObject = translation + case nil: + return nil, fmt.Errorf(`missing "translation" key`) + default: + return nil, fmt.Errorf(`unsupported type for "translation" key %T`, translation) + } + + templates := make(map[language.Plural]*template, len(pluralObject)) + for k, v := range pluralObject { + pc, err := language.NewPlural(k) + if err != nil { + return nil, err + } + str, ok := v.(string) + if !ok { + return nil, fmt.Errorf(`plural category "%s" has value of type %T; expected string`, pc, v) + } + tmpl, err := newTemplate(str) + if err != nil { + return nil, err + } + templates[pc] = tmpl + } + return &pluralTranslation{id, templates}, nil +} diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go new file mode 100644 index 000000000..7380d5a6f --- /dev/null +++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go @@ -0,0 +1,17 @@ +package translation + +import ( + "sort" + "testing" +) + +// Check this here to avoid unnecessary import of sort package. +var _ = sort.Interface(make(SortableByID, 0, 0)) + +func TestNewSingleTranslation(t *testing.T) { + t.Skipf("not implemented") +} + +func TestNewPluralTranslation(t *testing.T) { + t.Skipf("not implemented") +} |