golang.org/x/text@v0.14.0/message/message_test.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package message 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "testing" 12 13 "golang.org/x/text/internal" 14 "golang.org/x/text/internal/format" 15 "golang.org/x/text/language" 16 "golang.org/x/text/message/catalog" 17 ) 18 19 type formatFunc func(s fmt.State, v rune) 20 21 func (f formatFunc) Format(s fmt.State, v rune) { f(s, v) } 22 23 func TestBinding(t *testing.T) { 24 testCases := []struct { 25 tag string 26 value interface{} 27 want string 28 }{ 29 {"en", 1, "1"}, 30 {"en", "2", "2"}, 31 { // Language is passed. 32 "en", 33 formatFunc(func(fs fmt.State, v rune) { 34 s := fs.(format.State) 35 io.WriteString(s, s.Language().String()) 36 }), 37 "en", 38 }, 39 } 40 for i, tc := range testCases { 41 p := NewPrinter(language.MustParse(tc.tag)) 42 if got := p.Sprint(tc.value); got != tc.want { 43 t.Errorf("%d:%s:Sprint(%v) = %q; want %q", i, tc.tag, tc.value, got, tc.want) 44 } 45 var buf bytes.Buffer 46 p.Fprint(&buf, tc.value) 47 if got := buf.String(); got != tc.want { 48 t.Errorf("%d:%s:Fprint(%v) = %q; want %q", i, tc.tag, tc.value, got, tc.want) 49 } 50 } 51 } 52 53 func TestLocalization(t *testing.T) { 54 type test struct { 55 tag string 56 key Reference 57 args []interface{} 58 want string 59 } 60 args := func(x ...interface{}) []interface{} { return x } 61 empty := []interface{}{} 62 joe := []interface{}{"Joe"} 63 joeAndMary := []interface{}{"Joe", "Mary"} 64 65 testCases := []struct { 66 desc string 67 cat []entry 68 test []test 69 }{{ 70 desc: "empty", 71 test: []test{ 72 {"en", "key", empty, "key"}, 73 {"en", "", empty, ""}, 74 {"nl", "", empty, ""}, 75 }, 76 }, { 77 desc: "hierarchical languages", 78 cat: []entry{ 79 {"en", "hello %s", "Hello %s!"}, 80 {"en-GB", "hello %s", "Hellø %s!"}, 81 {"en-US", "hello %s", "Howdy %s!"}, 82 {"en", "greetings %s and %s", "Greetings %s and %s!"}, 83 }, 84 test: []test{ 85 {"und", "hello %s", joe, "hello Joe"}, 86 {"nl", "hello %s", joe, "hello Joe"}, 87 {"en", "hello %s", joe, "Hello Joe!"}, 88 {"en-US", "hello %s", joe, "Howdy Joe!"}, 89 {"en-GB", "hello %s", joe, "Hellø Joe!"}, 90 {"en-oxendict", "hello %s", joe, "Hello Joe!"}, 91 {"en-US-oxendict-u-ms-metric", "hello %s", joe, "Howdy Joe!"}, 92 93 {"und", "greetings %s and %s", joeAndMary, "greetings Joe and Mary"}, 94 {"nl", "greetings %s and %s", joeAndMary, "greetings Joe and Mary"}, 95 {"en", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, 96 {"en-US", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, 97 {"en-GB", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, 98 {"en-oxendict", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, 99 {"en-US-oxendict-u-ms-metric", "greetings %s and %s", joeAndMary, "Greetings Joe and Mary!"}, 100 }, 101 }, { 102 desc: "references", 103 cat: []entry{ 104 {"en", "hello", "Hello!"}, 105 }, 106 test: []test{ 107 {"en", "hello", empty, "Hello!"}, 108 {"en", Key("hello", "fallback"), empty, "Hello!"}, 109 {"en", Key("xxx", "fallback"), empty, "fallback"}, 110 {"und", Key("hello", "fallback"), empty, "fallback"}, 111 }, 112 }, { 113 desc: "zero substitution", // work around limitation of fmt 114 cat: []entry{ 115 {"en", "hello %s", "Hello!"}, 116 {"en", "hi %s and %s", "Hello %[2]s!"}, 117 }, 118 test: []test{ 119 {"en", "hello %s", joe, "Hello!"}, 120 {"en", "hello %s", joeAndMary, "Hello!"}, 121 {"en", "hi %s and %s", joeAndMary, "Hello Mary!"}, 122 // The following tests resolve to the fallback string. 123 {"und", "hello", joeAndMary, "hello"}, 124 {"und", "hello %%%%", joeAndMary, "hello %%"}, 125 {"und", "hello %#%%4.2% ", joeAndMary, "hello %% "}, 126 {"und", "hello %s", joeAndMary, "hello Joe%!(EXTRA string=Mary)"}, 127 {"und", "hello %+%%s", joeAndMary, "hello %Joe%!(EXTRA string=Mary)"}, 128 {"und", "hello %-42%%s ", joeAndMary, "hello %Joe %!(EXTRA string=Mary)"}, 129 }, 130 }, { 131 desc: "number formatting", 132 cat: []entry{ 133 {"und", "files", "%d files left"}, 134 {"und", "meters", "%.2f meters"}, 135 {"de", "files", "%d Dateien übrig"}, 136 }, 137 test: []test{ 138 {"en", "meters", args(3000.2), "3,000.20 meters"}, 139 {"en-u-nu-gujr", "files", args(123456), "૧૨૩,૪૫૬ files left"}, 140 {"de", "files", args(1234), "1.234 Dateien übrig"}, 141 {"de-CH", "files", args(1234), "1’234 Dateien übrig"}, 142 {"de-CH-u-nu-mong", "files", args(1234), "᠑’᠒᠓᠔ Dateien übrig"}, 143 }, 144 }, { 145 desc: "substitute translation", 146 cat: []entry{ 147 {"en", "google", "Google"}, 148 {"en", "sub", "%s"}, 149 {"en", "visit", "Lookup: %m."}, 150 }, 151 test: []test{ 152 {"en", "visit", args("google"), "Lookup: Google."}, 153 {"en", "visit", args("sub"), "Lookup: %s."}, 154 }, 155 }} 156 157 for _, tc := range testCases { 158 cat, _ := initCat(tc.cat) 159 160 for i, pt := range tc.test { 161 t.Run(fmt.Sprintf("%s:%d", tc.desc, i), func(t *testing.T) { 162 p := NewPrinter(language.MustParse(pt.tag), Catalog(cat)) 163 164 if got := p.Sprintf(pt.key, pt.args...); got != pt.want { 165 t.Errorf("Sprintf(%q, %v) = %s; want %s", 166 pt.key, pt.args, got, pt.want) 167 return // Next error will likely be the same. 168 } 169 170 w := &bytes.Buffer{} 171 p.Fprintf(w, pt.key, pt.args...) 172 if got := w.String(); got != pt.want { 173 t.Errorf("Fprintf(%q, %v) = %s; want %s", 174 pt.key, pt.args, got, pt.want) 175 } 176 }) 177 } 178 } 179 } 180 181 type entry struct{ tag, key, msg string } 182 183 func initCat(entries []entry) (*catalog.Builder, []language.Tag) { 184 tags := []language.Tag{} 185 cat := catalog.NewBuilder() 186 for _, e := range entries { 187 tag := language.MustParse(e.tag) 188 tags = append(tags, tag) 189 cat.SetString(tag, e.key, e.msg) 190 } 191 return cat, internal.UniqueTags(tags) 192 }