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