1// Copyright 2014 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
5package display
6
7import (
8	"fmt"
9	"reflect"
10	"strings"
11	"testing"
12	"unicode"
13
14	"golang.org/x/text/internal/testtext"
15	"golang.org/x/text/language"
16	"golang.org/x/text/message"
17)
18
19// TODO: test that tables are properly dropped by the linker for various use
20// cases.
21
22var (
23	firstLang2aa  = language.MustParseBase("aa")
24	lastLang2zu   = language.MustParseBase("zu")
25	firstLang3ace = language.MustParseBase("ace")
26	lastLang3zza  = language.MustParseBase("zza")
27	firstTagAr001 = language.MustParse("ar-001")
28	lastTagZhHant = language.MustParse("zh-Hant")
29)
30
31// TestValues tests that for all languages, regions, and scripts in Values, at
32// least one language has a name defined for it by checking it exists in
33// English, which is assumed to be the most comprehensive. It is also tested
34// that a Namer returns "" for unsupported values.
35func TestValues(t *testing.T) {
36	type testcase struct {
37		kind string
38		n    Namer
39	}
40	// checkDefined checks that a value exists in a Namer.
41	checkDefined := func(x interface{}, namers []testcase) {
42		for _, n := range namers {
43			testtext.Run(t, fmt.Sprintf("%s.Name(%s)", n.kind, x), func(t *testing.T) {
44				if n.n.Name(x) == "" {
45					// As of version 28 there is no data for az-Arab in English,
46					// although there is useful data in other languages.
47					if x.(fmt.Stringer).String() == "az-Arab" {
48						return
49					}
50					t.Errorf("supported but no result")
51				}
52			})
53		}
54	}
55	// checkUnsupported checks that a value does not exist in a Namer.
56	checkUnsupported := func(x interface{}, namers []testcase) {
57		for _, n := range namers {
58			if got := n.n.Name(x); got != "" {
59				t.Fatalf("%s.Name(%s): unsupported tag gave non-empty result: %q", n.kind, x, got)
60			}
61		}
62	}
63
64	tags := map[language.Tag]bool{}
65	namers := []testcase{
66		{"Languages(en)", Languages(language.English)},
67		{"Tags(en)", Tags(language.English)},
68		{"English.Languages()", English.Languages()},
69		{"English.Tags()", English.Tags()},
70	}
71	for _, tag := range Values.Tags() {
72		checkDefined(tag, namers)
73		tags[tag] = true
74	}
75	for _, base := range language.Supported.BaseLanguages() {
76		tag, _ := language.All.Compose(base)
77		if !tags[tag] {
78			checkUnsupported(tag, namers)
79		}
80	}
81
82	regions := map[language.Region]bool{}
83	namers = []testcase{
84		{"Regions(en)", Regions(language.English)},
85		{"English.Regions()", English.Regions()},
86	}
87	for _, r := range Values.Regions() {
88		checkDefined(r, namers)
89		regions[r] = true
90	}
91	for _, r := range language.Supported.Regions() {
92		if r = r.Canonicalize(); !regions[r] {
93			checkUnsupported(r, namers)
94		}
95	}
96
97	scripts := map[language.Script]bool{}
98	namers = []testcase{
99		{"Scripts(en)", Scripts(language.English)},
100		{"English.Scripts()", English.Scripts()},
101	}
102	for _, s := range Values.Scripts() {
103		checkDefined(s, namers)
104		scripts[s] = true
105	}
106	for _, s := range language.Supported.Scripts() {
107		// Canonicalize the script.
108		tag, _ := language.DeprecatedScript.Compose(s)
109		if _, s, _ = tag.Raw(); !scripts[s] {
110			checkUnsupported(s, namers)
111		}
112	}
113}
114
115// TestSupported tests that we have at least some Namers for languages that we
116// claim to support. To test the claims in the documentation, it also verifies
117// that if a Namer is returned, it will have at least some data.
118func TestSupported(t *testing.T) {
119	supportedTags := Supported.Tags()
120	if len(supportedTags) != numSupported {
121		t.Errorf("number of supported was %d; want %d", len(supportedTags), numSupported)
122	}
123
124	namerFuncs := []struct {
125		kind string
126		fn   func(language.Tag) Namer
127	}{
128		{"Tags", Tags},
129		{"Languages", Languages},
130		{"Regions", Regions},
131		{"Scripts", Scripts},
132	}
133
134	// Verify that we have at least one Namer for all tags we claim to support.
135	tags := make(map[language.Tag]bool)
136	for _, tag := range supportedTags {
137		// Test we have at least one Namer for this supported Tag.
138		found := false
139		for _, kind := range namerFuncs {
140			if defined(t, kind.kind, kind.fn(tag), tag) {
141				found = true
142			}
143		}
144		if !found {
145			t.Errorf("%s: supported, but no data available", tag)
146		}
147		if tags[tag] {
148			t.Errorf("%s: included in Supported.Tags more than once", tag)
149		}
150		tags[tag] = true
151	}
152
153	// Verify that we have no Namers for tags we don't claim to support.
154	for _, base := range language.Supported.BaseLanguages() {
155		tag, _ := language.All.Compose(base)
156		// Skip tags that are supported after matching.
157		if _, _, conf := matcher.Match(tag); conf != language.No {
158			continue
159		}
160		// Test there are no Namers for this tag.
161		for _, kind := range namerFuncs {
162			if defined(t, kind.kind, kind.fn(tag), tag) {
163				t.Errorf("%[1]s(%[2]s) returns a Namer, but %[2]s is not in the set of supported Tags.", kind.kind, tag)
164			}
165		}
166	}
167}
168
169// defined reports whether n is a proper Namer, which means it is non-nil and
170// must have at least one non-empty value.
171func defined(t *testing.T, kind string, n Namer, tag language.Tag) bool {
172	if n == nil {
173		return false
174	}
175	switch kind {
176	case "Tags":
177		for _, t := range Values.Tags() {
178			if n.Name(t) != "" {
179				return true
180			}
181		}
182	case "Languages":
183		for _, t := range Values.BaseLanguages() {
184			if n.Name(t) != "" {
185				return true
186			}
187		}
188	case "Regions":
189		for _, t := range Values.Regions() {
190			if n.Name(t) != "" {
191				return true
192			}
193		}
194	case "Scripts":
195		for _, t := range Values.Scripts() {
196			if n.Name(t) != "" {
197				return true
198			}
199		}
200	}
201	t.Errorf("%s(%s) returns non-nil Namer without content", kind, tag)
202	return false
203}
204
205func TestCoverage(t *testing.T) {
206	en := language.English
207	tests := []struct {
208		n Namer
209		x interface{}
210	}{
211		{Languages(en), Values.Tags()},
212		{Scripts(en), Values.Scripts()},
213		{Regions(en), Values.Regions()},
214	}
215	for i, tt := range tests {
216		uniq := make(map[string]interface{})
217
218		v := reflect.ValueOf(tt.x)
219		for j := 0; j < v.Len(); j++ {
220			x := v.Index(j).Interface()
221			// As of version 28 there is no data for az-Arab in English,
222			// although there is useful data in other languages.
223			if x.(fmt.Stringer).String() == "az-Arab" {
224				continue
225			}
226			s := tt.n.Name(x)
227			if s == "" {
228				t.Errorf("%d:%d:%s: missing content", i, j, x)
229			} else if uniq[s] != nil {
230				t.Errorf("%d:%d:%s: identical return value %q for %v and %v", i, j, x, s, x, uniq[s])
231			}
232			uniq[s] = x
233		}
234	}
235}
236
237// TestUpdate tests whether dictionary entries for certain languages need to be
238// updated. For some languages, some of the headers may be empty or they may be
239// identical to the parent. This code detects if such entries need to be updated
240// after a table update.
241func TestUpdate(t *testing.T) {
242	tests := []struct {
243		d   *Dictionary
244		tag string
245	}{
246		{ModernStandardArabic, "ar-001"},
247		{AmericanEnglish, "en-US"},
248		{EuropeanSpanish, "es-ES"},
249		{BrazilianPortuguese, "pt-BR"},
250		{SimplifiedChinese, "zh-Hans"},
251	}
252
253	for _, tt := range tests {
254		_, i, _ := matcher.Match(language.MustParse(tt.tag))
255		if !reflect.DeepEqual(tt.d.lang, langHeaders[i]) {
256			t.Errorf("%s: lang table update needed", tt.tag)
257		}
258		if !reflect.DeepEqual(tt.d.script, scriptHeaders[i]) {
259			t.Errorf("%s: script table update needed", tt.tag)
260		}
261		if !reflect.DeepEqual(tt.d.region, regionHeaders[i]) {
262			t.Errorf("%s: region table update needed", tt.tag)
263		}
264	}
265}
266
267func TestIndex(t *testing.T) {
268	notIn := []string{"aa", "xx", "zz", "aaa", "xxx", "zzz", "Aaaa", "Xxxx", "Zzzz"}
269	tests := []tagIndex{
270		{
271			"",
272			"",
273			"",
274		},
275		{
276			"bb",
277			"",
278			"",
279		},
280		{
281			"",
282			"bbb",
283			"",
284		},
285		{
286			"",
287			"",
288			"Bbbb",
289		},
290		{
291			"bb",
292			"bbb",
293			"Bbbb",
294		},
295		{
296			"bbccddyy",
297			"bbbcccdddyyy",
298			"BbbbCcccDdddYyyy",
299		},
300	}
301	for i, tt := range tests {
302		// Create the test set from the tagIndex.
303		cnt := 0
304		for sz := 2; sz <= 4; sz++ {
305			a := tt[sz-2]
306			for j := 0; j < len(a); j += sz {
307				s := a[j : j+sz]
308				if idx := tt.index(s); idx != cnt {
309					t.Errorf("%d:%s: index was %d; want %d", i, s, idx, cnt)
310				}
311				cnt++
312			}
313		}
314		if n := tt.len(); n != cnt {
315			t.Errorf("%d: len was %d; want %d", i, n, cnt)
316		}
317		for _, x := range notIn {
318			if idx := tt.index(x); idx != -1 {
319				t.Errorf("%d:%s: index was %d; want -1", i, x, idx)
320			}
321		}
322	}
323}
324
325func TestTag(t *testing.T) {
326	tests := []struct {
327		dict string
328		tag  string
329		name string
330	}{
331		// sr is in Value.Languages(), but is not supported by agq.
332		{"agq", "sr", "|[language: sr]"},
333		{"nl", "nl", "Nederlands"},
334		// CLDR 30 dropped Vlaams as the word for nl-BE. It is still called
335		// Flemish in English, though. TODO: check if this is a CLDR bug.
336		// {"nl", "nl-BE", "Vlaams"},
337		{"nl", "nl-BE", "Nederlands (België)"},
338		{"nl", "vls", "West-Vlaams"},
339		{"en", "nl-BE", "Flemish"},
340		{"en", "en", "English"},
341		{"en", "en-GB", "British English"},
342		{"en", "en-US", "American English"}, // American English in CLDR 24+
343		{"ru", "ru", "русский"},
344		{"ru", "ru-RU", "русский (Россия)"},
345		{"ru", "ru-Cyrl", "русский (кириллица)"},
346		{"en", lastLang2zu.String(), "Zulu"},
347		{"en", firstLang2aa.String(), "Afar"},
348		{"en", lastLang3zza.String(), "Zaza"},
349		{"en", firstLang3ace.String(), "Achinese"},
350		{"en", firstTagAr001.String(), "Modern Standard Arabic"},
351		{"en", lastTagZhHant.String(), "Traditional Chinese"},
352		{"en", "aaa", "|Unknown language (aaa)"},
353		{"en", "zzj", "|Unknown language (zzj)"},
354		// If full tag doesn't match, try without script or region.
355		{"en", "aa-Hans", "Afar (Simplified Han)"},
356		{"en", "af-Arab", "Afrikaans (Arabic)"},
357		{"en", "zu-Cyrl", "Zulu (Cyrillic)"},
358		{"en", "aa-GB", "Afar (United Kingdom)"},
359		{"en", "af-NA", "Afrikaans (Namibia)"},
360		{"en", "zu-BR", "Zulu (Brazil)"},
361		// Correct inheritance and language selection.
362		{"zh", "zh-TW", "中文 (台湾)"},
363		{"zh", "zh-Hant-TW", "繁体中文 (台湾)"},
364		{"zh-Hant", "zh-TW", "中文 (台灣)"},
365		{"zh-Hant", "zh-Hant-TW", "繁體中文 (台灣)"},
366		// Some rather arbitrary interpretations for Serbian. This is arguably
367		// correct and consistent with the way zh-[Hant-]TW is handled. It will
368		// also give results more in line with the expectations if users
369		// explicitly use "sh".
370		{"sr-Latn", "sr-ME", "srpski (Crna Gora)"},
371		{"sr-Latn", "sr-Latn-ME", "srpskohrvatski (Crna Gora)"},
372		// Double script and region
373		{"nl", "en-Cyrl-BE", "Engels (Cyrillisch, België)"},
374	}
375	for _, tt := range tests {
376		t.Run(tt.dict+"/"+tt.tag, func(t *testing.T) {
377			name, fmtName := splitName(tt.name)
378			dict := language.MustParse(tt.dict)
379			tag := language.Raw.MustParse(tt.tag)
380			d := Tags(dict)
381			if n := d.Name(tag); n != name {
382				// There are inconsistencies w.r.t. capitalization in the tests
383				// due to CLDR's update procedure which treats modern and other
384				// languages differently.
385				// See https://unicode.org/cldr/trac/ticket/8051.
386				// TODO: use language capitalization to sanitize the strings.
387				t.Errorf("Name(%s) = %q; want %q", tag, n, name)
388			}
389
390			p := message.NewPrinter(dict)
391			if n := p.Sprint(Tag(tag)); n != fmtName {
392				t.Errorf("Tag(%s) = %q; want %q", tag, n, fmtName)
393			}
394		})
395	}
396}
397
398func splitName(names string) (name, formatName string) {
399	split := strings.Split(names, "|")
400	name, formatName = split[0], split[0]
401	if len(split) > 1 {
402		formatName = split[1]
403	}
404	return name, formatName
405}
406
407func TestLanguage(t *testing.T) {
408	tests := []struct {
409		dict string
410		tag  string
411		name string
412	}{
413		// sr is in Value.Languages(), but is not supported by agq.
414		{"agq", "sr", "|[language: sr]"},
415		// CLDR 30 dropped Vlaams as the word for nl-BE. It is still called
416		// Flemish in English, though. TODO: this is probably incorrect.
417		// West-Vlaams (vls) is not Vlaams. West-Vlaams could be considered its
418		// own language, whereas Vlaams is generally Dutch. So expect to have
419		// to change these tests back.
420		{"nl", "nl", "Nederlands"},
421		{"nl", "vls", "West-Vlaams"},
422		{"nl", "nl-BE", "Nederlands"},
423		{"en", "pt", "Portuguese"},
424		{"en", "pt-PT", "European Portuguese"},
425		{"en", "pt-BR", "Brazilian Portuguese"},
426		{"en", "en", "English"},
427		{"en", "en-GB", "British English"},
428		{"en", "en-US", "American English"}, // American English in CLDR 24+
429		{"en", lastLang2zu.String(), "Zulu"},
430		{"en", firstLang2aa.String(), "Afar"},
431		{"en", lastLang3zza.String(), "Zaza"},
432		{"en", firstLang3ace.String(), "Achinese"},
433		{"en", firstTagAr001.String(), "Modern Standard Arabic"},
434		{"en", lastTagZhHant.String(), "Traditional Chinese"},
435		{"en", "aaa", "|Unknown language (aaa)"},
436		{"en", "zzj", "|Unknown language (zzj)"},
437		// If full tag doesn't match, try without script or region.
438		{"en", "aa-Hans", "Afar"},
439		{"en", "af-Arab", "Afrikaans"},
440		{"en", "zu-Cyrl", "Zulu"},
441		{"en", "aa-GB", "Afar"},
442		{"en", "af-NA", "Afrikaans"},
443		{"en", "zu-BR", "Zulu"},
444		{"agq", "zh-Hant", "|[language: zh-Hant]"},
445		{"en", "sh", "Serbo-Croatian"},
446		{"en", "sr-Latn", "Serbo-Croatian"},
447		{"en", "sr", "Serbian"},
448		{"en", "sr-ME", "Serbian"},
449		{"en", "sr-Latn-ME", "Serbo-Croatian"}, // See comments in TestTag.
450	}
451	for _, tt := range tests {
452		testtext.Run(t, tt.dict+"/"+tt.tag, func(t *testing.T) {
453			name, fmtName := splitName(tt.name)
454			dict := language.MustParse(tt.dict)
455			tag := language.Raw.MustParse(tt.tag)
456			p := message.NewPrinter(dict)
457			d := Languages(dict)
458			if n := d.Name(tag); n != name {
459				t.Errorf("Name(%v) = %q; want %q", tag, n, name)
460			}
461			if n := p.Sprint(Language(tag)); n != fmtName {
462				t.Errorf("Language(%v) = %q; want %q", tag, n, fmtName)
463			}
464			if len(tt.tag) <= 3 {
465				base := language.MustParseBase(tt.tag)
466				if n := d.Name(base); n != name {
467					t.Errorf("Name(%v) = %q; want %q", base, n, name)
468				}
469				if n := p.Sprint(Language(base)); n != fmtName {
470					t.Errorf("Language(%v) = %q; want %q", base, n, fmtName)
471				}
472			}
473		})
474	}
475}
476
477func TestScript(t *testing.T) {
478	tests := []struct {
479		dict string
480		scr  string
481		name string
482	}{
483		{"nl", "Arab", "Arabisch"},
484		{"en", "Arab", "Arabic"},
485		{"en", "Zzzz", "Unknown Script"},
486		{"zh-Hant", "Hang", "韓文字"},
487		{"zh-Hant-HK", "Hang", "韓文字"},
488		{"zh", "Arab", "阿拉伯文"},
489		{"zh-Hans-HK", "Arab", "阿拉伯文"}, // same as zh
490		{"zh-Hant", "Arab", "阿拉伯文"},
491		{"zh-Hant-HK", "Arab", "阿拉伯文"}, // same as zh
492		// Canonicalized form
493		{"en", "Qaai", "Inherited"},    // deprecated script, now is Zinh
494		{"en", "sh", "Unknown Script"}, // sh canonicalizes to sr-Latn
495		{"en", "en", "Unknown Script"},
496		// Don't introduce scripts with canonicalization.
497		{"en", "sh", "Unknown Script"}, // sh canonicalizes to sr-Latn
498	}
499	for _, tt := range tests {
500		t.Run(tt.dict+"/"+tt.scr, func(t *testing.T) {
501			name, fmtName := splitName(tt.name)
502			dict := language.MustParse(tt.dict)
503			p := message.NewPrinter(dict)
504			d := Scripts(dict)
505			var tag language.Tag
506			if unicode.IsUpper(rune(tt.scr[0])) {
507				x := language.MustParseScript(tt.scr)
508				if n := d.Name(x); n != name {
509					t.Errorf("Name(%v) = %q; want %q", x, n, name)
510				}
511				if n := p.Sprint(Script(x)); n != fmtName {
512					t.Errorf("Script(%v) = %q; want %q", x, n, fmtName)
513				}
514				tag, _ = language.Raw.Compose(x)
515			} else {
516				tag = language.Raw.MustParse(tt.scr)
517			}
518			if n := d.Name(tag); n != name {
519				t.Errorf("Name(%v) = %q; want %q", tag, n, name)
520			}
521			if n := p.Sprint(Script(tag)); n != fmtName {
522				t.Errorf("Script(%v) = %q; want %q", tag, n, fmtName)
523			}
524		})
525	}
526}
527
528func TestRegion(t *testing.T) {
529	tests := []struct {
530		dict string
531		reg  string
532		name string
533	}{
534		{"nl", "NL", "Nederland"},
535		{"en", "US", "United States"},
536		{"en", "ZZ", "Unknown Region"},
537		{"en-GB", "NL", "Netherlands"},
538		// Canonical equivalents
539		{"en", "UK", "United Kingdom"},
540		// No region
541		{"en", "pt", "Unknown Region"},
542		{"en", "und", "Unknown Region"},
543		// Don't introduce regions with canonicalization.
544		{"en", "mo", "Unknown Region"},
545	}
546	for _, tt := range tests {
547		t.Run(tt.dict+"/"+tt.reg, func(t *testing.T) {
548			dict := language.MustParse(tt.dict)
549			p := message.NewPrinter(dict)
550			d := Regions(dict)
551			var tag language.Tag
552			if unicode.IsUpper(rune(tt.reg[0])) {
553				// Region
554				x := language.MustParseRegion(tt.reg)
555				if n := d.Name(x); n != tt.name {
556					t.Errorf("Name(%v) = %q; want %q", x, n, tt.name)
557				}
558				if n := p.Sprint(Region(x)); n != tt.name {
559					t.Errorf("Region(%v) = %q; want %q", x, n, tt.name)
560				}
561				tag, _ = language.Raw.Compose(x)
562			} else {
563				tag = language.Raw.MustParse(tt.reg)
564			}
565			if n := d.Name(tag); n != tt.name {
566				t.Errorf("Name(%v) = %q; want %q", tag, n, tt.name)
567			}
568			if n := p.Sprint(Region(tag)); n != tt.name {
569				t.Errorf("Region(%v) = %q; want %q", tag, n, tt.name)
570			}
571		})
572	}
573}
574
575func TestSelf(t *testing.T) {
576	tests := []struct {
577		tag  string
578		name string
579	}{
580		{"nl", "Nederlands"},
581		// CLDR 30 dropped Vlaams as the word for nl-BE. It is still called
582		// Flemish in English, though. TODO: check if this is a CLDR bug.
583		// {"nl-BE", "Vlaams"},
584		{"nl-BE", "Nederlands"},
585		{"en-GB", "British English"},
586		{lastLang2zu.String(), "isiZulu"},
587		{firstLang2aa.String(), ""},  // not defined
588		{lastLang3zza.String(), ""},  // not defined
589		{firstLang3ace.String(), ""}, // not defined
590		{firstTagAr001.String(), "العربية الرسمية الحديثة"},
591		{"ar", "العربية"},
592		{lastTagZhHant.String(), "繁體中文"},
593		{"aaa", ""},
594		{"zzj", ""},
595		// Drop entries that are not in the requested script, even if there is
596		// an entry for the language.
597		{"aa-Hans", ""},
598		{"af-Arab", ""},
599		{"zu-Cyrl", ""},
600		// Append the country name in the language of the matching language.
601		{"af-NA", "Afrikaans"},
602		{"zh", "中文"},
603		// zh-TW should match zh-Hant instead of zh!
604		{"zh-TW", "繁體中文"},
605		{"zh-Hant", "繁體中文"},
606		{"zh-Hans", "简体中文"},
607		{"zh-Hant-TW", "繁體中文"},
608		{"zh-Hans-TW", "简体中文"},
609		// Take the entry for sr which has the matching script.
610		// TODO: Capitalization changed as of CLDR 26, but change seems
611		// arbitrary. Revisit capitalization with revision 27. See
612		// https://unicode.org/cldr/trac/ticket/8051.
613		{"sr", "српски"},
614		// TODO: sr-ME should show up as Serbian or Montenegrin, not Serbo-
615		// Croatian. This is an artifact of the current algorithm, which is the
616		// way it is to have the preferred behavior for other languages such as
617		// Chinese. We can hardwire this case in the table generator or package
618		// code, but we first check if CLDR can be updated.
619		// {"sr-ME", "Srpski"}, // Is Srpskohrvatski
620		{"sr-Latn-ME", "srpskohrvatski"},
621		{"sr-Cyrl-ME", "српски"},
622		{"sr-NL", "српски"},
623		// NOTE: kk is defined, but in Cyrillic script. For China, Arab is the
624		// dominant script. We do not have data for kk-Arab and we chose to not
625		// fall back in such cases.
626		{"kk-CN", ""},
627	}
628	for i, tt := range tests {
629		d := Self
630		if n := d.Name(language.Raw.MustParse(tt.tag)); n != tt.name {
631			t.Errorf("%d:%s: was %q; want %q", i, tt.tag, n, tt.name)
632		}
633	}
634}
635
636func TestEquivalence(t *testing.T) {
637	testCases := []struct {
638		desc  string
639		namer Namer
640	}{
641		{"Self", Self},
642		{"Tags", Tags(language.Romanian)},
643		{"Languages", Languages(language.Romanian)},
644		{"Scripts", Scripts(language.Romanian)},
645	}
646	for _, tc := range testCases {
647		t.Run(tc.desc, func(t *testing.T) {
648			ro := tc.namer.Name(language.Raw.MustParse("ro-MD"))
649			mo := tc.namer.Name(language.Raw.MustParse("mo"))
650			if ro != mo {
651				t.Errorf("%q != %q", ro, mo)
652			}
653		})
654	}
655}
656
657func TestDictionaryLang(t *testing.T) {
658	tests := []struct {
659		d    *Dictionary
660		tag  string
661		name string
662	}{
663		{English, "en", "English"},
664		{Portuguese, "af", "africâner"},
665		{EuropeanPortuguese, "af", "africanês"},
666		{English, "nl-BE", "Flemish"},
667	}
668	for i, test := range tests {
669		tag := language.MustParse(test.tag)
670		if got := test.d.Tags().Name(tag); got != test.name {
671			t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name)
672		}
673		if base, _ := language.Compose(tag.Base()); base == tag {
674			if got := test.d.Languages().Name(base); got != test.name {
675				t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name)
676			}
677		}
678	}
679}
680
681func TestDictionaryRegion(t *testing.T) {
682	tests := []struct {
683		d      *Dictionary
684		region string
685		name   string
686	}{
687		{English, "FR", "France"},
688		{Portuguese, "009", "Oceania"},
689		{EuropeanPortuguese, "009", "Oceânia"},
690	}
691	for i, test := range tests {
692		tag := language.MustParseRegion(test.region)
693		if got := test.d.Regions().Name(tag); got != test.name {
694			t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name)
695		}
696	}
697}
698
699func TestDictionaryScript(t *testing.T) {
700	tests := []struct {
701		d      *Dictionary
702		script string
703		name   string
704	}{
705		{English, "Cyrl", "Cyrillic"},
706		{EuropeanPortuguese, "Gujr", "guzerate"},
707	}
708	for i, test := range tests {
709		tag := language.MustParseScript(test.script)
710		if got := test.d.Scripts().Name(tag); got != test.name {
711			t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name)
712		}
713	}
714}
715