From e308da951effe19655b632cf33757cffc00ee9fe Mon Sep 17 00:00:00 2001 From: Nick Snyder Date: Sat, 2 Dec 2023 23:55:46 -0800 Subject: [PATCH] Update to CLDR data v44 (#310) --- v2/goi18n/merge_command_test.go | 2 + v2/internal/plural/codegen/README.md | 3 +- v2/internal/plural/codegen/plurals.xml | 82 ++++++---- v2/internal/plural/codegen/xml.go | 35 ++-- v2/internal/plural/operands.go | 80 +++++++++- v2/internal/plural/operands_test.go | 51 ++++-- v2/internal/plural/rule_gen.go | 177 ++++++++++++++------- v2/internal/plural/rule_gen_test.go | 212 +++++++++++++++++-------- 8 files changed, 462 insertions(+), 180 deletions(-) diff --git a/v2/goi18n/merge_command_test.go b/v2/goi18n/merge_command_test.go index 1d2264b2..9af9cf9d 100644 --- a/v2/goi18n/merge_command_test.go +++ b/v2/goi18n/merge_command_test.go @@ -297,6 +297,7 @@ other = "{{.Count}} unread emails" [UnreadEmails] description = "Message that tells the user how many unread emails they have" hash = "sha1-5afbc91dfedb9755627655c365eb47a89e541099" +many = "{{.Count}} unread emails" one = "{{.Count}} unread email" other = "{{.Count}} unread emails" `), @@ -435,6 +436,7 @@ other = "{{.Count}} unread emails!" [UnreadEmails] description = "Message that tells the user how many unread emails they have" hash = "sha1-92a24983c5bbc0c42462cdc252dca68ebdb46501" +many = "{{.Count}} unread emails!" one = "{{.Count}} unread emails!" other = "{{.Count}} unread emails!" `), diff --git a/v2/internal/plural/codegen/README.md b/v2/internal/plural/codegen/README.md index f1f5ff80..bf831756 100644 --- a/v2/internal/plural/codegen/README.md +++ b/v2/internal/plural/codegen/README.md @@ -1,6 +1,5 @@ # How to upgrade CLDR data -1. Go to http://cldr.unicode.org/index/downloads to find the latest version. -1. Download the latest version of cldr-common (e.g. https://unicode.org/Public/cldr/39/cldr-common-39.0.zip) +1. Go to https://github.com/unicode-org/cldr/releases to find the latest release and download the source code. 1. Unzip and copy `common/supplemental/plurals.xml` to this directory. 1. Run `generate.sh`. diff --git a/v2/internal/plural/codegen/plurals.xml b/v2/internal/plural/codegen/plurals.xml index 606c8744..ab8e0e8e 100644 --- a/v2/internal/plural/codegen/plurals.xml +++ b/v2/internal/plural/codegen/plurals.xml @@ -1,9 +1,10 @@ @@ -12,25 +13,21 @@ For terms of use, see http://www.unicode.org/copyright.html - + @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - + i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04 @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - + i = 0,1 @integer 0, 1 @decimal 0.0~1.5 @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - - i = 0..1 @integer 0, 1 @decimal 0.0~1.5 - @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - - + i = 1 and v = 0 @integer 1 @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … @@ -46,7 +43,7 @@ For terms of use, see http://www.unicode.org/copyright.html n = 0..1 or n = 11..99 @integer 0, 1, 11~24 @decimal 0.0, 1.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0 @integer 2~10, 100~106, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - + n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … @@ -55,8 +52,8 @@ For terms of use, see http://www.unicode.org/copyright.html @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0~3.4, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1~1.6, 10.1, 100.1, 1000.1, … - @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + t = 0 and i % 10 = 1 and i % 100 != 11 or t % 10 = 1 and t % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.0, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, … + @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~0.9, 1.2~1.8, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, … @@ -84,9 +81,19 @@ For terms of use, see http://www.unicode.org/copyright.html n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + + n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000 + n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 + @integer 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~2.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + + + i = 1 and v = 0 or i = 0 and v != 0 @integer 1 @decimal 0.0~0.9, 0.00~0.05 + i = 2 and v = 0 @integer 2 + @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.0~2.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000 @@ -102,7 +109,7 @@ For terms of use, see http://www.unicode.org/copyright.html i = 1 and v = 0 @integer 1 - v != 0 or n = 0 or n % 100 = 2..19 @integer 0, 2~16, 102, 1002, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + v != 0 or n = 0 or n != 1 and n % 100 = 1..19 @integer 0, 2~16, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … @integer 20~35, 100, 1000, 10000, 100000, 1000000, … @@ -111,6 +118,29 @@ For terms of use, see http://www.unicode.org/copyright.html @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + + + + i = 0,1 @integer 0, 1 @decimal 0.0~1.5 + e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 @integer 1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, … @decimal 1.0000001c6, 1.1c6, 2.0000001c6, 2.1c6, 3.0000001c6, 3.1c6, … + @integer 2~17, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0001c3, 1.1c3, 2.0001c3, 2.1c3, 3.0001c3, 3.1c3, … + + + i = 0..1 @integer 0, 1 @decimal 0.0~1.5 + e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 @integer 1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, … @decimal 1.0000001c6, 1.1c6, 2.0000001c6, 2.1c6, 3.0000001c6, 3.1c6, … + @integer 2~17, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0001c3, 1.1c3, 2.0001c3, 2.1c3, 3.0001c3, 3.1c3, … + + + i = 1 and v = 0 @integer 1 + e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 @integer 1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, … @decimal 1.0000001c6, 1.1c6, 2.0000001c6, 2.1c6, 3.0000001c6, 3.1c6, … + @integer 0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0001c3, 1.1c3, 2.0001c3, 2.1c3, 3.0001c3, 3.1c3, … + + + n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 + e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 @integer 1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, … @decimal 1.0000001c6, 1.1c6, 2.0000001c6, 2.1c6, 3.0000001c6, 3.1c6, … + @integer 0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0001c3, 1.1c3, 2.0001c3, 2.1c3, 3.0001c3, 3.1c3, … + + @@ -132,15 +162,6 @@ For terms of use, see http://www.unicode.org/copyright.html @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - - - - i = 1 and v = 0 @integer 1 - i = 2 and v = 0 @integer 2 - v = 0 and n != 0..10 and n % 10 = 0 @integer 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000, 10000, 100000, 1000000, … - @integer 0, 3~17, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - - @@ -167,12 +188,6 @@ For terms of use, see http://www.unicode.org/copyright.html f != 0 @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, … @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - - n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 - n = 0 or n % 100 = 2..10 @integer 0, 2~10, 102~107, 1002, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 102.0, 1002.0, … - n % 100 = 11..19 @integer 11~19, 111~117, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, … - @integer 20~35, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - v = 0 and i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @@ -186,9 +201,16 @@ For terms of use, see http://www.unicode.org/copyright.html n % 10 = 1 and n % 100 != 11,71,91 @integer 1, 21, 31, 41, 51, 61, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 81.0, 101.0, 1001.0, … n % 10 = 2 and n % 100 != 12,72,92 @integer 2, 22, 32, 42, 52, 62, 82, 102, 1002, … @decimal 2.0, 22.0, 32.0, 42.0, 52.0, 62.0, 82.0, 102.0, 1002.0, … n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99 @integer 3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 103, 1003, … @decimal 3.0, 4.0, 9.0, 23.0, 24.0, 29.0, 33.0, 34.0, 103.0, 1003.0, … - n != 0 and n % 1000000 = 0 @integer 1000000, … @decimal 1000000.0, 1000000.00, 1000000.000, … + n != 0 and n % 1000000 = 0 @integer 1000000, … @decimal 1000000.0, 1000000.00, 1000000.000, 1000000.0000, … @integer 0, 5~8, 10~20, 100, 1000, 10000, 100000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, … + + n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 + n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000 + n = 0 or n % 100 = 3..10 @integer 0, 3~10, 103~109, 1003, … @decimal 0.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, … + n % 100 = 11..19 @integer 11~19, 111~117, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, … + @integer 20~35, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000 n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000 diff --git a/v2/internal/plural/codegen/xml.go b/v2/internal/plural/codegen/xml.go index adcc1e26..c5122ec9 100644 --- a/v2/internal/plural/codegen/xml.go +++ b/v2/internal/plural/codegen/xml.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "fmt" "regexp" + "strconv" "strings" ) @@ -47,36 +48,47 @@ func (pr *PluralRule) Condition() string { return pr.Rule[:i] } -// Examples returns the integer and decimal exmaples for the PLuralRule. -func (pr *PluralRule) Examples() (integer []string, decimal []string) { - ex := strings.Replace(pr.Rule, ", …", "", -1) +// Examples returns the integer and decimal examples for the PluralRule. +func (pr *PluralRule) Examples() (integers []string, decimals []string) { + ex := strings.ReplaceAll(pr.Rule, ", …", "") ddelim := "@decimal" if i := strings.Index(ex, ddelim); i > 0 { dex := strings.TrimSpace(ex[i+len(ddelim):]) - decimal = strings.Split(dex, ", ") + dex = strings.ReplaceAll(dex, "c", "e") + decimals = strings.Split(dex, ", ") ex = ex[:i] } idelim := "@integer" if i := strings.Index(ex, idelim); i > 0 { iex := strings.TrimSpace(ex[i+len(idelim):]) - integer = strings.Split(iex, ", ") + integers = strings.Split(iex, ", ") + for j, integer := range integers { + ii := strings.IndexAny(integer, "eEcC") + if ii > 0 { + zeros, err := strconv.ParseInt(integer[ii+1:], 10, 0) + if err != nil { + panic(err) + } + integers[j] = integer[:ii] + strings.Repeat("0", int(zeros)) + } + } } - return integer, decimal + return integers, decimals } -// IntegerExamples returns the integer exmaples for the PLuralRule. +// IntegerExamples returns the integer examples for the PluralRule. func (pr *PluralRule) IntegerExamples() []string { integer, _ := pr.Examples() return integer } -// DecimalExamples returns the decimal exmaples for the PLuralRule. +// DecimalExamples returns the decimal examples for the PluralRule. func (pr *PluralRule) DecimalExamples() []string { _, decimal := pr.Examples() return decimal } -var relationRegexp = regexp.MustCompile(`([niftvw])(?:\s*%\s*([0-9]+))?\s*(!=|=)(.*)`) +var relationRegexp = regexp.MustCompile(`([niftvwce])(?:\s*%\s*([0-9]+))?\s*(!=|=)(.*)`) // GoCondition converts the XML condition to valid Go code. func (pr *PluralRule) GoCondition() string { @@ -92,6 +104,11 @@ func (pr *PluralRule) GoCondition() string { if op == "=" { op = "==" } + if lvar == "E" { + // E is a deprecated symbol for C + // https://unicode.org/reports/tr35/tr35-numbers.html#Plural_Operand_Meanings + lvar = "C" + } lvar = "ops." + lvar var rhor []string var rany []string diff --git a/v2/internal/plural/operands.go b/v2/internal/plural/operands.go index 4d297265..06c5a7b0 100644 --- a/v2/internal/plural/operands.go +++ b/v2/internal/plural/operands.go @@ -7,6 +7,9 @@ import ( ) // Operands is a representation of http://unicode.org/reports/tr35/tr35-numbers.html#Operands +// If there is a compact decimal exponent value C, then the N, I, V, W, F, and T values are computed after shifting the decimal point in the original by the ‘c’ value. +// So for 1.2c3, the values are the same as those of 1200: i=1200 and f=0. +// Similarly, 1.2005c3 has i=1200 and f=5 (corresponding to 1200.5). type Operands struct { N float64 // absolute value of the source number (integer and decimals) I int64 // integer digits of n @@ -14,6 +17,7 @@ type Operands struct { W int64 // number of visible fraction digits in n, without trailing zeros F int64 // visible fractional digits in n, with trailing zeros T int64 // visible fractional digits in n, without trailing zeros + C int64 // compact decimal exponent value: exponent of the power of 10 used in compact decimal formatting. } // NEqualsAny returns true if o represents an integer equal to any of the arguments. @@ -74,19 +78,87 @@ func newOperandsInt64(i int64) *Operands { if i < 0 { i = -i } - return &Operands{float64(i), i, 0, 0, 0, 0} + return &Operands{float64(i), i, 0, 0, 0, 0, 0} +} + +func splitSignificandExponent(s string) (significand, exponent string) { + i := strings.IndexAny(s, "eE") + if i < 0 { + return s, "" + } + return s[:i], s[i+1:] +} + +func shiftDecimalLeft(s string, n int) string { + if n <= 0 { + return s + } + i := strings.IndexRune(s, '.') + tilt := 0 + if i < 0 { + i = len(s) + tilt = -1 + } + switch { + case n == i: + return "0." + s[:i] + s[i+1+tilt:] + case n > i: + return "0." + strings.Repeat("0", n-i) + s[:i] + s[i+1+tilt:] + default: + return s[:i-n] + "." + s[i-n:i] + s[i+1+tilt:] + } +} + +func shiftDecimalRight(s string, n int) string { + if n <= 0 { + return s + } + i := strings.IndexRune(s, '.') + if i < 0 { + return s + strings.Repeat("0", n) + } + switch rest := len(s) - i - 1; { + case n == rest: + return s[:i] + s[i+1:] + case n > rest: + return s[:i] + s[i+1:] + strings.Repeat("0", n-rest) + default: + return s[:i] + s[i+1:i+1+n] + "." + s[i+1+n:] + } +} + +func applyExponent(s string, exponent int) string { + switch { + case exponent > 0: + return shiftDecimalRight(s, exponent) + case exponent < 0: + return shiftDecimalLeft(s, -exponent) + } + return s } func newOperandsString(s string) (*Operands, error) { if s[0] == '-' { s = s[1:] } - n, err := strconv.ParseFloat(s, 64) + ops := &Operands{} + var err error + ops.N, err = strconv.ParseFloat(s, 64) if err != nil { return nil, err } - ops := &Operands{N: n} - parts := strings.SplitN(s, ".", 2) + significand, exponent := splitSignificandExponent(s) + if exponent != "" { + // We are storing C as an int64 but only allowing + // numbers that fit into the bitsize of an int + // so C is safe to cast as a int later. + ops.C, err = strconv.ParseInt(exponent, 10, 0) + if err != nil { + return nil, err + } + } + value := applyExponent(significand, int(ops.C)) + parts := strings.SplitN(value, ".", 2) ops.I, err = strconv.ParseInt(parts[0], 10, 64) if err != nil { return nil, err diff --git a/v2/internal/plural/operands_test.go b/v2/internal/plural/operands_test.go index 9074e5cb..800e8ada 100644 --- a/v2/internal/plural/operands_test.go +++ b/v2/internal/plural/operands_test.go @@ -11,18 +11,47 @@ func TestNewOperands(t *testing.T) { ops *Operands err bool }{ - {int64(0), &Operands{0.0, 0, 0, 0, 0, 0}, false}, - {int64(1), &Operands{1.0, 1, 0, 0, 0, 0}, false}, - {"0", &Operands{0.0, 0, 0, 0, 0, 0}, false}, - {"1", &Operands{1.0, 1, 0, 0, 0, 0}, false}, - {"1.0", &Operands{1.0, 1, 1, 0, 0, 0}, false}, - {"1.00", &Operands{1.0, 1, 2, 0, 0, 0}, false}, - {"1.3", &Operands{1.3, 1, 1, 1, 3, 3}, false}, - {"1.30", &Operands{1.3, 1, 2, 1, 30, 3}, false}, - {"1.03", &Operands{1.03, 1, 2, 2, 3, 3}, false}, - {"1.230", &Operands{1.23, 1, 3, 2, 230, 23}, false}, - {"20.0230", &Operands{20.023, 20, 4, 3, 230, 23}, false}, + {int64(0), &Operands{0.0, 0, 0, 0, 0, 0, 0}, false}, + {int64(1), &Operands{1.0, 1, 0, 0, 0, 0, 0}, false}, + {"0", &Operands{0.0, 0, 0, 0, 0, 0, 0}, false}, + {"1", &Operands{1.0, 1, 0, 0, 0, 0, 0}, false}, + {"1.0", &Operands{1.0, 1, 1, 0, 0, 0, 0}, false}, + {"1.00", &Operands{1.0, 1, 2, 0, 0, 0, 0}, false}, + {"1.3", &Operands{1.3, 1, 1, 1, 3, 3, 0}, false}, + {"1.30", &Operands{1.3, 1, 2, 1, 30, 3, 0}, false}, + {"1.03", &Operands{1.03, 1, 2, 2, 3, 3, 0}, false}, + {"1.230", &Operands{1.23, 1, 3, 2, 230, 23, 0}, false}, + {"20.0230", &Operands{20.023, 20, 4, 3, 230, 23, 0}, false}, {20.0230, nil, true}, + + {"1200", &Operands{1200, 1200, 0, 0, 0, 0, 0}, false}, + {"1.2e3", &Operands{1200, 1200, 0, 0, 0, 0, 3}, false}, + {"1.2E3", &Operands{1200, 1200, 0, 0, 0, 0, 3}, false}, + + {"1234", &Operands{1234, 1234, 0, 0, 0, 0, 0}, false}, + {"1234e0", &Operands{1234, 1234, 0, 0, 0, 0, 0}, false}, + {"123.4e1", &Operands{1234, 1234, 0, 0, 0, 0, 1}, false}, + {"12.34e2", &Operands{1234, 1234, 0, 0, 0, 0, 2}, false}, + {"1.234e3", &Operands{1234, 1234, 0, 0, 0, 0, 3}, false}, + {"0.1234e4", &Operands{1234, 1234, 0, 0, 0, 0, 4}, false}, + {"0.01234e5", &Operands{1234, 1234, 0, 0, 0, 0, 5}, false}, + + {"1234.0", &Operands{1234, 1234, 1, 0, 0, 0, 0}, false}, + {"12340e-1", &Operands{1234, 1234, 1, 0, 0, 0, -1}, false}, + + {"1200.5", &Operands{1200.5, 1200, 1, 1, 5, 5, 0}, false}, + {"1.2005e3", &Operands{1200.5, 1200, 1, 1, 5, 5, 3}, false}, + + {"1200e3", &Operands{1200000, 1200000, 0, 0, 0, 0, 3}, false}, + + {"0.0012340", &Operands{0.001234, 0, 7, 6, 12340, 1234, 0}, false}, + {"0.012340e-1", &Operands{0.001234, 0, 7, 6, 12340, 1234, -1}, false}, + {"0.12340e-2", &Operands{0.001234, 0, 7, 6, 12340, 1234, -2}, false}, + {"1.2340e-3", &Operands{0.001234, 0, 7, 6, 12340, 1234, -3}, false}, + {"12.340e-4", &Operands{0.001234, 0, 7, 6, 12340, 1234, -4}, false}, + {"123.40e-5", &Operands{0.001234, 0, 7, 6, 12340, 1234, -5}, false}, + {"1234.0e-6", &Operands{0.001234, 0, 7, 6, 12340, 1234, -6}, false}, + {"12340e-7", &Operands{0.001234, 0, 7, 6, 12340, 1234, -7}, false}, } for _, test := range tests { ops, err := NewOperands(test.input) diff --git a/v2/internal/plural/rule_gen.go b/v2/internal/plural/rule_gen.go index 9a32ca48..258311ca 100644 --- a/v2/internal/plural/rule_gen.go +++ b/v2/internal/plural/rule_gen.go @@ -6,13 +6,13 @@ package plural func DefaultRules() Rules { rules := Rules{} - addPluralRules(rules, []string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "osa", "root", "sah", "ses", "sg", "su", "th", "to", "vi", "wo", "yo", "yue", "zh"}, &Rule{ + addPluralRules(rules, []string{"bm", "bo", "dz", "hnj", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "osa", "root", "sah", "ses", "sg", "su", "th", "to", "tpi", "vi", "wo", "yo", "yue", "zh"}, &Rule{ PluralForms: newPluralFormSet(Other), PluralFormFunc: func(ops *Operands) Form { return Other }, }) - addPluralRules(rules, []string{"am", "as", "bn", "fa", "gu", "hi", "kn", "pcm", "zu"}, &Rule{ + addPluralRules(rules, []string{"am", "as", "bn", "doi", "fa", "gu", "hi", "kn", "pcm", "zu"}, &Rule{ PluralForms: newPluralFormSet(One, Other), PluralFormFunc: func(ops *Operands) Form { // i = 0 or n = 1 @@ -23,7 +23,7 @@ func DefaultRules() Rules { return Other }, }) - addPluralRules(rules, []string{"ff", "fr", "hy", "kab"}, &Rule{ + addPluralRules(rules, []string{"ff", "hy", "kab"}, &Rule{ PluralForms: newPluralFormSet(One, Other), PluralFormFunc: func(ops *Operands) Form { // i = 0,1 @@ -33,17 +33,7 @@ func DefaultRules() Rules { return Other }, }) - addPluralRules(rules, []string{"pt"}, &Rule{ - PluralForms: newPluralFormSet(One, Other), - PluralFormFunc: func(ops *Operands) Form { - // i = 0..1 - if intInRange(ops.I, 0, 1) { - return One - } - return Other - }, - }) - addPluralRules(rules, []string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "ia", "io", "it", "ji", "nl", "pt_PT", "sc", "scn", "sv", "sw", "ur", "yi"}, &Rule{ + addPluralRules(rules, []string{"ast", "de", "en", "et", "fi", "fy", "gl", "ia", "io", "ji", "lij", "nl", "sc", "scn", "sv", "sw", "ur", "yi"}, &Rule{ PluralForms: newPluralFormSet(One, Other), PluralFormFunc: func(ops *Operands) Form { // i = 1 and v = 0 @@ -85,7 +75,7 @@ func DefaultRules() Rules { return Other }, }) - addPluralRules(rules, []string{"af", "an", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "mr", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sd", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &Rule{ + addPluralRules(rules, []string{"af", "an", "asa", "az", "bal", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "mr", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sd", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &Rule{ PluralForms: newPluralFormSet(One, Other), PluralFormFunc: func(ops *Operands) Form { // n = 1 @@ -109,9 +99,9 @@ func DefaultRules() Rules { addPluralRules(rules, []string{"is"}, &Rule{ PluralForms: newPluralFormSet(One, Other), PluralFormFunc: func(ops *Operands) Form { - // t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 + // t = 0 and i % 10 = 1 and i % 100 != 11 or t % 10 = 1 and t % 100 != 11 if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) || - !intEqualsAny(ops.T, 0) { + intEqualsAny(ops.T%10, 1) && !intEqualsAny(ops.T%100, 11) { return One } return Other @@ -186,6 +176,35 @@ func DefaultRules() Rules { return Other }, }) + addPluralRules(rules, []string{"blo"}, &Rule{ + PluralForms: newPluralFormSet(Zero, One, Other), + PluralFormFunc: func(ops *Operands) Form { + // n = 0 + if ops.NEqualsAny(0) { + return Zero + } + // n = 1 + if ops.NEqualsAny(1) { + return One + } + return Other + }, + }) + addPluralRules(rules, []string{"he", "iw"}, &Rule{ + PluralForms: newPluralFormSet(One, Two, Other), + PluralFormFunc: func(ops *Operands) Form { + // i = 1 and v = 0 or i = 0 and v != 0 + if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) || + intEqualsAny(ops.I, 0) && !intEqualsAny(ops.V, 0) { + return One + } + // i = 2 and v = 0 + if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) { + return Two + } + return Other + }, + }) addPluralRules(rules, []string{"iu", "naq", "sat", "se", "sma", "smi", "smj", "smn", "sms"}, &Rule{ PluralForms: newPluralFormSet(One, Two, Other), PluralFormFunc: func(ops *Operands) Form { @@ -222,10 +241,10 @@ func DefaultRules() Rules { if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { return One } - // v != 0 or n = 0 or n % 100 = 2..19 + // v != 0 or n = 0 or n != 1 and n % 100 = 1..19 if !intEqualsAny(ops.V, 0) || ops.NEqualsAny(0) || - ops.NModInRange(100, 2, 19) { + !ops.NEqualsAny(1) && ops.NModInRange(100, 1, 19) { return Few } return Other @@ -247,6 +266,66 @@ func DefaultRules() Rules { return Other }, }) + addPluralRules(rules, []string{"fr"}, &Rule{ + PluralForms: newPluralFormSet(One, Many, Other), + PluralFormFunc: func(ops *Operands) Form { + // i = 0,1 + if intEqualsAny(ops.I, 0, 1) { + return One + } + // e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 + if intEqualsAny(ops.C, 0) && !intEqualsAny(ops.I, 0) && intEqualsAny(ops.I%1000000, 0) && intEqualsAny(ops.V, 0) || + !intInRange(ops.C, 0, 5) { + return Many + } + return Other + }, + }) + addPluralRules(rules, []string{"pt"}, &Rule{ + PluralForms: newPluralFormSet(One, Many, Other), + PluralFormFunc: func(ops *Operands) Form { + // i = 0..1 + if intInRange(ops.I, 0, 1) { + return One + } + // e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 + if intEqualsAny(ops.C, 0) && !intEqualsAny(ops.I, 0) && intEqualsAny(ops.I%1000000, 0) && intEqualsAny(ops.V, 0) || + !intInRange(ops.C, 0, 5) { + return Many + } + return Other + }, + }) + addPluralRules(rules, []string{"ca", "it", "pt_PT", "vec"}, &Rule{ + PluralForms: newPluralFormSet(One, Many, Other), + PluralFormFunc: func(ops *Operands) Form { + // i = 1 and v = 0 + if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { + return One + } + // e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 + if intEqualsAny(ops.C, 0) && !intEqualsAny(ops.I, 0) && intEqualsAny(ops.I%1000000, 0) && intEqualsAny(ops.V, 0) || + !intInRange(ops.C, 0, 5) { + return Many + } + return Other + }, + }) + addPluralRules(rules, []string{"es"}, &Rule{ + PluralForms: newPluralFormSet(One, Many, Other), + PluralFormFunc: func(ops *Operands) Form { + // n = 1 + if ops.NEqualsAny(1) { + return One + } + // e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5 + if intEqualsAny(ops.C, 0) && !intEqualsAny(ops.I, 0) && intEqualsAny(ops.I%1000000, 0) && intEqualsAny(ops.V, 0) || + !intInRange(ops.C, 0, 5) { + return Many + } + return Other + }, + }) addPluralRules(rules, []string{"gd"}, &Rule{ PluralForms: newPluralFormSet(One, Two, Few, Other), PluralFormFunc: func(ops *Operands) Form { @@ -305,24 +384,6 @@ func DefaultRules() Rules { return Other }, }) - addPluralRules(rules, []string{"he", "iw"}, &Rule{ - PluralForms: newPluralFormSet(One, Two, Many, Other), - PluralFormFunc: func(ops *Operands) Form { - // i = 1 and v = 0 - if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { - return One - } - // i = 2 and v = 0 - if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) { - return Two - } - // v = 0 and n != 0..10 and n % 10 = 0 - if intEqualsAny(ops.V, 0) && !ops.NInRange(0, 10) && ops.NModEqualsAny(10, 0) { - return Many - } - return Other - }, - }) addPluralRules(rules, []string{"cs", "sk"}, &Rule{ PluralForms: newPluralFormSet(One, Few, Many, Other), PluralFormFunc: func(ops *Operands) Form { @@ -399,25 +460,6 @@ func DefaultRules() Rules { return Other }, }) - addPluralRules(rules, []string{"mt"}, &Rule{ - PluralForms: newPluralFormSet(One, Few, Many, Other), - PluralFormFunc: func(ops *Operands) Form { - // n = 1 - if ops.NEqualsAny(1) { - return One - } - // n = 0 or n % 100 = 2..10 - if ops.NEqualsAny(0) || - ops.NModInRange(100, 2, 10) { - return Few - } - // n % 100 = 11..19 - if ops.NModInRange(100, 11, 19) { - return Many - } - return Other - }, - }) addPluralRules(rules, []string{"ru", "uk"}, &Rule{ PluralForms: newPluralFormSet(One, Few, Many, Other), PluralFormFunc: func(ops *Operands) Form { @@ -460,6 +502,29 @@ func DefaultRules() Rules { return Other }, }) + addPluralRules(rules, []string{"mt"}, &Rule{ + PluralForms: newPluralFormSet(One, Two, Few, Many, Other), + PluralFormFunc: func(ops *Operands) Form { + // n = 1 + if ops.NEqualsAny(1) { + return One + } + // n = 2 + if ops.NEqualsAny(2) { + return Two + } + // n = 0 or n % 100 = 3..10 + if ops.NEqualsAny(0) || + ops.NModInRange(100, 3, 10) { + return Few + } + // n % 100 = 11..19 + if ops.NModInRange(100, 11, 19) { + return Many + } + return Other + }, + }) addPluralRules(rules, []string{"ga"}, &Rule{ PluralForms: newPluralFormSet(One, Two, Few, Many, Other), PluralFormFunc: func(ops *Operands) Form { diff --git a/v2/internal/plural/rule_gen_test.go b/v2/internal/plural/rule_gen_test.go index ca3dc595..a1aded36 100644 --- a/v2/internal/plural/rule_gen_test.go +++ b/v2/internal/plural/rule_gen_test.go @@ -4,19 +4,19 @@ package plural import "testing" -func TestBmBoDzIdIgIiInJaJboJvJwKdeKeaKmKoLktLoMsMyNqoOsaRootSahSesSgSuThToViWoYoYueZh(t *testing.T) { +func TestBmBoDzHnjIdIgIiInJaJboJvJwKdeKeaKmKoLktLoMsMyNqoOsaRootSahSesSgSuThToTpiViWoYoYueZh(t *testing.T) { var tests []pluralFormTest tests = appendIntegerTests(tests, Other, []string{"0~15", "100", "1000", "10000", "100000", "1000000"}) tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - locales := []string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "osa", "root", "sah", "ses", "sg", "su", "th", "to", "vi", "wo", "yo", "yue", "zh"} + locales := []string{"bm", "bo", "dz", "hnj", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "osa", "root", "sah", "ses", "sg", "su", "th", "to", "tpi", "vi", "wo", "yo", "yue", "zh"} for _, locale := range locales { runTests(t, locale, tests) } } -func TestAmAsBnFaGuHiKnPcmZu(t *testing.T) { +func TestAmAsBnDoiFaGuHiKnPcmZu(t *testing.T) { var tests []pluralFormTest tests = appendIntegerTests(tests, One, []string{"0", "1"}) @@ -25,13 +25,13 @@ func TestAmAsBnFaGuHiKnPcmZu(t *testing.T) { tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"}) tests = appendDecimalTests(tests, Other, []string{"1.1~2.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - locales := []string{"am", "as", "bn", "fa", "gu", "hi", "kn", "pcm", "zu"} + locales := []string{"am", "as", "bn", "doi", "fa", "gu", "hi", "kn", "pcm", "zu"} for _, locale := range locales { runTests(t, locale, tests) } } -func TestFfFrHyKab(t *testing.T) { +func TestFfHyKab(t *testing.T) { var tests []pluralFormTest tests = appendIntegerTests(tests, One, []string{"0", "1"}) @@ -40,28 +40,13 @@ func TestFfFrHyKab(t *testing.T) { tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"}) tests = appendDecimalTests(tests, Other, []string{"2.0~3.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - locales := []string{"ff", "fr", "hy", "kab"} + locales := []string{"ff", "hy", "kab"} for _, locale := range locales { runTests(t, locale, tests) } } -func TestPt(t *testing.T) { - var tests []pluralFormTest - - tests = appendIntegerTests(tests, One, []string{"0", "1"}) - tests = appendDecimalTests(tests, One, []string{"0.0~1.5"}) - - tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000000"}) - tests = appendDecimalTests(tests, Other, []string{"2.0~3.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - - locales := []string{"pt"} - for _, locale := range locales { - runTests(t, locale, tests) - } -} - -func TestAstCaDeEnEtFiFyGlIaIoItJiNlPt_PTScScnSvSwUrYi(t *testing.T) { +func TestAstDeEnEtFiFyGlIaIoJiLijNlScScnSvSwUrYi(t *testing.T) { var tests []pluralFormTest tests = appendIntegerTests(tests, One, []string{"1"}) @@ -69,7 +54,7 @@ func TestAstCaDeEnEtFiFyGlIaIoItJiNlPt_PTScScnSvSwUrYi(t *testing.T) { tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"}) tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - locales := []string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "ia", "io", "it", "ji", "nl", "pt_PT", "sc", "scn", "sv", "sw", "ur", "yi"} + locales := []string{"ast", "de", "en", "et", "fi", "fy", "gl", "ia", "io", "ji", "lij", "nl", "sc", "scn", "sv", "sw", "ur", "yi"} for _, locale := range locales { runTests(t, locale, tests) } @@ -120,7 +105,7 @@ func TestTzm(t *testing.T) { } } -func TestAfAnAsaAzBemBezBgBrxCeCggChrCkbDvEeElEoEsEuFoFurGswHaHawHuJgoJmcKaKajKcgKkKkjKlKsKsbKuKyLbLgMasMgoMlMnMrNahNbNdNeNnNnhNoNrNyNynOmOrOsPapPsRmRofRwkSaqSdSdhSehSnSoSqSsSsyStSyrTaTeTeoTigTkTnTrTsUgUzVeVoVunWaeXhXog(t *testing.T) { +func TestAfAnAsaAzBalBemBezBgBrxCeCggChrCkbDvEeElEoEuFoFurGswHaHawHuJgoJmcKaKajKcgKkKkjKlKsKsbKuKyLbLgMasMgoMlMnMrNahNbNdNeNnNnhNoNrNyNynOmOrOsPapPsRmRofRwkSaqSdSdhSehSnSoSqSsSsyStSyrTaTeTeoTigTkTnTrTsUgUzVeVoVunWaeXhXog(t *testing.T) { var tests []pluralFormTest tests = appendIntegerTests(tests, One, []string{"1"}) @@ -129,7 +114,7 @@ func TestAfAnAsaAzBemBezBgBrxCeCggChrCkbDvEeElEoEsEuFoFurGswHaHawHuJgoJmcKaKajKc tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"}) tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - locales := []string{"af", "an", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "mr", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sd", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"} + locales := []string{"af", "an", "asa", "az", "bal", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "mr", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sd", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"} for _, locale := range locales { runTests(t, locale, tests) } @@ -154,10 +139,10 @@ func TestIs(t *testing.T) { var tests []pluralFormTest tests = appendIntegerTests(tests, One, []string{"1", "21", "31", "41", "51", "61", "71", "81", "101", "1001"}) - tests = appendDecimalTests(tests, One, []string{"0.1~1.6", "10.1", "100.1", "1000.1"}) + tests = appendDecimalTests(tests, One, []string{"0.1", "1.0", "1.1", "2.1", "3.1", "4.1", "5.1", "6.1", "7.1", "10.1", "100.1", "1000.1"}) tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000000"}) - tests = appendDecimalTests(tests, Other, []string{"0.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) + tests = appendDecimalTests(tests, Other, []string{"0.0", "0.2~0.9", "1.2~1.8", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) locales := []string{"is"} for _, locale := range locales { @@ -249,6 +234,41 @@ func TestKsh(t *testing.T) { } } +func TestBlo(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, Zero, []string{"0"}) + tests = appendDecimalTests(tests, Zero, []string{"0.0", "0.00", "0.000", "0.0000"}) + + tests = appendIntegerTests(tests, One, []string{"1"}) + tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"}) + + tests = appendIntegerTests(tests, Other, []string{"2~16", "100", "1000", "10000", "100000", "1000000"}) + tests = appendDecimalTests(tests, Other, []string{"2.0~2.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) + + locales := []string{"blo"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + +func TestHeIw(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, One, []string{"1"}) + tests = appendDecimalTests(tests, One, []string{"0.0~0.9", "0.00~0.05"}) + + tests = appendIntegerTests(tests, Two, []string{"2"}) + + tests = appendIntegerTests(tests, Other, []string{"0", "3~17", "100", "1000", "10000", "100000", "1000000"}) + tests = appendDecimalTests(tests, Other, []string{"1.0~2.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) + + locales := []string{"he", "iw"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + func TestIuNaqSatSeSmaSmiSmjSmnSms(t *testing.T) { var tests []pluralFormTest @@ -290,7 +310,7 @@ func TestMoRo(t *testing.T) { tests = appendIntegerTests(tests, One, []string{"1"}) - tests = appendIntegerTests(tests, Few, []string{"0", "2~16", "102", "1002"}) + tests = appendIntegerTests(tests, Few, []string{"0", "2~16", "101", "1001"}) tests = appendDecimalTests(tests, Few, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) tests = appendIntegerTests(tests, Other, []string{"20~35", "100", "1000", "10000", "100000", "1000000"}) @@ -319,6 +339,77 @@ func TestBsHrShSr(t *testing.T) { } } +func TestFr(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, One, []string{"0", "1"}) + tests = appendDecimalTests(tests, One, []string{"0.0~1.5"}) + + tests = appendIntegerTests(tests, Many, []string{"1000000", "1000000", "2000000", "3000000", "4000000", "5000000", "6000000"}) + tests = appendDecimalTests(tests, Many, []string{"1.0000001e6", "1.1e6", "2.0000001e6", "2.1e6", "3.0000001e6", "3.1e6"}) + + tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000", "2000", "3000", "4000", "5000", "6000"}) + tests = appendDecimalTests(tests, Other, []string{"2.0~3.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0", "1.0001e3", "1.1e3", "2.0001e3", "2.1e3", "3.0001e3", "3.1e3"}) + + locales := []string{"fr"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + +func TestPt(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, One, []string{"0", "1"}) + tests = appendDecimalTests(tests, One, []string{"0.0~1.5"}) + + tests = appendIntegerTests(tests, Many, []string{"1000000", "1000000", "2000000", "3000000", "4000000", "5000000", "6000000"}) + tests = appendDecimalTests(tests, Many, []string{"1.0000001e6", "1.1e6", "2.0000001e6", "2.1e6", "3.0000001e6", "3.1e6"}) + + tests = appendIntegerTests(tests, Other, []string{"2~17", "100", "1000", "10000", "100000", "1000", "2000", "3000", "4000", "5000", "6000"}) + tests = appendDecimalTests(tests, Other, []string{"2.0~3.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0", "1.0001e3", "1.1e3", "2.0001e3", "2.1e3", "3.0001e3", "3.1e3"}) + + locales := []string{"pt"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + +func TestCaItPt_PTVec(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, One, []string{"1"}) + + tests = appendIntegerTests(tests, Many, []string{"1000000", "1000000", "2000000", "3000000", "4000000", "5000000", "6000000"}) + tests = appendDecimalTests(tests, Many, []string{"1.0000001e6", "1.1e6", "2.0000001e6", "2.1e6", "3.0000001e6", "3.1e6"}) + + tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000", "2000", "3000", "4000", "5000", "6000"}) + tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0", "1.0001e3", "1.1e3", "2.0001e3", "2.1e3", "3.0001e3", "3.1e3"}) + + locales := []string{"ca", "it", "pt_PT", "vec"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + +func TestEs(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, One, []string{"1"}) + tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"}) + + tests = appendIntegerTests(tests, Many, []string{"1000000", "1000000", "2000000", "3000000", "4000000", "5000000", "6000000"}) + tests = appendDecimalTests(tests, Many, []string{"1.0000001e6", "1.1e6", "2.0000001e6", "2.1e6", "3.0000001e6", "3.1e6"}) + + tests = appendIntegerTests(tests, Other, []string{"0", "2~16", "100", "1000", "10000", "100000", "1000", "2000", "3000", "4000", "5000", "6000"}) + tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0", "1.0001e3", "1.1e3", "2.0001e3", "2.1e3", "3.0001e3", "3.1e3"}) + + locales := []string{"es"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + func TestGd(t *testing.T) { var tests []pluralFormTest @@ -379,24 +470,6 @@ func TestDsbHsb(t *testing.T) { } } -func TestHeIw(t *testing.T) { - var tests []pluralFormTest - - tests = appendIntegerTests(tests, One, []string{"1"}) - - tests = appendIntegerTests(tests, Two, []string{"2"}) - - tests = appendIntegerTests(tests, Many, []string{"20", "30", "40", "50", "60", "70", "80", "90", "100", "1000", "10000", "100000", "1000000"}) - - tests = appendIntegerTests(tests, Other, []string{"0", "3~17", "101", "1001"}) - tests = appendDecimalTests(tests, Other, []string{"0.0~1.5", "10.0", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - - locales := []string{"he", "iw"} - for _, locale := range locales { - runTests(t, locale, tests) - } -} - func TestCsSk(t *testing.T) { var tests []pluralFormTest @@ -471,27 +544,6 @@ func TestLt(t *testing.T) { } } -func TestMt(t *testing.T) { - var tests []pluralFormTest - - tests = appendIntegerTests(tests, One, []string{"1"}) - tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"}) - - tests = appendIntegerTests(tests, Few, []string{"0", "2~10", "102~107", "1002"}) - tests = appendDecimalTests(tests, Few, []string{"0.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "10.0", "102.0", "1002.0"}) - - tests = appendIntegerTests(tests, Many, []string{"11~19", "111~117", "1011"}) - tests = appendDecimalTests(tests, Many, []string{"11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "111.0", "1011.0"}) - - tests = appendIntegerTests(tests, Other, []string{"20~35", "100", "1000", "10000", "100000", "1000000"}) - tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) - - locales := []string{"mt"} - for _, locale := range locales { - runTests(t, locale, tests) - } -} - func TestRuUk(t *testing.T) { var tests []pluralFormTest @@ -522,7 +574,7 @@ func TestBr(t *testing.T) { tests = appendDecimalTests(tests, Few, []string{"3.0", "4.0", "9.0", "23.0", "24.0", "29.0", "33.0", "34.0", "103.0", "1003.0"}) tests = appendIntegerTests(tests, Many, []string{"1000000"}) - tests = appendDecimalTests(tests, Many, []string{"1000000.0", "1000000.00", "1000000.000"}) + tests = appendDecimalTests(tests, Many, []string{"1000000.0", "1000000.00", "1000000.000", "1000000.0000"}) tests = appendIntegerTests(tests, Other, []string{"0", "5~8", "10~20", "100", "1000", "10000", "100000"}) tests = appendDecimalTests(tests, Other, []string{"0.0~0.9", "1.1~1.6", "10.0", "100.0", "1000.0", "10000.0", "100000.0"}) @@ -533,6 +585,30 @@ func TestBr(t *testing.T) { } } +func TestMt(t *testing.T) { + var tests []pluralFormTest + + tests = appendIntegerTests(tests, One, []string{"1"}) + tests = appendDecimalTests(tests, One, []string{"1.0", "1.00", "1.000", "1.0000"}) + + tests = appendIntegerTests(tests, Two, []string{"2"}) + tests = appendDecimalTests(tests, Two, []string{"2.0", "2.00", "2.000", "2.0000"}) + + tests = appendIntegerTests(tests, Few, []string{"0", "3~10", "103~109", "1003"}) + tests = appendDecimalTests(tests, Few, []string{"0.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "103.0", "1003.0"}) + + tests = appendIntegerTests(tests, Many, []string{"11~19", "111~117", "1011"}) + tests = appendDecimalTests(tests, Many, []string{"11.0", "12.0", "13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "111.0", "1011.0"}) + + tests = appendIntegerTests(tests, Other, []string{"20~35", "100", "1000", "10000", "100000", "1000000"}) + tests = appendDecimalTests(tests, Other, []string{"0.1~0.9", "1.1~1.7", "10.1", "100.0", "1000.0", "10000.0", "100000.0", "1000000.0"}) + + locales := []string{"mt"} + for _, locale := range locales { + runTests(t, locale, tests) + } +} + func TestGa(t *testing.T) { var tests []pluralFormTest