// Copyright 2017 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 catalog import ( "bytes" "fmt" "reflect" "testing" "golang.org/x/text/internal" "golang.org/x/text/internal/catmsg" "golang.org/x/text/language" ) type entry struct { tag, key string msg interface{} } var testCases = []struct { desc string cat []entry lookup []entry }{{ desc: "empty catalog", lookup: []entry{ {"en", "key", ""}, {"en", "", ""}, {"nl", "", ""}, }, }, { desc: "one entry", cat: []entry{ {"en", "hello", "Hello!"}, }, lookup: []entry{ {"und", "hello", ""}, {"nl", "hello", ""}, {"en", "hello", "Hello!"}, {"en-US", "hello", "Hello!"}, {"en-GB", "hello", "Hello!"}, {"en-oxendict", "hello", "Hello!"}, {"en-oxendict-u-ms-metric", "hello", "Hello!"}, }, }, { desc: "hierarchical languages", cat: []entry{ {"en", "hello", "Hello!"}, {"en-GB", "hello", "Hellø!"}, {"en-US", "hello", "Howdy!"}, {"en", "greetings", "Greetings!"}, }, lookup: []entry{ {"und", "hello", ""}, {"nl", "hello", ""}, {"en", "hello", "Hello!"}, {"en-US", "hello", "Howdy!"}, {"en-GB", "hello", "Hellø!"}, {"en-oxendict", "hello", "Hello!"}, {"en-US-oxendict-u-ms-metric", "hello", "Howdy!"}, {"und", "greetings", ""}, {"nl", "greetings", ""}, {"en", "greetings", "Greetings!"}, {"en-US", "greetings", "Greetings!"}, {"en-GB", "greetings", "Greetings!"}, {"en-oxendict", "greetings", "Greetings!"}, {"en-US-oxendict-u-ms-metric", "greetings", "Greetings!"}, }, }, { desc: "variables", cat: []entry{ {"en", "hello %s", []Message{ Var("person", String("Jane")), String("Hello ${person}!"), }}, {"en", "hello error", []Message{ Var("person", String("Jane")), noMatchMessage{}, // trigger sequence path. String("Hello ${person."), }}, {"en", "fallback to var value", []Message{ Var("you", noMatchMessage{}, noMatchMessage{}), String("Hello ${you}."), }}, {"en", "scopes", []Message{ Var("person1", String("Mark")), Var("person2", String("Jane")), Var("couple", Var("person1", String("Joe")), String("${person1} and ${person2}")), String("Hello ${couple}."), }}, {"en", "missing var", String("Hello ${missing}.")}, }, lookup: []entry{ {"en", "hello %s", "Hello Jane!"}, {"en", "hello error", "Hello $!(MISSINGBRACE)"}, {"en", "fallback to var value", "Hello you."}, {"en", "scopes", "Hello Joe and Jane."}, {"en", "missing var", "Hello missing."}, }, }, { desc: "macros", cat: []entry{ {"en", "macro1", String("Hello ${macro1(1)}.")}, {"en", "macro2", String("Hello ${ macro1(2) }!")}, {"en", "macroWS", String("Hello ${ macro1( 2 ) }!")}, {"en", "missing", String("Hello ${ missing(1 }.")}, {"en", "badnum", String("Hello ${ badnum(1b) }.")}, {"en", "undefined", String("Hello ${ undefined(1) }.")}, {"en", "macroU", String("Hello ${ macroU(2) }!")}, }, lookup: []entry{ {"en", "macro1", "Hello Joe."}, {"en", "macro2", "Hello Joe!"}, {"en-US", "macroWS", "Hello Joe!"}, {"en-NL", "missing", "Hello $!(MISSINGPAREN)."}, {"en", "badnum", "Hello $!(BADNUM)."}, {"en", "undefined", "Hello undefined."}, {"en", "macroU", "Hello macroU!"}, }}} func initCat(entries []entry) (*Catalog, []language.Tag) { tags := []language.Tag{} cat := New() for _, e := range entries { tag := language.MustParse(e.tag) tags = append(tags, tag) switch msg := e.msg.(type) { case string: cat.SetString(tag, e.key, msg) case Message: cat.Set(tag, e.key, msg) case []Message: cat.Set(tag, e.key, msg...) } } return cat, internal.UniqueTags(tags) } func TestCatalog(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%s", tc.desc), func(t *testing.T) { cat, wantTags := initCat(tc.cat) cat.SetMacro(language.English, "macro1", String("Joe")) cat.SetMacro(language.Und, "macro2", String("${macro1(1)}")) cat.SetMacro(language.English, "macroU", noMatchMessage{}) if got := cat.Languages(); !reflect.DeepEqual(got, wantTags) { t.Errorf("%s:Languages: got %v; want %v", tc.desc, got, wantTags) } for _, e := range tc.lookup { t.Run(fmt.Sprintf("%s/%s", e.tag, e.key), func(t *testing.T) { tag := language.MustParse(e.tag) buf := testRenderer{} ctx := cat.Context(tag, &buf) want := e.msg.(string) err := ctx.Execute(e.key) gotFound := err != ErrNotFound wantFound := want != "" if gotFound != wantFound { t.Fatalf("err: got %v (%v); want %v", gotFound, err, wantFound) } if got := buf.buf.String(); got != want { t.Errorf("Lookup:\ngot %q\nwant %q", got, want) } }) } }) } } type testRenderer struct { buf bytes.Buffer } func (f *testRenderer) Arg(i int) interface{} { return nil } func (f *testRenderer) Render(s string) { f.buf.WriteString(s) } var msgNoMatch = catmsg.Register("no match", func(d *catmsg.Decoder) bool { return false // no match }) type noMatchMessage struct{} func (noMatchMessage) Compile(e *catmsg.Encoder) error { e.EncodeMessageType(msgNoMatch) return catmsg.ErrIncomplete }