MediaWiki REL1_31
LanguageTest.php
Go to the documentation of this file.
1<?php
2
9 $this->assertEquals(
10 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
11 $this->getLang()->normalizeForSearch(
12 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
13 ),
14 'convertDoubleWidth() with the full alphabet and digits'
15 );
16 }
17
22 public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
23 $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
24 }
25
26 public static function provideFormattableTimes() {
27 return [
28 [
29 9.45,
30 [],
31 '9.5 s',
32 'formatTimePeriod() rounding (<10s)'
33 ],
34 [
35 9.45,
36 [ 'noabbrevs' => true ],
37 '9.5 seconds',
38 'formatTimePeriod() rounding (<10s)'
39 ],
40 [
41 9.95,
42 [],
43 '10 s',
44 'formatTimePeriod() rounding (<10s)'
45 ],
46 [
47 9.95,
48 [ 'noabbrevs' => true ],
49 '10 seconds',
50 'formatTimePeriod() rounding (<10s)'
51 ],
52 [
53 59.55,
54 [],
55 '1 min 0 s',
56 'formatTimePeriod() rounding (<60s)'
57 ],
58 [
59 59.55,
60 [ 'noabbrevs' => true ],
61 '1 minute 0 seconds',
62 'formatTimePeriod() rounding (<60s)'
63 ],
64 [
65 119.55,
66 [],
67 '2 min 0 s',
68 'formatTimePeriod() rounding (<1h)'
69 ],
70 [
71 119.55,
72 [ 'noabbrevs' => true ],
73 '2 minutes 0 seconds',
74 'formatTimePeriod() rounding (<1h)'
75 ],
76 [
77 3599.55,
78 [],
79 '1 h 0 min 0 s',
80 'formatTimePeriod() rounding (<1h)'
81 ],
82 [
83 3599.55,
84 [ 'noabbrevs' => true ],
85 '1 hour 0 minutes 0 seconds',
86 'formatTimePeriod() rounding (<1h)'
87 ],
88 [
89 7199.55,
90 [],
91 '2 h 0 min 0 s',
92 'formatTimePeriod() rounding (>=1h)'
93 ],
94 [
95 7199.55,
96 [ 'noabbrevs' => true ],
97 '2 hours 0 minutes 0 seconds',
98 'formatTimePeriod() rounding (>=1h)'
99 ],
100 [
101 7199.55,
102 'avoidseconds',
103 '2 h 0 min',
104 'formatTimePeriod() rounding (>=1h), avoidseconds'
105 ],
106 [
107 7199.55,
108 [ 'avoid' => 'avoidseconds', 'noabbrevs' => true ],
109 '2 hours 0 minutes',
110 'formatTimePeriod() rounding (>=1h), avoidseconds'
111 ],
112 [
113 7199.55,
114 'avoidminutes',
115 '2 h 0 min',
116 'formatTimePeriod() rounding (>=1h), avoidminutes'
117 ],
118 [
119 7199.55,
120 [ 'avoid' => 'avoidminutes', 'noabbrevs' => true ],
121 '2 hours 0 minutes',
122 'formatTimePeriod() rounding (>=1h), avoidminutes'
123 ],
124 [
125 172799.55,
126 'avoidseconds',
127 '48 h 0 min',
128 'formatTimePeriod() rounding (=48h), avoidseconds'
129 ],
130 [
131 172799.55,
132 [ 'avoid' => 'avoidseconds', 'noabbrevs' => true ],
133 '48 hours 0 minutes',
134 'formatTimePeriod() rounding (=48h), avoidseconds'
135 ],
136 [
137 259199.55,
138 'avoidminutes',
139 '3 d 0 h',
140 'formatTimePeriod() rounding (>48h), avoidminutes'
141 ],
142 [
143 259199.55,
144 [ 'avoid' => 'avoidminutes', 'noabbrevs' => true ],
145 '3 days 0 hours',
146 'formatTimePeriod() rounding (>48h), avoidminutes'
147 ],
148 [
149 176399.55,
150 'avoidseconds',
151 '2 d 1 h 0 min',
152 'formatTimePeriod() rounding (>48h), avoidseconds'
153 ],
154 [
155 176399.55,
156 [ 'avoid' => 'avoidseconds', 'noabbrevs' => true ],
157 '2 days 1 hour 0 minutes',
158 'formatTimePeriod() rounding (>48h), avoidseconds'
159 ],
160 [
161 176399.55,
162 'avoidminutes',
163 '2 d 1 h',
164 'formatTimePeriod() rounding (>48h), avoidminutes'
165 ],
166 [
167 176399.55,
168 [ 'avoid' => 'avoidminutes', 'noabbrevs' => true ],
169 '2 days 1 hour',
170 'formatTimePeriod() rounding (>48h), avoidminutes'
171 ],
172 [
173 259199.55,
174 'avoidseconds',
175 '3 d 0 h 0 min',
176 'formatTimePeriod() rounding (>48h), avoidseconds'
177 ],
178 [
179 259199.55,
180 [ 'avoid' => 'avoidseconds', 'noabbrevs' => true ],
181 '3 days 0 hours 0 minutes',
182 'formatTimePeriod() rounding (>48h), avoidseconds'
183 ],
184 [
185 172801.55,
186 'avoidseconds',
187 '2 d 0 h 0 min',
188 'formatTimePeriod() rounding, (>48h), avoidseconds'
189 ],
190 [
191 172801.55,
192 [ 'avoid' => 'avoidseconds', 'noabbrevs' => true ],
193 '2 days 0 hours 0 minutes',
194 'formatTimePeriod() rounding, (>48h), avoidseconds'
195 ],
196 [
197 176460.55,
198 [],
199 '2 d 1 h 1 min 1 s',
200 'formatTimePeriod() rounding, recursion, (>48h)'
201 ],
202 [
203 176460.55,
204 [ 'noabbrevs' => true ],
205 '2 days 1 hour 1 minute 1 second',
206 'formatTimePeriod() rounding, recursion, (>48h)'
207 ],
208 ];
209 }
210
215 public function testTruncateForDatabase() {
216 $this->assertEquals(
217 "XXX",
218 $this->getLang()->truncateForDatabase( "1234567890", 0, 'XXX' ),
219 'truncate prefix, len 0, small ellipsis'
220 );
221
222 $this->assertEquals(
223 "12345XXX",
224 $this->getLang()->truncateForDatabase( "1234567890", 8, 'XXX' ),
225 'truncate prefix, small ellipsis'
226 );
227
228 $this->assertEquals(
229 "123456789",
230 $this->getLang()->truncateForDatabase( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
231 'truncate prefix, large ellipsis'
232 );
233
234 $this->assertEquals(
235 "XXX67890",
236 $this->getLang()->truncateForDatabase( "1234567890", -8, 'XXX' ),
237 'truncate suffix, small ellipsis'
238 );
239
240 $this->assertEquals(
241 "123456789",
242 $this->getLang()->truncateForDatabase( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
243 'truncate suffix, large ellipsis'
244 );
245 $this->assertEquals(
246 "123XXX",
247 $this->getLang()->truncateForDatabase( "123 ", 9, 'XXX' ),
248 'truncate prefix, with spaces'
249 );
250 $this->assertEquals(
251 "12345XXX",
252 $this->getLang()->truncateForDatabase( "12345 8", 11, 'XXX' ),
253 'truncate prefix, with spaces and non-space ending'
254 );
255 $this->assertEquals(
256 "XXX234",
257 $this->getLang()->truncateForDatabase( "1 234", -8, 'XXX' ),
258 'truncate suffix, with spaces'
259 );
260 $this->assertEquals(
261 "12345XXX",
262 $this->getLang()->truncateForDatabase( "1234567890", 5, 'XXX', false ),
263 'truncate without adjustment'
264 );
265 $this->assertEquals(
266 "泰乐菌...",
267 $this->getLang()->truncateForDatabase( "泰乐菌素123456789", 11, '...', false ),
268 'truncate does not chop Unicode characters in half'
269 );
270 $this->assertEquals(
271 "\n泰乐菌...",
272 $this->getLang()->truncateForDatabase( "\n泰乐菌素123456789", 12, '...', false ),
273 'truncate does not chop Unicode characters in half if there is a preceding newline'
274 );
275 }
276
282 public function testTruncateForVisual(
283 $expected, $string, $length, $ellipsis = '...', $adjustLength = true
284 ) {
285 $this->assertEquals(
286 $expected,
287 $this->getLang()->truncateForVisual( $string, $length, $ellipsis, $adjustLength )
288 );
289 }
290
294 public static function provideTruncateData() {
295 return [
296 [ "XXX", "тестирам да ли ради", 0, "XXX" ],
297 [ "testnXXX", "testni scenarij", 8, "XXX" ],
298 [ "حالة اختبار", "حالة اختبار", 5, "XXXXXXXXXXXXXXX" ],
299 [ "XXXедент", "прецедент", -8, "XXX" ],
300 [ "XXപിൾ", "ആപ്പിൾ", -5, "XX" ],
301 [ "神秘XXX", "神秘 ", 9, "XXX" ],
302 [ "ΔημιουργXXX", "Δημιουργία Σύμπαντος", 11, "XXX" ],
303 [ "XXXの家です", "地球は私たちの唯 の家です", -8, "XXX" ],
304 [ "زندگیXXX", "زندگی زیباست", 6, "XXX", false ],
305 [ "ცხოვრება...", "ცხოვრება არის საოცარი", 8, "...", false ],
306 [ "\nທ່ານ...", "\nທ່ານບໍ່ຮູ້ຫນັງສື", 5, "...", false ],
307 ];
308 }
309
314 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
315 // Actual HTML...
316 $this->assertEquals(
317 $expected,
318 $this->getLang()->truncateHtml( $input, $len, $ellipsis )
319 );
320 }
321
325 public static function provideHTMLTruncateData() {
326 return [
327 [ 0, 'XXX', "1234567890", "XXX" ],
328 [ 8, 'XXX', "1234567890", "12345XXX" ],
329 [ 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ],
330 [ 2, '***',
331 '<p><span style="font-weight:bold;"></span></p>',
332 '<p><span style="font-weight:bold;"></span></p>',
333 ],
334 [ 2, '***',
335 '<p><span style="font-weight:bold;">123456789</span></p>',
336 '<p><span style="font-weight:bold;">***</span></p>',
337 ],
338 [ 2, '***',
339 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
340 '<p><span style="font-weight:bold;">***</span></p>',
341 ],
342 [ 3, '***',
343 '<p><span style="font-weight:bold;">123456789</span></p>',
344 '<p><span style="font-weight:bold;">***</span></p>',
345 ],
346 [ 4, '***',
347 '<p><span style="font-weight:bold;">123456789</span></p>',
348 '<p><span style="font-weight:bold;">1***</span></p>',
349 ],
350 [ 5, '***',
351 '<tt><span style="font-weight:bold;">123456789</span></tt>',
352 '<tt><span style="font-weight:bold;">12***</span></tt>',
353 ],
354 [ 6, '***',
355 '<p><a href="www.mediawiki.org">123456789</a></p>',
356 '<p><a href="www.mediawiki.org">123***</a></p>',
357 ],
358 [ 6, '***',
359 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
360 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
361 ],
362 [ 7, '***',
363 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
364 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
365 ],
366 [ 8, '***',
367 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
368 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
369 ],
370 [ 9, '***',
371 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
372 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
373 ],
374 [ 10, '***',
375 '<p><font style="font-weight:bold;">123456789</font></p>',
376 '<p><font style="font-weight:bold;">123456789</font></p>',
377 ],
378 ];
379 }
380
386 public function testWellFormedLanguageTag( $code, $message = '' ) {
387 $this->assertTrue(
388 Language::isWellFormedLanguageTag( $code ),
389 "validating code $code $message"
390 );
391 }
392
399 public static function provideWellFormedLanguageTags() {
400 return [
401 [ 'fr', 'two-letter code' ],
402 [ 'fr-latn', 'two-letter code with lower case script code' ],
403 [ 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ],
404 [ 'fr-Latn-419', 'two-letter code with title case script code and region number' ],
405 [ 'fr-FR', 'two-letter code with uppercase' ],
406 [ 'ax-TZ', 'Not in the registry, but well-formed' ],
407 [ 'fr-shadok', 'two-letter code with variant' ],
408 [ 'fr-y-myext-myext2', 'non-x singleton' ],
409 [ 'fra-Latn', 'ISO 639 can be 3-letters' ],
410 [ 'fra', 'three-letter language code' ],
411 [ 'fra-FX', 'three-letter language code with country code' ],
412 [ 'i-klingon', 'grandfathered with singleton' ],
413 [ 'I-kLINgon', 'tags are case-insensitive...' ],
414 [ 'no-bok', 'grandfathered without singleton' ],
415 [ 'i-enochian', 'Grandfathered' ],
416 [ 'x-fr-CH', 'private use' ],
417 [ 'es-419', 'two-letter code with region number' ],
418 [ 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ],
419 [ 'ab-x-abc-x-abc', 'anything goes after x' ],
420 [ 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ],
421 [ 'i-default', 'grandfathered' ],
422 [ 'abcd-Latn', 'Language of 4 chars reserved for future use' ],
423 [ 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ],
424 [ 'de-CH-1901', 'with country and year' ],
425 [ 'en-US-x-twain', 'with country and singleton' ],
426 [ 'zh-cmn', 'three-letter variant' ],
427 [ 'zh-cmn-Hant', 'three-letter variant and script' ],
428 [ 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ],
429 [ 'xr-p-lze', 'Extension' ],
430 ];
431 }
432
438 public function testMalformedLanguageTag( $code, $message = '' ) {
439 $this->assertFalse(
440 Language::isWellFormedLanguageTag( $code ),
441 "validating that code $code is a malformed language tag - $message"
442 );
443 }
444
451 public static function provideMalformedLanguageTags() {
452 return [
453 [ 'f', 'language too short' ],
454 [ 'f-Latn', 'language too short with script' ],
455 [ 'xr-lxs-qut', 'variants too short' ], # extlangS
456 [ 'fr-Latn-F', 'region too short' ],
457 [ 'a-value', 'language too short with region' ],
458 [ 'tlh-a-b-foo', 'valid three-letter with wrong variant' ],
459 [
460 'i-notexist',
461 'grandfathered but not registered: invalid, even if we only test well-formedness'
462 ],
463 [ 'abcdefghi-012345678', 'numbers too long' ],
464 [ 'ab-abc-abc-abc-abc', 'invalid extensions' ],
465 [ 'ab-abcd-abc', 'invalid extensions' ],
466 [ 'ab-ab-abc', 'invalid extensions' ],
467 [ 'ab-123-abc', 'invalid extensions' ],
468 [ 'a-Hant-ZH', 'short language with valid extensions' ],
469 [ 'a1-Hant-ZH', 'invalid character in language' ],
470 [ 'ab-abcde-abc', 'invalid extensions' ],
471 [ 'ab-1abc-abc', 'invalid characters in extensions' ],
472 [ 'ab-ab-abcd', 'invalid order of extensions' ],
473 [ 'ab-123-abcd', 'invalid order of extensions' ],
474 [ 'ab-abcde-abcd', 'invalid extensions' ],
475 [ 'ab-1abc-abcd', 'invalid characters in extensions' ],
476 [ 'ab-a-b', 'extensions too short' ],
477 [ 'ab-a-x', 'extensions too short, even with singleton' ],
478 [ 'ab--ab', 'two separators' ],
479 [ 'ab-abc-', 'separator in the end' ],
480 [ '-ab-abc', 'separator in the beginning' ],
481 [ 'abcd-efg', 'language too long' ],
482 [ 'aabbccddE', 'tag too long' ],
483 [ 'pa_guru', 'A tag with underscore is invalid in strict mode' ],
484 [ 'de-f', 'subtag too short' ],
485 ];
486 }
487
492 public function testLenientLanguageTag() {
493 $this->assertTrue(
494 Language::isWellFormedLanguageTag( 'pa_guru', true ),
495 'pa_guru is a well-formed language tag in lenient mode'
496 );
497 }
498
504 public function testBuiltInCodeValidation( $code, $expected, $message = '' ) {
505 $this->assertEquals( $expected,
506 (bool)Language::isValidBuiltInCode( $code ),
507 "validating code $code $message"
508 );
509 }
510
511 public static function provideLanguageCodes() {
512 return [
513 [ 'fr', true, 'Two letters, minor case' ],
514 [ 'EN', false, 'Two letters, upper case' ],
515 [ 'tyv', true, 'Three letters' ],
516 [ 'be-tarask', true, 'With dash' ],
517 [ 'be-x-old', true, 'With extension (two dashes)' ],
518 [ 'be_tarask', false, 'Reject underscores' ],
519 ];
520 }
521
527 public function testKnownLanguageTag( $code, $message = '' ) {
528 $this->assertTrue(
529 (bool)Language::isKnownLanguageTag( $code ),
530 "validating code $code - $message"
531 );
532 }
533
534 public static function provideKnownLanguageTags() {
535 return [
536 [ 'fr', 'simple code' ],
537 [ 'bat-smg', 'an MW legacy tag' ],
538 [ 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ],
539 ];
540 }
541
545 public function testKnownCldrLanguageTag() {
546 if ( !class_exists( 'LanguageNames' ) ) {
547 $this->markTestSkipped( 'The LanguageNames class is not available. '
548 . 'The CLDR extension is probably not installed.' );
549 }
550
551 $this->assertTrue(
552 (bool)Language::isKnownLanguageTag( 'pal' ),
553 'validating code "pal" an ancient language, which probably will '
554 . 'not appear in Names.php, but appears in CLDR in English'
555 );
556 }
557
563 public function testUnknownLanguageTag( $code, $message = '' ) {
564 $this->assertFalse(
565 (bool)Language::isKnownLanguageTag( $code ),
566 "checking that code $code is invalid - $message"
567 );
568 }
569
570 public static function provideUnknownLanguageTags() {
571 return [
572 [ 'mw', 'non-existent two-letter code' ],
573 [ 'foo"<bar', 'very invalid language code' ],
574 ];
575 }
576
583 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
584 }
585
592 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
593 }
594
601 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
602 }
603
608 public function testSprintfDate( $format, $ts, $expected, $msg ) {
609 $ttl = null;
610 $this->assertEquals(
611 $expected,
612 $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
613 "sprintfDate('$format', '$ts'): $msg"
614 );
615 if ( $ttl ) {
616 $dt = new DateTime( $ts );
617 $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
618 $this->assertEquals(
619 $expected,
620 $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
621 "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
622 );
623 } else {
624 // advance the time enough to make all of the possible outputs different (except possibly L)
625 $dt = new DateTime( $ts );
626 $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
627 $this->assertEquals(
628 $expected,
629 $this->getLang()->sprintfDate( $format, $newTS, null ),
630 "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
631 );
632 }
633 }
634
640 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
641 $oldTZ = date_default_timezone_get();
642 $res = date_default_timezone_set( 'Asia/Seoul' );
643 if ( !$res ) {
644 $this->markTestSkipped( "Error setting Timezone" );
645 }
646
647 $this->assertEquals(
648 $expected,
649 $this->getLang()->sprintfDate( $format, $ts ),
650 "sprintfDate('$format', '$ts'): $msg"
651 );
652
653 date_default_timezone_set( $oldTZ );
654 }
655
661 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
662 $tz = new DateTimeZone( 'Asia/Seoul' );
663 if ( !$tz ) {
664 $this->markTestSkipped( "Error getting Timezone" );
665 }
666
667 $this->assertEquals(
668 $expected,
669 $this->getLang()->sprintfDate( $format, $ts, $tz ),
670 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
671 );
672 }
673
679 $noTtl = 'unused'; // Value used to represent that the caller didn't pass a variable in.
680 $ttl = null;
681 $this->getLang()->sprintfDate( 'YmdHis', wfTimestampNow(), null, $noTtl );
682 $this->getLang()->sprintfDate( 'YmdHis', wfTimestampNow(), null, $ttl );
683
684 $this->assertSame(
685 'unused',
686 $noTtl,
687 'If the caller does not set the $ttl variable, do not compute it.'
688 );
689 $this->assertInternalType( 'int', $ttl, 'TTL should have been computed.' );
690 }
691
692 public static function provideSprintfDateSamples() {
693 return [
694 [
695 'xiY',
696 '20111212000000',
697 '1390', // note because we're testing English locale we get Latin-standard digits
698 '1390',
699 'Iranian calendar full year'
700 ],
701 [
702 'xiy',
703 '20111212000000',
704 '90',
705 '90',
706 'Iranian calendar short year'
707 ],
708 [
709 'o',
710 '20120101235000',
711 '2011',
712 '2011',
713 'ISO 8601 (week) year'
714 ],
715 [
716 'W',
717 '20120101235000',
718 '52',
719 '52',
720 'Week number'
721 ],
722 [
723 'W',
724 '20120102235000',
725 '1',
726 '1',
727 'Week number'
728 ],
729 [
730 'o-\\WW-N',
731 '20091231235000',
732 '2009-W53-4',
733 '2009-W53-4',
734 'leap week'
735 ],
736 // What follows is mostly copied from
737 // https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
738 [
739 'Y',
740 '20120102090705',
741 '2012',
742 '2012',
743 'Full year'
744 ],
745 [
746 'y',
747 '20120102090705',
748 '12',
749 '12',
750 '2 digit year'
751 ],
752 [
753 'L',
754 '20120102090705',
755 '1',
756 '1',
757 'Leap year'
758 ],
759 [
760 'n',
761 '20120102090705',
762 '1',
763 '1',
764 'Month index, not zero pad'
765 ],
766 [
767 'N',
768 '20120102090705',
769 '01',
770 '01',
771 'Month index. Zero pad'
772 ],
773 [
774 'M',
775 '20120102090705',
776 'Jan',
777 'Jan',
778 'Month abbrev'
779 ],
780 [
781 'F',
782 '20120102090705',
783 'January',
784 'January',
785 'Full month'
786 ],
787 [
788 'xg',
789 '20120102090705',
790 'January',
791 'January',
792 'Genitive month name (same in EN)'
793 ],
794 [
795 'j',
796 '20120102090705',
797 '2',
798 '2',
799 'Day of month (not zero pad)'
800 ],
801 [
802 'd',
803 '20120102090705',
804 '02',
805 '02',
806 'Day of month (zero-pad)'
807 ],
808 [
809 'z',
810 '20120102090705',
811 '1',
812 '1',
813 'Day of year (zero-indexed)'
814 ],
815 [
816 'D',
817 '20120102090705',
818 'Mon',
819 'Mon',
820 'Day of week (abbrev)'
821 ],
822 [
823 'l',
824 '20120102090705',
825 'Monday',
826 'Monday',
827 'Full day of week'
828 ],
829 [
830 'N',
831 '20120101090705',
832 '7',
833 '7',
834 'Day of week (Mon=1, Sun=7)'
835 ],
836 [
837 'w',
838 '20120101090705',
839 '0',
840 '0',
841 'Day of week (Sun=0, Sat=6)'
842 ],
843 [
844 'N',
845 '20120102090705',
846 '1',
847 '1',
848 'Day of week'
849 ],
850 [
851 'a',
852 '20120102090705',
853 'am',
854 'am',
855 'am vs pm'
856 ],
857 [
858 'A',
859 '20120102120000',
860 'PM',
861 'PM',
862 'AM vs PM'
863 ],
864 [
865 'a',
866 '20120102000000',
867 'am',
868 'am',
869 'AM vs PM'
870 ],
871 [
872 'g',
873 '20120102090705',
874 '9',
875 '9',
876 '12 hour, not Zero'
877 ],
878 [
879 'h',
880 '20120102090705',
881 '09',
882 '09',
883 '12 hour, zero padded'
884 ],
885 [
886 'G',
887 '20120102090705',
888 '9',
889 '9',
890 '24 hour, not zero'
891 ],
892 [
893 'H',
894 '20120102090705',
895 '09',
896 '09',
897 '24 hour, zero'
898 ],
899 [
900 'H',
901 '20120102110705',
902 '11',
903 '11',
904 '24 hour, zero'
905 ],
906 [
907 'i',
908 '20120102090705',
909 '07',
910 '07',
911 'Minutes'
912 ],
913 [
914 's',
915 '20120102090705',
916 '05',
917 '05',
918 'seconds'
919 ],
920 [
921 'U',
922 '20120102090705',
923 '1325495225',
924 '1325462825',
925 'unix time'
926 ],
927 [
928 't',
929 '20120102090705',
930 '31',
931 '31',
932 'Days in current month'
933 ],
934 [
935 'c',
936 '20120102090705',
937 '2012-01-02T09:07:05+00:00',
938 '2012-01-02T09:07:05+09:00',
939 'ISO 8601 timestamp'
940 ],
941 [
942 'r',
943 '20120102090705',
944 'Mon, 02 Jan 2012 09:07:05 +0000',
945 'Mon, 02 Jan 2012 09:07:05 +0900',
946 'RFC 5322'
947 ],
948 [
949 'e',
950 '20120102090705',
951 'UTC',
952 'Asia/Seoul',
953 'Timezone identifier'
954 ],
955 [
956 'I',
957 '19880602090705',
958 '0',
959 '1',
960 'DST indicator'
961 ],
962 [
963 'O',
964 '20120102090705',
965 '+0000',
966 '+0900',
967 'Timezone offset'
968 ],
969 [
970 'P',
971 '20120102090705',
972 '+00:00',
973 '+09:00',
974 'Timezone offset with colon'
975 ],
976 [
977 'T',
978 '20120102090705',
979 'UTC',
980 'KST',
981 'Timezone abbreviation'
982 ],
983 [
984 'Z',
985 '20120102090705',
986 '0',
987 '32400',
988 'Timezone offset in seconds'
989 ],
990 [
991 'xmj xmF xmn xmY',
992 '20120102090705',
993 '7 Safar 2 1433',
994 '7 Safar 2 1433',
995 'Islamic'
996 ],
997 [
998 'xij xiF xin xiY',
999 '20120102090705',
1000 '12 Dey 10 1390',
1001 '12 Dey 10 1390',
1002 'Iranian'
1003 ],
1004 [
1005 'xjj xjF xjn xjY',
1006 '20120102090705',
1007 '7 Tevet 4 5772',
1008 '7 Tevet 4 5772',
1009 'Hebrew'
1010 ],
1011 [
1012 'xjt',
1013 '20120102090705',
1014 '29',
1015 '29',
1016 'Hebrew number of days in month'
1017 ],
1018 [
1019 'xjx',
1020 '20120102090705',
1021 'Tevet',
1022 'Tevet',
1023 'Hebrew genitive month name (No difference in EN)'
1024 ],
1025 [
1026 'xkY',
1027 '20120102090705',
1028 '2555',
1029 '2555',
1030 'Thai year'
1031 ],
1032 [
1033 'xoY',
1034 '20120102090705',
1035 '101',
1036 '101',
1037 'Minguo'
1038 ],
1039 [
1040 'xtY',
1041 '20120102090705',
1042 '平成24',
1043 '平成24',
1044 'nengo'
1045 ],
1046 [
1047 'xtY',
1048 '20190430235959',
1049 '平成31',
1050 '平成31',
1051 'nengo - last day of heisei'
1052 ],
1053 [
1054 'xtY',
1055 '20190501000000',
1056 '令和元',
1057 '令和元',
1058 'nengo - first day of reiwa'
1059 ],
1060 [
1061 'xtY',
1062 '20200501000000',
1063 '令和2',
1064 '令和2',
1065 'nengo - second year of reiwa'
1066 ],
1067 [
1068 'xrxkYY',
1069 '20120102090705',
1070 'MMDLV2012',
1071 'MMDLV2012',
1072 'Roman numerals'
1073 ],
1074 [
1075 'xhxjYY',
1076 '20120102090705',
1077 'ה\'תשע"ב2012',
1078 'ה\'תשע"ב2012',
1079 'Hebrew numberals'
1080 ],
1081 [
1082 'xnY',
1083 '20120102090705',
1084 '2012',
1085 '2012',
1086 'Raw numerals (doesn\'t mean much in EN)'
1087 ],
1088 [
1089 '[[Y "(yea"\\r)]] \\"xx\\"',
1090 '20120102090705',
1091 '[[2012 (year)]] "x"',
1092 '[[2012 (year)]] "x"',
1093 'Various escaping'
1094 ],
1095
1096 ];
1097 }
1098
1103 public function testFormatSize( $size, $expected, $msg ) {
1104 $this->assertEquals(
1105 $expected,
1106 $this->getLang()->formatSize( $size ),
1107 "formatSize('$size'): $msg"
1108 );
1109 }
1110
1111 public static function provideFormatSizes() {
1112 return [
1113 [
1114 0,
1115 "0 bytes",
1116 "Zero bytes"
1117 ],
1118 [
1119 1024,
1120 "1 KB",
1121 "1 kilobyte"
1122 ],
1123 [
1124 1024 * 1024,
1125 "1 MB",
1126 "1,024 megabytes"
1127 ],
1128 [
1129 1024 * 1024 * 1024,
1130 "1 GB",
1131 "1 gigabyte"
1132 ],
1133 [
1134 pow( 1024, 4 ),
1135 "1 TB",
1136 "1 terabyte"
1137 ],
1138 [
1139 pow( 1024, 5 ),
1140 "1 PB",
1141 "1 petabyte"
1142 ],
1143 [
1144 pow( 1024, 6 ),
1145 "1 EB",
1146 "1,024 exabyte"
1147 ],
1148 [
1149 pow( 1024, 7 ),
1150 "1 ZB",
1151 "1 zetabyte"
1152 ],
1153 [
1154 pow( 1024, 8 ),
1155 "1 YB",
1156 "1 yottabyte"
1157 ],
1158 // How big!? THIS BIG!
1159 ];
1160 }
1161
1166 public function testFormatBitrate( $bps, $expected, $msg ) {
1167 $this->assertEquals(
1168 $expected,
1169 $this->getLang()->formatBitrate( $bps ),
1170 "formatBitrate('$bps'): $msg"
1171 );
1172 }
1173
1174 public static function provideFormatBitrate() {
1175 return [
1176 [
1177 0,
1178 "0 bps",
1179 "0 bits per second"
1180 ],
1181 [
1182 999,
1183 "999 bps",
1184 "999 bits per second"
1185 ],
1186 [
1187 1000,
1188 "1 kbps",
1189 "1 kilobit per second"
1190 ],
1191 [
1192 1000 * 1000,
1193 "1 Mbps",
1194 "1 megabit per second"
1195 ],
1196 [
1197 pow( 10, 9 ),
1198 "1 Gbps",
1199 "1 gigabit per second"
1200 ],
1201 [
1202 pow( 10, 12 ),
1203 "1 Tbps",
1204 "1 terabit per second"
1205 ],
1206 [
1207 pow( 10, 15 ),
1208 "1 Pbps",
1209 "1 petabit per second"
1210 ],
1211 [
1212 pow( 10, 18 ),
1213 "1 Ebps",
1214 "1 exabit per second"
1215 ],
1216 [
1217 pow( 10, 21 ),
1218 "1 Zbps",
1219 "1 zetabit per second"
1220 ],
1221 [
1222 pow( 10, 24 ),
1223 "1 Ybps",
1224 "1 yottabit per second"
1225 ],
1226 [
1227 pow( 10, 27 ),
1228 "1,000 Ybps",
1229 "1,000 yottabits per second"
1230 ],
1231 ];
1232 }
1233
1238 public function testFormatDuration( $duration, $expected, $intervals = [] ) {
1239 $this->assertEquals(
1240 $expected,
1241 $this->getLang()->formatDuration( $duration, $intervals ),
1242 "formatDuration('$duration'): $expected"
1243 );
1244 }
1245
1246 public static function provideFormatDuration() {
1247 return [
1248 [
1249 0,
1250 '0 seconds',
1251 ],
1252 [
1253 1,
1254 '1 second',
1255 ],
1256 [
1257 2,
1258 '2 seconds',
1259 ],
1260 [
1261 60,
1262 '1 minute',
1263 ],
1264 [
1265 2 * 60,
1266 '2 minutes',
1267 ],
1268 [
1269 3600,
1270 '1 hour',
1271 ],
1272 [
1273 2 * 3600,
1274 '2 hours',
1275 ],
1276 [
1277 24 * 3600,
1278 '1 day',
1279 ],
1280 [
1281 2 * 86400,
1282 '2 days',
1283 ],
1284 [
1285 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1286 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
1287 '1 year',
1288 ],
1289 [
1290 2 * 31556952,
1291 '2 years',
1292 ],
1293 [
1294 10 * 31556952,
1295 '1 decade',
1296 ],
1297 [
1298 20 * 31556952,
1299 '2 decades',
1300 ],
1301 [
1302 100 * 31556952,
1303 '1 century',
1304 ],
1305 [
1306 200 * 31556952,
1307 '2 centuries',
1308 ],
1309 [
1310 1000 * 31556952,
1311 '1 millennium',
1312 ],
1313 [
1314 2000 * 31556952,
1315 '2 millennia',
1316 ],
1317 [
1318 9001,
1319 '2 hours, 30 minutes and 1 second'
1320 ],
1321 [
1322 3601,
1323 '1 hour and 1 second'
1324 ],
1325 [
1326 31556952 + 2 * 86400 + 9000,
1327 '1 year, 2 days, 2 hours and 30 minutes'
1328 ],
1329 [
1330 42 * 1000 * 31556952 + 42,
1331 '42 millennia and 42 seconds'
1332 ],
1333 [
1334 60,
1335 '60 seconds',
1336 [ 'seconds' ],
1337 ],
1338 [
1339 61,
1340 '61 seconds',
1341 [ 'seconds' ],
1342 ],
1343 [
1344 1,
1345 '1 second',
1346 [ 'seconds' ],
1347 ],
1348 [
1349 31556952 + 2 * 86400 + 9000,
1350 '1 year, 2 days and 150 minutes',
1351 [ 'years', 'days', 'minutes' ],
1352 ],
1353 [
1354 42,
1355 '0 days',
1356 [ 'years', 'days' ],
1357 ],
1358 [
1359 31556952 + 2 * 86400 + 9000,
1360 '1 year, 2 days and 150 minutes',
1361 [ 'minutes', 'days', 'years' ],
1362 ],
1363 [
1364 42,
1365 '0 days',
1366 [ 'days', 'years' ],
1367 ],
1368 ];
1369 }
1370
1375 public function testCheckTitleEncoding( $s ) {
1376 $this->assertEquals(
1377 $s,
1378 $this->getLang()->checkTitleEncoding( $s ),
1379 "checkTitleEncoding('$s')"
1380 );
1381 }
1382
1383 public static function provideCheckTitleEncodingData() {
1384 // phpcs:disable Generic.Files.LineLength
1385 return [
1386 [ "" ],
1387 [ "United States of America" ], // 7bit ASCII
1388 [ rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ],
1389 [
1390 rawurldecode(
1391 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1392 )
1393 ],
1394 // The following two data sets come from T38839. They fail if checkTitleEncoding uses a regexp to test for
1395 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1396 // uses mb_check_encoding for its test.
1397 [
1398 rawurldecode(
1399 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1400 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1401 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1402 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1403 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1404 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1405 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1406 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1407 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1408 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1409 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1410 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1411 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1412 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1413 ),
1414 ],
1415 [
1416 rawurldecode(
1417 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1418 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1419 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1420 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1421 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1422 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1423 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1424 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1425 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1426 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1427 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1428 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1429 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1430 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1431 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1432 )
1433 ]
1434 ];
1435 // phpcs:enable
1436 }
1437
1442 public function testRomanNumerals( $num, $numerals ) {
1443 $this->assertEquals(
1444 $numerals,
1445 Language::romanNumeral( $num ),
1446 "romanNumeral('$num')"
1447 );
1448 }
1449
1450 public static function provideRomanNumeralsData() {
1451 return [
1452 [ 1, 'I' ],
1453 [ 2, 'II' ],
1454 [ 3, 'III' ],
1455 [ 4, 'IV' ],
1456 [ 5, 'V' ],
1457 [ 6, 'VI' ],
1458 [ 7, 'VII' ],
1459 [ 8, 'VIII' ],
1460 [ 9, 'IX' ],
1461 [ 10, 'X' ],
1462 [ 20, 'XX' ],
1463 [ 30, 'XXX' ],
1464 [ 40, 'XL' ],
1465 [ 49, 'XLIX' ],
1466 [ 50, 'L' ],
1467 [ 60, 'LX' ],
1468 [ 70, 'LXX' ],
1469 [ 80, 'LXXX' ],
1470 [ 90, 'XC' ],
1471 [ 99, 'XCIX' ],
1472 [ 100, 'C' ],
1473 [ 200, 'CC' ],
1474 [ 300, 'CCC' ],
1475 [ 400, 'CD' ],
1476 [ 500, 'D' ],
1477 [ 600, 'DC' ],
1478 [ 700, 'DCC' ],
1479 [ 800, 'DCCC' ],
1480 [ 900, 'CM' ],
1481 [ 999, 'CMXCIX' ],
1482 [ 1000, 'M' ],
1483 [ 1989, 'MCMLXXXIX' ],
1484 [ 2000, 'MM' ],
1485 [ 3000, 'MMM' ],
1486 [ 4000, 'MMMM' ],
1487 [ 5000, 'MMMMM' ],
1488 [ 6000, 'MMMMMM' ],
1489 [ 7000, 'MMMMMMM' ],
1490 [ 8000, 'MMMMMMMM' ],
1491 [ 9000, 'MMMMMMMMM' ],
1492 [ 9999, 'MMMMMMMMMCMXCIX' ],
1493 [ 10000, 'MMMMMMMMMM' ],
1494 ];
1495 }
1496
1501 public function testHebrewNumeral( $num, $numerals ) {
1502 $this->assertEquals(
1503 $numerals,
1504 Language::hebrewNumeral( $num ),
1505 "hebrewNumeral('$num')"
1506 );
1507 }
1508
1509 public static function provideHebrewNumeralsData() {
1510 return [
1511 [ -1, -1 ],
1512 [ 0, 0 ],
1513 [ 1, "א'" ],
1514 [ 2, "ב'" ],
1515 [ 3, "ג'" ],
1516 [ 4, "ד'" ],
1517 [ 5, "ה'" ],
1518 [ 6, "ו'" ],
1519 [ 7, "ז'" ],
1520 [ 8, "ח'" ],
1521 [ 9, "ט'" ],
1522 [ 10, "י'" ],
1523 [ 11, 'י"א' ],
1524 [ 14, 'י"ד' ],
1525 [ 15, 'ט"ו' ],
1526 [ 16, 'ט"ז' ],
1527 [ 17, 'י"ז' ],
1528 [ 20, "כ'" ],
1529 [ 21, 'כ"א' ],
1530 [ 30, "ל'" ],
1531 [ 40, "מ'" ],
1532 [ 50, "נ'" ],
1533 [ 60, "ס'" ],
1534 [ 70, "ע'" ],
1535 [ 80, "פ'" ],
1536 [ 90, "צ'" ],
1537 [ 99, 'צ"ט' ],
1538 [ 100, "ק'" ],
1539 [ 101, 'ק"א' ],
1540 [ 110, 'ק"י' ],
1541 [ 200, "ר'" ],
1542 [ 300, "ש'" ],
1543 [ 400, "ת'" ],
1544 [ 500, 'ת"ק' ],
1545 [ 800, 'ת"ת' ],
1546 [ 1000, "א' אלף" ],
1547 [ 1001, "א'א'" ],
1548 [ 1012, "א'י\"ב" ],
1549 [ 1020, "א'ך'" ],
1550 [ 1030, "א'ל'" ],
1551 [ 1081, "א'פ\"א" ],
1552 [ 2000, "ב' אלפים" ],
1553 [ 2016, "ב'ט\"ז" ],
1554 [ 3000, "ג' אלפים" ],
1555 [ 4000, "ד' אלפים" ],
1556 [ 4904, "ד'תתק\"ד" ],
1557 [ 5000, "ה' אלפים" ],
1558 [ 5680, "ה'תר\"ף" ],
1559 [ 5690, "ה'תר\"ץ" ],
1560 [ 5708, "ה'תש\"ח" ],
1561 [ 5720, "ה'תש\"ך" ],
1562 [ 5740, "ה'תש\"ם" ],
1563 [ 5750, "ה'תש\"ן" ],
1564 [ 5775, "ה'תשע\"ה" ],
1565 ];
1566 }
1567
1572 public function testConvertPlural( $expected, $number, $forms ) {
1573 $chosen = $this->getLang()->convertPlural( $number, $forms );
1574 $this->assertEquals( $expected, $chosen );
1575 }
1576
1577 public static function providePluralData() {
1578 // Params are: [expected text, number given, [the plural forms]]
1579 return [
1580 [ 'plural', 0, [
1581 'singular', 'plural'
1582 ] ],
1583 [ 'explicit zero', 0, [
1584 '0=explicit zero', 'singular', 'plural'
1585 ] ],
1586 [ 'explicit one', 1, [
1587 'singular', 'plural', '1=explicit one',
1588 ] ],
1589 [ 'singular', 1, [
1590 'singular', 'plural', '0=explicit zero',
1591 ] ],
1592 [ 'plural', 3, [
1593 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1594 ] ],
1595 [ 'explicit eleven', 11, [
1596 'singular', 'plural', '11=explicit eleven',
1597 ] ],
1598 [ 'plural', 12, [
1599 'singular', 'plural', '11=explicit twelve',
1600 ] ],
1601 [ 'plural', 12, [
1602 'singular', 'plural', '=explicit form',
1603 ] ],
1604 [ 'other', 2, [
1605 'kissa=kala', '1=2=3', 'other',
1606 ] ],
1607 [ '', 2, [
1608 '0=explicit zero', '1=explicit one',
1609 ] ],
1610 ];
1611 }
1612
1616 public function testEmbedBidi() {
1617 $lre = "\xE2\x80\xAA"; // U+202A LEFT-TO-RIGHT EMBEDDING
1618 $rle = "\xE2\x80\xAB"; // U+202B RIGHT-TO-LEFT EMBEDDING
1619 $pdf = "\xE2\x80\xAC"; // U+202C POP DIRECTIONAL FORMATTING
1620 $lang = $this->getLang();
1621 $this->assertEquals(
1622 '123',
1623 $lang->embedBidi( '123' ),
1624 'embedBidi with neutral argument'
1625 );
1626 $this->assertEquals(
1627 $lre . 'Ben_(WMF)' . $pdf,
1628 $lang->embedBidi( 'Ben_(WMF)' ),
1629 'embedBidi with LTR argument'
1630 );
1631 $this->assertEquals(
1632 $rle . 'יהודי (מנוחין)' . $pdf,
1633 $lang->embedBidi( 'יהודי (מנוחין)' ),
1634 'embedBidi with RTL argument'
1635 );
1636 }
1637
1642 public function testTranslateBlockExpiry( $expectedData, $str, $now, $desc ) {
1643 $lang = $this->getLang();
1644 if ( is_array( $expectedData ) ) {
1645 list( $func, $arg ) = $expectedData;
1646 $expected = $lang->$func( $arg );
1647 } else {
1648 $expected = $expectedData;
1649 }
1650 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str, null, $now ), $desc );
1651 }
1652
1653 public static function provideTranslateBlockExpiry() {
1654 return [
1655 [ '2 hours', '2 hours', 0, 'simple data from ipboptions' ],
1656 [ 'indefinite', 'infinite', 0, 'infinite from ipboptions' ],
1657 [ 'indefinite', 'infinity', 0, 'alternative infinite from ipboptions' ],
1658 [ 'indefinite', 'indefinite', 0, 'another alternative infinite from ipboptions' ],
1659 [ [ 'formatDuration', 1023 * 60 * 60 ], '1023 hours', 0, 'relative' ],
1660 [ [ 'formatDuration', -1023 ], '-1023 seconds', 0, 'negative relative' ],
1661 [
1662 [ 'formatDuration', 1023 * 60 * 60 ],
1663 '1023 hours',
1664 wfTimestamp( TS_UNIX, '19910203040506' ),
1665 'relative with initial timestamp'
1666 ],
1667 [ [ 'formatDuration', 0 ], 'now', 0, 'now' ],
1668 [
1669 [ 'timeanddate', '20120102070000' ],
1670 '2012-1-1 7:00 +1 day',
1671 0,
1672 'mixed, handled as absolute'
1673 ],
1674 [ [ 'timeanddate', '19910203040506' ], '1991-2-3 4:05:06', 0, 'absolute' ],
1675 [ [ 'timeanddate', '19700101000000' ], '1970-1-1 0:00:00', 0, 'absolute at epoch' ],
1676 [ [ 'timeanddate', '19691231235959' ], '1969-12-31 23:59:59', 0, 'time before epoch' ],
1677 [
1678 [ 'timeanddate', '19910910000000' ],
1679 '10 september',
1680 wfTimestamp( TS_UNIX, '19910203040506' ),
1681 'partial'
1682 ],
1683 [ 'dummy', 'dummy', 0, 'return garbage as is' ],
1684 ];
1685 }
1686
1691 public function testFormatNum(
1692 $translateNumerals, $langCode, $number, $nocommafy, $expected
1693 ) {
1694 $this->setMwGlobals( [ 'wgTranslateNumerals' => $translateNumerals ] );
1695 $lang = Language::factory( $langCode );
1696 $formattedNum = $lang->formatNum( $number, $nocommafy );
1697 $this->assertType( 'string', $formattedNum );
1698 $this->assertEquals( $expected, $formattedNum );
1699 }
1700
1701 public function provideFormatNum() {
1702 return [
1703 [ true, 'en', 100, false, '100' ],
1704 [ true, 'en', 101, true, '101' ],
1705 [ false, 'en', 103, false, '103' ],
1706 [ false, 'en', 104, true, '104' ],
1707 [ true, 'en', '105', false, '105' ],
1708 [ true, 'en', '106', true, '106' ],
1709 [ false, 'en', '107', false, '107' ],
1710 [ false, 'en', '108', true, '108' ],
1711 ];
1712 }
1713
1718 public function testParseFormattedNumber( $langCode, $number ) {
1719 $lang = Language::factory( $langCode );
1720
1721 $localisedNum = $lang->formatNum( $number );
1722 $normalisedNum = $lang->parseFormattedNumber( $localisedNum );
1723
1724 $this->assertEquals( $number, $normalisedNum );
1725 }
1726
1728 return [
1729 [ 'de', 377.01 ],
1730 [ 'fa', 334 ],
1731 [ 'fa', 382.772 ],
1732 [ 'ar', 1844 ],
1733 [ 'lzh', 3731 ],
1734 [ 'zh-classical', 7432 ]
1735 ];
1736 }
1737
1742 public function testCommafy( $number, $numbersWithCommas ) {
1743 $this->assertEquals(
1744 $numbersWithCommas,
1745 $this->getLang()->commafy( $number ),
1746 "commafy('$number')"
1747 );
1748 }
1749
1750 public static function provideCommafyData() {
1751 return [
1752 [ -1, '-1' ],
1753 [ 10, '10' ],
1754 [ 100, '100' ],
1755 [ 1000, '1,000' ],
1756 [ 10000, '10,000' ],
1757 [ 100000, '100,000' ],
1758 [ 1000000, '1,000,000' ],
1759 [ -1.0001, '-1.0001' ],
1760 [ 1.0001, '1.0001' ],
1761 [ 10.0001, '10.0001' ],
1762 [ 100.0001, '100.0001' ],
1763 [ 1000.0001, '1,000.0001' ],
1764 [ 10000.0001, '10,000.0001' ],
1765 [ 100000.0001, '100,000.0001' ],
1766 [ 1000000.0001, '1,000,000.0001' ],
1767 [ '200000000000000000000', '200,000,000,000,000,000,000' ],
1768 [ '-200000000000000000000', '-200,000,000,000,000,000,000' ],
1769 ];
1770 }
1771
1775 public function testListToText() {
1776 $lang = $this->getLang();
1777 $and = $lang->getMessageFromDB( 'and' );
1778 $s = $lang->getMessageFromDB( 'word-separator' );
1779 $c = $lang->getMessageFromDB( 'comma-separator' );
1780
1781 $this->assertEquals( '', $lang->listToText( [] ) );
1782 $this->assertEquals( 'a', $lang->listToText( [ 'a' ] ) );
1783 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( [ 'a', 'b' ] ) );
1784 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( [ 'a', 'b', 'c' ] ) );
1785 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( [ 'a', 'b', 'c', 'd' ] ) );
1786 }
1787
1792 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1793 $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
1794 }
1795
1796 public static function provideIsSupportedLanguage() {
1797 return [
1798 [ 'en', true, 'is supported language' ],
1799 [ 'fi', true, 'is supported language' ],
1800 [ 'bunny', false, 'is not supported language' ],
1801 [ 'FI', false, 'is not supported language, input should be in lower case' ],
1802 ];
1803 }
1804
1809 public function testGetParentLanguage( $code, $expected, $comment ) {
1810 $lang = Language::factory( $code );
1811 if ( is_null( $expected ) ) {
1812 $this->assertNull( $lang->getParentLanguage(), $comment );
1813 } else {
1814 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1815 }
1816 }
1817
1818 public static function provideGetParentLanguage() {
1819 return [
1820 [ 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ],
1821 [ 'zh', 'zh', 'zh is defined as the parent language of zh, '
1822 . 'because zh converter can convert zh-cn to zh' ],
1823 [ 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ],
1824 [ 'de-formal', null, 'de does not have converter' ],
1825 [ 'de', null, 'de does not have converter' ],
1826 ];
1827 }
1828
1833 public function testGetNamespaceAliases( $languageCode, $subset ) {
1834 $language = Language::factory( $languageCode );
1835 $aliases = $language->getNamespaceAliases();
1836 foreach ( $subset as $alias => $nsId ) {
1837 $this->assertEquals( $nsId, $aliases[$alias] );
1838 }
1839 }
1840
1841 public static function provideGetNamespaceAliases() {
1842 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
1843 return [
1844 [
1845 'zh',
1846 [
1847 '文件' => NS_FILE,
1848 '檔案' => NS_FILE,
1849 ],
1850 ],
1851 ];
1852 }
1853
1857 public function testEquals() {
1858 $en1 = new Language();
1859 $en1->setCode( 'en' );
1860
1861 $en2 = Language::factory( 'en' );
1862 $en2->setCode( 'en' );
1863
1864 $this->assertTrue( $en1->equals( $en2 ), 'en equals en' );
1865
1866 $fr = Language::factory( 'fr' );
1867 $this->assertFalse( $en1->equals( $fr ), 'en not equals fr' );
1868 }
1869}
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Helping class to run tests using a clean language instance.
static provideTranslateBlockExpiry()
static provideSprintfDateSamples()
static provideCheckTitleEncodingData()
static provideLanguageCodes()
testLenientLanguageTag()
Negative test for Language::isWellFormedLanguageTag() Language::isWellFormedLanguageTag.
static provideGetParentLanguage()
static provideRomanNumeralsData()
testSprintfDateTooLongTimestamp()
Test too long timestamp MWException Language::sprintfDate.
testWellFormedLanguageTag( $code, $message='')
Test Language::isWellFormedLanguageTag() provideWellFormedLanguageTags Language::isWellFormedLanguage...
static provideMalformedLanguageTags()
The test cases are based on the tests in the GaBuZoMeu parser written by Stéphane Bortzmeyer bortzmey...
testTruncateHtml( $len, $ellipsis, $input, $expected)
provideHTMLTruncateData Language::truncateHTML
testConvertPlural( $expected, $number, $forms)
providePluralData Language::convertPlural
static provideHTMLTruncateData()
testFormatDuration( $duration, $expected, $intervals=[])
provideFormatDuration Language::formatDuration
static provideFormattableTimes()
testFormatSize( $size, $expected, $msg)
provideFormatSizes Language::formatSize
testUnknownLanguageTag( $code, $message='')
Negative tests for Language::isKnownLanguageTag() provideUnKnownLanguageTags Language::isKnownLanguag...
testCommafy( $number, $numbersWithCommas)
Language::commafy() provideCommafyData.
testFormatTimePeriod( $seconds, $format, $expected, $desc)
provideFormattableTimes Language::formatTimePeriod
testHebrewNumeral( $num, $numerals)
provideHebrewNumeralsData Language::hebrewNumeral
static provideFormatDuration()
testListToText()
Language::listToText.
testFormatNum( $translateNumerals, $langCode, $number, $nocommafy, $expected)
provideFormatNum Language::formatNum
testParseFormattedNumber( $langCode, $number)
Language::parseFormattedNumber parseFormattedNumberProvider.
testIsSupportedLanguage( $code, $expected, $comment)
provideIsSupportedLanguage Language::isSupportedLanguage
static provideWellFormedLanguageTags()
The test cases are based on the tests in the GaBuZoMeu parser written by Stéphane Bortzmeyer bortzmey...
testSprintfDateNotAllDigitTimestamp()
Test too short timestamp MWException Language::sprintfDate.
static provideFormatBitrate()
testKnownCldrLanguageTag()
Language::isKnownLanguageTag.
testEmbedBidi()
Language::embedBidi()
testLanguageConvertDoubleWidthToSingleWidth()
Language::convertDoubleWidth Language::normalizeForSearch.
static provideHebrewNumeralsData()
testGetParentLanguage( $code, $expected, $comment)
provideGetParentLanguage Language::getParentLanguage
testCheckTitleEncoding( $s)
provideCheckTitleEncodingData Language::checkTitleEncoding
testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg)
sprintfDate should use passed timezone provideSprintfDateSamples Language::sprintfDate
static providePluralData()
testTranslateBlockExpiry( $expectedData, $str, $now, $desc)
Language::translateBlockExpiry() provideTranslateBlockExpiry.
testKnownLanguageTag( $code, $message='')
Test Language::isKnownLanguageTag() provideKnownLanguageTags Language::isKnownLanguageTag.
testGetNamespaceAliases( $languageCode, $subset)
provideGetNamespaceAliases Language::getNamespaceAliases
testFormatBitrate( $bps, $expected, $msg)
provideFormatBitrate Language::formatBitrate
static provideKnownLanguageTags()
static provideGetNamespaceAliases()
testBuiltInCodeValidation( $code, $expected, $message='')
Test Language::isValidBuiltInCode() provideLanguageCodes Language::isValidBuiltInCode.
static provideCommafyData()
testRomanNumerals( $num, $numerals)
provideRomanNumeralsData Language::romanNumeral
testTruncateForVisual( $expected, $string, $length, $ellipsis='...', $adjustLength=true)
provideTruncateData Language::truncateForVisual Language::truncateInternal
testSprintfDateNoTtlIfNotNeeded()
sprintfDate should only calculate a TTL if the caller is going to use it.
testTruncateForDatabase()
Language::truncateForDatabase Language::truncateInternal.
static provideFormatSizes()
testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg)
sprintfDate should always use UTC when no zone is given.
testSprintfDateTooShortTimestamp()
Test too short timestamp MWException Language::sprintfDate.
testEquals()
Language::equals.
static provideTruncateData()
testSprintfDate( $format, $ts, $expected, $msg)
provideSprintfDateSamples Language::sprintfDate
testMalformedLanguageTag( $code, $message='')
Negative test for Language::isWellFormedLanguageTag() provideMalformedLanguageTags Language::isWellFo...
static provideIsSupportedLanguage()
static provideUnknownLanguageTags()
Internationalisation code.
Definition Language.php:35
assertType( $type, $actual, $message='')
Asserts the type of the provided value.
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
$res
Definition database.txt:21
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition hooks.txt:865
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition hooks.txt:2006
processing should stop and the error should be shown to the user * false
Definition hooks.txt:187
const NS_FILE
Definition Defines.php:80
if(is_array($mode)) switch( $mode) $input
if(!isset( $args[0])) $lang