MediaWiki REL1_28
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
214 public function testTruncate() {
215 $this->assertEquals(
216 "XXX",
217 $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
218 'truncate prefix, len 0, small ellipsis'
219 );
220
221 $this->assertEquals(
222 "12345XXX",
223 $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
224 'truncate prefix, small ellipsis'
225 );
226
227 $this->assertEquals(
228 "123456789",
229 $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
230 'truncate prefix, large ellipsis'
231 );
232
233 $this->assertEquals(
234 "XXX67890",
235 $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
236 'truncate suffix, small ellipsis'
237 );
238
239 $this->assertEquals(
240 "123456789",
241 $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
242 'truncate suffix, large ellipsis'
243 );
244 $this->assertEquals(
245 "123XXX",
246 $this->getLang()->truncate( "123 ", 9, 'XXX' ),
247 'truncate prefix, with spaces'
248 );
249 $this->assertEquals(
250 "12345XXX",
251 $this->getLang()->truncate( "12345 8", 11, 'XXX' ),
252 'truncate prefix, with spaces and non-space ending'
253 );
254 $this->assertEquals(
255 "XXX234",
256 $this->getLang()->truncate( "1 234", -8, 'XXX' ),
257 'truncate suffix, with spaces'
258 );
259 $this->assertEquals(
260 "12345XXX",
261 $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
262 'truncate without adjustment'
263 );
264 $this->assertEquals(
265 "泰乐菌...",
266 $this->getLang()->truncate( "泰乐菌素123456789", 11, '...', false ),
267 'truncate does not chop Unicode characters in half'
268 );
269 $this->assertEquals(
270 "\n泰乐菌...",
271 $this->getLang()->truncate( "\n泰乐菌素123456789", 12, '...', false ),
272 'truncate does not chop Unicode characters in half if there is a preceding newline'
273 );
274 }
275
280 public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
281 // Actual HTML...
282 $this->assertEquals(
283 $expected,
284 $this->getLang()->truncateHtml( $input, $len, $ellipsis )
285 );
286 }
287
291 public static function provideHTMLTruncateData() {
292 return [
293 [ 0, 'XXX', "1234567890", "XXX" ],
294 [ 8, 'XXX', "1234567890", "12345XXX" ],
295 [ 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ],
296 [ 2, '***',
297 '<p><span style="font-weight:bold;"></span></p>',
298 '<p><span style="font-weight:bold;"></span></p>',
299 ],
300 [ 2, '***',
301 '<p><span style="font-weight:bold;">123456789</span></p>',
302 '<p><span style="font-weight:bold;">***</span></p>',
303 ],
304 [ 2, '***',
305 '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
306 '<p><span style="font-weight:bold;">***</span></p>',
307 ],
308 [ 3, '***',
309 '<p><span style="font-weight:bold;">123456789</span></p>',
310 '<p><span style="font-weight:bold;">***</span></p>',
311 ],
312 [ 4, '***',
313 '<p><span style="font-weight:bold;">123456789</span></p>',
314 '<p><span style="font-weight:bold;">1***</span></p>',
315 ],
316 [ 5, '***',
317 '<tt><span style="font-weight:bold;">123456789</span></tt>',
318 '<tt><span style="font-weight:bold;">12***</span></tt>',
319 ],
320 [ 6, '***',
321 '<p><a href="www.mediawiki.org">123456789</a></p>',
322 '<p><a href="www.mediawiki.org">123***</a></p>',
323 ],
324 [ 6, '***',
325 '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
326 '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
327 ],
328 [ 7, '***',
329 '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
330 '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
331 ],
332 [ 8, '***',
333 '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
334 '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
335 ],
336 [ 9, '***',
337 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
338 '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
339 ],
340 [ 10, '***',
341 '<p><font style="font-weight:bold;">123456789</font></p>',
342 '<p><font style="font-weight:bold;">123456789</font></p>',
343 ],
344 ];
345 }
346
352 public function testWellFormedLanguageTag( $code, $message = '' ) {
353 $this->assertTrue(
354 Language::isWellFormedLanguageTag( $code ),
355 "validating code $code $message"
356 );
357 }
358
365 public static function provideWellFormedLanguageTags() {
366 return [
367 [ 'fr', 'two-letter code' ],
368 [ 'fr-latn', 'two-letter code with lower case script code' ],
369 [ 'fr-Latn-FR', 'two-letter code with title case script code and uppercase country code' ],
370 [ 'fr-Latn-419', 'two-letter code with title case script code and region number' ],
371 [ 'fr-FR', 'two-letter code with uppercase' ],
372 [ 'ax-TZ', 'Not in the registry, but well-formed' ],
373 [ 'fr-shadok', 'two-letter code with variant' ],
374 [ 'fr-y-myext-myext2', 'non-x singleton' ],
375 [ 'fra-Latn', 'ISO 639 can be 3-letters' ],
376 [ 'fra', 'three-letter language code' ],
377 [ 'fra-FX', 'three-letter language code with country code' ],
378 [ 'i-klingon', 'grandfathered with singleton' ],
379 [ 'I-kLINgon', 'tags are case-insensitive...' ],
380 [ 'no-bok', 'grandfathered without singleton' ],
381 [ 'i-enochian', 'Grandfathered' ],
382 [ 'x-fr-CH', 'private use' ],
383 [ 'es-419', 'two-letter code with region number' ],
384 [ 'en-Latn-GB-boont-r-extended-sequence-x-private', 'weird, but well-formed' ],
385 [ 'ab-x-abc-x-abc', 'anything goes after x' ],
386 [ 'ab-x-abc-a-a', 'anything goes after x, including several non-x singletons' ],
387 [ 'i-default', 'grandfathered' ],
388 [ 'abcd-Latn', 'Language of 4 chars reserved for future use' ],
389 [ 'AaBbCcDd-x-y-any-x', 'Language of 5-8 chars, registered' ],
390 [ 'de-CH-1901', 'with country and year' ],
391 [ 'en-US-x-twain', 'with country and singleton' ],
392 [ 'zh-cmn', 'three-letter variant' ],
393 [ 'zh-cmn-Hant', 'three-letter variant and script' ],
394 [ 'zh-cmn-Hant-HK', 'three-letter variant, script and country' ],
395 [ 'xr-p-lze', 'Extension' ],
396 ];
397 }
398
404 public function testMalformedLanguageTag( $code, $message = '' ) {
405 $this->assertFalse(
406 Language::isWellFormedLanguageTag( $code ),
407 "validating that code $code is a malformed language tag - $message"
408 );
409 }
410
417 public static function provideMalformedLanguageTags() {
418 return [
419 [ 'f', 'language too short' ],
420 [ 'f-Latn', 'language too short with script' ],
421 [ 'xr-lxs-qut', 'variants too short' ], # extlangS
422 [ 'fr-Latn-F', 'region too short' ],
423 [ 'a-value', 'language too short with region' ],
424 [ 'tlh-a-b-foo', 'valid three-letter with wrong variant' ],
425 [
426 'i-notexist',
427 'grandfathered but not registered: invalid, even if we only test well-formedness'
428 ],
429 [ 'abcdefghi-012345678', 'numbers too long' ],
430 [ 'ab-abc-abc-abc-abc', 'invalid extensions' ],
431 [ 'ab-abcd-abc', 'invalid extensions' ],
432 [ 'ab-ab-abc', 'invalid extensions' ],
433 [ 'ab-123-abc', 'invalid extensions' ],
434 [ 'a-Hant-ZH', 'short language with valid extensions' ],
435 [ 'a1-Hant-ZH', 'invalid character in language' ],
436 [ 'ab-abcde-abc', 'invalid extensions' ],
437 [ 'ab-1abc-abc', 'invalid characters in extensions' ],
438 [ 'ab-ab-abcd', 'invalid order of extensions' ],
439 [ 'ab-123-abcd', 'invalid order of extensions' ],
440 [ 'ab-abcde-abcd', 'invalid extensions' ],
441 [ 'ab-1abc-abcd', 'invalid characters in extensions' ],
442 [ 'ab-a-b', 'extensions too short' ],
443 [ 'ab-a-x', 'extensions too short, even with singleton' ],
444 [ 'ab--ab', 'two separators' ],
445 [ 'ab-abc-', 'separator in the end' ],
446 [ '-ab-abc', 'separator in the beginning' ],
447 [ 'abcd-efg', 'language too long' ],
448 [ 'aabbccddE', 'tag too long' ],
449 [ 'pa_guru', 'A tag with underscore is invalid in strict mode' ],
450 [ 'de-f', 'subtag too short' ],
451 ];
452 }
453
458 public function testLenientLanguageTag() {
459 $this->assertTrue(
460 Language::isWellFormedLanguageTag( 'pa_guru', true ),
461 'pa_guru is a well-formed language tag in lenient mode'
462 );
463 }
464
470 public function testBuiltInCodeValidation( $code, $expected, $message = '' ) {
471 $this->assertEquals( $expected,
472 (bool)Language::isValidBuiltInCode( $code ),
473 "validating code $code $message"
474 );
475 }
476
477 public static function provideLanguageCodes() {
478 return [
479 [ 'fr', true, 'Two letters, minor case' ],
480 [ 'EN', false, 'Two letters, upper case' ],
481 [ 'tyv', true, 'Three letters' ],
482 [ 'tokipona', true, 'long language code' ],
483 [ 'be-tarask', true, 'With dash' ],
484 [ 'be-x-old', true, 'With extension (two dashes)' ],
485 [ 'be_tarask', false, 'Reject underscores' ],
486 ];
487 }
488
494 public function testKnownLanguageTag( $code, $message = '' ) {
495 $this->assertTrue(
496 (bool)Language::isKnownLanguageTag( $code ),
497 "validating code $code - $message"
498 );
499 }
500
501 public static function provideKnownLanguageTags() {
502 return [
503 [ 'fr', 'simple code' ],
504 [ 'bat-smg', 'an MW legacy tag' ],
505 [ 'sgs', 'an internal standard MW name, for which a legacy tag is used externally' ],
506 ];
507 }
508
512 public function testKnownCldrLanguageTag() {
513 if ( !class_exists( 'LanguageNames' ) ) {
514 $this->markTestSkipped( 'The LanguageNames class is not available. '
515 . 'The CLDR extension is probably not installed.' );
516 }
517
518 $this->assertTrue(
519 (bool)Language::isKnownLanguageTag( 'pal' ),
520 'validating code "pal" an ancient language, which probably will '
521 . 'not appear in Names.php, but appears in CLDR in English'
522 );
523 }
524
530 public function testUnknownLanguageTag( $code, $message = '' ) {
531 $this->assertFalse(
532 (bool)Language::isKnownLanguageTag( $code ),
533 "checking that code $code is invalid - $message"
534 );
535 }
536
537 public static function provideUnknownLanguageTags() {
538 return [
539 [ 'mw', 'non-existent two-letter code' ],
540 [ 'foo"<bar', 'very invalid language code' ],
541 ];
542 }
543
550 $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
551 }
552
559 $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
560 }
561
568 $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
569 }
570
575 public function testSprintfDate( $format, $ts, $expected, $msg ) {
576 $ttl = null;
577 $this->assertEquals(
578 $expected,
579 $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
580 "sprintfDate('$format', '$ts'): $msg"
581 );
582 if ( $ttl ) {
583 $dt = new DateTime( $ts );
584 $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
585 $this->assertEquals(
586 $expected,
587 $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
588 "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
589 );
590 } else {
591 // advance the time enough to make all of the possible outputs different (except possibly L)
592 $dt = new DateTime( $ts );
593 $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
594 $this->assertEquals(
595 $expected,
596 $this->getLang()->sprintfDate( $format, $newTS, null ),
597 "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
598 );
599 }
600 }
601
607 public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
608 $oldTZ = date_default_timezone_get();
609 $res = date_default_timezone_set( 'Asia/Seoul' );
610 if ( !$res ) {
611 $this->markTestSkipped( "Error setting Timezone" );
612 }
613
614 $this->assertEquals(
615 $expected,
616 $this->getLang()->sprintfDate( $format, $ts ),
617 "sprintfDate('$format', '$ts'): $msg"
618 );
619
620 date_default_timezone_set( $oldTZ );
621 }
622
628 public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
629 $tz = new DateTimeZone( 'Asia/Seoul' );
630 if ( !$tz ) {
631 $this->markTestSkipped( "Error getting Timezone" );
632 }
633
634 $this->assertEquals(
635 $expected,
636 $this->getLang()->sprintfDate( $format, $ts, $tz ),
637 "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg"
638 );
639 }
640
646 $noTtl = 'unused'; // Value used to represent that the caller didn't pass a variable in.
647 $ttl = null;
648 $this->getLang()->sprintfDate( 'YmdHis', wfTimestampNow(), null, $noTtl );
649 $this->getLang()->sprintfDate( 'YmdHis', wfTimestampNow(), null, $ttl );
650
651 $this->assertSame(
652 'unused',
653 $noTtl,
654 'If the caller does not set the $ttl variable, do not compute it.'
655 );
656 $this->assertInternalType( 'int', $ttl, 'TTL should have been computed.' );
657 }
658
659 public static function provideSprintfDateSamples() {
660 return [
661 [
662 'xiY',
663 '20111212000000',
664 '1390', // note because we're testing English locale we get Latin-standard digits
665 '1390',
666 'Iranian calendar full year'
667 ],
668 [
669 'xiy',
670 '20111212000000',
671 '90',
672 '90',
673 'Iranian calendar short year'
674 ],
675 [
676 'o',
677 '20120101235000',
678 '2011',
679 '2011',
680 'ISO 8601 (week) year'
681 ],
682 [
683 'W',
684 '20120101235000',
685 '52',
686 '52',
687 'Week number'
688 ],
689 [
690 'W',
691 '20120102235000',
692 '1',
693 '1',
694 'Week number'
695 ],
696 [
697 'o-\\WW-N',
698 '20091231235000',
699 '2009-W53-4',
700 '2009-W53-4',
701 'leap week'
702 ],
703 // What follows is mostly copied from
704 // https://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time
705 [
706 'Y',
707 '20120102090705',
708 '2012',
709 '2012',
710 'Full year'
711 ],
712 [
713 'y',
714 '20120102090705',
715 '12',
716 '12',
717 '2 digit year'
718 ],
719 [
720 'L',
721 '20120102090705',
722 '1',
723 '1',
724 'Leap year'
725 ],
726 [
727 'n',
728 '20120102090705',
729 '1',
730 '1',
731 'Month index, not zero pad'
732 ],
733 [
734 'N',
735 '20120102090705',
736 '01',
737 '01',
738 'Month index. Zero pad'
739 ],
740 [
741 'M',
742 '20120102090705',
743 'Jan',
744 'Jan',
745 'Month abbrev'
746 ],
747 [
748 'F',
749 '20120102090705',
750 'January',
751 'January',
752 'Full month'
753 ],
754 [
755 'xg',
756 '20120102090705',
757 'January',
758 'January',
759 'Genitive month name (same in EN)'
760 ],
761 [
762 'j',
763 '20120102090705',
764 '2',
765 '2',
766 'Day of month (not zero pad)'
767 ],
768 [
769 'd',
770 '20120102090705',
771 '02',
772 '02',
773 'Day of month (zero-pad)'
774 ],
775 [
776 'z',
777 '20120102090705',
778 '1',
779 '1',
780 'Day of year (zero-indexed)'
781 ],
782 [
783 'D',
784 '20120102090705',
785 'Mon',
786 'Mon',
787 'Day of week (abbrev)'
788 ],
789 [
790 'l',
791 '20120102090705',
792 'Monday',
793 'Monday',
794 'Full day of week'
795 ],
796 [
797 'N',
798 '20120101090705',
799 '7',
800 '7',
801 'Day of week (Mon=1, Sun=7)'
802 ],
803 [
804 'w',
805 '20120101090705',
806 '0',
807 '0',
808 'Day of week (Sun=0, Sat=6)'
809 ],
810 [
811 'N',
812 '20120102090705',
813 '1',
814 '1',
815 'Day of week'
816 ],
817 [
818 'a',
819 '20120102090705',
820 'am',
821 'am',
822 'am vs pm'
823 ],
824 [
825 'A',
826 '20120102120000',
827 'PM',
828 'PM',
829 'AM vs PM'
830 ],
831 [
832 'a',
833 '20120102000000',
834 'am',
835 'am',
836 'AM vs PM'
837 ],
838 [
839 'g',
840 '20120102090705',
841 '9',
842 '9',
843 '12 hour, not Zero'
844 ],
845 [
846 'h',
847 '20120102090705',
848 '09',
849 '09',
850 '12 hour, zero padded'
851 ],
852 [
853 'G',
854 '20120102090705',
855 '9',
856 '9',
857 '24 hour, not zero'
858 ],
859 [
860 'H',
861 '20120102090705',
862 '09',
863 '09',
864 '24 hour, zero'
865 ],
866 [
867 'H',
868 '20120102110705',
869 '11',
870 '11',
871 '24 hour, zero'
872 ],
873 [
874 'i',
875 '20120102090705',
876 '07',
877 '07',
878 'Minutes'
879 ],
880 [
881 's',
882 '20120102090705',
883 '05',
884 '05',
885 'seconds'
886 ],
887 [
888 'U',
889 '20120102090705',
890 '1325495225',
891 '1325462825',
892 'unix time'
893 ],
894 [
895 't',
896 '20120102090705',
897 '31',
898 '31',
899 'Days in current month'
900 ],
901 [
902 'c',
903 '20120102090705',
904 '2012-01-02T09:07:05+00:00',
905 '2012-01-02T09:07:05+09:00',
906 'ISO 8601 timestamp'
907 ],
908 [
909 'r',
910 '20120102090705',
911 'Mon, 02 Jan 2012 09:07:05 +0000',
912 'Mon, 02 Jan 2012 09:07:05 +0900',
913 'RFC 5322'
914 ],
915 [
916 'e',
917 '20120102090705',
918 'UTC',
919 'Asia/Seoul',
920 'Timezone identifier'
921 ],
922 [
923 'I',
924 '19880602090705',
925 '0',
926 '1',
927 'DST indicator'
928 ],
929 [
930 'O',
931 '20120102090705',
932 '+0000',
933 '+0900',
934 'Timezone offset'
935 ],
936 [
937 'P',
938 '20120102090705',
939 '+00:00',
940 '+09:00',
941 'Timezone offset with colon'
942 ],
943 [
944 'T',
945 '20120102090705',
946 'UTC',
947 'KST',
948 'Timezone abbreviation'
949 ],
950 [
951 'Z',
952 '20120102090705',
953 '0',
954 '32400',
955 'Timezone offset in seconds'
956 ],
957 [
958 'xmj xmF xmn xmY',
959 '20120102090705',
960 '7 Safar 2 1433',
961 '7 Safar 2 1433',
962 'Islamic'
963 ],
964 [
965 'xij xiF xin xiY',
966 '20120102090705',
967 '12 Dey 10 1390',
968 '12 Dey 10 1390',
969 'Iranian'
970 ],
971 [
972 'xjj xjF xjn xjY',
973 '20120102090705',
974 '7 Tevet 4 5772',
975 '7 Tevet 4 5772',
976 'Hebrew'
977 ],
978 [
979 'xjt',
980 '20120102090705',
981 '29',
982 '29',
983 'Hebrew number of days in month'
984 ],
985 [
986 'xjx',
987 '20120102090705',
988 'Tevet',
989 'Tevet',
990 'Hebrew genitive month name (No difference in EN)'
991 ],
992 [
993 'xkY',
994 '20120102090705',
995 '2555',
996 '2555',
997 'Thai year'
998 ],
999 [
1000 'xoY',
1001 '20120102090705',
1002 '101',
1003 '101',
1004 'Minguo'
1005 ],
1006 [
1007 'xtY',
1008 '20120102090705',
1009 '平成24',
1010 '平成24',
1011 'nengo'
1012 ],
1013 [
1014 'xrxkYY',
1015 '20120102090705',
1016 'MMDLV2012',
1017 'MMDLV2012',
1018 'Roman numerals'
1019 ],
1020 [
1021 'xhxjYY',
1022 '20120102090705',
1023 'ה\'תשע"ב2012',
1024 'ה\'תשע"ב2012',
1025 'Hebrew numberals'
1026 ],
1027 [
1028 'xnY',
1029 '20120102090705',
1030 '2012',
1031 '2012',
1032 'Raw numerals (doesn\'t mean much in EN)'
1033 ],
1034 [
1035 '[[Y "(yea"\\r)]] \\"xx\\"',
1036 '20120102090705',
1037 '[[2012 (year)]] "x"',
1038 '[[2012 (year)]] "x"',
1039 'Various escaping'
1040 ],
1041
1042 ];
1043 }
1044
1049 public function testFormatSize( $size, $expected, $msg ) {
1050 $this->assertEquals(
1051 $expected,
1052 $this->getLang()->formatSize( $size ),
1053 "formatSize('$size'): $msg"
1054 );
1055 }
1056
1057 public static function provideFormatSizes() {
1058 return [
1059 [
1060 0,
1061 "0 bytes",
1062 "Zero bytes"
1063 ],
1064 [
1065 1024,
1066 "1 KB",
1067 "1 kilobyte"
1068 ],
1069 [
1070 1024 * 1024,
1071 "1 MB",
1072 "1,024 megabytes"
1073 ],
1074 [
1075 1024 * 1024 * 1024,
1076 "1 GB",
1077 "1 gigabyte"
1078 ],
1079 [
1080 pow( 1024, 4 ),
1081 "1 TB",
1082 "1 terabyte"
1083 ],
1084 [
1085 pow( 1024, 5 ),
1086 "1 PB",
1087 "1 petabyte"
1088 ],
1089 [
1090 pow( 1024, 6 ),
1091 "1 EB",
1092 "1,024 exabyte"
1093 ],
1094 [
1095 pow( 1024, 7 ),
1096 "1 ZB",
1097 "1 zetabyte"
1098 ],
1099 [
1100 pow( 1024, 8 ),
1101 "1 YB",
1102 "1 yottabyte"
1103 ],
1104 // How big!? THIS BIG!
1105 ];
1106 }
1107
1112 public function testFormatBitrate( $bps, $expected, $msg ) {
1113 $this->assertEquals(
1114 $expected,
1115 $this->getLang()->formatBitrate( $bps ),
1116 "formatBitrate('$bps'): $msg"
1117 );
1118 }
1119
1120 public static function provideFormatBitrate() {
1121 return [
1122 [
1123 0,
1124 "0 bps",
1125 "0 bits per second"
1126 ],
1127 [
1128 999,
1129 "999 bps",
1130 "999 bits per second"
1131 ],
1132 [
1133 1000,
1134 "1 kbps",
1135 "1 kilobit per second"
1136 ],
1137 [
1138 1000 * 1000,
1139 "1 Mbps",
1140 "1 megabit per second"
1141 ],
1142 [
1143 pow( 10, 9 ),
1144 "1 Gbps",
1145 "1 gigabit per second"
1146 ],
1147 [
1148 pow( 10, 12 ),
1149 "1 Tbps",
1150 "1 terabit per second"
1151 ],
1152 [
1153 pow( 10, 15 ),
1154 "1 Pbps",
1155 "1 petabit per second"
1156 ],
1157 [
1158 pow( 10, 18 ),
1159 "1 Ebps",
1160 "1 exabit per second"
1161 ],
1162 [
1163 pow( 10, 21 ),
1164 "1 Zbps",
1165 "1 zetabit per second"
1166 ],
1167 [
1168 pow( 10, 24 ),
1169 "1 Ybps",
1170 "1 yottabit per second"
1171 ],
1172 [
1173 pow( 10, 27 ),
1174 "1,000 Ybps",
1175 "1,000 yottabits per second"
1176 ],
1177 ];
1178 }
1179
1184 public function testFormatDuration( $duration, $expected, $intervals = [] ) {
1185 $this->assertEquals(
1186 $expected,
1187 $this->getLang()->formatDuration( $duration, $intervals ),
1188 "formatDuration('$duration'): $expected"
1189 );
1190 }
1191
1192 public static function provideFormatDuration() {
1193 return [
1194 [
1195 0,
1196 '0 seconds',
1197 ],
1198 [
1199 1,
1200 '1 second',
1201 ],
1202 [
1203 2,
1204 '2 seconds',
1205 ],
1206 [
1207 60,
1208 '1 minute',
1209 ],
1210 [
1211 2 * 60,
1212 '2 minutes',
1213 ],
1214 [
1215 3600,
1216 '1 hour',
1217 ],
1218 [
1219 2 * 3600,
1220 '2 hours',
1221 ],
1222 [
1223 24 * 3600,
1224 '1 day',
1225 ],
1226 [
1227 2 * 86400,
1228 '2 days',
1229 ],
1230 [
1231 // ( 365 + ( 24 * 3 + 25 ) / 400 ) * 86400 = 31556952
1232 ( 365 + ( 24 * 3 + 25 ) / 400.0 ) * 86400,
1233 '1 year',
1234 ],
1235 [
1236 2 * 31556952,
1237 '2 years',
1238 ],
1239 [
1240 10 * 31556952,
1241 '1 decade',
1242 ],
1243 [
1244 20 * 31556952,
1245 '2 decades',
1246 ],
1247 [
1248 100 * 31556952,
1249 '1 century',
1250 ],
1251 [
1252 200 * 31556952,
1253 '2 centuries',
1254 ],
1255 [
1256 1000 * 31556952,
1257 '1 millennium',
1258 ],
1259 [
1260 2000 * 31556952,
1261 '2 millennia',
1262 ],
1263 [
1264 9001,
1265 '2 hours, 30 minutes and 1 second'
1266 ],
1267 [
1268 3601,
1269 '1 hour and 1 second'
1270 ],
1271 [
1272 31556952 + 2 * 86400 + 9000,
1273 '1 year, 2 days, 2 hours and 30 minutes'
1274 ],
1275 [
1276 42 * 1000 * 31556952 + 42,
1277 '42 millennia and 42 seconds'
1278 ],
1279 [
1280 60,
1281 '60 seconds',
1282 [ 'seconds' ],
1283 ],
1284 [
1285 61,
1286 '61 seconds',
1287 [ 'seconds' ],
1288 ],
1289 [
1290 1,
1291 '1 second',
1292 [ 'seconds' ],
1293 ],
1294 [
1295 31556952 + 2 * 86400 + 9000,
1296 '1 year, 2 days and 150 minutes',
1297 [ 'years', 'days', 'minutes' ],
1298 ],
1299 [
1300 42,
1301 '0 days',
1302 [ 'years', 'days' ],
1303 ],
1304 [
1305 31556952 + 2 * 86400 + 9000,
1306 '1 year, 2 days and 150 minutes',
1307 [ 'minutes', 'days', 'years' ],
1308 ],
1309 [
1310 42,
1311 '0 days',
1312 [ 'days', 'years' ],
1313 ],
1314 ];
1315 }
1316
1321 public function testCheckTitleEncoding( $s ) {
1322 $this->assertEquals(
1323 $s,
1324 $this->getLang()->checkTitleEncoding( $s ),
1325 "checkTitleEncoding('$s')"
1326 );
1327 }
1328
1329 public static function provideCheckTitleEncodingData() {
1330 // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
1331 return [
1332 [ "" ],
1333 [ "United States of America" ], // 7bit ASCII
1334 [ rawurldecode( "S%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e" ) ],
1335 [
1336 rawurldecode(
1337 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn"
1338 )
1339 ],
1340 // The following two data sets come from bug 36839. They fail if checkTitleEncoding uses a regexp to test for
1341 // valid UTF-8 encoding and the pcre.recursion_limit is low (like, say, 1024). They succeed if checkTitleEncoding
1342 // uses mb_check_encoding for its test.
1343 [
1344 rawurldecode(
1345 "Acteur%7CAlbert%20Robbins%7CAnglais%7CAnn%20Donahue%7CAnthony%20E.%20Zuiker%7CCarol%20Mendelsohn%7C"
1346 . "Catherine%20Willows%7CDavid%20Hodges%7CDavid%20Phillips%7CGil%20Grissom%7CGreg%20Sanders%7CHodges%7C"
1347 . "Internet%20Movie%20Database%7CJim%20Brass%7CLady%20Heather%7C"
1348 . "Les%20Experts%20(s%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e)%7CLes%20Experts%20:%20Manhattan%7C"
1349 . "Les%20Experts%20:%20Miami%7CListe%20des%20personnages%20des%20Experts%7C"
1350 . "Liste%20des%20%C3%A9pisodes%20des%20Experts%7CMod%C3%A8le%20discussion:Palette%20Les%20Experts%7C"
1351 . "Nick%20Stokes%7CPersonnage%20de%20fiction%7CPersonnage%20fictif%7CPersonnage%20de%20fiction%7C"
1352 . "Personnages%20r%C3%A9currents%20dans%20Les%20Experts%7CRaymond%20Langston%7CRiley%20Adams%7C"
1353 . "Saison%201%20des%20Experts%7CSaison%2010%20des%20Experts%7CSaison%2011%20des%20Experts%7C"
1354 . "Saison%2012%20des%20Experts%7CSaison%202%20des%20Experts%7CSaison%203%20des%20Experts%7C"
1355 . "Saison%204%20des%20Experts%7CSaison%205%20des%20Experts%7CSaison%206%20des%20Experts%7C"
1356 . "Saison%207%20des%20Experts%7CSaison%208%20des%20Experts%7CSaison%209%20des%20Experts%7C"
1357 . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C"
1358 . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis"
1359 ),
1360 ],
1361 [
1362 rawurldecode(
1363 "Mod%C3%A8le%3AArrondissements%20homonymes%7CMod%C3%A8le%3ABandeau%20standard%20pour%20page%20d'homonymie%7C"
1364 . "Mod%C3%A8le%3ABatailles%20homonymes%7CMod%C3%A8le%3ACantons%20homonymes%7C"
1365 . "Mod%C3%A8le%3ACommunes%20fran%C3%A7aises%20homonymes%7CMod%C3%A8le%3AFilms%20homonymes%7C"
1366 . "Mod%C3%A8le%3AGouvernements%20homonymes%7CMod%C3%A8le%3AGuerres%20homonymes%7CMod%C3%A8le%3AHomonymie%7C"
1367 . "Mod%C3%A8le%3AHomonymie%20bateau%7CMod%C3%A8le%3AHomonymie%20d'%C3%A9tablissements%20scolaires%20ou"
1368 . "%20universitaires%7CMod%C3%A8le%3AHomonymie%20d'%C3%AEles%7CMod%C3%A8le%3AHomonymie%20de%20clubs%20sportifs%7C"
1369 . "Mod%C3%A8le%3AHomonymie%20de%20comt%C3%A9s%7CMod%C3%A8le%3AHomonymie%20de%20monument%7C"
1370 . "Mod%C3%A8le%3AHomonymie%20de%20nom%20romain%7CMod%C3%A8le%3AHomonymie%20de%20parti%20politique%7C"
1371 . "Mod%C3%A8le%3AHomonymie%20de%20route%7CMod%C3%A8le%3AHomonymie%20dynastique%7C"
1372 . "Mod%C3%A8le%3AHomonymie%20vid%C3%A9oludique%7CMod%C3%A8le%3AHomonymie%20%C3%A9difice%20religieux%7C"
1373 . "Mod%C3%A8le%3AInternationalisation%7CMod%C3%A8le%3AIsom%C3%A9rie%7CMod%C3%A8le%3AParonymie%7C"
1374 . "Mod%C3%A8le%3APatronyme%7CMod%C3%A8le%3APatronyme%20basque%7CMod%C3%A8le%3APatronyme%20italien%7C"
1375 . "Mod%C3%A8le%3APatronymie%7CMod%C3%A8le%3APersonnes%20homonymes%7CMod%C3%A8le%3ASaints%20homonymes%7C"
1376 . "Mod%C3%A8le%3ATitres%20homonymes%7CMod%C3%A8le%3AToponymie%7CMod%C3%A8le%3AUnit%C3%A9s%20homonymes%7C"
1377 . "Mod%C3%A8le%3AVilles%20homonymes%7CMod%C3%A8le%3A%C3%89difices%20religieux%20homonymes"
1378 )
1379 ]
1380 ];
1381 // @codingStandardsIgnoreEnd
1382 }
1383
1388 public function testRomanNumerals( $num, $numerals ) {
1389 $this->assertEquals(
1390 $numerals,
1391 Language::romanNumeral( $num ),
1392 "romanNumeral('$num')"
1393 );
1394 }
1395
1396 public static function provideRomanNumeralsData() {
1397 return [
1398 [ 1, 'I' ],
1399 [ 2, 'II' ],
1400 [ 3, 'III' ],
1401 [ 4, 'IV' ],
1402 [ 5, 'V' ],
1403 [ 6, 'VI' ],
1404 [ 7, 'VII' ],
1405 [ 8, 'VIII' ],
1406 [ 9, 'IX' ],
1407 [ 10, 'X' ],
1408 [ 20, 'XX' ],
1409 [ 30, 'XXX' ],
1410 [ 40, 'XL' ],
1411 [ 49, 'XLIX' ],
1412 [ 50, 'L' ],
1413 [ 60, 'LX' ],
1414 [ 70, 'LXX' ],
1415 [ 80, 'LXXX' ],
1416 [ 90, 'XC' ],
1417 [ 99, 'XCIX' ],
1418 [ 100, 'C' ],
1419 [ 200, 'CC' ],
1420 [ 300, 'CCC' ],
1421 [ 400, 'CD' ],
1422 [ 500, 'D' ],
1423 [ 600, 'DC' ],
1424 [ 700, 'DCC' ],
1425 [ 800, 'DCCC' ],
1426 [ 900, 'CM' ],
1427 [ 999, 'CMXCIX' ],
1428 [ 1000, 'M' ],
1429 [ 1989, 'MCMLXXXIX' ],
1430 [ 2000, 'MM' ],
1431 [ 3000, 'MMM' ],
1432 [ 4000, 'MMMM' ],
1433 [ 5000, 'MMMMM' ],
1434 [ 6000, 'MMMMMM' ],
1435 [ 7000, 'MMMMMMM' ],
1436 [ 8000, 'MMMMMMMM' ],
1437 [ 9000, 'MMMMMMMMM' ],
1438 [ 9999, 'MMMMMMMMMCMXCIX' ],
1439 [ 10000, 'MMMMMMMMMM' ],
1440 ];
1441 }
1442
1447 public function testHebrewNumeral( $num, $numerals ) {
1448 $this->assertEquals(
1449 $numerals,
1450 Language::hebrewNumeral( $num ),
1451 "hebrewNumeral('$num')"
1452 );
1453 }
1454
1455 public static function provideHebrewNumeralsData() {
1456 return [
1457 [ -1, -1 ],
1458 [ 0, 0 ],
1459 [ 1, "א'" ],
1460 [ 2, "ב'" ],
1461 [ 3, "ג'" ],
1462 [ 4, "ד'" ],
1463 [ 5, "ה'" ],
1464 [ 6, "ו'" ],
1465 [ 7, "ז'" ],
1466 [ 8, "ח'" ],
1467 [ 9, "ט'" ],
1468 [ 10, "י'" ],
1469 [ 11, 'י"א' ],
1470 [ 14, 'י"ד' ],
1471 [ 15, 'ט"ו' ],
1472 [ 16, 'ט"ז' ],
1473 [ 17, 'י"ז' ],
1474 [ 20, "כ'" ],
1475 [ 21, 'כ"א' ],
1476 [ 30, "ל'" ],
1477 [ 40, "מ'" ],
1478 [ 50, "נ'" ],
1479 [ 60, "ס'" ],
1480 [ 70, "ע'" ],
1481 [ 80, "פ'" ],
1482 [ 90, "צ'" ],
1483 [ 99, 'צ"ט' ],
1484 [ 100, "ק'" ],
1485 [ 101, 'ק"א' ],
1486 [ 110, 'ק"י' ],
1487 [ 200, "ר'" ],
1488 [ 300, "ש'" ],
1489 [ 400, "ת'" ],
1490 [ 500, 'ת"ק' ],
1491 [ 800, 'ת"ת' ],
1492 [ 1000, "א' אלף" ],
1493 [ 1001, "א'א'" ],
1494 [ 1012, "א'י\"ב" ],
1495 [ 1020, "א'ך'" ],
1496 [ 1030, "א'ל'" ],
1497 [ 1081, "א'פ\"א" ],
1498 [ 2000, "ב' אלפים" ],
1499 [ 2016, "ב'ט\"ז" ],
1500 [ 3000, "ג' אלפים" ],
1501 [ 4000, "ד' אלפים" ],
1502 [ 4904, "ד'תתק\"ד" ],
1503 [ 5000, "ה' אלפים" ],
1504 [ 5680, "ה'תר\"ף" ],
1505 [ 5690, "ה'תר\"ץ" ],
1506 [ 5708, "ה'תש\"ח" ],
1507 [ 5720, "ה'תש\"ך" ],
1508 [ 5740, "ה'תש\"ם" ],
1509 [ 5750, "ה'תש\"ן" ],
1510 [ 5775, "ה'תשע\"ה" ],
1511 ];
1512 }
1513
1518 public function testConvertPlural( $expected, $number, $forms ) {
1519 $chosen = $this->getLang()->convertPlural( $number, $forms );
1520 $this->assertEquals( $expected, $chosen );
1521 }
1522
1523 public static function providePluralData() {
1524 // Params are: [expected text, number given, [the plural forms]]
1525 return [
1526 [ 'plural', 0, [
1527 'singular', 'plural'
1528 ] ],
1529 [ 'explicit zero', 0, [
1530 '0=explicit zero', 'singular', 'plural'
1531 ] ],
1532 [ 'explicit one', 1, [
1533 'singular', 'plural', '1=explicit one',
1534 ] ],
1535 [ 'singular', 1, [
1536 'singular', 'plural', '0=explicit zero',
1537 ] ],
1538 [ 'plural', 3, [
1539 '0=explicit zero', '1=explicit one', 'singular', 'plural'
1540 ] ],
1541 [ 'explicit eleven', 11, [
1542 'singular', 'plural', '11=explicit eleven',
1543 ] ],
1544 [ 'plural', 12, [
1545 'singular', 'plural', '11=explicit twelve',
1546 ] ],
1547 [ 'plural', 12, [
1548 'singular', 'plural', '=explicit form',
1549 ] ],
1550 [ 'other', 2, [
1551 'kissa=kala', '1=2=3', 'other',
1552 ] ],
1553 [ '', 2, [
1554 '0=explicit zero', '1=explicit one',
1555 ] ],
1556 ];
1557 }
1558
1562 public function testEmbedBidi() {
1563 $lre = "\xE2\x80\xAA"; // U+202A LEFT-TO-RIGHT EMBEDDING
1564 $rle = "\xE2\x80\xAB"; // U+202B RIGHT-TO-LEFT EMBEDDING
1565 $pdf = "\xE2\x80\xAC"; // U+202C POP DIRECTIONAL FORMATTING
1566 $lang = $this->getLang();
1567 $this->assertEquals(
1568 '123',
1569 $lang->embedBidi( '123' ),
1570 'embedBidi with neutral argument'
1571 );
1572 $this->assertEquals(
1573 $lre . 'Ben_(WMF)' . $pdf,
1574 $lang->embedBidi( 'Ben_(WMF)' ),
1575 'embedBidi with LTR argument'
1576 );
1577 $this->assertEquals(
1578 $rle . 'יהודי (מנוחין)' . $pdf,
1579 $lang->embedBidi( 'יהודי (מנוחין)' ),
1580 'embedBidi with RTL argument'
1581 );
1582 }
1583
1588 public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
1589 $lang = $this->getLang();
1590 if ( is_array( $expectedData ) ) {
1591 list( $func, $arg ) = $expectedData;
1592 $expected = $lang->$func( $arg );
1593 } else {
1594 $expected = $expectedData;
1595 }
1596 $this->assertEquals( $expected, $lang->translateBlockExpiry( $str ), $desc );
1597 }
1598
1599 public static function provideTranslateBlockExpiry() {
1600 return [
1601 [ '2 hours', '2 hours', 'simple data from ipboptions' ],
1602 [ 'indefinite', 'infinite', 'infinite from ipboptions' ],
1603 [ 'indefinite', 'infinity', 'alternative infinite from ipboptions' ],
1604 [ 'indefinite', 'indefinite', 'another alternative infinite from ipboptions' ],
1605 [ [ 'formatDuration', 1023 * 60 * 60 ], '1023 hours', 'relative' ],
1606 [ [ 'formatDuration', -1023 ], '-1023 seconds', 'negative relative' ],
1607 [ [ 'formatDuration', 0 ], 'now', 'now' ],
1608 [
1609 [ 'timeanddate', '20120102070000' ],
1610 '2012-1-1 7:00 +1 day',
1611 'mixed, handled as absolute'
1612 ],
1613 [ [ 'timeanddate', '19910203040506' ], '1991-2-3 4:05:06', 'absolute' ],
1614 [ [ 'timeanddate', '19700101000000' ], '1970-1-1 0:00:00', 'absolute at epoch' ],
1615 [ [ 'timeanddate', '19691231235959' ], '1969-12-31 23:59:59', 'time before epoch' ],
1616 [ 'dummy', 'dummy', 'return garbage as is' ],
1617 ];
1618 }
1619
1623 public function testParseFormattedNumber( $langCode, $number ) {
1624 $lang = Language::factory( $langCode );
1625
1626 $localisedNum = $lang->formatNum( $number );
1627 $normalisedNum = $lang->parseFormattedNumber( $localisedNum );
1628
1629 $this->assertEquals( $number, $normalisedNum );
1630 }
1631
1633 return [
1634 [ 'de', 377.01 ],
1635 [ 'fa', 334 ],
1636 [ 'fa', 382.772 ],
1637 [ 'ar', 1844 ],
1638 [ 'lzh', 3731 ],
1639 [ 'zh-classical', 7432 ]
1640 ];
1641 }
1642
1647 public function testCommafy( $number, $numbersWithCommas ) {
1648 $this->assertEquals(
1649 $numbersWithCommas,
1650 $this->getLang()->commafy( $number ),
1651 "commafy('$number')"
1652 );
1653 }
1654
1655 public static function provideCommafyData() {
1656 return [
1657 [ -1, '-1' ],
1658 [ 10, '10' ],
1659 [ 100, '100' ],
1660 [ 1000, '1,000' ],
1661 [ 10000, '10,000' ],
1662 [ 100000, '100,000' ],
1663 [ 1000000, '1,000,000' ],
1664 [ -1.0001, '-1.0001' ],
1665 [ 1.0001, '1.0001' ],
1666 [ 10.0001, '10.0001' ],
1667 [ 100.0001, '100.0001' ],
1668 [ 1000.0001, '1,000.0001' ],
1669 [ 10000.0001, '10,000.0001' ],
1670 [ 100000.0001, '100,000.0001' ],
1671 [ 1000000.0001, '1,000,000.0001' ],
1672 [ '200000000000000000000', '200,000,000,000,000,000,000' ],
1673 [ '-200000000000000000000', '-200,000,000,000,000,000,000' ],
1674 ];
1675 }
1676
1680 public function testListToText() {
1681 $lang = $this->getLang();
1682 $and = $lang->getMessageFromDB( 'and' );
1683 $s = $lang->getMessageFromDB( 'word-separator' );
1684 $c = $lang->getMessageFromDB( 'comma-separator' );
1685
1686 $this->assertEquals( '', $lang->listToText( [] ) );
1687 $this->assertEquals( 'a', $lang->listToText( [ 'a' ] ) );
1688 $this->assertEquals( "a{$and}{$s}b", $lang->listToText( [ 'a', 'b' ] ) );
1689 $this->assertEquals( "a{$c}b{$and}{$s}c", $lang->listToText( [ 'a', 'b', 'c' ] ) );
1690 $this->assertEquals( "a{$c}b{$c}c{$and}{$s}d", $lang->listToText( [ 'a', 'b', 'c', 'd' ] ) );
1691 }
1692
1697 public function testIsSupportedLanguage( $code, $expected, $comment ) {
1698 $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
1699 }
1700
1701 public static function provideIsSupportedLanguage() {
1702 return [
1703 [ 'en', true, 'is supported language' ],
1704 [ 'fi', true, 'is supported language' ],
1705 [ 'bunny', false, 'is not supported language' ],
1706 [ 'FI', false, 'is not supported language, input should be in lower case' ],
1707 ];
1708 }
1709
1714 public function testGetParentLanguage( $code, $expected, $comment ) {
1715 $lang = Language::factory( $code );
1716 if ( is_null( $expected ) ) {
1717 $this->assertNull( $lang->getParentLanguage(), $comment );
1718 } else {
1719 $this->assertEquals( $expected, $lang->getParentLanguage()->getCode(), $comment );
1720 }
1721 }
1722
1723 public static function provideGetParentLanguage() {
1724 return [
1725 [ 'zh-cn', 'zh', 'zh is the parent language of zh-cn' ],
1726 [ 'zh', 'zh', 'zh is defined as the parent language of zh, '
1727 . 'because zh converter can convert zh-cn to zh' ],
1728 [ 'zh-invalid', null, 'do not be fooled by arbitrarily composed language codes' ],
1729 [ 'en-gb', null, 'en does not have converter' ],
1730 [ 'en', null, 'en does not have converter. Although FakeConverter '
1731 . 'handles en -> en conversion but it is useless' ],
1732 ];
1733 }
1734
1739 public function testGetNamespaceAliases( $languageCode, $subset ) {
1740 $language = Language::factory( $languageCode );
1741 $aliases = $language->getNamespaceAliases();
1742 foreach ( $subset as $alias => $nsId ) {
1743 $this->assertEquals( $nsId, $aliases[$alias] );
1744 }
1745 }
1746
1747 public static function provideGetNamespaceAliases() {
1748 // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
1749 return [
1750 [
1751 'zh',
1752 [
1753 '文件' => NS_FILE,
1754 '檔案' => NS_FILE,
1755 ],
1756 ],
1757 ];
1758 }
1759
1760 public function testEquals() {
1761 $en1 = new Language();
1762 $en1->setCode( 'en' );
1763
1764 $en2 = Language::factory( 'en' );
1765 $en2->setCode( 'en' );
1766
1767 $this->assertTrue( $en1->equals( $en2 ), 'en equals en' );
1768
1769 $fr = Language::factory( 'fr' );
1770 $this->assertFalse( $en1->equals( $fr ), 'en not equals fr' );
1771 }
1772}
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Helping class to run tests using a clean language instance.
testTranslateBlockExpiry( $expectedData, $str, $desc)
Language::translateBlockExpiry() provideTranslateBlockExpiry.
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.
testParseFormattedNumber( $langCode, $number)
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()
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
testTruncate()
Language::truncate.
testSprintfDateNoTtlIfNotNeeded()
sprintfDate should only calculate a TTL if the caller is going to use it.
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.
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
$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 document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
const NS_FILE
Definition Defines.php:62
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:1950
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:887
$comment
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
if(!isset( $args[0])) $lang