| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package message |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "testing" |
| |
| "golang.org/x/text/internal" |
| "golang.org/x/text/internal/format" |
| "golang.org/x/text/language" |
| "golang.org/x/text/message/catalog" |
| ) |
| |
| type formatFunc func(s fmt.State, v rune) |
| |
| func (f formatFunc) Format(s fmt.State, v rune) { f(s, v) } |
| |
| func TestBinding(t *testing.T) { |
| testCases := []struct { |
| tag string |
| value interface{} |
| want string |
| }{ |
| {"en", 1, "1"}, |
| {"en", "2", "2"}, |
| { // Language is passed. |
| "en", |
| formatFunc(func(fs fmt.State, v rune) { |
| s := fs.(format.State) |
| io.WriteString(s, s.Language().String()) |
| }), |
| "en", |
| }, |
| } |
| for i, tc := range testCases { |
| p := NewPrinter(language.MustParse(tc.tag)) |
| if got := p.Sprint(tc.value); got != tc.want { |
| t.Errorf("%d:%s:Sprint(%v) = %q; want %q", i, tc.tag, tc.value, got, tc.want) |
| } |
| var buf bytes.Buffer |
| p.Fprint(&buf, tc.value) |
| if got := buf.String(); got != tc.want { |
| t.Errorf("%d:%s:Fprint(%v) = %q; want %q", i, tc.tag, tc.value, got, tc.want) |
| } |
| } |
| } |
| |
| func TestLocalization(t *testing.T) { |
| type test struct { |
| tag string |
| key Reference |
| args []interface{} |
| want string |
| } |
| args := func(x ...interface{}) []interface{} { return x } |
| empty := []interface{}{} |
| joe := []interface{}{"Joe"} |
| joeAndMary := []interface{}{"Joe", "Mary"} |
| |
| testCases := []struct { |
| desc string |
| cat []entry |
| test []test |
| }{{ |
| desc: "empty", |
| test: []test{ |
| {"en", "key", empty, "key"}, |
| {"en", "", empty, ""}, |
| {"nl", "", empty, ""}, |
| }, |
| }, { |
| desc: "hierarchical languages", |
| cat: []entry{ |
| {"en", "hello %s", "Hello %s!"}, |
| {"en-GB", "hello %s", "Hellø %s!"}, |
| {"en-US", "hello %s", "Howdy %s!"}, |
| {"en", "greetings %s and %s", "Greetings %s and %s!"}, |
| }, |
| test: []test{ |
| {"und", "hello %s", joe, "hello Joe"}, |
| {"nl", "hello %s", joe, "hello Joe"}, |
| {"en", "hello %s", joe, "Hello Joe!"}, |
| {"en-US", "hello %s", joe, "Howdy Joe!"}, |
| {"en-GB", "hello %s", joe, "Hellø Joe!"}, |
| {"en-oxendict", "hello %s", joe, "Hello Joe!"}, |
| {"en-US-oxendict-u-ms-metric", "hello %s", joe, "Howdy Joe!"}, |
| |
| {"und", "greetings %s and %s", joeAndMary, "greetings Joe and Mary"}, |
| {"nl", "greetings %s and %s", joeAndMary, "greetings Joe and Mary"}, |
| {"en", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, |
| {"en-US", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, |
| {"en-GB", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, |
| {"en-oxendict", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, |
| {"en-US-oxendict-u-ms-metric", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, |
| }, |
| }, { |
| desc: "references", |
| cat: []entry{ |
| {"en", "hello", "Hello!"}, |
| }, |
| test: []test{ |
| {"en", "hello", empty, "Hello!"}, |
| {"en", Key("hello", "fallback"), empty, "Hello!"}, |
| {"en", Key("xxx", "fallback"), empty, "fallback"}, |
| {"und", Key("hello", "fallback"), empty, "fallback"}, |
| }, |
| }, { |
| desc: "zero substitution", // work around limitation of fmt |
| cat: []entry{ |
| {"en", "hello %s", "Hello!"}, |
| {"en", "hi %s and %s", "Hello %[2]s!"}, |
| }, |
| test: []test{ |
| {"en", "hello %s", joe, "Hello!"}, |
| {"en", "hello %s", joeAndMary, "Hello!"}, |
| {"en", "hi %s and %s", joeAndMary, "Hello Mary!"}, |
| // The following tests resolve to the fallback string. |
| {"und", "hello", joeAndMary, "hello"}, |
| {"und", "hello %%%%", joeAndMary, "hello %%"}, |
| {"und", "hello %#%%4.2% ", joeAndMary, "hello %% "}, |
| {"und", "hello %s", joeAndMary, "hello Joe%!(EXTRA string=Mary)"}, |
| {"und", "hello %+%%s", joeAndMary, "hello %Joe%!(EXTRA string=Mary)"}, |
| {"und", "hello %-42%%s ", joeAndMary, "hello %Joe %!(EXTRA string=Mary)"}, |
| }, |
| }, { |
| desc: "number formatting", |
| cat: []entry{ |
| {"und", "files", "%d files left"}, |
| {"und", "meters", "%.2f meters"}, |
| {"de", "files", "%d Dateien übrig"}, |
| }, |
| test: []test{ |
| {"en", "meters", args(3000.2), "3,000.20 meters"}, |
| {"en-u-nu-gujr", "files", args(123456), "૧૨૩,૪૫૬ files left"}, |
| {"de", "files", args(1234), "1.234 Dateien übrig"}, |
| {"de-CH", "files", args(1234), "1’234 Dateien übrig"}, |
| {"de-CH-u-nu-mong", "files", args(1234), "᠑’᠒᠓᠔ Dateien übrig"}, |
| }, |
| }, { |
| desc: "substitute translation", |
| cat: []entry{ |
| {"en", "google", "Google"}, |
| {"en", "sub", "%s"}, |
| {"en", "visit", "Lookup: %m."}, |
| }, |
| test: []test{ |
| {"en", "visit", args("google"), "Lookup: Google."}, |
| {"en", "visit", args("sub"), "Lookup: %s."}, |
| }, |
| }} |
| |
| for _, tc := range testCases { |
| cat, _ := initCat(tc.cat) |
| |
| for i, pt := range tc.test { |
| t.Run(fmt.Sprintf("%s:%d", tc.desc, i), func(t *testing.T) { |
| p := NewPrinter(language.MustParse(pt.tag), Catalog(cat)) |
| |
| if got := p.Sprintf(pt.key, pt.args...); got != pt.want { |
| t.Errorf("Sprintf(%q, %v) = %s; want %s", |
| pt.key, pt.args, got, pt.want) |
| return // Next error will likely be the same. |
| } |
| |
| w := &bytes.Buffer{} |
| p.Fprintf(w, pt.key, pt.args...) |
| if got := w.String(); got != pt.want { |
| t.Errorf("Fprintf(%q, %v) = %s; want %s", |
| pt.key, pt.args, got, pt.want) |
| } |
| }) |
| } |
| } |
| } |
| |
| type entry struct{ tag, key, msg string } |
| |
| func initCat(entries []entry) (*catalog.Builder, []language.Tag) { |
| tags := []language.Tag{} |
| cat := catalog.NewBuilder() |
| for _, e := range entries { |
| tag := language.MustParse(e.tag) |
| tags = append(tags, tag) |
| cat.SetString(tag, e.key, e.msg) |
| } |
| return cat, internal.UniqueTags(tags) |
| } |