language: move to CLDR test format This helps exchanging test files between platforms. Change-Id: I1ed8e2a9d99d85afdeb0e8bd76cb1d6354cbbd67 Reviewed-on: https://go-review.googlesource.com/55391 Run-TryBot: Marcel van Lohuizen <mpvl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/language/data_test.go b/language/data_test.go deleted file mode 100644 index e1b9606..0000000 --- a/language/data_test.go +++ /dev/null
@@ -1,461 +0,0 @@ -// Copyright 2013 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 language - -import ( - "flag" - "fmt" - "os" - "testing" -) - -var outfile = flag.String("genucd", "", "generate UCD file from test data") - -func TestGenerate(t *testing.T) { - if *outfile == "" { - return - } - - w, err := os.Create(*outfile) - if err != nil { - t.Error(err) - } - defer w.Close() - - for _, tc := range matchTests { - fmt.Fprintln(w, "# "+tc.comment) - for _, t := range tc.test { - fmt.Fprintf(w, "%s ; %s ; %s\n", tc.supported, t.desired, t.match) - } - fmt.Fprintln(w) - } -} - -type matchTest struct { - comment string - supported string - test []struct{ match, desired string } -} - -var matchTests = []matchTest{ - { - "basics", - "fr, en-GB, en", - []struct{ match, desired string }{ - {"en-GB", "en-GB"}, - {"en", "en-US"}, - {"fr", "fr-FR"}, - {"fr", "ja-JP"}, - }, - }, - { - "script fallbacks", - "zh-CN, zh-TW, iw", - []struct{ match, desired string }{ - {"zh-TW", "zh-Hant"}, - {"zh-CN", "zh"}, - {"zh-CN", "zh-Hans-CN"}, - {"zh-TW", "zh-Hant-HK"}, - {"iw", "he-IT"}, - }, - }, - { - "language-specific script fallbacks 1", - "en, sr, nl", - []struct{ match, desired string }{ - {"sr", "sr-Latn"}, - {"en", "sh"}, - {"en", "hr"}, - {"en", "bs"}, - // TODO: consider if the following match is a good one. - // Due to new script first rule, which maybe should be an option. - {"sr", "nl-Cyrl"}, - }, - }, - { - "language-specific script fallbacks 2", - "en, sh", - []struct{ match, desired string }{ - {"sh", "sr"}, - {"sh", "sr-Cyrl"}, - {"sh", "hr"}, - }, - }, - { - "don't match hr to sr-Latn", - "en, sr-Latn", - []struct{ match, desired string }{ - {"en", "hr"}, - }, - }, - { - "both deprecated and not", - "fil, tl, iw, he", - []struct{ match, desired string }{ - {"he", "he-IT"}, - {"he", "he"}, - {"iw", "iw"}, - {"fil", "fil-IT"}, - {"fil", "fil"}, - {"tl", "tl"}, - }, - }, - { - "nearby languages", - "en, fil, ro, nn", - []struct{ match, desired string }{ - {"fil", "tl"}, - {"ro", "mo"}, - {"nn", "nb"}, - {"en", "ja"}, // make sure default works - }, - }, - { - "nearby languages: Nynorsk to Bokmål", - "en, nb", - []struct{ match, desired string }{ - {"nb", "nn"}, - }, - }, - { - "nearby languages: Danish does not match nn", - "en, nn", - []struct{ match, desired string }{ - {"en", "da"}, - }, - }, - { - "nearby languages: Danish matches no", - "en, no", - []struct{ match, desired string }{ - {"no", "da"}, - }, - }, - { - "nearby languages: Danish matches nb", - "en, nb", - []struct{ match, desired string }{ - {"nb", "da"}, - }, - }, - { - "prefer matching languages over language variants.", - "nn, en-GB", - []struct{ match, desired string }{ - {"en-GB", "no, en-US"}, - {"en-GB", "nb, en-US"}, - }, - }, - { - "deprecated version is closer than same language with other differences", - "nl, he, en-GB", - []struct{ match, desired string }{ - {"he", "iw, en-US"}, - }, - }, - { - "macro equivalent is closer than same language with other differences", - "nl, zh, en-GB, no", - []struct{ match, desired string }{ - {"zh", "cmn, en-US"}, - {"no", "nb, en-US"}, - }, - }, - { - "legacy equivalent is closer than same language with other differences", - "nl, fil, en-GB", - []struct{ match, desired string }{ - {"fil", "tl, en-US"}, - }, - }, - { - "exact over equivalent", - "en, ro, mo, ro-MD", - []struct{ match, desired string }{ - {"ro", "ro"}, - {"mo", "mo"}, - {"ro-MD", "ro-MD"}, - }, - }, - { - "maximization of legacy", - "sr-Cyrl, sr-Latn, ro, ro-MD", - []struct{ match, desired string }{ - {"sr-Latn", "sh"}, - {"ro-MD", "mo"}, - }, - }, - { - "empty", - "", - []struct{ match, desired string }{ - {"und", "fr"}, - {"und", "en"}, - }, - }, - { - "private use subtags", - "fr, en-GB, x-bork, es-ES, es-419", - []struct{ match, desired string }{ - {"fr", "x-piglatin"}, - {"x-bork", "x-bork"}, - }, - }, - { - "grandfathered codes", - "fr, i-klingon, en-Latn-US", - []struct{ match, desired string }{ - {"en-Latn-US", "en-GB-oed"}, - {"tlh", "i-klingon"}, - }, - }, - { - "exact match", - "fr, en-GB, ja, es-ES, es-MX", - []struct{ match, desired string }{ - {"ja", "ja, de"}, - }, - }, - { - "simple variant match", - "fr, en-GB, ja, es-ES, es-MX", - []struct{ match, desired string }{ - // Intentionally avoiding a perfect-match or two candidates for variant matches. - {"en-GB", "de, en-US"}, - // Fall back. - {"fr", "de, zh"}, - }, - }, - { - "best match for traditional Chinese", - // Scenario: An application that only supports Simplified Chinese (and some - // other languages), but does not support Traditional Chinese. zh-Hans-CN - // could be replaced with zh-CN, zh, or zh-Hans, it wouldn't make much of - // a difference. - "fr, zh-Hans-CN, en-US", - []struct{ match, desired string }{ - {"zh-Hans-CN", "zh-TW"}, - {"zh-Hans-CN", "zh-Hant"}, - // One can avoid a zh-Hant to zh-Hans match by including a second language - // preference which is a better match. - {"en-US", "zh-TW, en"}, - {"en-US", "zh-Hant-CN, en"}, - {"zh-Hans-CN", "zh-Hans, en"}, - }, - }, - // More specific region and script tie-breakers. - { - "more specific script should win in case regions are identical", - "af, af-Latn, af-Arab", - []struct{ match, desired string }{ - {"af", "af"}, - {"af", "af-ZA"}, - {"af-Latn", "af-Latn-ZA"}, - {"af-Latn", "af-Latn"}, - }, - }, - { - "more specific region should win", - "nl, nl-NL, nl-BE", - []struct{ match, desired string }{ - {"nl", "nl"}, - {"nl", "nl-Latn"}, - {"nl-NL", "nl-Latn-NL"}, - {"nl-NL", "nl-NL"}, - }, - }, - { - "region may replace matched if matched is enclosing", - "es-419,es", - []struct{ match, desired string }{ - {"es-MX", "es-MX"}, - {"es", "es-SG"}, - }, - }, - { - "more specific region wins over more specific script", - "nl, nl-Latn, nl-NL, nl-BE", - []struct{ match, desired string }{ - {"nl", "nl"}, - {"nl-Latn", "nl-Latn"}, - {"nl-NL", "nl-NL"}, - {"nl-NL", "nl-Latn-NL"}, - }, - }, - // Region distance tie-breakers. - { - "region distance Portuguese", - "pt, pt-PT", - []struct{ match, desired string }{ - {"pt-PT", "pt-ES"}, - }, - }, - { - "region distance French", - "en, fr, fr-CA, fr-CH", - []struct{ match, desired string }{ - {"fr-CA", "fr-US"}, - }, - }, - { - "region distance German", - "de-AT, de-DE, de-CH", - []struct{ match, desired string }{ - {"de-DE", "de"}, - }, - }, - { - "en-AU is closer to en-GB than to en (which is en-US)", - "en, en-GB, es-ES, es-419", - []struct{ match, desired string }{ - {"en-GB", "en-AU"}, - {"es-MX", "es-MX"}, - {"es-ES", "es-PT"}, - }, - }, - // Test exceptions with "und". - // When the undefined language doesn't match anything in the list, return the default, as usual. - // max("und") = "en-Latn-US", and since matching is based on maximized tags, the undefined - // language would normally match English. But that would produce the counterintuitive results. - // Matching "und" to "it,en" would be "en" matching "en" to "it,und" would be "und". - // To avoid this max("und") is defined as "und" - { - "undefined", - "it, fr", - []struct{ match, desired string }{ - {"it", "und"}, - }, - }, - { - "und does not match en", - "it, en", - []struct{ match, desired string }{ - {"it", "und"}, - }, - }, - { - "undefined in priority list", - "it, und", - []struct{ match, desired string }{ - {"und", "und"}, - {"it", "en"}, - }, - }, - // Undefined scripts and regions. - { - "undefined", - "it, fr, zh", - []struct{ match, desired string }{ - {"fr", "und-FR"}, - {"zh", "und-CN"}, - {"zh", "und-Hans"}, - {"zh", "und-Hant"}, - {"it", "und-Latn"}, - }, - }, - // Early termination conditions: do not consider all desired strings if - // a match is good enough. - { - "match on maximized tag", - "fr, en-GB, ja, es-ES, es-MX", - []struct{ match, desired string }{ - // ja-JP matches ja on likely subtags, and it's listed first, - // thus it wins over the second preference en-GB. - {"ja", "ja-JP, en-GB"}, - {"ja", "ja-Jpan-JP, en-GB"}, - }, - }, - { - "pick best maximized tag", - "ja, ja-Jpan-US, ja-JP, en, ru", - []struct{ match, desired string }{ - {"ja", "ja-Jpan, ru"}, - {"ja-JP", "ja-JP, ru"}, - {"ja-Jpan-US", "ja-US, ru"}, - }, - }, - { - "termination: pick best maximized match", - "ja, ja-Jpan, ja-JP, en, ru", - []struct{ match, desired string }{ - {"ja-JP", "ja-Jpan-JP, ru"}, - {"ja-Jpan", "ja-Jpan, ru"}, - }, - }, - { - "no match on maximized", - "en, de, fr, ja", - []struct{ match, desired string }{ - // de maximizes to de-DE. - // Pick the exact match for the secondary language instead. - {"fr", "de-CH, fr"}, - }, - }, - - // Test that the CLDR parent relations are correctly preserved by the matcher. - // These matches may change for different CLDR versions. - { - "parent relation preserved", - "en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK", - []struct{ match, desired string }{ - {"en-GB", "en-150"}, - // {"en-GB", "en-001"}, // TODO: currently en, should probably be en-GB - {"en-GB", "en-AU"}, - {"en-GB", "en-BE"}, - {"en-GB", "en-GG"}, - {"en-GB", "en-GI"}, - {"en-GB", "en-HK"}, - {"en-GB", "en-IE"}, - {"en-GB", "en-IM"}, - {"en-GB", "en-IN"}, - {"en-GB", "en-JE"}, - {"en-GB", "en-MT"}, - {"en-GB", "en-NZ"}, - {"en-GB", "en-PK"}, - {"en-GB", "en-SG"}, - {"en-GB", "en-DE"}, - {"en-GB", "en-MT"}, - {"es-AR", "es-AR"}, - {"es-BO", "es-BO"}, - {"es-CL", "es-CL"}, - {"es-CO", "es-CO"}, - {"es-CR", "es-CR"}, - {"es-CU", "es-CU"}, - {"es-DO", "es-DO"}, - {"es-EC", "es-EC"}, - {"es-GT", "es-GT"}, - {"es-HN", "es-HN"}, - {"es-MX", "es-MX"}, - {"es-NI", "es-NI"}, - {"es-PA", "es-PA"}, - {"es-PE", "es-PE"}, - {"es-PR", "es-PR"}, - {"es", "es-PT"}, - {"es-PY", "es-PY"}, - {"es-SV", "es-SV"}, - {"es-419", "es-US"}, // US is not in Latin America, so don't make more specific. - {"es-UY", "es-UY"}, - {"es-VE", "es-VE"}, - {"pt-PT", "pt-AO"}, - {"pt-PT", "pt-CV"}, - {"pt-PT", "pt-GW"}, - {"pt-PT", "pt-MO"}, - {"pt-PT", "pt-MZ"}, - {"pt-PT", "pt-ST"}, - {"pt-PT", "pt-TL"}, - }, - }, - // Options and variants are inherited from user-defined settings. - { - "preserve Unicode extension", - "en, de, sl-nedis", - []struct{ match, desired string }{ - {"de-u-co-phonebk", "de-FR-u-co-phonebk"}, - {"sl-nedis-u-cu-eur", "sl-nedis-u-cu-eur"}, - {"sl-nedis-u-cu-eur", "sl-u-cu-eur"}, - {"sl-nedis-u-cu-eur", "sl-HR-nedis-u-cu-eur"}, - }, - }, -}
diff --git a/language/match_test.go b/language/match_test.go index 4fb56a0..ae9a2f6 100644 --- a/language/match_test.go +++ b/language/match_test.go
@@ -10,6 +10,7 @@ "fmt" "os" "path" + "path/filepath" "strings" "testing" @@ -19,31 +20,37 @@ var verbose = flag.Bool("verbose", false, "set to true to print the internal tables of matchers") -func TestCLDRCompliance(t *testing.T) { - r, err := os.Open("testdata/localeMatcherTest.txt") - if err != nil { - t.Fatal(err) - } - ucd.Parse(r, func(p *ucd.Parser) { - name := strings.Replace(path.Join(p.String(0), p.String(1)), " ", "", -1) - if skip[name] { - return +func TestCompliance(t *testing.T) { + filepath.Walk("testdata", func(file string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil } - t.Run(name, func(t *testing.T) { - supported := makeTagList(p.String(0)) - desired := makeTagList(p.String(1)) - gotCombined, index, _ := NewMatcher(supported).Match(desired...) + r, err := os.Open(file) + if err != nil { + t.Fatal(err) + } + ucd.Parse(r, func(p *ucd.Parser) { + name := strings.Replace(path.Join(p.String(0), p.String(1)), " ", "", -1) + if skip[name] { + return + } + t.Run(info.Name()+"/"+name, func(t *testing.T) { + supported := makeTagList(p.String(0)) + desired := makeTagList(p.String(1)) + gotCombined, index, _ := NewMatcher(supported).Match(desired...) - gotMatch := supported[index] - wantMatch := Make(p.String(2)) - if gotMatch != wantMatch { - t.Fatalf("match: got %q; want %q", gotMatch, wantMatch) - } - wantCombined, err := Parse(p.String(3)) - if err == nil && gotCombined != wantCombined { - t.Errorf("combined: got %q; want %q", gotCombined, wantCombined) - } + gotMatch := supported[index] + wantMatch := mk(p.String(2)) + if gotMatch != wantMatch { + t.Fatalf("match: got %q; want %q", gotMatch, wantMatch) + } + wantCombined, err := Raw.Parse(p.String(3)) + if err == nil && gotCombined != wantCombined { + t.Errorf("combined: got %q; want %q", gotCombined, wantCombined) + } + }) }) + return nil }) } @@ -106,7 +113,7 @@ func makeTagList(s string) (tags []Tag) { for _, s := range strings.Split(s, ",") { - tags = append(tags, Make(strings.TrimSpace(s))) + tags = append(tags, mk(strings.TrimSpace(s))) } return tags } @@ -370,35 +377,8 @@ return fmt.Sprintf("%v:%d:%v:%v-%v|%v", t.tag, t.index, t.conf, t.maxRegion, t.maxScript, t.altScript) } -func parseSupported(list string) (out []Tag) { - for _, s := range strings.Split(list, ",") { - out = append(out, mk(strings.TrimSpace(s))) - } - return out -} - -// The test set for TestBestMatch is defined in data_test.go. -func TestBestMatch(t *testing.T) { - for _, tt := range matchTests { - supported := parseSupported(tt.supported) - m := newMatcher(supported, nil) - if *verbose { - fmt.Printf("%s:\n%v\n", tt.comment, m) - } - for _, tm := range tt.test { - t.Run(path.Join(tt.comment, tt.supported, tm.desired), func(t *testing.T) { - tag, _, conf := m.Match(parseSupported(tm.desired)...) - if tag.String() != tm.match { - t.Errorf("find %s in %q: have %s; want %s (%v)", tm.desired, tt.supported, tag, tm.match, conf) - } - }) - - } - } -} - func TestBestMatchAlloc(t *testing.T) { - m := NewMatcher(parseSupported("en sr nl")) + m := NewMatcher(makeTagList("en sr nl")) // Go allocates when creating a list of tags from a single tag! list := []Tag{English} avg := testtext.AllocsPerRun(1, func() {
diff --git a/language/testdata/localeMatcherTest.txt b/language/testdata/CLDRLocaleMatcherTest.txt similarity index 100% rename from language/testdata/localeMatcherTest.txt rename to language/testdata/CLDRLocaleMatcherTest.txt
diff --git a/language/testdata/GoLocaleMatcherTest.txt b/language/testdata/GoLocaleMatcherTest.txt index cc59e9b..7817979 100644 --- a/language/testdata/GoLocaleMatcherTest.txt +++ b/language/testdata/GoLocaleMatcherTest.txt
@@ -9,7 +9,7 @@ zh-CN, zh-TW, iw ; zh ; zh-CN zh-CN, zh-TW, iw ; zh-Hans-CN ; zh-CN zh-CN, zh-TW, iw ; zh-Hant-HK ; zh-TW -zh-CN, zh-TW, iw ; he-IT ; iw +zh-CN, zh-TW, iw ; he-IT ; iw ; iw # should the surface tag be he? # language-specific script fallbacks 1 en, sr, nl ; sr-Latn ; sr @@ -114,7 +114,7 @@ nl, nl-NL, nl-BE ; nl-NL ; nl-NL # region may replace matched if matched is enclosing -es-419,es ; es-MX ; es-MX +es-419,es ; es-MX ; es-419 ; es-MX es-419,es ; es-SG ; es # more specific region wins over more specific script @@ -134,7 +134,7 @@ # en-AU is closer to en-GB than to en (which is en-US) en, en-GB, es-ES, es-419 ; en-AU ; en-GB -en, en-GB, es-ES, es-419 ; es-MX ; es-MX +en, en-GB, es-ES, es-419 ; es-MX ; es-419 ; es-MX en, en-GB, es-ES, es-419 ; es-PT ; es-ES # undefined @@ -187,27 +187,27 @@ en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; en-SG ; en-GB en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; en-DE ; en-GB en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; en-MT ; en-GB -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-AR ; es-AR -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-BO ; es-BO -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CL ; es-CL -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CO ; es-CO -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CR ; es-CR -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CU ; es-CU -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-DO ; es-DO -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-EC ; es-EC -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-GT ; es-GT -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-HN ; es-HN -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-MX ; es-MX -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-NI ; es-NI -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PA ; es-PA -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PE ; es-PE -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PR ; es-PR +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-AR ; es-419 ; es-AR +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-BO ; es-419 ; es-BO +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CL ; es-419 ; es-CL +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CO ; es-419 ; es-CO +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CR ; es-419 ; es-CR +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-CU ; es-419 ; es-CU +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-DO ; es-419 ; es-DO +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-EC ; es-419 ; es-EC +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-GT ; es-419 ; es-GT +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-HN ; es-419 ; es-HN +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-MX ; es-419 ; es-MX +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-NI ; es-419 ; es-NI +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PA ; es-419 ; es-PA +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PE ; es-419 ; es-PE +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PR ; es-419 ; es-PR en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PT ; es -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PY ; es-PY -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-SV ; es-SV +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-PY ; es-419 ; es-PY +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-SV ; es-419 ; es-SV en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-US ; es-419 -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-UY ; es-UY -en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-VE ; es-VE +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-UY ; es-419 ; es-UY +en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; es-VE ; es-419 ; es-VE en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; pt-AO ; pt-PT en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; pt-CV ; pt-PT en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; pt-GW ; pt-PT @@ -217,8 +217,8 @@ en, en-US, en-GB, es, es-419, pt, pt-BR, pt-PT, zh, zh-Hant, zh-Hant-HK ; pt-TL ; pt-PT # preserve Unicode extension -en, de, sl-nedis ; de-FR-u-co-phonebk ; de-u-co-phonebk -en, de, sl-nedis ; sl-nedis-u-cu-eur ; sl-nedis-u-cu-eur -en, de, sl-nedis ; sl-u-cu-eur ; sl-nedis-u-cu-eur -en, de, sl-nedis ; sl-HR-nedis-u-cu-eur ; sl-nedis-u-cu-eur +en, de, sl-nedis ; de-FR-u-co-phonebk ; de ; de-u-co-phonebk +en, de, sl-nedis ; sl-nedis-u-cu-eur ; sl-nedis ; sl-nedis-u-cu-eur +en, de, sl-nedis ; sl-u-cu-eur ; sl-nedis ; sl-nedis-u-cu-eur +en, de, sl-nedis ; sl-HR-nedis-u-cu-eur ; sl-nedis ; sl-nedis-u-cu-eur