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