3 class LanguageTest
extends LanguageClassesTestCase
{
5 * @covers Language::convertDoubleWidth
6 * @covers Language::normalizeForSearch
8 public function testLanguageConvertDoubleWidthToSingleWidth() {
10 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
11 $this->getLang()->normalizeForSearch(
12 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
14 'convertDoubleWidth() with the full alphabet and digits'
19 * @dataProvider provideFormattableTimes
20 * @covers Language::formatTimePeriod
22 public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
23 $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
26 public static function provideFormattableTimes() {
32 'formatTimePeriod() rounding (<10s)'
36 array( 'noabbrevs' => true ),
38 'formatTimePeriod() rounding (<10s)'
44 'formatTimePeriod() rounding (<10s)'
48 array( 'noabbrevs' => true ),
50 'formatTimePeriod() rounding (<10s)'
56 'formatTimePeriod() rounding (<60s)'
60 array( 'noabbrevs' => true ),
62 'formatTimePeriod() rounding (<60s)'
68 'formatTimePeriod() rounding (<1h)'
72 array( 'noabbrevs' => true ),
73 '2 minutes 0 seconds',
74 'formatTimePeriod() rounding (<1h)'
80 'formatTimePeriod() rounding (<1h)'
84 array( 'noabbrevs' => true ),
85 '1 hour 0 minutes 0 seconds',
86 'formatTimePeriod() rounding (<1h)'
92 'formatTimePeriod() rounding (>=1h)'
96 array( 'noabbrevs' => true ),
97 '2 hours 0 minutes 0 seconds',
98 'formatTimePeriod() rounding (>=1h)'
104 'formatTimePeriod() rounding (>=1h), avoidseconds'
108 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
110 'formatTimePeriod() rounding (>=1h), avoidseconds'
116 'formatTimePeriod() rounding (>=1h), avoidminutes'
120 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
122 'formatTimePeriod() rounding (>=1h), avoidminutes'
128 'formatTimePeriod() rounding (=48h), avoidseconds'
132 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
133 '48 hours 0 minutes',
134 'formatTimePeriod() rounding (=48h), avoidseconds'
140 'formatTimePeriod() rounding (>48h), avoidminutes'
144 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
146 'formatTimePeriod() rounding (>48h), avoidminutes'
152 'formatTimePeriod() rounding (>48h), avoidseconds'
156 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
157 '2 days 1 hour 0 minutes',
158 'formatTimePeriod() rounding (>48h), avoidseconds'
164 'formatTimePeriod() rounding (>48h), avoidminutes'
168 array( 'avoid' => 'avoidminutes', 'noabbrevs' => true ),
170 'formatTimePeriod() rounding (>48h), avoidminutes'
176 'formatTimePeriod() rounding (>48h), avoidseconds'
180 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
181 '3 days 0 hours 0 minutes',
182 'formatTimePeriod() rounding (>48h), avoidseconds'
188 'formatTimePeriod() rounding, (>48h), avoidseconds'
192 array( 'avoid' => 'avoidseconds', 'noabbrevs' => true ),
193 '2 days 0 hours 0 minutes',
194 'formatTimePeriod() rounding, (>48h), avoidseconds'
200 'formatTimePeriod() rounding, recursion, (>48h)'
204 array( 'noabbrevs' => true ),
205 '2 days 1 hour 1 minute 1 second',
206 'formatTimePeriod() rounding, recursion, (>48h)'
212 * @covers Language::truncate
214 public function testTruncate() {
217 $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
218 'truncate prefix, len 0, small ellipsis'
223 $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
224 'truncate prefix, small ellipsis'
229 $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
230 'truncate prefix, large ellipsis'
235 $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
236 'truncate suffix, small ellipsis'
241 $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
242 'truncate suffix, large ellipsis'
246 $this->getLang()->truncate( "123 ", 9, 'XXX' ),
247 'truncate prefix, with spaces'
251 $this->getLang()->truncate( "12345 8", 11, 'XXX' ),
252 'truncate prefix, with spaces and non-space ending'
256 $this->getLang()->truncate( "1 234", -8, 'XXX' ),
257 'truncate suffix, with spaces'
261 $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
262 'truncate without adjustment'
266 $this->getLang()->truncate( "泰乐菌素123456789", 11, '...', false ),
267 'truncate does not chop Unicode characters in half'
271 $this->getLang()->truncate( "\n泰乐菌素123456789", 12, '...', false ),
272 'truncate does not chop Unicode characters in half if there is a preceding newline'
277 * @dataProvider provideHTMLTruncateData
278 * @covers Language::truncateHTML
280 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
284 $this->getLang()->truncateHTML( $input, $len, $ellipsis )
289 * @return array Format is ($len, $ellipsis, $input, $expected)
291 public static function provideHTMLTruncateData() {
293 array( 0, 'XXX', "1234567890", "XXX" ),
294 array( 8, 'XXX', "1234567890", "12345XXX" ),
295 array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
297 '<p><span style="font-weight:bold;"></span></p>',
298 '<p><span style="font-weight:bold;"></span></p>',
301 '<p><span style="font-weight:bold;">123456789</span></p>',
302 '<p><span style="font-weight:bold;">***</span></p>',
305 '<p><span style="font-weight:bold;"> 23456789</span></p>',
306 '<p><span style="font-weight:bold;">***</span></p>',
309 '<p><span style="font-weight:bold;">123456789</span></p>',
310 '<p><span style="font-weight:bold;">***</span></p>',
313 '<p><span style="font-weight:bold;">123456789</span></p>',
314 '<p><span style="font-weight:bold;">1***</span></p>',
317 '<tt><span style="font-weight:bold;">123456789</span></tt>',
318 '<tt><span style="font-weight:bold;">12***</span></tt>',
321 '<p><a href="www.mediawiki.org">123456789</a></p>',
322 '<p><a href="www.mediawiki.org">123***</a></p>',
325 '<p><a href="www.mediawiki.org">12 456789</a></p>',
326 '<p><a href="www.mediawiki.org">12 ***</a></p>',
329 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
330 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
333 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
334 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
337 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
338 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
341 '<p><font style="font-weight:bold;">123456789</font></p>',
342 '<p><font style="font-weight:bold;">123456789</font></p>',
348 * Test Language::isWellFormedLanguageTag()
349 * @dataProvider provideWellFormedLanguageTags
350 * @covers Language::isWellFormedLanguageTag
352 public function testWellFormedLanguageTag( $code, $message = '' ) {
354 Language
::isWellFormedLanguageTag( $code ),
355 "validating code $code $message"
360 * The test cases are based on the tests in the GaBuZoMeu parser
361 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
362 * and distributed as free software, under the GNU General Public Licence.
363 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
365 public static function provideWellFormedLanguageTags() {
367 array( 'fr', 'two-letter code' ),
368 array( 'fr-latn', 'two-letter code with lower case script code' ),
369 array( 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ),
370 array( 'fr-Latn-419', 'two-letter code with title case script code and region number' ),
371 array( 'fr-FR', 'two-letter code with uppercase' ),
372 array( 'ax-TZ', 'Not in the registry, but well-formed' ),
373 array( 'fr-shadok', 'two-letter code with variant' ),
374 array( 'fr-y-myext-myext2', 'non-x singleton' ),
375 array( 'fra-Latn', 'ISO 639 can be 3-letters' ),
376 array( 'fra', 'three-letter language code' ),
377 array( 'fra-FX', 'three-letter language code with country code' ),
378 array( 'i-klingon', 'grandfathered with singleton' ),
379 array( 'I-kLINgon', 'tags are case-insensitive...' ),
380 array( 'no-bok', 'grandfathered without singleton' ),
381 array( 'i-enochian', 'Grandfathered' ),
382 array( 'x-fr-CH', 'private use' ),
383 array( 'es-419', 'two-letter code with region number' ),
384 array( 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ),
385 array( 'ab-x-abc-x-abc', 'anything goes after x' ),
386 array( 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ),
387 array( 'i-default', 'grandfathered' ),
388 array( 'abcd-Latn', 'Language of 4 chars reserved for future use' ),
389 array( 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ),
390 array( 'de-CH-1901', 'with country and year' ),
391 array( 'en-US-x-twain', 'with country and singleton' ),
392 array( 'zh-cmn', 'three-letter variant' ),
393 array( 'zh-cmn-Hant', 'three-letter variant and script' ),
394 array( 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ),
395 array( 'xr-p-lze', 'Extension' ),
400 * Negative test for Language::isWellFormedLanguageTag()
401 * @dataProvider provideMalformedLanguageTags
402 * @covers Language::isWellFormedLanguageTag
404 public function testMalformedLanguageTag( $code, $message = '' ) {
406 Language
::isWellFormedLanguageTag( $code ),
407 "validating that code $code is a malformed language tag - $message"
412 * The test cases are based on the tests in the GaBuZoMeu parser
413 * written by Stéphane Bortzmeyer <bortzmeyer@nic.fr>
414 * and distributed as free software, under the GNU General Public Licence.
415 * http://www.bortzmeyer.org/gabuzomeu-parsing-language-tags.html
417 public static function provideMalformedLanguageTags() {
419 array( 'f', 'language too short' ),
420 array( 'f-Latn', 'language too short with script' ),
421 array( 'xr-lxs-qut', 'variants too short' ), # extlangS
422 array( 'fr-Latn-F', 'region too short' ),
423 array( 'a-value', 'language too short with region' ),
424 array( 'tlh-a-b-foo', 'valid three-letter with wrong variant' ),
427 'grandfathered but not registered: invalid, even if we only test well-formedness'
429 array( 'abcdefghi-012345678', 'numbers too long' ),
430 array( 'ab-abc-abc-abc-abc', 'invalid extensions' ),
431 array( 'ab-abcd-abc', 'invalid extensions' ),
432 array( 'ab-ab-abc', 'invalid extensions' ),
433 array( 'ab-123-abc', 'invalid extensions' ),
434 array( 'a-Hant-ZH', 'short language with valid extensions' ),
435 array( 'a1-Hant-ZH', 'invalid character in language' ),
436 array( 'ab-abcde-abc', 'invalid extensions' ),
437 array( 'ab-1abc-abc', 'invalid characters in extensions' ),
438 array( 'ab-ab-abcd', 'invalid order of extensions' ),
439 array( 'ab-123-abcd', 'invalid order of extensions' ),
440 array( 'ab-abcde-abcd', 'invalid extensions' ),
441 array( 'ab-1abc-abcd', 'invalid characters in extensions' ),
442 array( 'ab-a-b', 'extensions too short' ),
443 array( 'ab-a-x', 'extensions too short, even with singleton' ),
444 array( 'ab--ab', 'two separators' ),
445 array( 'ab-abc-', 'separator in the end' ),
446 array( '-ab-abc', 'separator in the beginning' ),
447 array( 'abcd-efg', 'language too long' ),
448 array( 'aabbccddE', 'tag too long' ),
449 array( 'pa_guru', 'A tag with underscore is invalid in strict mode' ),
450 array( 'de-f', 'subtag too short' ),
455 * Negative test for Language::isWellFormedLanguageTag()
456 * @covers Language::isWellFormedLanguageTag
458 public function testLenientLanguageTag() {
460 Language
::isWellFormedLanguageTag( 'pa_guru', true ),
461 'pa_guru is a well-formed language tag in lenient mode'
466 * Test Language::isValidBuiltInCode()
467 * @dataProvider provideLanguageCodes
468 * @covers Language::isValidBuiltInCode
470 public function testBuiltInCodeValidation( $code, $expected, $message = '' ) {
471 $this->assertEquals( $expected,
472 (bool)Language
::isValidBuiltInCode( $code ),
473 "validating code $code $message"
477 public static function provideLanguageCodes() {
479 array( 'fr', true, 'Two letters, minor case' ),
480 array( 'EN', false, 'Two letters, upper case' ),
481 array( 'tyv', true, 'Three letters' ),
482 array( 'tokipona', true, 'long language code' ),
483 array( 'be-tarask', true, 'With dash' ),
484 array( 'be-x-old', true, 'With extension (two dashes)' ),
485 array( 'be_tarask', false, 'Reject underscores' ),
490 * Test Language::isKnownLanguageTag()
491 * @dataProvider provideKnownLanguageTags
492 * @covers Language::isKnownLanguageTag
494 public function testKnownLanguageTag( $code, $message = '' ) {
496 (bool)Language
::isKnownLanguageTag( $code ),
497 "validating code $code - $message"
501 public static function provideKnownLanguageTags() {
503 array( 'fr', 'simple code' ),
504 array( 'bat-smg', 'an MW legacy tag' ),
505 array( 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ),
510 * @covers Language::isKnownLanguageTag
512 public function testKnownCldrLanguageTag() {
513 if ( !class_exists( 'LanguageNames' ) ) {
514 $this->markTestSkipped( 'The LanguageNames class is not available. '
515 . 'The CLDR extension is probably not installed.' );
519 (bool)Language
::isKnownLanguageTag( 'pal' ),
520 'validating code "pal" an ancient language, which probably will '
521 . 'not appear in Names.php, but appears in CLDR in English'
526 * Negative tests for Language::isKnownLanguageTag()
527 * @dataProvider provideUnKnownLanguageTags
528 * @covers Language::isKnownLanguageTag
530 public function testUnknownLanguageTag( $code, $message = '' ) {
532 (bool)Language
::isKnownLanguageTag( $code ),
533 "checking that code $code is invalid - $message"
537 public static function provideUnknownLanguageTags() {
539 array( 'mw', 'non-existent two-letter code' ),
540 array( 'foo"<bar', 'very invalid language code' ),
545 * Test too short timestamp
546 * @expectedException MWException
547 * @covers Language::sprintfDate
549 public function testSprintfDateTooShortTimestamp() {
550 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
554 * Test too long timestamp
555 * @expectedException MWException
556 * @covers Language::sprintfDate
558 public function testSprintfDateTooLongTimestamp() {
559 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
563 * Test too short timestamp
564 * @expectedException MWException
565 * @covers Language::sprintfDate
567 public function testSprintfDateNotAllDigitTimestamp() {
568 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
572 * @dataProvider provideSprintfDateSamples
573 * @covers Language::sprintfDate
575 public function testSprintfDate( $format, $ts, $expected, $msg ) {
579 $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
580 "sprintfDate('$format', '$ts'): $msg"
583 $dt = new DateTime( $ts );
584 $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
587 $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
588 "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
591 // advance the time enough to make all of the possible outputs different (except possibly L)
592 $dt = new DateTime( $ts );
593 $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
596 $this->getLang()->sprintfDate( $format, $newTS, null ),
597 "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
603 * sprintfDate should always use UTC when no zone is given.
604 * @dataProvider provideSprintfDateSamples
605 * @covers Language::sprintfDate
607 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
608 $oldTZ = date_default_timezone_get();
609 $res = date_default_timezone_set( 'Asia/Seoul' );
611 $this->markTestSkipped( "Error setting Timezone" );
616 $this->getLang()->sprintfDate( $format, $ts ),
617 "sprintfDate('$format', '$ts'): $msg"
620 date_default_timezone_set( $oldTZ );
624 * sprintfDate should use passed timezone
625 * @dataProvider provideSprintfDateSamples
626 * @covers Language::sprintfDate
628 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
629 $tz = new DateTimeZone( 'Asia/Seoul' );
631 $this->markTestSkipped( "Error getting Timezone" );
636 $this->getLang()->sprintfDate( $format, $ts, $tz ),
637 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
641 public static function provideSprintfDateSamples() {
646 '1390', // note because we're testing English locale we get Latin-standard digits
648 'Iranian calendar full year'
655 'Iranian calendar short year'
662 'ISO 8601 (week) year'
685 // What follows is mostly copied from
686 // https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
713 'Month index, not zero pad'
720 'Month index. Zero pad'
741 'Genitive month name (same in EN)'
748 'Day of month (not zero pad)'
755 'Day of month (zero-pad)'
762 'Day of year (zero-indexed)'
769 'Day of week (abbrev)'
783 'Day of week (Mon=1, Sun=7)'
790 'Day of week (Sun=0, Sat=6)'
832 '12 hour, zero padded'
881 'Days in current month'
886 '2012-01-02T09:07:05+00:00',
887 '2012-01-02T09:07:05+09:00',
893 'Mon, 02 Jan 2012 09:07:05 +0000',
894 'Mon, 02 Jan 2012 09:07:05 +0900',
902 'Timezone identifier'
923 'Timezone offset with colon'
930 'Timezone abbreviation'
937 'Timezone offset in seconds'
965 'Hebrew number of days in month'
972 'Hebrew genitive month name (No difference in EN)'
1014 'Raw numerals (doesn\'t mean much in EN)'
1017 '[[Y "(yea"\\r)]] \\"xx\\"',
1019 '[[2012 (year)]] "x"',
1020 '[[2012 (year)]] "x"',
1028 * @dataProvider provideFormatSizes
1029 * @covers Language::formatSize
1031 public function testFormatSize( $size, $expected, $msg ) {
1032 $this->assertEquals(
1034 $this->getLang()->formatSize( $size ),
1035 "formatSize('$size'): $msg"
1039 public static function provideFormatSizes() {
1086 // How big!? THIS BIG!
1091 * @dataProvider provideFormatBitrate
1092 * @covers Language::formatBitrate
1094 public function testFormatBitrate( $bps, $expected, $msg ) {
1095 $this->assertEquals(
1097 $this->getLang()->formatBitrate( $bps ),
1098 "formatBitrate('$bps'): $msg"
1102 public static function provideFormatBitrate() {
1112 "999 bits per second"
1117 "1 kilobit per second"
1122 "1 megabit per second"
1127 "1 gigabit per second"
1132 "1 terabit per second"
1137 "1 petabit per second"
1142 "1 exabit per second"
1147 "1 zetabit per second"
1152 "1 yottabit per second"
1157 "1,000 yottabits per second"
1163 * @dataProvider provideFormatDuration
1164 * @covers Language::formatDuration
1166 public function testFormatDuration( $duration, $expected, $intervals = array() ) {
1167 $this->assertEquals(
1169 $this->getLang()->formatDuration( $duration, $intervals ),
1170 "formatDuration('$duration'): $expected"
1174 public static function provideFormatDuration() {
1213 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1214 ( 365 +
( 24 * 3 +
25 ) / 400.0 ) * 86400,
1247 '2 hours, 30 minutes and 1 second'
1251 '1 hour and 1 second'
1254 31556952 +
2 * 86400 +
9000,
1255 '1 year, 2 days, 2 hours and 30 minutes'
1258 42 * 1000 * 31556952 +
42,
1259 '42 millennia and 42 seconds'
1277 31556952 +
2 * 86400 +
9000,
1278 '1 year, 2 days and 150 minutes',
1279 array( 'years', 'days', 'minutes' ),
1284 array( 'years', 'days' ),
1287 31556952 +
2 * 86400 +
9000,
1288 '1 year, 2 days and 150 minutes',
1289 array( 'minutes', 'days', 'years' ),
1294 array( 'days', 'years' ),
1300 * @dataProvider provideCheckTitleEncodingData
1301 * @covers Language::checkTitleEncoding
1303 public function testCheckTitleEncoding( $s ) {
1304 $this->assertEquals(
1306 $this->getLang()->checkTitleEncoding( $s ),
1307 "checkTitleEncoding('$s')"
1311 public static function provideCheckTitleEncodingData() {
1312 // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
1315 array( "United States of America" ), // 7bit ASCII
1316 array( rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ),
1319 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1322 // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
1323 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1324 // uses mb_check_encoding for its test.
1327 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1328 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1329 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1330 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1331 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1332 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1333 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1334 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1335 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1336 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1337 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1338 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1339 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1340 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1345 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1346 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1347 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1348 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1349 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1350 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1351 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1352 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1353 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1354 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1355 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1356 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1357 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1358 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1359 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1363 // @codingStandardsIgnoreEnd
1367 * @dataProvider provideRomanNumeralsData
1368 * @covers Language::romanNumeral
1370 public function testRomanNumerals( $num, $numerals ) {
1371 $this->assertEquals(
1373 Language
::romanNumeral( $num ),
1374 "romanNumeral('$num')"
1378 public static function provideRomanNumeralsData() {
1393 array( 49, 'XLIX' ),
1397 array( 80, 'LXXX' ),
1399 array( 99, 'XCIX' ),
1402 array( 300, 'CCC' ),
1406 array( 700, 'DCC' ),
1407 array( 800, 'DCCC' ),
1409 array( 999, 'CMXCIX' ),
1411 array( 1989, 'MCMLXXXIX' ),
1412 array( 2000, 'MM' ),
1413 array( 3000, 'MMM' ),
1414 array( 4000, 'MMMM' ),
1415 array( 5000, 'MMMMM' ),
1416 array( 6000, 'MMMMMM' ),
1417 array( 7000, 'MMMMMMM' ),
1418 array( 8000, 'MMMMMMMM' ),
1419 array( 9000, 'MMMMMMMMM' ),
1420 array( 9999, 'MMMMMMMMMCMXCIX' ),
1421 array( 10000, 'MMMMMMMMMM' ),
1426 * @dataProvider provideHebrewNumeralsData
1427 * @covers Language::hebrewNumeral
1429 public function testHebrewNumeral( $num, $numerals ) {
1430 $this->assertEquals(
1432 Language
::hebrewNumeral( $num ),
1433 "hebrewNumeral('$num')"
1437 public static function provideHebrewNumeralsData() {
1467 array( 101, 'ק"א' ),
1468 array( 110, 'ק"י' ),
1472 array( 500, 'ת"ק' ),
1473 array( 800, 'ת"ת' ),
1474 array( 1000, "א' אלף" ),
1475 array( 1001, "א'א'" ),
1476 array( 1012, "א'י\"ב" ),
1477 array( 1020, "א'ך'" ),
1478 array( 1030, "א'ל'" ),
1479 array( 1081, "א'פ\"א" ),
1480 array( 2000, "ב' אלפים" ),
1481 array( 2016, "ב'ט\"ז" ),
1482 array( 3000, "ג' אלפים" ),
1483 array( 4000, "ד' אלפים" ),
1484 array( 4904, "ד'תתק\"ד" ),
1485 array( 5000, "ה' אלפים" ),
1486 array( 5680, "ה'תר\"ף" ),
1487 array( 5690, "ה'תר\"ץ" ),
1488 array( 5708, "ה'תש\"ח" ),
1489 array( 5720, "ה'תש\"ך" ),
1490 array( 5740, "ה'תש\"ם" ),
1491 array( 5750, "ה'תש\"ן" ),
1492 array( 5775, "ה'תשע\"ה" ),
1497 * @dataProvider providePluralData
1498 * @covers Language::convertPlural
1500 public function testConvertPlural( $expected, $number, $forms ) {
1501 $chosen = $this->getLang()->convertPlural( $number, $forms );
1502 $this->assertEquals( $expected, $chosen );
1505 public static function providePluralData() {
1506 // Params are: [expected text, number given, [the plural forms]]
1508 array( 'plural', 0, array(
1509 'singular', 'plural'
1511 array( 'explicit zero', 0, array(
1512 '0=explicit zero', 'singular', 'plural'
1514 array( 'explicit one', 1, array(
1515 'singular', 'plural', '1=explicit one',
1517 array( 'singular', 1, array(
1518 'singular', 'plural', '0=explicit zero',
1520 array( 'plural', 3, array(
1521 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1523 array( 'explicit eleven', 11, array(
1524 'singular', 'plural', '11=explicit eleven',
1526 array( 'plural', 12, array(
1527 'singular', 'plural', '11=explicit twelve',
1529 array( 'plural', 12, array(
1530 'singular', 'plural', '=explicit form',
1532 array( 'other', 2, array(
1533 'kissa=kala', '1=2=3', 'other',
1535 array( '', 2, array(
1536 '0=explicit zero', '1=explicit one',
1542 * @covers Language::embedBidi()
1544 public function testEmbedBidi() {
1545 $lre = "\xE2\x80\xAA"; // U+202A LEFT-TO-RIGHT EMBEDDING
1546 $rle = "\xE2\x80\xAB"; // U+202B RIGHT-TO-LEFT EMBEDDING
1547 $pdf = "\xE2\x80\xAC"; // U+202C POP DIRECTIONAL FORMATTING
1548 $lang = $this->getLang();
1549 $this->assertEquals(
1551 $lang->embedBidi( '123' ),
1552 'embedBidi with neutral argument'
1554 $this->assertEquals(
1555 $lre . 'Ben_(WMF)' . $pdf,
1556 $lang->embedBidi( 'Ben_(WMF)' ),
1557 'embedBidi with LTR argument'
1559 $this->assertEquals(
1560 $rle . 'יהודי (מנוחין)' . $pdf,
1561 $lang->embedBidi( 'יהודי (מנוחין)' ),
1562 'embedBidi with RTL argument'
1567 * @covers Language::translateBlockExpiry()
1568 * @dataProvider provideTranslateBlockExpiry
1570 public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
1571 $lang = $this->getLang();
1572 if ( is_array( $expectedData ) ) {
1573 list( $func, $arg ) = $expectedData;
1574 $expected = $lang->$func( $arg );
1576 $expected = $expectedData;
1578 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1581 public static function provideTranslateBlockExpiry() {
1583 array( '2 hours', '2 hours', 'simple data from ipboptions' ),
1584 array( 'indefinite', 'infinite', 'infinite from ipboptions' ),
1585 array( 'indefinite', 'infinity', 'alternative infinite from ipboptions' ),
1586 array( 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ),
1587 array( array( 'formatDuration', 1023 * 60 * 60 ), '1023 hours', 'relative' ),
1588 array( array( 'formatDuration', -1023 ), '-1023 seconds', 'negative relative' ),
1589 array( array( 'formatDuration', 0 ), 'now', 'now' ),
1591 array( 'timeanddate', '20120102070000' ),
1592 '2012-1-1 7:00 +1 day',
1593 'mixed, handled as absolute'
1595 array( array( 'timeanddate', '19910203040506' ), '1991-2-3 4:05:06', 'absolute' ),
1596 array( array( 'timeanddate', '19700101000000' ), '1970-1-1 0:00:00', 'absolute at epoch' ),
1597 array( array( 'timeanddate', '19691231235959' ), '1969-12-31 23:59:59', 'time before epoch' ),
1598 array( 'dummy', 'dummy', 'return garbage as is' ),
1603 * @dataProvider parseFormattedNumberProvider
1605 public function testParseFormattedNumber( $langCode, $number ) {
1606 $lang = Language
::factory( $langCode );
1608 $localisedNum = $lang->formatNum( $number );
1609 $normalisedNum = $lang->parseFormattedNumber( $localisedNum );
1611 $this->assertEquals( $number, $normalisedNum );
1614 public function parseFormattedNumberProvider() {
1616 array( 'de', 377.01 ),
1618 array( 'fa', 382.772 ),
1619 array( 'ar', 1844 ),
1620 array( 'lzh', 3731 ),
1621 array( 'zh-classical', 7432 )
1626 * @covers Language::commafy()
1627 * @dataProvider provideCommafyData
1629 public function testCommafy( $number, $numbersWithCommas ) {
1630 $this->assertEquals(
1632 $this->getLang()->commafy( $number ),
1633 "commafy('$number')"
1637 public static function provideCommafyData() {
1641 array( 100, '100' ),
1642 array( 1000, '1,000' ),
1643 array( 10000, '10,000' ),
1644 array( 100000, '100,000' ),
1645 array( 1000000, '1,000,000' ),
1646 array( -1.0001, '-1.0001' ),
1647 array( 1.0001, '1.0001' ),
1648 array( 10.0001, '10.0001' ),
1649 array( 100.0001, '100.0001' ),
1650 array( 1000.0001, '1,000.0001' ),
1651 array( 10000.0001, '10,000.0001' ),
1652 array( 100000.0001, '100,000.0001' ),
1653 array( 1000000.0001, '1,000,000.0001' ),
1654 array( '200000000000000000000', '200,000,000,000,000,000,000' ),
1655 array( '-200000000000000000000', '-200,000,000,000,000,000,000' ),
1660 * @covers Language::listToText
1662 public function testListToText() {
1663 $lang = $this->getLang();
1664 $and = $lang->getMessageFromDB( 'and' );
1665 $s = $lang->getMessageFromDB( 'word-separator' );
1666 $c = $lang->getMessageFromDB( 'comma-separator' );
1668 $this->assertEquals( '', $lang->listToText( array() ) );
1669 $this->assertEquals( 'a', $lang->listToText( array( 'a' ) ) );
1670 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( array( 'a', 'b' ) ) );
1671 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( array( 'a', 'b', 'c' ) ) );
1672 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( array( 'a', 'b', 'c', 'd' ) ) );
1676 * @dataProvider provideIsSupportedLanguage
1677 * @covers Language::isSupportedLanguage
1679 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1680 $this->assertEquals( $expected, Language
::isSupportedLanguage( $code ), $comment );
1683 public static function provideIsSupportedLanguage() {
1685 array( 'en', true, 'is supported language' ),
1686 array( 'fi', true, 'is supported language' ),
1687 array( 'bunny', false, 'is not supported language' ),
1688 array( 'FI', false, 'is not supported language, input should be in lower case' ),
1693 * @dataProvider provideGetParentLanguage
1694 * @covers Language::getParentLanguage
1696 public function testGetParentLanguage( $code, $expected, $comment ) {
1697 $lang = Language
::factory( $code );
1698 if ( is_null( $expected ) ) {
1699 $this->assertNull( $lang->getParentLanguage(), $comment );
1701 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1705 public static function provideGetParentLanguage() {
1707 array( 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ),
1708 array( 'zh', 'zh', 'zh is defined as the parent language of zh, '
1709 . 'because zh converter can convert zh-cn to zh' ),
1710 array( 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ),
1711 array( 'en-gb', null, 'en does not have converter' ),
1712 array( 'en', null, 'en does not have converter. Although FakeConverter '
1713 . 'handles en -> en conversion but it is useless' ),
1718 * @dataProvider provideGetNamespaceAliases
1719 * @covers Language::getNamespaceAliases
1721 public function testGetNamespaceAliases( $languageCode, $subset ) {
1722 $language = Language
::factory( $languageCode );
1723 $aliases = $language->getNamespaceAliases();
1724 foreach ( $subset as $alias => $nsId ) {
1725 $this->assertEquals( $nsId, $aliases[$alias] );
1729 public static function provideGetNamespaceAliases() {
1730 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces