From 1441f511a204107554439d73f2401dbb99d8ccad Mon Sep 17 00:00:00 2001 From: Santhosh Thottingal Date: Wed, 1 Jan 2014 14:35:39 +0530 Subject: [PATCH] Update plural rules to CLDR 24 Updated plurals.xml with new data from CLDR 24. This data is according to UTS #35 Rev 33. Update the CLDRPluralRuleParser.js to version 1.1 from upstream https://github.com/santhoshtr/CLDRPluralRuleParser Changes to the plural rules: * Hebrew override removed since CLDR 24 matches with MW plural rules. * Updated the syntax of overridden rules to TR35 Rev 33 for Lower Sorbian (dsb), Upper Sorbian (hsb), Belarusian in Taraskievica orthography (be_tarask), Old Church Slavonic (cu), Bhojpuri (bho), Samogitian (sgs). * Removed Manx (gv) override. See I46ab3dadc7fe08c1e60bbd81a1ee841e166e9608. * Removed the overriden convertPlural method for Serbian from LanguageSr.php, since CLDR 24 matches with MW rules. Updated and added more tests. Tests updated for Serbocroatian (sh), too. Old CLDR versions had 4 plural rules and MW had only 3. In CLDR 24, the form 'many' was removed and it became identical to the MW. Same for Bosnian (bs) and Croatian (hr). Also for variants sr-ec and sr-el * Macedonian (mk) used to count 11 as 'other' form. CLDR 24 counts it as 'one'. Not overriding, using CLDR 24 here. Updated the tests. MW will not override this. * Armenian (hy) used to count 0 as 'other'. Now it is 'one' form. Updated the tests. MW will not override this. * Latvian (lv) used to count only 0 as 'zero' form, but CLDR 24, any number satisifying the following formula is counted as zero: n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 Examples: 0, 10~20, 30, 40, 50, 60, 100. Updated the tests accordingly. Not overriding it in MW. Users will see different plural form for the above numbers. * Removed Ukranian custom plural rule since it match with MW * Russian (ru) plural rules have a major change. The 'few' form is merged with the 'other' form. The current forms are 'one', 'many', 'other'. In MW ru plural rules were overridden using convertPlural methdod in LanguagesRu.php with 3 forms. Effectively forms[1] and forms[2] are swapped. This will affect the messages, and such messages must be reviewed and updated. This change is not included in this patch and wil be done separately. Russian is the only remaining language class with convertPlural method overridden. Notable impact on the exising messages: * For languages ru, uk, be_tarask, sr, For the special case of two plural forms and first mapped to 1 and rest to the other form, syntax like {{plural:$1|1=one|other}} should be used. For further information regarding each of the above language changes, see 1. http://unicode.org/cldr/trac/ticket/3727 2. http://goo.gl/H2HEz CLDR 24 can handle fractions. Ideally it should start working in MW without any code changes, but MW language test suite does not have enough tests to confirm. Followup: e571717e06667228ec8d689be067e00bdd06d34d Bug: 56931 Change-Id: I9930b290d004667a3bb09e5c1663ec2c9c27d8a6 --- languages/classes/LanguageBe_tarask.php | 48 --- languages/classes/LanguageHr.php | 60 --- languages/classes/LanguageSr.php | 50 +-- languages/classes/LanguageSr_ec.php | 58 --- languages/classes/LanguageSr_el.php | 58 --- languages/classes/LanguageUk.php | 39 -- languages/data/plurals-mediawiki.xml | 58 ++- languages/data/plurals.xml | 251 +++++++----- .../mediawiki.libs/CLDRPluralRuleParser.js | 356 +++++++++++++----- .../languages/LanguageBe_taraskTest.php | 4 +- tests/phpunit/languages/LanguageBsTest.php | 16 +- tests/phpunit/languages/LanguageHrTest.php | 14 +- tests/phpunit/languages/LanguageHyTest.php | 5 +- tests/phpunit/languages/LanguageLvTest.php | 14 +- tests/phpunit/languages/LanguageMkTest.php | 4 +- tests/phpunit/languages/LanguageRuTest.php | 2 + tests/phpunit/languages/LanguageSgsTest.php | 2 +- tests/phpunit/languages/LanguageShTest.php | 16 +- tests/phpunit/languages/LanguageSrTest.php | 15 +- tests/phpunit/languages/LanguageUkTest.php | 4 +- 20 files changed, 507 insertions(+), 567 deletions(-) delete mode 100644 languages/classes/LanguageHr.php delete mode 100644 languages/classes/LanguageSr_ec.php delete mode 100644 languages/classes/LanguageSr_el.php mode change 100644 => 100755 languages/data/plurals.xml diff --git a/languages/classes/LanguageBe_tarask.php b/languages/classes/LanguageBe_tarask.php index d3e78fe68e..05b9550f77 100644 --- a/languages/classes/LanguageBe_tarask.php +++ b/languages/classes/LanguageBe_tarask.php @@ -31,54 +31,6 @@ * @see http://be-x-old.wikipedia.org/wiki/Project_talk:LanguageBe_tarask.php */ class LanguageBe_tarask extends Language { - /** - * Plural form transformations - * - * $wordform1 - singular form (for 1, 21, 31, 41...) - * $wordform2 - plural form (for 2, 3, 4, 22, 23, 24, 32, 33, 34...) - * $wordform3 - plural form (for 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26...) - */ - - /** - * @param $count int - * @param $forms array - * - * @return string - */ - function convertPlural( $count, $forms ) { - $forms = $this->handleExplicitPluralForms( $count, $forms ); - if ( is_string( $forms ) ) { - return $forms; - } - if ( !count( $forms ) ) { - return ''; - } - - // If the actual number is not mentioned in the expression, then just two forms are enough: - // singular for $count == 1 - // plural for $count != 1 - // For example, "This user belongs to {{PLURAL:$1|one group|several groups}}." - if ( count( $forms ) === 2 ) { - return $count == 1 ? $forms[0] : $forms[1]; - } - - // @todo FIXME: CLDR defines 4 plural forms instead of 3 - // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - $forms = $this->preConvertPlural( $forms, 3 ); - - if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) { - return $forms[2]; - } else { - switch ( $count % 10 ) { - case 1: return $forms[0]; - case 2: - case 3: - case 4: return $forms[1]; - default: return $forms[2]; - } - } - } - /** * The Belarusian language uses apostrophe sign, * but the characters used for this could be both U+0027 and U+2019. diff --git a/languages/classes/LanguageHr.php b/languages/classes/LanguageHr.php deleted file mode 100644 index 0c65ec79f8..0000000000 --- a/languages/classes/LanguageHr.php +++ /dev/null @@ -1,60 +0,0 @@ -handleExplicitPluralForms( $count, $forms ); - if ( is_string( $forms ) ) { - return $forms; - } - if ( !count( $forms ) ) { - return ''; - } - // @todo FIXME: CLDR defines 4 plural forms instead of 3. Plural for for decimals is missing. - // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - $forms = $this->preConvertPlural( $forms, 3 ); - - if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) { - return $forms[2]; - } else { - switch ( $count % 10 ) { - case 1: return $forms[0]; - case 2: - case 3: - case 4: return $forms[1]; - default: return $forms[2]; - } - } - } -} diff --git a/languages/classes/LanguageSr.php b/languages/classes/LanguageSr.php index 283ef22983..a728c9d0d7 100644 --- a/languages/classes/LanguageSr.php +++ b/languages/classes/LanguageSr.php @@ -22,8 +22,6 @@ */ require_once __DIR__ . '/../LanguageConverter.php'; -require_once __DIR__ . '/LanguageSr_ec.php'; -require_once __DIR__ . '/LanguageSr_el.php'; /** * There are two levels of conversion for Serbian: the script level @@ -166,14 +164,16 @@ class SrConverter extends LanguageConverter { // regexp for roman numbers $roman = 'M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})'; - $reg = '/^' . $roman . '$|^' . $roman . $breaks . '|' . $breaks . $roman . '$|' . $breaks . $roman . $breaks . '/'; + $reg = '/^' . $roman . '$|^' . $roman . $breaks . '|' . $breaks + . $roman . '$|' . $breaks . $roman . $breaks . '/'; $matches = preg_split( $reg, $text, -1, PREG_SPLIT_OFFSET_CAPTURE ); $m = array_shift( $matches ); $this->loadTables(); if ( !isset( $this->mTables[$toVariant] ) ) { - throw new MWException( "Broken variant table: " . implode( ',', array_keys( $this->mTables ) ) ); + throw new MWException( "Broken variant table: " + . implode( ',', array_keys( $this->mTables ) ) ); } $ret = $this->mTables[$toVariant]->replace( $m[0] ); $mstart = $m[1] + strlen( $m[0] ); @@ -218,7 +218,7 @@ class SrConverter extends LanguageConverter { * * @ingroup Language */ -class LanguageSr extends LanguageSr_ec { +class LanguageSr extends Language { function __construct() { global $wgHooks; @@ -238,44 +238,4 @@ class LanguageSr extends LanguageSr_ec { $this->mConverter = new SrConverter( $this, 'sr', $variants, $variantfallbacks, $flags ); $wgHooks['PageContentSaveComplete'][] = $this->mConverter; } - - /** - * @param $count int - * @param $forms array - * - * @return string - */ - function convertPlural( $count, $forms ) { - $forms = $this->handleExplicitPluralForms( $count, $forms ); - if ( is_string( $forms ) ) { - return $forms; - } - if ( !count( $forms ) ) { - return ''; - } - - // If the actual number is not mentioned in the expression, then just two forms are enough: - // singular for $count == 1 - // plural for $count != 1 - // For example, "This user belongs to {{PLURAL:$1|one group|several groups}}." - if ( count( $forms ) === 2 ) { - return $count == 1 ? $forms[0] : $forms[1]; - } - - // @todo FIXME: CLDR defines 4 plural forms. Form with decimals missing. - // See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#ru - $forms = $this->preConvertPlural( $forms, 3 ); - - if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) { - return $forms[2]; - } else { - switch ( $count % 10 ) { - case 1: return $forms[0]; - case 2: - case 3: - case 4: return $forms[1]; - default: return $forms[2]; - } - } - } } diff --git a/languages/classes/LanguageSr_ec.php b/languages/classes/LanguageSr_ec.php deleted file mode 100644 index 4787856217..0000000000 --- a/languages/classes/LanguageSr_ec.php +++ /dev/null @@ -1,58 +0,0 @@ -handleExplicitPluralForms( $count, $forms ); - if ( is_string( $forms ) ) { - return $forms; - } - if ( !count( $forms ) ) { - return ''; - } - $forms = $this->preConvertPlural( $forms, 3 ); - - if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) { - return $forms[2]; - } else { - switch ( $count % 10 ) { - case 1: return $forms[0]; - case 2: - case 3: - case 4: return $forms[1]; - default: return $forms[2]; - } - } - } -} diff --git a/languages/classes/LanguageSr_el.php b/languages/classes/LanguageSr_el.php deleted file mode 100644 index 3f086df436..0000000000 --- a/languages/classes/LanguageSr_el.php +++ /dev/null @@ -1,58 +0,0 @@ -handleExplicitPluralForms( $count, $forms ); - if ( is_string( $forms ) ) { - return $forms; - } - if ( !count( $forms ) ) { - return ''; - } - $forms = $this->preConvertPlural( $forms, 3 ); - - if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) { - return $forms[2]; - } else { - switch ( $count % 10 ) { - case 1: return $forms[0]; - case 2: - case 3: - case 4: return $forms[1]; - default: return $forms[2]; - } - } - } -} diff --git a/languages/classes/LanguageUk.php b/languages/classes/LanguageUk.php index aabe3902b8..08041a928e 100644 --- a/languages/classes/LanguageUk.php +++ b/languages/classes/LanguageUk.php @@ -86,45 +86,6 @@ class LanguageUk extends Language { return $word; } - /** - * @param $count int - * @param $forms array - * @return string - */ - function convertPlural( $count, $forms ) { - $forms = $this->handleExplicitPluralForms( $count, $forms ); - if ( is_string( $forms ) ) { - return $forms; - } - if ( !count( $forms ) ) { - return ''; - } - - // If the actual number is not mentioned in the expression, then just two forms are enough: - // singular for $count == 1 - // plural for $count != 1 - // For example, "This user belongs to {{PLURAL:$1|one group|several groups}}." - if ( count( $forms ) === 2 ) { - return $count == 1 ? $forms[0] : $forms[1]; - } - - // @todo FIXME: CLDR defines 4 plural forms. Form for decimals is missing/ - // See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#uk - $forms = $this->preConvertPlural( $forms, 3 ); - - if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) { - return $forms[2]; - } else { - switch ( $count % 10 ) { - case 1: return $forms[0]; - case 2: - case 3: - case 4: return $forms[1]; - default: return $forms[2]; - } - } - } - /** * Ukrainian numeric format is "12 345,67" but "1234,56" * diff --git a/languages/data/plurals-mediawiki.xml b/languages/data/plurals-mediawiki.xml index 3314793038..aafc393b7e 100644 --- a/languages/data/plurals-mediawiki.xml +++ b/languages/data/plurals-mediawiki.xml @@ -2,48 +2,44 @@ - - - n is 1 - n is 2 - n is not 0 AND n is not 10 AND n mod 10 is 0 - + - n mod 100 is 1 - n mod 100 is 2 - n mod 100 in 3..4 + n % 100 = 1 @integer 1, 101, 201, 301, … + n % 100 = 2 @integer 2, 102, 202, 302, … + n % 100 = 3..4 @integer 3~4, 103~104, … + @integer 5, 6, 7, 8, 9, 10, 105, 206, 307, … - + + - n mod 10 is 1 and n mod 100 is not 11 - n mod 10 in 2..4 and n mod 100 not in 12..14 - n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14 - + n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, … + n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, … + n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, … + - n mod 10 is 1 - n mod 10 is 2 - n mod 10 in 3..4 + n % 10 = 1 @integer 1, 11, 21, 31, … + n % 10 = 2 @integer 2, 12, 22, 32, … + n % 10 = 3..4 @integer 3~4, 13~14, 23~24, … + @integer 5, 6, 7, 8, 9, 10, 15, 105, 206, 307, … - n in 0..1 + n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 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 mod 10 is 1 and n mod 100 is not 11 - n mod 10 is 2 and n mod 100 is not 12 - n is 0 or n mod 100 is 0 or n mod 100 in 10..19 + n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, … + n % 10 = 2 and n % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 92, 102, 122, … + n = 0 or n % 100 = 0 or n % 100 = 10..19 @integer 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100, 111,112, … + @integer 3, 4, 5, 6, 7, 8, 9, 20, 103, 104, … - - - n mod 10 is 1 - n mod 10 is 2 - n mod 100 in 0,20,40,60 + + 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, … + v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, … + @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, … diff --git a/languages/data/plurals.xml b/languages/data/plurals.xml old mode 100644 new mode 100755 index d5a1cfe223..fd4eaf6b7f --- a/languages/data/plurals.xml +++ b/languages/data/plurals.xml @@ -1,119 +1,200 @@ + - - - + + + + - - n is 0 - n is 1 - n is 2 - n mod 100 in 3..10 - n mod 100 in 11..99 - - - n is 1 - n is 2 - n is not 0 AND n mod 10 is 0 - - - n is 1 - - - n in 0..1 - - - n within 0..2 and n is not 2 + 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 + n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000 + n % 100 = 3..10 @integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, … + n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, … + @integer 100~102, 200~202, 300~302, 400~402, 500~502, 600, 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, … + + + 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, … + + + 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, … + + + n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 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, … + + + 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, … - n is 0 - n mod 10 is 1 and n mod 100 is not 11 + n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 @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 % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1 @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 2~9, 22~29, 102, 1002, … @decimal 0.2~0.9, 1.2~1.9, 10.2, 100.2, 1000.2, … - n is 1 - n is 2 - - - n is 1 - n is 2 - n in 3..6 - n in 7..10 - - - n is 1 - n is 0 OR n is not 1 AND n mod 100 in 1..19 + 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 + @integer 0, 3~17, 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, … + + + 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 = 3..6 @integer 3~6 @decimal 3.0, 4.0, 5.0, 6.0, 3.00, 4.00, 5.00, 6.00, 3.000, 4.000, 5.000, 6.000, 3.0000, 4.0000, 5.0000, 6.0000 + n = 7..10 @integer 7~10 @decimal 7.0, 8.0, 9.0, 10.0, 7.00, 8.00, 9.00, 10.00, 7.000, 8.000, 9.000, 10.000, 7.0000, 8.0000, 9.0000, 10.0000 + @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + + + i = 1 and v = 0 @integer 1 + 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, … - n mod 10 is 1 and n mod 100 not in 11..19 - n mod 10 in 2..9 and n mod 100 not in 11..19 + n % 10 = 1 and n % 100 != 11..19 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, … + n % 10 = 2..9 and n % 100 != 11..19 @integer 2~9, 22~29, 102, 1002, … @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 22.0, 102.0, 1002.0, … + 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 mod 10 is 1 and n mod 100 is not 11 - n mod 10 in 2..4 and n mod 100 not in 12..14 - n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14 - + + n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, … + n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, … + n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, … - n is 1 - n in 2..4 + i = 1 and v = 0 @integer 1 + i = 2..4 and v = 0 @integer 2~4 + v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … - n is 1 - n mod 10 in 2..4 and n mod 100 not in 12..14 - n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14 - - + i = 1 and v = 0 @integer 1 + 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, … + v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … + @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - n mod 100 is 1 - n mod 100 is 2 - n mod 100 in 3..4 - - - n is 1 - n is 0 or n mod 100 in 2..10 - n mod 100 in 11..19 - - - n mod 10 is 1 and n is not 11 - - - n is 0 - n is 1 - n is 2 - n is 3 - n is 6 + v = 0 and i % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, … + v = 0 and i % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, … + v = 0 and i % 100 = 3..4 or v != 0 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … + + + 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 or f % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 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, … + @integer 0, 2~10, 12~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.0, 1.2~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 + n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000 + n = 3 @integer 3 @decimal 3.0, 3.00, 3.000, 3.0000 + n = 6 @integer 6 @decimal 6.0, 6.00, 6.000, 6.0000 + @integer 4, 5, 7~20, 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 is 0 - n within 0..2 and n is not 0 and n is not 2 + n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000 + i = 0,1 and n != 0 @integer 1 @decimal 0.1~1.6 + @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, … - n within 0..1 - n in 2..10 + i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04 + n = 2..10 @integer 2~10 @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 2.00, 3.00, 4.00, 5.00, 6.00, 7.00, 8.00 + @integer 11~26, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~1.9, 2.1~2.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … - - n mod 10 is 1 and n mod 100 not in 11,71,91 - n mod 10 is 2 and n mod 100 not in 12,72,92 - n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99 - n is not 0 and n mod 1000000 is 0 + + 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, … + @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 is 0 - n is 1 + 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~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 in 0..1 or n in 11..99 + 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 mod 10 in 1..2 or n mod 20 is 0 + n % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, … @decimal 1.0, 11.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 101.0, 1001.0, … + n % 10 = 2 @integer 2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, … @decimal 2.0, 12.0, 22.0, 32.0, 42.0, 52.0, 62.0, 72.0, 102.0, 1002.0, … + n % 100 = 0,20,40,60 @integer 0, 20, 40, 60, 100, 120, 140, 160, 1000, 10000, 100000, 1000000, … @decimal 0.0, 20.0, 40.0, 60.0, 100.0, 120.0, 140.0, 160.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + @integer 3~10, 13~19, 23, 103, 1003, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.1, 1000.1, … - n in 1,11 - n in 2,12 - n in 3..10,13..19 + n = 1,11 @integer 1, 11 @decimal 1.0, 11.0, 1.00, 11.00, 1.000, 11.000, 1.0000 + n = 2,12 @integer 2, 12 @decimal 2.0, 12.0, 2.00, 12.00, 2.000, 12.000, 2.0000 + n = 3..10,13..19 @integer 3~10, 13~19 @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 3.00 + @integer 0, 20~34, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + + + @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..1 and v = 0 @integer 0, 1 + @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.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, … + + + i = 1 and v = 0 or i = 0 and t = 1 @integer 1 @decimal 0.1, 0.01, 0.10, 0.001, 0.010, 0.100, 0.0001, 0.0010, 0.0100, 0.1000 + @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … + + + n = 1 or t != 0 and i = 0,1 @integer 1 @decimal 0.1~1.6 + @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, … + + + n = 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, … + + + 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, … + + + 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, … + + + n = 0,1 or i = 0 and f = 1 @integer 0, 1 @decimal 0.0, 0.1, 1.0, 0.00, 0.01, 1.00, 0.000, 0.001, 1.000, 0.0000, 0.0001, 1.0000 + @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.2~0.9, 1.1~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, … + v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, … + @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, … + + + 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 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … + @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.0~1.5, 10.0, 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, … + v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … + @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, … diff --git a/resources/mediawiki.libs/CLDRPluralRuleParser.js b/resources/mediawiki.libs/CLDRPluralRuleParser.js index 441bc91f68..3def37c58e 100644 --- a/resources/mediawiki.libs/CLDRPluralRuleParser.js +++ b/resources/mediawiki.libs/CLDRPluralRuleParser.js @@ -1,7 +1,7 @@ -/* This is cldrpluralparser 1.0, ported to MediaWiki ResourceLoader */ +/* This is CLDRPluralRuleParser v1.1, ported to MediaWiki ResourceLoader */ /** -* cldrpluralparser.js +* CLDRPluralRuleParser.js * A parser engine for CLDR plural rules. * * Copyright 2012 GPLV3+, Santhosh Thottingal @@ -13,59 +13,83 @@ * @author Amir Aharoni */ +( function ( mw ) { /** * Evaluates a plural rule in CLDR syntax for a number - * @param rule - * @param number - * @return true|false|null + * @param {string} rule + * @param {integer} number + * @return {boolean} true if evaluation passed, false if evaluation failed. */ -( function( mw ) { function pluralRuleParser(rule, number) { /* Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules ----------------------------------------------------------------- - condition = and_condition ('or' and_condition)* + ('@integer' samples)? + ('@decimal' samples)? and_condition = relation ('and' relation)* - relation = is_relation | in_relation | within_relation | 'n' + relation = is_relation | in_relation | within_relation is_relation = expr 'is' ('not')? value - in_relation = expr ('not')? 'in' range_list + in_relation = expr (('not')? 'in' | '=' | '!=') range_list within_relation = expr ('not')? 'within' range_list - expr = 'n' ('mod' value)? + expr = operand (('mod' | '%') value)? + operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w' range_list = (range | value) (',' range_list)* value = digit+ digit = 0|1|2|3|4|5|6|7|8|9 range = value'..'value - + samples = sampleRange (',' sampleRange)* (',' ('…'|'...'))? + sampleRange = decimalValue '~' decimalValue + decimalValue = value ('.' value)? */ + + // we don't evaluate the samples section of the rule. Ignore it. + rule = rule.split('@')[0].trim(); + + if (!rule.length) { + // empty rule or 'other' rule. + return true; + } // Indicates current position in the rule as we parse through it. // Shared among all parsing functions below. - var pos = 0; - - var whitespace = makeRegexParser(/^\s+/); - var digits = makeRegexParser(/^\d+/); - - var _n_ = makeStringParser('n'); - var _is_ = makeStringParser('is'); - var _mod_ = makeStringParser('mod'); - var _not_ = makeStringParser('not'); - var _in_ = makeStringParser('in'); - var _within_ = makeStringParser('within'); - var _range_ = makeStringParser('..'); - var _comma_ = makeStringParser(','); - var _or_ = makeStringParser('or'); - var _and_ = makeStringParser('and'); + var pos = 0, + operand, + expression, + relation, + result, + whitespace = makeRegexParser(/^\s+/), + value = makeRegexParser(/^\d+/), + _n_ = makeStringParser('n'), + _i_ = makeStringParser('i'), + _f_ = makeStringParser('f'), + _t_ = makeStringParser('t'), + _v_ = makeStringParser('v'), + _w_ = makeStringParser('w'), + _is_ = makeStringParser('is'), + _isnot_ = makeStringParser('is not'), + _isnot_sign_ = makeStringParser('!='), + _equal_ = makeStringParser('='), + _mod_ = makeStringParser('mod'), + _percent_ = makeStringParser('%'), + _not_ = makeStringParser('not'), + _in_ = makeStringParser('in'), + _within_ = makeStringParser('within'), + _range_ = makeStringParser('..'), + _comma_ = makeStringParser(','), + _or_ = makeStringParser('or'), + _and_ = makeStringParser('and'); function debug() { - /* console.log.apply(console, arguments);*/ + // console.log.apply(console, arguments); } debug('pluralRuleParser', rule, number); // Try parsers until one works, if none work return null + function choice(parserSyntax) { - return function () { + return function() { for (var i = 0; i < parserSyntax.length; i++) { var result = parserSyntax[i](); if (result !== null) { @@ -79,6 +103,7 @@ function pluralRuleParser(rule, number) { // Try several parserSyntax-es in a row. // All must succeed; otherwise, return null. // This is the only eager one. + function sequence(parserSyntax) { var originalPos = pos; var result = []; @@ -95,8 +120,9 @@ function pluralRuleParser(rule, number) { // Run the same parser over and over until it fails. // Must succeed a minimum of n times; otherwise, return null. + function nOrMore(n, p) { - return function () { + return function() { var originalPos = pos; var result = []; var parsed = p(); @@ -113,21 +139,21 @@ function pluralRuleParser(rule, number) { } // Helpers -- just make parserSyntax out of simpler JS builtin types - function makeStringParser(s) { var len = s.length; - return function () { + return function() { var result = null; if (rule.substr(pos, len) === s) { result = s; pos += len; } + return result; }; } function makeRegexParser(regex) { - return function () { + return function() { var matches = rule.substr(pos).match(regex); if (matches === null) { return null; @@ -137,62 +163,166 @@ function pluralRuleParser(rule, number) { }; } + /* + * integer digits of n. + */ + function i() { + var result = _i_(); + if (result === null) { + debug(' -- failed i', parseInt(number, 10)); + return result; + } + result = parseInt(number, 10); + debug(' -- passed i ', result); + return result; + } + + /* + * absolute value of the source number (integer and decimals). + */ function n() { var result = _n_(); if (result === null) { - debug(" -- failed n"); + debug(' -- failed n ', number); return result; } - result = parseInt(number, 10); - debug(" -- passed n ", result); + result = parseFloat(number, 10); + debug(' -- passed n ', result); + return result; + } + + /* + * visible fractional digits in n, with trailing zeros. + */ + function f() { + var result = _f_(); + if (result === null) { + debug(' -- failed f ', number); + return result; + } + result = (number + '.').split('.')[1] || 0; + debug(' -- passed f ', result); + return result; + } + + /* + * visible fractional digits in n, without trailing zeros. + */ + function t() { + var result = _t_(); + if (result === null) { + debug(' -- failed t ', number); + return result; + } + result = (number + '.').split('.')[1].replace(/0$/, '') || 0; + debug(' -- passed t ', result); + return result; + } + + /* + * number of visible fraction digits in n, with trailing zeros. + */ + function v() { + var result = _v_(); + if (result === null) { + debug(' -- failed v ', number); + return result; + } + result = (number + '.').split('.')[1].length || 0; + debug(' -- passed v ', result); + return result; + } + + /* + * number of visible fraction digits in n, without trailing zeros. + */ + function w() { + var result = _w_(); + if (result === null) { + debug(' -- failed w ', number); + return result; + } + result = (number + '.').split('.')[1].replace(/0$/, '').length || 0; + debug(' -- passed w ', result); return result; } - var expression = choice([mod, n]); + // operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w' + operand = choice([n, i, f, t, v, w]); + + // expr = operand (('mod' | '%') value)? + expression = choice([mod, operand]); function mod() { - var result = sequence([n, whitespace, _mod_, whitespace, digits]); + var result = sequence([operand, whitespace, choice([_mod_, _percent_]), whitespace, value]); if (result === null) { - debug(" -- failed mod"); + debug(' -- failed mod'); return null; } - debug(" -- passed mod"); + debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] + ' ' + parseInt(result[4], 10)); return parseInt(result[0], 10) % parseInt(result[4], 10); } function not() { var result = sequence([whitespace, _not_]); if (result === null) { - debug(" -- failed not"); + debug(' -- failed not'); return null; - } else { - return result[1]; } + + return result[1]; } + // is_relation = expr 'is' ('not')? value function is() { - var result = sequence([expression, whitespace, _is_, nOrMore(0, not), whitespace, digits]); + var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]); if (result !== null) { - debug(" -- passed is"); - if (result[3][0] === 'not') { - return result[0] !== parseInt(result[5], 10); - } else { - return result[0] === parseInt(result[5], 10); + debug(' -- passed is : ' + result[0] + ' == ' + parseInt(result[4], 10)); + return result[0] === parseInt(result[4], 10); + } + debug(' -- failed is'); + return null; + } + + // is_relation = expr 'is' ('not')? value + function isnot() { + var result = sequence([expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value]); + if (result !== null) { + debug(' -- passed isnot: ' + result[0] + ' != ' + parseInt(result[4], 10)); + return result[0] !== parseInt(result[4], 10); + } + debug(' -- failed isnot'); + return null; + } + + function not_in() { + var result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]); + if (result !== null) { + debug(' -- passed not_in: ' + result[0] + ' != ' + result[4]); + var range_list = result[4]; + for (var i = 0; i < range_list.length; i++) { + if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) { + return false; + } } + return true; } - debug(" -- failed is"); + debug(' -- failed not_in'); return null; } + // range_list = (range | value) (',' range_list)* function rangeList() { - // range_list = (range | value) (',' range_list)* - var result = sequence([choice([range, digits]), nOrMore(0, rangeTail)]); + var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]); var resultList = []; if (result !== null) { - resultList = resultList.concat(result[0], result[1][0]); + resultList = resultList.concat(result[0]); + if (result[1][0]) { + resultList = resultList.concat(result[1][0]); + } return resultList; } - debug(" -- failed rangeList"); + debug(' -- failed rangeList'); return null; } @@ -202,111 +332,141 @@ function pluralRuleParser(rule, number) { if (result !== null) { return result[1]; } - debug(" -- failed rangeTail"); + debug(' -- failed rangeTail'); return null; } + // range = value'..'value + function range() { var i; - var result = sequence([digits, _range_, digits]); + var result = sequence([value, _range_, value]); if (result !== null) { - debug(" -- passed range"); + debug(' -- passed range'); var array = []; var left = parseInt(result[0], 10); var right = parseInt(result[2], 10); - for ( i = left; i <= right; i++) { + for (i = left; i <= right; i++) { array.push(i); } return array; } - debug(" -- failed range"); + debug(' -- failed range'); return null; } function _in() { // in_relation = expr ('not')? 'in' range_list - var result = sequence([expression, nOrMore(0, not), whitespace, _in_, whitespace, rangeList]); + var result = sequence([expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList]); if (result !== null) { - debug(" -- passed _in"); + debug(' -- passed _in:' + result); var range_list = result[5]; for (var i = 0; i < range_list.length; i++) { - if (parseInt(range_list[i], 10) === result[0]) { + if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) { return (result[1][0] !== 'not'); } } return (result[1][0] === 'not'); } - debug(" -- failed _in "); + debug(' -- failed _in '); return null; } + /* + * The difference between in and within is that in only includes integers in the specified range, + * while within includes all values. + */ + function within() { - var result = sequence([expression, whitespace, _within_, whitespace, rangeList]); + // within_relation = expr ('not')? 'within' range_list + var result = sequence([expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList]); if (result !== null) { - debug(" -- passed within "); - var range_list = result[4]; - return (parseInt( range_list[0],10 )<= result[0] && result[0] <= parseInt( range_list[1], 10)); + debug(' -- passed within'); + var range_list = result[5]; + if ((result[0] >= parseInt(range_list[0], 10)) && + (result[0] < parseInt(range_list[range_list.length - 1], 10))) { + return (result[1][0] !== 'not'); + } + return (result[1][0] === 'not'); } - debug(" -- failed within "); + debug(' -- failed within '); return null; } + // relation = is_relation | in_relation | within_relation + relation = choice([is, not_in, isnot, _in, within]); - var relation = choice([is, _in, within]); - + // and_condition = relation ('and' relation)* function and() { - var result = sequence([relation, whitespace, _and_, whitespace, condition]); + var result = sequence([relation, nOrMore(0, andTail)]); if (result) { - debug(" -- passed and"); - return result[0] && result[4]; + if (!result[0]) { + return false; + } + for (var i = 0; i < result[1].length; i++) { + if (!result[1][i]) { + return false; + } + } + return true; } - debug(" -- failed and"); + debug(' -- failed and'); return null; } - function or() { - var result = sequence([relation, whitespace, _or_, whitespace, condition]); - if (result) { - debug(" -- passed or"); - return result[0] || result[4]; + // ('and' relation)* + function andTail() { + var result = sequence([whitespace, _and_, whitespace, relation]); + if (result !== null) { + debug(' -- passed andTail' + result); + return result[3]; } - debug(" -- failed or"); + debug(' -- failed andTail'); return null; - } - var condition = choice([and, or, relation]); + } + // ('or' and_condition)* + function orTail() { + var result = sequence([whitespace, _or_, whitespace, and]); + if (result !== null) { + debug(' -- passed orTail: ' + result[3]); + return result[3]; + } + debug(' -- failed orTail'); + return null; - function isInt(n) { - return parseFloat(n) % 1 === 0; } + // condition = and_condition ('or' and_condition)* + function condition() { + var result = sequence([and, nOrMore(0, orTail)]); + if (result) { + for (var i = 0; i < result[1].length; i++) { + if (result[1][i]) { + return true; + } + } + return result[0]; - function start() { - if (!isInt(number)) { - return false; } - var result = condition(); - return result; + return false; } - - var result = start(); - + result = condition(); /* * For success, the pos must have gotten to the end of the rule * and returned a non-null. * n.b. This is part of language infrastructure, so we do not throw an internationalizable message. */ - if (result === null || pos !== rule.length) { - // throw new Error("Parse error at position " + pos.toString() + " in input: " + rule + " result is " + result); + if (result === null) { + throw new Error('Parse error at position ' + pos.toString() + ' for rule: ' + rule); } - return result; -} + if (pos !== rule.length) { + debug('Warning: Rule not parsed completely. Parser stopped at ' + rule.substr(0, pos) + ' for rule: ' + rule); + } -/* For module loaders, e.g. NodeJS, NPM */ -if (typeof module !== 'undefined' && module.exports) { - module.exports = pluralRuleParser; + return result; } /* pluralRuleParser ends here */ diff --git a/tests/phpunit/languages/LanguageBe_taraskTest.php b/tests/phpunit/languages/LanguageBe_taraskTest.php index 97a17ec030..dbdb588991 100644 --- a/tests/phpunit/languages/LanguageBe_taraskTest.php +++ b/tests/phpunit/languages/LanguageBe_taraskTest.php @@ -79,13 +79,13 @@ class LanguageBe_taraskTest extends LanguageClassesTestCase { * @covers Language::convertPlural */ public function testPluralTwoForms( $result, $value ) { - $forms = array( 'one', 'other', '0=one' ); + $forms = array( '1=one', 'other' ); $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) ); } public static function providePluralTwoForms() { return array( - array( 'one', 0 ), + array( 'other', 0 ), array( 'one', 1 ), array( 'other', 11 ), array( 'other', 91 ), diff --git a/tests/phpunit/languages/LanguageBsTest.php b/tests/phpunit/languages/LanguageBsTest.php index fb965b894d..7aca2ab1a2 100644 --- a/tests/phpunit/languages/LanguageBsTest.php +++ b/tests/phpunit/languages/LanguageBsTest.php @@ -5,14 +5,14 @@ * @file */ -/** Tests for MediaWiki languages/LanguageBs.php */ +/** Tests for Croatian (hrvatski) */ class LanguageBsTest extends LanguageClassesTestCase { /** * @dataProvider providePlural * @covers Language::convertPlural */ public function testPlural( $result, $value ) { - $forms = array( 'one', 'few', 'many', 'other' ); + $forms = array( 'one', 'few', 'other' ); $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) ); } @@ -26,17 +26,17 @@ class LanguageBsTest extends LanguageClassesTestCase { public static function providePlural() { return array( - array( 'many', 0 ), + array( 'other', 0 ), array( 'one', 1 ), array( 'few', 2 ), array( 'few', 4 ), - array( 'many', 5 ), - array( 'many', 11 ), - array( 'many', 20 ), + array( 'other', 5 ), + array( 'other', 11 ), + array( 'other', 20 ), array( 'one', 21 ), array( 'few', 24 ), - array( 'many', 25 ), - array( 'many', 200 ), + array( 'other', 25 ), + array( 'other', 200 ), ); } } diff --git a/tests/phpunit/languages/LanguageHrTest.php b/tests/phpunit/languages/LanguageHrTest.php index 6ce4aff9ba..644c525565 100644 --- a/tests/phpunit/languages/LanguageHrTest.php +++ b/tests/phpunit/languages/LanguageHrTest.php @@ -12,7 +12,7 @@ class LanguageHrTest extends LanguageClassesTestCase { * @covers Language::convertPlural */ public function testPlural( $result, $value ) { - $forms = array( 'one', 'few', 'many', 'other' ); + $forms = array( 'one', 'few', 'other' ); $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) ); } @@ -26,17 +26,17 @@ class LanguageHrTest extends LanguageClassesTestCase { public static function providePlural() { return array( - array( 'many', 0 ), + array( 'other', 0 ), array( 'one', 1 ), array( 'few', 2 ), array( 'few', 4 ), - array( 'many', 5 ), - array( 'many', 11 ), - array( 'many', 20 ), + array( 'other', 5 ), + array( 'other', 11 ), + array( 'other', 20 ), array( 'one', 21 ), array( 'few', 24 ), - array( 'many', 25 ), - array( 'many', 200 ), + array( 'other', 25 ), + array( 'other', 200 ), ); } } diff --git a/tests/phpunit/languages/LanguageHyTest.php b/tests/phpunit/languages/LanguageHyTest.php index 896522b097..92e0ef943b 100644 --- a/tests/phpunit/languages/LanguageHyTest.php +++ b/tests/phpunit/languages/LanguageHyTest.php @@ -5,7 +5,7 @@ * @file */ -/** Tests for MediaWiki languages/LanguageHy.php */ +/** Tests for Armenian (Հայերեն) */ class LanguageHyTest extends LanguageClassesTestCase { /** * @dataProvider providePlural @@ -21,13 +21,12 @@ class LanguageHyTest extends LanguageClassesTestCase { * @covers Language::getPluralRuleType */ public function testGetPluralRuleType( $result, $value ) { - // This fails for 0, but I'm not sure why. Some voodoo going on here. $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) ); } public static function providePlural() { return array( - array( 'other', 0 ), + array( 'one', 0 ), array( 'one', 1 ), array( 'other', 2 ), array( 'other', 200 ), diff --git a/tests/phpunit/languages/LanguageLvTest.php b/tests/phpunit/languages/LanguageLvTest.php index c4d8a6f08b..7120cfe384 100644 --- a/tests/phpunit/languages/LanguageLvTest.php +++ b/tests/phpunit/languages/LanguageLvTest.php @@ -5,7 +5,7 @@ * @file */ -/** Tests for MediaWiki languages/classes/LanguageLv.php */ +/** Tests for Latvian */ class LanguageLvTest extends LanguageClassesTestCase { /** * @dataProvider providePlural @@ -28,13 +28,17 @@ class LanguageLvTest extends LanguageClassesTestCase { return array( array( 'zero', 0 ), array( 'one', 1 ), - array( 'other', 11 ), + array( 'zero', 11 ), array( 'one', 21 ), - array( 'other', 411 ), + array( 'zero', 411 ), + array( 'other', 2 ), + array( 'other', 9 ), + array( 'zero', 12 ), array( 'other', 12.345 ), - array( 'other', 20 ), + array( 'zero', 20 ), + array( 'other', 22 ), array( 'one', 31 ), - array( 'other', 200 ), + array( 'zero', 200 ), ); } } diff --git a/tests/phpunit/languages/LanguageMkTest.php b/tests/phpunit/languages/LanguageMkTest.php index 7d47b37552..ed155263e3 100644 --- a/tests/phpunit/languages/LanguageMkTest.php +++ b/tests/phpunit/languages/LanguageMkTest.php @@ -5,7 +5,7 @@ * @file */ -/** Tests for MediaWiki languages/classes/LanguageMk.php */ +/** Tests for македонски/Macedonian */ class LanguageMkTest extends LanguageClassesTestCase { /** * @dataProvider providePlural @@ -28,7 +28,7 @@ class LanguageMkTest extends LanguageClassesTestCase { return array( array( 'other', 0 ), array( 'one', 1 ), - array( 'other', 11 ), + array( 'one', 11 ), array( 'one', 21 ), array( 'one', 411 ), array( 'other', 12.345 ), diff --git a/tests/phpunit/languages/LanguageRuTest.php b/tests/phpunit/languages/LanguageRuTest.php index 56f849063b..ac18276634 100644 --- a/tests/phpunit/languages/LanguageRuTest.php +++ b/tests/phpunit/languages/LanguageRuTest.php @@ -33,6 +33,8 @@ class LanguageRuTest extends LanguageClassesTestCase { * @covers Language::getPluralRuleType */ public function testGetPluralRuleType( $result, $value ) { + // TODO: Remove after MW plurals rules made in sync with CLDR + $this->markTestSkipped( 'Skipping. Russian plural forms are overridden in MW' ); $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) ); } diff --git a/tests/phpunit/languages/LanguageSgsTest.php b/tests/phpunit/languages/LanguageSgsTest.php index bf6a14b19c..fa49a4dddc 100644 --- a/tests/phpunit/languages/LanguageSgsTest.php +++ b/tests/phpunit/languages/LanguageSgsTest.php @@ -5,7 +5,7 @@ * @file */ -/** Tests for MediaWiki languages/classes/LanguageSgs.php */ +/** Tests for Samogitian */ class LanguageSgsTest extends LanguageClassesTestCase { /** * @dataProvider providePluralAllForms diff --git a/tests/phpunit/languages/LanguageShTest.php b/tests/phpunit/languages/LanguageShTest.php index 6d2e25a617..1b390872af 100644 --- a/tests/phpunit/languages/LanguageShTest.php +++ b/tests/phpunit/languages/LanguageShTest.php @@ -5,14 +5,14 @@ * @file */ -/** Tests for MediaWiki languages/classes/LanguageSh.php */ +/** Tests for srpskohrvatski / српскохрватски / Serbocroatian */ class LanguageShTest extends LanguageClassesTestCase { /** * @dataProvider providePlural * @covers Language::convertPlural */ public function testPlural( $result, $value ) { - $forms = array( 'one', 'few', 'many', 'other' ); + $forms = array( 'one', 'few', 'other' ); $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) ); } @@ -26,17 +26,17 @@ class LanguageShTest extends LanguageClassesTestCase { public static function providePlural() { return array( - array( 'many', 0 ), + array( 'other', 0 ), array( 'one', 1 ), array( 'few', 2 ), array( 'few', 4 ), - array( 'many', 5 ), - array( 'many', 10 ), - array( 'many', 11 ), - array( 'many', 12 ), + array( 'other', 5 ), + array( 'other', 10 ), + array( 'other', 11 ), + array( 'other', 12 ), array( 'one', 101 ), array( 'few', 102 ), - array( 'many', 111 ), + array( 'other', 111 ), ); } } diff --git a/tests/phpunit/languages/LanguageSrTest.php b/tests/phpunit/languages/LanguageSrTest.php index f55124820c..8d35f36534 100644 --- a/tests/phpunit/languages/LanguageSrTest.php +++ b/tests/phpunit/languages/LanguageSrTest.php @@ -130,7 +130,7 @@ class LanguageSrTest extends LanguageClassesTestCase { * @covers Language::convertPlural */ public function testPlural( $result, $value ) { - $forms = array( 'one', 'few', 'many', 'other' ); + $forms = array( 'one', 'few', 'other' ); $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) ); } @@ -145,16 +145,16 @@ class LanguageSrTest extends LanguageClassesTestCase { public static function providePlural() { return array( array( 'one', 1 ), - array( 'many', 11 ), + array( 'other', 11 ), array( 'one', 91 ), array( 'one', 121 ), array( 'few', 2 ), array( 'few', 3 ), array( 'few', 4 ), array( 'few', 334 ), - array( 'many', 5 ), - array( 'many', 15 ), - array( 'many', 120 ), + array( 'other', 5 ), + array( 'other', 15 ), + array( 'other', 120 ), ); } @@ -171,8 +171,9 @@ class LanguageSrTest extends LanguageClassesTestCase { return array( array( 'one', 1 ), array( 'other', 11 ), - array( 'other', 91 ), - array( 'other', 121 ), + array( 'other', 4 ), + array( 'one', 91 ), + array( 'one', 121 ), ); } diff --git a/tests/phpunit/languages/LanguageUkTest.php b/tests/phpunit/languages/LanguageUkTest.php index 1d81bc5fec..9051bcff6f 100644 --- a/tests/phpunit/languages/LanguageUkTest.php +++ b/tests/phpunit/languages/LanguageUkTest.php @@ -6,7 +6,7 @@ * @file */ -/** Tests for MediaWiki languages/classes/LanguageUk.php */ +/** Tests for Ukrainian */ class LanguageUkTest extends LanguageClassesTestCase { /** * @dataProvider providePlural @@ -57,7 +57,7 @@ class LanguageUkTest extends LanguageClassesTestCase { * @covers Language::convertPlural */ public function testPluralTwoForms( $result, $value ) { - $forms = array( 'one', 'other' ); + $forms = array( '1=one', 'other' ); $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) ); } -- 2.20.1