28 if ( !defined(
'MEDIAWIKI' ) ) {
29 echo
"This file is part of MediaWiki, it is not a valid entry point.\n";
33 if ( function_exists(
'mb_strtoupper' ) ) {
34 mb_internal_encoding(
'UTF-8' );
48 function autoConvert( $text, $variant =
false ) {
return $text; }
51 function convertTo( $text, $variant ) {
return $text; }
67 function validateVariant( $variant =
null ) {
return $variant === $this->mLang->getCode() ? $variant :
null; }
68 function translate( $text, $variant ) {
return $text; }
104 'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
109 'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat'
113 'january',
'february',
'march',
'april',
'may_long',
'june',
114 'july',
'august',
'september',
'october',
'november',
118 'january-gen',
'february-gen',
'march-gen',
'april-gen',
'may-gen',
'june-gen',
119 'july-gen',
'august-gen',
'september-gen',
'october-gen',
'november-gen',
123 'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
124 'sep',
'oct',
'nov',
'dec'
128 'iranian-calendar-m1',
'iranian-calendar-m2',
'iranian-calendar-m3',
129 'iranian-calendar-m4',
'iranian-calendar-m5',
'iranian-calendar-m6',
130 'iranian-calendar-m7',
'iranian-calendar-m8',
'iranian-calendar-m9',
131 'iranian-calendar-m10',
'iranian-calendar-m11',
'iranian-calendar-m12'
135 'hebrew-calendar-m1',
'hebrew-calendar-m2',
'hebrew-calendar-m3',
136 'hebrew-calendar-m4',
'hebrew-calendar-m5',
'hebrew-calendar-m6',
137 'hebrew-calendar-m7',
'hebrew-calendar-m8',
'hebrew-calendar-m9',
138 'hebrew-calendar-m10',
'hebrew-calendar-m11',
'hebrew-calendar-m12',
139 'hebrew-calendar-m6a',
'hebrew-calendar-m6b'
143 'hebrew-calendar-m1-gen',
'hebrew-calendar-m2-gen',
'hebrew-calendar-m3-gen',
144 'hebrew-calendar-m4-gen',
'hebrew-calendar-m5-gen',
'hebrew-calendar-m6-gen',
145 'hebrew-calendar-m7-gen',
'hebrew-calendar-m8-gen',
'hebrew-calendar-m9-gen',
146 'hebrew-calendar-m10-gen',
'hebrew-calendar-m11-gen',
'hebrew-calendar-m12-gen',
147 'hebrew-calendar-m6a-gen',
'hebrew-calendar-m6b-gen'
151 'hijri-calendar-m1',
'hijri-calendar-m2',
'hijri-calendar-m3',
152 'hijri-calendar-m4',
'hijri-calendar-m5',
'hijri-calendar-m6',
153 'hijri-calendar-m7',
'hijri-calendar-m8',
'hijri-calendar-m9',
154 'hijri-calendar-m10',
'hijri-calendar-m11',
'hijri-calendar-m12'
162 'millennia' => 31556952000,
163 'centuries' => 3155695200,
164 'decades' => 315569520,
186 static function factory( $code ) {
187 global $wgDummyLanguageCodes, $wgLangObjCacheSize;
189 if ( isset( $wgDummyLanguageCodes[$code] ) ) {
190 $code = $wgDummyLanguageCodes[$code];
194 $langObj = isset( self::$mLangObjCache[$code] )
195 ? self::$mLangObjCache[$code]
199 self::$mLangObjCache = array_merge(
array( $code => $langObj ), self::$mLangObjCache );
201 self::$mLangObjCache = array_slice( self::$mLangObjCache, 0, $wgLangObjCacheSize,
true );
215 || strcspn( $code,
":/\\\000" ) !== strlen( $code )
217 throw new MWException(
"Invalid language code \"$code\"" );
231 if ( class_exists( $class ) ) {
238 foreach ( $fallbacks
as $fallbackCode ) {
240 throw new MWException(
"Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
245 if ( class_exists( $class ) ) {
247 $lang->setCode( $code );
252 throw new MWException(
"Invalid fallback sequence for language '$code'" );
265 && ( is_readable( self::getMessagesFileName( $code ) )
266 || is_readable( self::getJsonMessagesFileName( $code ) )
288 $alphanum =
'[a-z0-9]';
289 $x =
'x'; #
private use singleton
290 $singleton =
'[a-wy-z]'; # other singleton
291 $s = $lenient ?
'[-_]' :
'-';
293 $language =
"$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
294 $script =
"$alpha{4}"; # ISO 15924
295 $region =
"(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2 or UN M.49
296 $variant =
"(?:$alphanum{5,8}|$digit$alphanum{3})";
297 $extension =
"$singleton(?:$s$alphanum{2,8})+";
298 $privateUse =
"$x(?:$s$alphanum{1,8})+";
300 # Define certain grandfathered codes, since otherwise the regex is pretty useless.
301 # Since these are limited, this is safe even later changes to the registry --
302 # the only oddity is that it might change the type of the tag, and thus
303 # the results from the capturing groups.
304 # http://www.iana.org/assignments/language-subtag-registry
306 $grandfathered =
"en{$s}GB{$s}oed"
307 .
"|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
308 .
"|no{$s}(?:bok|nyn)"
309 .
"|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
310 .
"|zh{$s}min{$s}nan";
312 $variantList =
"$variant(?:$s$variant)*";
313 $extensionList =
"$extension(?:$s$extension)*";
315 $langtag =
"(?:($language)"
318 .
"(?:$s$variantList)?"
319 .
"(?:$s$extensionList)?"
320 .
"(?:$s$privateUse)?)";
322 # The final breakdown, with capturing groups for each of these components
323 # The variants, extensions, grandfathered, and private-use may have interior '-'
325 $root =
"^(?:$langtag|$privateUse|$grandfathered)$";
327 return (
bool)preg_match(
"/$root/", strtolower( $code ) );
341 if ( isset(
$cache[$code] ) ) {
349 strcspn( $code,
":/\\\000&<>'\"" ) === strlen( $code )
367 if ( !is_string( $code ) ) {
368 if ( is_object( $code ) ) {
369 $addmsg =
" of class " . get_class( $code );
373 $type = gettype( $code );
374 throw new MWException( __METHOD__ .
" must be passed a string, $type given$addmsg" );
377 return (
bool)preg_match(
'/^[a-z0-9-]{2,}$/i', $code );
393 if ( !self::isValidBuiltInCode( $tag ) ) {
399 include
"$IP/languages/Names.php";
403 || self::fetchLanguageName( $tag, $tag ) !==
''
416 if ( $code ==
'en' ) {
419 return 'Language' . str_replace(
'-',
'_',
ucfirst( $code ) );
431 if ( $class ===
'Language' ) {
435 if ( file_exists(
"$IP/languages/classes/$class.php" ) ) {
436 include_once
"$IP/languages/classes/$class.php";
446 if ( is_null( self::$dataCache ) ) {
447 global $wgLocalisationCacheConf;
448 $class = $wgLocalisationCacheConf[
'class'];
449 self::$dataCache =
new $class( $wgLocalisationCacheConf );
457 if ( get_class( $this ) ==
'Language' ) {
460 $this->mCode = str_replace(
'_',
'-', strtolower( substr( get_class( $this ), 8 ) ) );
470 unset( $this->
$name );
503 return self::$dataCache->getItem( $this->mCode,
'bookstoreList' );
513 if ( is_null( $this->namespaceNames ) ) {
514 global $wgMetaNamespace, $wgMetaNamespaceTalk, $wgExtraNamespaces;
516 $this->namespaceNames = self::$dataCache->getItem( $this->mCode,
'namespaceNames' );
519 $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames + $validNamespaces;
521 $this->namespaceNames[
NS_PROJECT] = $wgMetaNamespace;
522 if ( $wgMetaNamespaceTalk ) {
530 # Sometimes a language will be localised but not actually exist on this wiki.
531 foreach ( $this->namespaceNames
as $key => $text ) {
532 if ( !isset( $validNamespaces[$key] ) ) {
533 unset( $this->namespaceNames[$key] );
537 # The above mixing may leave namespaces out of canonical order.
538 # Re-order by namespace ID number...
539 ksort( $this->namespaceNames );
541 wfRunHooks(
'LanguageGetNamespaces',
array( &$this->namespaceNames ) );
552 $this->mNamespaceIds =
null;
559 $this->namespaceNames =
null;
560 $this->mNamespaceIds =
null;
561 $this->namespaceAliases =
null;
574 foreach ( $ns
as $k => $v ) {
575 $ns[$k] = strtr( $v,
'_',
' ' );
592 return isset( $ns[$index] ) ? $ns[$index] :
false;
610 return strtr( $ns,
'_',
' ' );
622 global $wgExtraGenderNamespaces;
624 $ns = $wgExtraGenderNamespaces + self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
625 return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->
getNsText( $index );
635 global $wgExtraGenderNamespaces, $wgExtraNamespaces;
636 if ( count( $wgExtraGenderNamespaces ) > 0 ) {
639 } elseif ( isset( $wgExtraNamespaces[
NS_USER] ) && isset( $wgExtraNamespaces[
NS_USER_TALK] ) ) {
645 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
646 return count( $aliases ) > 0;
659 $lctext = $this->
lc( $text );
661 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
668 if ( is_null( $this->namespaceAliases ) ) {
669 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceAliases' );
673 foreach ( $aliases
as $name => $index ) {
675 unset( $aliases[
$name] );
677 $aliases[
$name] = $index;
682 global $wgExtraGenderNamespaces;
683 $genders = $wgExtraGenderNamespaces + (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
684 foreach ( $genders
as $index => $forms ) {
685 foreach ( $forms
as $alias ) {
686 $aliases[$alias] = $index;
690 # Also add converted namespace names as aliases, to avoid confusion.
691 $convertedNames =
array();
693 if ( $variant === $this->mCode ) {
697 $convertedNames[$this->
getConverter()->convertNamespace( $ns, $variant )] = $ns;
701 $this->namespaceAliases = $aliases + $convertedNames;
710 if ( is_null( $this->mNamespaceIds ) ) {
712 # Put namespace names and aliases into a hashtable.
713 # If this is too slow, then we should arrange it so that it is done
714 # before caching. The catch is that at pre-cache time, the above
715 # class-specific fixup hasn't been done.
716 $this->mNamespaceIds =
array();
718 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
721 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
725 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
740 $lctext = $this->
lc( $text );
742 if ( $ns !==
null ) {
746 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
757 $msg =
"variantname-$code";
758 if ( $usemsg &&
wfMessage( $msg )->exists() ) {
763 return $name; #
if it's defined as a language name, show that
765 # otherwise, output the language code
774 function specialPage( $name ) {
775 $aliases = $this->getSpecialPageAliases();
776 if ( isset( $aliases[$name][0] ) ) {
777 $name = $aliases[$name][0];
779 return $this->getNsText( NS_SPECIAL ) . ':
' . $name;
785 function getDatePreferences() {
786 return self::$dataCache->getItem( $this->mCode, 'datePreferences
' );
792 function getDateFormats() {
793 return self::$dataCache->getItem( $this->mCode, 'dateFormats
' );
799 function getDefaultDateFormat() {
800 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat
' );
801 if ( $df === 'dmy or mdy
' ) {
802 global $wgAmericanDates;
803 return $wgAmericanDates ? 'mdy
' : 'dmy
';
812 function getDatePreferenceMigrationMap() {
813 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap
' );
820 function getImageFile( $image ) {
821 return self::$dataCache->getSubitem( $this->mCode, 'imageFiles
', $image );
827 function getExtraUserToggles() {
828 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles
' );
835 function getUserToggle( $tog ) {
836 return $this->getMessageFromDB( "tog-$tog" );
849 public static function getLanguageNames( $customisedOnly = false ) {
850 return self::fetchLanguageNames( null, $customisedOnly ? 'mwfile
' : 'mw
' );
862 public static function getTranslatedLanguageNames( $code ) {
863 return self::fetchLanguageNames( $code, 'all
' );
877 public static function fetchLanguageNames( $inLanguage = null, $include = 'mw
' ) {
878 global $wgExtraLanguageNames;
879 static $coreLanguageNames;
881 if ( $coreLanguageNames === null ) {
883 include "$IP/languages/Names.php";
889 # TODO: also include when $inLanguage is null, when this code is more efficient
890 wfRunHooks( 'LanguageGetTranslatedLanguageNames
', array( &$names, $inLanguage ) );
893 $mwNames = $wgExtraLanguageNames + $coreLanguageNames;
894 foreach ( $mwNames as $mwCode => $mwName ) {
895 # - Prefer own MediaWiki native name when not using the hook
896 # - For other names just add if not added through the hook
897 if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
898 $names[$mwCode] = $mwName;
902 if ( $include === 'all
' ) {
907 $coreCodes = array_keys( $mwNames );
908 foreach ( $coreCodes as $coreCode ) {
909 $returnMw[$coreCode] = $names[$coreCode];
912 if ( $include === 'mwfile
' ) {
913 $namesMwFile = array();
914 # We do this using a foreach over the codes instead of a directory
915 # loop so that messages files in extensions will work correctly.
916 foreach ( $returnMw as $code => $value ) {
917 if ( is_readable( self::getMessagesFileName( $code ) )
918 || is_readable( self::getJsonMessagesFileName( $code ) )
920 $namesMwFile[$code] = $names[$code];
927 # 'mw
' option; default if it's not one
of the other two
options (all/mwfile)
938 public static function fetchLanguageName( $code, $inLanguage =
null, $include =
'all' ) {
939 $code = strtolower( $code );
941 return !array_key_exists( $code, $array ) ?
'' : $array[$code];
951 return wfMessage( $msg )->inLanguage( $this )->text();
977 $monthNames =
array(
'' );
978 for ( $i = 1; $i < 13; $i++ ) {
1004 $monthNames =
array(
'' );
1005 for ( $i = 1; $i < 13; $i++ ) {
1032 return $this->
getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
1040 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1048 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1056 return $this->
getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1124 function sprintfDate( $format, $ts, DateTimeZone $zone =
null ) {
1129 $dateTimeObj =
false;
1138 if ( strlen( $ts ) !== 14 ) {
1139 throw new MWException( __METHOD__ .
": The timestamp $ts should have 14 characters" );
1142 if ( !ctype_digit( $ts ) ) {
1143 throw new MWException( __METHOD__ .
": The timestamp $ts should be a number" );
1146 for ( $p = 0; $p < strlen( $format ); $p++ ) {
1148 $code = $format[$p];
1149 if ( $code ==
'x' && $p < strlen( $format ) - 1 ) {
1150 $code .= $format[++$p];
1153 if ( ( $code ===
'xi' || $code ==
'xj' || $code ==
'xk' || $code ==
'xm' || $code ==
'xo' || $code ==
'xt' ) && $p < strlen( $format ) - 1 ) {
1154 $code .= $format[++$p];
1165 $rawToggle = !$rawToggle;
1183 $num = substr( $ts, 6, 2 );
1186 if ( !$dateTimeObj ) {
1187 $dateTimeObj = DateTime::createFromFormat(
1188 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1194 $num = intval( substr( $ts, 6, 2 ) );
1215 if ( !$dateTimeObj ) {
1216 $dateTimeObj = DateTime::createFromFormat(
1217 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1244 $num = substr( $ts, 4, 2 );
1250 $num = intval( substr( $ts, 4, 2 ) );
1277 $num = substr( $ts, 0, 4 );
1316 $num = substr( $ts, 2, 2 );
1322 $num = substr( $iranian[0], -2 );
1325 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'am' :
'pm';
1328 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'AM' :
'PM';
1331 $h = substr( $ts, 8, 2 );
1332 $num = $h % 12 ? $h % 12 : 12;
1335 $num = intval( substr( $ts, 8, 2 ) );
1338 $h = substr( $ts, 8, 2 );
1339 $num = sprintf(
'%02d', $h % 12 ? $h % 12 : 12 );
1342 $num = substr( $ts, 8, 2 );
1345 $num = substr( $ts, 10, 2 );
1348 $num = substr( $ts, 12, 2 );
1357 if ( !$dateTimeObj ) {
1358 $dateTimeObj = DateTime::createFromFormat(
1359 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1362 $s .= $dateTimeObj->format( $code );
1375 if ( !$dateTimeObj ) {
1376 $dateTimeObj = DateTime::createFromFormat(
1377 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1380 $num = $dateTimeObj->format( $code );
1383 # Backslash escaping
1384 if ( $p < strlen( $format ) - 1 ) {
1385 $s .= $format[++$p];
1392 if ( $p < strlen( $format ) - 1 ) {
1393 $endQuote = strpos( $format,
'"', $p + 1 );
1394 if ( $endQuote ===
false ) {
1395 # No terminating quote, assume literal "
1398 $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1402 # Quote at end of string, assume literal "
1409 if ( $num !==
false ) {
1410 if ( $rawToggle || $raw ) {
1413 } elseif ( $roman ) {
1416 } elseif ( $hebrewNum ) {
1427 private static $GREG_DAYS =
array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
1428 private static $IRANIAN_DAYS =
array( 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 );
1443 $gy = substr( $ts, 0, 4 ) -1600;
1444 $gm = substr( $ts, 4, 2 ) -1;
1445 $gd = substr( $ts, 6, 2 ) -1;
1447 # Days passed from the beginning (including leap years)
1449 + floor( ( $gy + 3 ) / 4 )
1450 - floor( ( $gy + 99 ) / 100 )
1451 + floor( ( $gy + 399 ) / 400 );
1454 for ( $i = 0; $i < $gm; $i++ ) {
1455 $gDayNo += self::$GREG_DAYS[$i];
1459 if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1464 $gDayNo += (int)$gd;
1466 $jDayNo = $gDayNo - 79;
1468 $jNp = floor( $jDayNo / 12053 );
1471 $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1474 if ( $jDayNo >= 366 ) {
1475 $jy += floor( ( $jDayNo - 1 ) / 365 );
1476 $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1479 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1480 $jDayNo -= self::$IRANIAN_DAYS[$i];
1486 return array( $jy, $jm, $jd );
1500 private static function tsToHijri( $ts ) {
1501 $year = substr( $ts, 0, 4 );
1502 $month = substr( $ts, 4, 2 );
1503 $day = substr( $ts, 6, 2 );
1511 ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1512 ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1514 $zjd = (int)( ( 1461 * ( $zy + 4800 + (
int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1515 (
int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1516 (
int)( ( 3 * (int)( ( ( $zy + 4900 + (
int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1519 $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (
int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1520 (
int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1523 $zl = $zjd -1948440 + 10632;
1524 $zn = (int)( ( $zl - 1 ) / 10631 );
1525 $zl = $zl - 10631 * $zn + 354;
1526 $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) + ( (int)( $zl / 5670 ) ) * ( (
int)( ( 43 * $zl ) / 15238 ) );
1527 $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) - ( (int)( $zj / 16 ) ) * ( (
int)( ( 15238 * $zj ) / 43 ) ) + 29;
1528 $zm = (int)( ( 24 * $zl ) / 709 );
1529 $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1530 $zy = 30 * $zn + $zj - 30;
1532 return array( $zy, $zm, $zd );
1552 $year = substr( $ts, 0, 4 );
1553 $month = substr( $ts, 4, 2 );
1554 $day = substr( $ts, 6, 2 );
1556 # Calculate Hebrew year
1557 $hebrewYear = $year + 3760;
1559 # Month number when September = 1, August = 12
1561 if ( $month > 12 ) {
1568 # Calculate day of year from 1 September
1570 for ( $i = 1; $i < $month; $i++ ) {
1574 # Check if the year is leap
1575 if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1578 } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1585 # Calculate the start of the Hebrew year
1588 # Calculate next year's start
1589 if ( $dayOfYear <= $start ) {
1590 # Day is before the start of the year - it is the previous year
1592 $nextStart = $start;
1596 # Add days since previous year's 1 September
1598 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1602 # Start of the new (previous) year
1609 # Calculate Hebrew day of year
1610 $hebrewDayOfYear = $dayOfYear - $start;
1612 # Difference between year's days
1613 $diff = $nextStart - $start;
1614 # Add 12 (or 13 for leap years) days to ignore the difference between
1615 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1616 # difference is only about the year type
1617 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1623 # Check the year pattern, and is leap year
1624 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1625 # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1626 # and non-leap years
1627 $yearPattern = $diff % 30;
1628 # Check if leap year
1629 $isLeap = $diff >= 30;
1631 # Calculate day in the month from number of day in the Hebrew year
1632 # Don't check Adar - if the day is not in Adar, we will stop before;
1633 # if it is in Adar, we will use it to check if it is Adar I or Adar II
1634 $hebrewDay = $hebrewDayOfYear;
1637 while ( $hebrewMonth <= 12 ) {
1638 # Calculate days in this month
1639 if ( $isLeap && $hebrewMonth == 6 ) {
1640 # Adar in a leap year
1642 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1644 if ( $hebrewDay <= $days ) {
1648 # Subtract the days of Adar I
1649 $hebrewDay -= $days;
1652 if ( $hebrewDay <= $days ) {
1658 } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1659 # Cheshvan in a complete year (otherwise as the rule below)
1661 } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1662 # Kislev in an incomplete year (otherwise as the rule below)
1665 # Odd months have 30 days, even have 29
1666 $days = 30 - ( $hebrewMonth - 1 ) % 2;
1668 if ( $hebrewDay <= $days ) {
1669 # In the current month
1672 # Subtract the days of the current month
1673 $hebrewDay -= $days;
1674 # Try in the next month
1679 return array( $hebrewYear, $hebrewMonth, $hebrewDay, $days );
1692 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1693 $b = intval( ( $year - 1 ) % 4 );
1694 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1698 $Mar = intval( $m );
1704 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1705 if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1707 } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1709 } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1713 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1729 private static function tsToYear( $ts, $cName ) {
1730 $gy = substr( $ts, 0, 4 );
1731 $gm = substr( $ts, 4, 2 );
1732 $gd = substr( $ts, 6, 2 );
1734 if ( !strcmp( $cName,
'thai' ) ) {
1736 # Add 543 years to the Gregorian calendar
1737 # Months and days are identical
1738 $gy_offset = $gy + 543;
1739 } elseif ( ( !strcmp( $cName,
'minguo' ) ) || !strcmp( $cName,
'juche' ) ) {
1741 # Deduct 1911 years from the Gregorian calendar
1742 # Months and days are identical
1743 $gy_offset = $gy - 1911;
1744 } elseif ( !strcmp( $cName,
'tenno' ) ) {
1745 # Nengō dates up to Meiji period
1746 # Deduct years from the Gregorian calendar
1747 # depending on the nengo periods
1748 # Months and days are identical
1749 if ( ( $gy < 1912 ) || ( ( $gy == 1912 ) && ( $gm < 7 ) ) || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) ) ) {
1751 $gy_gannen = $gy - 1868 + 1;
1752 $gy_offset = $gy_gannen;
1753 if ( $gy_gannen == 1 ) {
1756 $gy_offset =
'明治' . $gy_offset;
1758 ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1759 ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1760 ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1761 ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1762 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1765 $gy_gannen = $gy - 1912 + 1;
1766 $gy_offset = $gy_gannen;
1767 if ( $gy_gannen == 1 ) {
1770 $gy_offset =
'大正' . $gy_offset;
1772 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1773 ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1774 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1777 $gy_gannen = $gy - 1926 + 1;
1778 $gy_offset = $gy_gannen;
1779 if ( $gy_gannen == 1 ) {
1782 $gy_offset =
'昭和' . $gy_offset;
1785 $gy_gannen = $gy - 1989 + 1;
1786 $gy_offset = $gy_gannen;
1787 if ( $gy_gannen == 1 ) {
1790 $gy_offset =
'平成' . $gy_offset;
1796 return array( $gy_offset, $gm, $gd );
1807 static $table =
array(
1808 array(
'',
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X' ),
1809 array(
'',
'X',
'XX',
'XXX',
'XL',
'L',
'LX',
'LXX',
'LXXX',
'XC',
'C' ),
1810 array(
'',
'C',
'CC',
'CCC',
'CD',
'D',
'DC',
'DCC',
'DCCC',
'CM',
'M' ),
1811 array(
'',
'M',
'MM',
'MMM',
'MMMM',
'MMMMM',
'MMMMMM',
'MMMMMMM',
'MMMMMMMM',
'MMMMMMMMM',
'MMMMMMMMMM' )
1814 $num = intval( $num );
1815 if ( $num > 10000 || $num <= 0 ) {
1820 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1821 if ( $num >= $pow10 ) {
1822 $s .= $table[$i][(int)floor( $num / $pow10 )];
1824 $num = $num % $pow10;
1837 static $table =
array(
1838 array(
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ),
1839 array(
'',
'י',
'כ',
'ל',
'מ',
'נ',
'ס',
'ע',
'פ',
'צ',
'ק' ),
1840 array(
'',
'ק',
'ר',
'ש',
'ת',
'תק',
'תר',
'תש',
'תת',
'תתק',
'תתר' ),
1841 array(
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' )
1844 $num = intval( $num );
1845 if ( $num > 9999 || $num <= 0 ) {
1850 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1851 if ( $num >= $pow10 ) {
1852 if ( $num == 15 || $num == 16 ) {
1853 $s .= $table[0][9] . $table[0][$num - 9];
1856 $s .= $table[$i][intval( ( $num / $pow10 ) )];
1857 if ( $pow10 == 1000 ) {
1862 $num = $num % $pow10;
1864 if ( strlen(
$s ) == 2 ) {
1867 $str = substr(
$s, 0, strlen(
$s ) - 2 ) .
'"';
1868 $str .= substr(
$s, strlen(
$s ) - 2, 2 );
1870 $start = substr( $str, 0, strlen( $str ) - 2 );
1871 $end = substr( $str, strlen( $str ) - 2 );
1874 $str = $start .
'ך';
1877 $str = $start .
'ם';
1880 $str = $start .
'ן';
1883 $str = $start .
'ף';
1886 $str = $start .
'ץ';
1903 if ( $tz ===
false ) {
1904 $tz =
$wgUser->getOption(
'timecorrection' );
1907 $data = explode(
'|', $tz, 3 );
1909 if ( $data[0] ==
'ZoneInfo' ) {
1911 $userTZ = timezone_open( $data[2] );
1913 if ( $userTZ !==
false ) {
1914 $date = date_create( $ts, timezone_open(
'UTC' ) );
1915 date_timezone_set( $date, $userTZ );
1916 $date = date_format( $date,
'YmdHis' );
1919 # Unrecognized timezone, default to 'Offset' with the stored offset.
1920 $data[0] =
'Offset';
1924 if ( $data[0] ==
'System' || $tz ==
'' ) {
1925 # Global offset in minutes.
1926 if ( isset( $wgLocalTZoffset ) ) {
1927 $minDiff = $wgLocalTZoffset;
1929 } elseif ( $data[0] ==
'Offset' ) {
1930 $minDiff = intval( $data[1] );
1932 $data = explode(
':', $tz );
1933 if ( count( $data ) == 2 ) {
1934 $data[0] = intval( $data[0] );
1935 $data[1] = intval( $data[1] );
1936 $minDiff = abs( $data[0] ) * 60 + $data[1];
1937 if ( $data[0] < 0 ) {
1938 $minDiff = -$minDiff;
1941 $minDiff = intval( $data[0] ) * 60;
1945 # No difference ? Return time unchanged
1946 if ( 0 == $minDiff ) {
1951 # Generate an adjusted date; take advantage of the fact that mktime
1952 # will normalize out-of-range values so we don't have to split $minDiff
1953 # into hours and minutes.
1955 (
int)substr( $ts, 8, 2 ) ), # Hours
1956 (
int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
1957 (
int)substr( $ts, 12, 2 ), # Seconds
1958 (
int)substr( $ts, 4, 2 ), # Month
1959 (
int)substr( $ts, 6, 2 ), # Day
1960 (
int)substr( $ts, 0, 4 ) ); # Year
1962 $date =
date(
'YmdHis',
$t );
1988 if ( is_bool( $usePrefs ) ) {
1990 $datePreference =
$wgUser->getDatePreference();
1995 $datePreference = (string)$usePrefs;
1999 if ( $datePreference ==
'' ) {
2003 return $datePreference;
2016 if ( !isset( $this->dateFormatStrings[
$type][$pref] ) ) {
2017 if ( $pref ==
'default' ) {
2019 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2021 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2023 if (
$type ===
'pretty' && $df ===
null ) {
2027 if ( $df ===
null ) {
2029 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2032 $this->dateFormatStrings[
$type][$pref] = $df;
2034 return $this->dateFormatStrings[
$type][$pref];
2047 function date( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2050 $ts = $this->
userAdjust( $ts, $timecorrection );
2066 function time( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2069 $ts = $this->
userAdjust( $ts, $timecorrection );
2086 function timeanddate( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2089 $ts = $this->
userAdjust( $ts, $timecorrection );
2108 $segments =
array();
2110 foreach ( $intervals
as $intervalName => $intervalValue ) {
2113 $message =
wfMessage(
'duration-' . $intervalName )->numParams( $intervalValue );
2114 $segments[] = $message->inLanguage( $this )->escaped();
2132 if ( empty( $chosenIntervals ) ) {
2133 $chosenIntervals =
array(
'millennia',
'centuries',
'decades',
'years',
'days',
'hours',
'minutes',
'seconds' );
2136 $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2137 $sortedNames = array_keys( $intervals );
2138 $smallestInterval = array_pop( $sortedNames );
2140 $segments =
array();
2142 foreach ( $intervals
as $name => $length ) {
2143 $value = floor( $seconds / $length );
2145 if (
$value > 0 || (
$name == $smallestInterval && empty( $segments ) ) ) {
2146 $seconds -=
$value * $length;
2175 $options +=
array(
'timecorrection' =>
true,
'format' =>
true );
2176 if (
$options[
'timecorrection'] !==
false ) {
2177 if (
$options[
'timecorrection'] ===
true ) {
2178 $offset =
$user->getOption(
'timecorrection' );
2180 $offset =
$options[
'timecorrection'];
2184 if (
$options[
'format'] ===
true ) {
2185 $format =
$user->getDatePreference();
2278 $diff = $ts->
diff( $relativeTo );
2279 $diffDay = (bool)( (
int)$ts->timestamp->
format(
'w' ) - (int)$relativeTo->timestamp->
format(
'w' ) );
2280 $days = $diff->days ?: (int)$diffDay;
2281 if ( $diff->invert || $days > 5 && $ts->timestamp->
format(
'Y' ) !== $relativeTo->timestamp->
format(
'Y' ) ) {
2289 } elseif ( $days > 5 ) {
2293 } elseif ( $days > 1 ) {
2296 $weekday = self::$mWeekdayMsgs[$ts->timestamp->format(
'w' )];
2300 ->inLanguage( $this )
2303 } elseif ( $days == 1 ) {
2307 ->inLanguage( $this )
2310 } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2314 ->inLanguage( $this )
2320 } elseif ( $diff->h == 1 ) {
2322 $ts =
wfMessage(
'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2323 } elseif ( $diff->i >= 1 ) {
2325 $ts =
wfMessage(
'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2326 } elseif ( $diff->s >= 30 ) {
2328 $ts =
wfMessage(
'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2342 return self::$dataCache->getSubitem( $this->mCode,
'messages', $key );
2349 return self::$dataCache->getItem( $this->mCode,
'messages' );
2359 # This is a wrapper for iconv in all languages except esperanto,
2360 # which does some nasty x-conversions beforehand
2362 # Even with //IGNORE iconv can whine about illegal characters in
2363 # *input* string. We just ignore those too.
2364 # REF: http://bugs.php.net/bug.php?id=37166
2365 # REF: https://bugzilla.wikimedia.org/show_bug.cgi?id=16885
2387 return mb_strtoupper(
$matches[0] );
2396 return strtr(
$matches[1], $wikiUpperChars );
2405 return strtr(
$matches[1], $wikiLowerChars );
2413 return mb_strtoupper(
$matches[0] );
2422 return strtr(
$matches[0], $wikiUpperChars );
2436 } elseif ( $o < 128 ) {
2440 return $this->
uc( $str,
true );
2452 function uc( $str, $first =
false ) {
2453 if ( function_exists(
'mb_strtoupper' ) ) {
2456 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2461 return $this->
isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2465 $x = $first ?
'^' :
'';
2466 return preg_replace_callback(
2467 "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
2468 array( $this,
'ucCallback' ),
2472 return $first ?
ucfirst( $str ) : strtoupper( $str );
2484 return strval( $str );
2485 } elseif ( $o >= 128 ) {
2486 return $this->
lc( $str,
true );
2487 } elseif ( $o > 96 ) {
2490 $str[0] = strtolower( $str[0] );
2500 function lc( $str, $first =
false ) {
2501 if ( function_exists(
'mb_strtolower' ) ) {
2504 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2506 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2509 return $this->
isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2513 $x = $first ?
'^' :
'';
2514 return preg_replace_callback(
2515 "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
2516 array( $this,
'lcCallback' ),
2520 return $first ? strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str );
2530 return (
bool)preg_match(
'/[\x80-\xff]/', $str );
2539 $str = $this->
lc( $str );
2542 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2545 if ( function_exists(
'mb_strtoupper' ) ) {
2546 return preg_replace_callback(
2548 array( $this,
'ucwordsCallbackMB' ),
2552 return preg_replace_callback(
2554 array( $this,
'ucwordsCallbackWiki' ),
2559 return ucwords( strtolower( $str ) );
2571 $str = $this->
lc( $str );
2574 $breaks =
"[ \-\(\)\}\{\.,\?!]";
2577 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2579 if ( function_exists(
'mb_strtoupper' ) ) {
2580 return preg_replace_callback(
2582 array( $this,
'ucwordbreaksCallbackMB' ),
2586 return preg_replace_callback(
2588 array( $this,
'ucwordsCallbackWiki' ),
2593 return preg_replace_callback(
2594 '/\b([\w\x80-\xff]+)\b/',
2595 array( $this,
'ucwordbreaksCallbackAscii' ),
2617 return $this->
uc( $s );
2625 if ( is_array(
$s ) ) {
2626 throw new MWException(
'Given array to checkTitleEncoding.' );
2639 return self::$dataCache->getItem( $this->mCode,
'fallback8bitEncoding' );
2685 static $full =
null;
2686 static $half =
null;
2688 if ( $full ===
null ) {
2689 $fullWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2690 $halfWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2691 $full = str_split( $fullWidth, 3 );
2692 $half = str_split( $halfWidth );
2695 $string = str_replace( $full, $half, $string );
2704 protected static function insertSpace( $string, $pattern ) {
2705 $string = preg_replace( $pattern,
" $1 ", $string );
2706 $string = preg_replace(
'/ +/',
' ', $string );
2715 # some languages, e.g. Chinese, need to do a conversion
2716 # in order for search results to be displayed correctly
2729 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2730 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2736 if ( strlen(
$matches[1] ) != 3 ) {
2742 if ( $code < 0xac00 || 0xd7a4 <= $code ) {
2744 } elseif ( $code < 0xb098 ) {
2745 return "\xe3\x84\xb1";
2746 } elseif ( $code < 0xb2e4 ) {
2747 return "\xe3\x84\xb4";
2748 } elseif ( $code < 0xb77c ) {
2749 return "\xe3\x84\xb7";
2750 } elseif ( $code < 0xb9c8 ) {
2751 return "\xe3\x84\xb9";
2752 } elseif ( $code < 0xbc14 ) {
2753 return "\xe3\x85\x81";
2754 } elseif ( $code < 0xc0ac ) {
2755 return "\xe3\x85\x82";
2756 } elseif ( $code < 0xc544 ) {
2757 return "\xe3\x85\x85";
2758 } elseif ( $code < 0xc790 ) {
2759 return "\xe3\x85\x87";
2760 } elseif ( $code < 0xcc28 ) {
2761 return "\xe3\x85\x88";
2762 } elseif ( $code < 0xce74 ) {
2763 return "\xe3\x85\x8a";
2764 } elseif ( $code < 0xd0c0 ) {
2765 return "\xe3\x85\x8b";
2766 } elseif ( $code < 0xd30c ) {
2767 return "\xe3\x85\x8c";
2768 } elseif ( $code < 0xd558 ) {
2769 return "\xe3\x85\x8d";
2771 return "\xe3\x85\x8e";
2779 # Some languages may have an alternate char encoding option
2780 # (Esperanto X-coding, Japanese furigana conversion, etc)
2781 # If this language is used as the primary content language,
2782 # an override to the defaults can be set here on startup.
2790 # For some languages we'll want to explicitly specify
2791 # which characters make it into the edit box raw
2792 # or are converted in some way or another.
2794 if ( $wgEditEncoding ==
'' || $wgEditEncoding ==
'UTF-8' ) {
2797 return $this->
iconv(
'UTF-8', $wgEditEncoding,
$s );
2806 # Take the previous into account.
2808 if ( $wgEditEncoding !=
'' ) {
2809 $enc = $wgEditEncoding;
2813 if ( $enc ==
'UTF-8' ) {
2816 return $this->
iconv( $enc,
'UTF-8',
$s );
2832 global $wgAllUnicodeFixes;
2834 if ( $wgAllUnicodeFixes ) {
2857 if ( !isset( $this->transformData[
$file] ) ) {
2859 if ( $data ===
false ) {
2860 throw new MWException( __METHOD__ .
": The transformation file $file is missing" );
2864 return $this->transformData[
$file]->replace( $string );
2873 return self::$dataCache->getItem( $this->mCode,
'rtl' );
2881 return $this->
isRTL() ?
'rtl' :
'ltr';
2893 return $this->
isRTL() ?
'right' :
'left';
2905 return $this->
isRTL() ?
'left' :
'right';
2921 return $this->
isRTL() ?
'‎' :
'‏';
2923 return $this->
isRTL() ?
'‏' :
'‎';
2937 $lrm =
"\xE2\x80\x8E"; #
LEFT-TO-
RIGHT MARK, commonly abbreviated LRM
2938 $rlm =
"\xE2\x80\x8F"; #
RIGHT-TO-
LEFT MARK, commonly abbreviated RLM
2940 return $this->
isRTL() ? $lrm : $rlm;
2942 return $this->
isRTL() ? $rlm : $lrm;
2949 return self::$dataCache->getItem( $this->mCode,
'capitalizeAllNouns' );
2958 function getArrow( $direction =
'forwards' ) {
2959 switch ( $direction ) {
2961 return $this->
isRTL() ?
'←' :
'→';
2963 return $this->
isRTL() ?
'→' :
'←';
2981 return self::$dataCache->getItem( $this->mCode,
'linkPrefixExtension' );
2989 return self::$dataCache->getItem( $this->mCode,
'magicWords' );
2996 if ( $this->mMagicHookDone ) {
2999 $this->mMagicHookDone =
true;
3012 if ( ! $this->mMagicHookDone ) {
3016 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3017 $rawEntry = $this->mMagicExtensions[$mw->mId];
3019 $rawEntry = self::$dataCache->getSubitem(
3020 $this->mCode,
'magicWords', $mw->mId );
3023 if ( !is_array( $rawEntry ) ) {
3024 error_log(
"\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3026 $mw->mCaseSensitive = $rawEntry[0];
3027 $mw->mSynonyms = array_slice( $rawEntry, 1 );
3038 $fallbackChain = array_reverse( $fallbackChain );
3039 foreach ( $fallbackChain
as $code ) {
3040 if ( isset( $newWords[$code] ) ) {
3052 if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3054 $this->mExtendedSpecialPageAliases =
3055 self::$dataCache->getItem( $this->mCode,
'specialPageAliases' );
3057 array( &$this->mExtendedSpecialPageAliases, $this->
getCode() ) );
3070 return "<em>$text</em>";
3096 public function formatNum( $number, $nocommafy =
false ) {
3097 global $wgTranslateNumerals;
3098 if ( !$nocommafy ) {
3099 $number = $this->
commafy( $number );
3102 $number = strtr( $number,
$s );
3106 if ( $wgTranslateNumerals ) {
3109 $number = strtr( $number,
$s );
3125 return $this->
formatNum( $number,
true );
3135 $number = strtr( $number, array_flip(
$s ) );
3140 $number = strtr( $number, array_flip(
$s ) );
3143 $number = strtr( $number,
array(
',' =>
'' ) );
3155 if ( $number ===
null ) {
3161 return strrev( (
string)preg_replace(
'/(\d{3})(?=\d)(?!\d*\.)/',
'$1,', strrev( $number ) ) );
3165 if ( intval( $number ) < 0 ) {
3168 $number = substr( $number, 1 );
3170 $integerPart =
array();
3171 $decimalPart =
array();
3173 preg_match(
"/\d+/", $number, $integerPart );
3174 preg_match(
"/\.\d*/", $number, $decimalPart );
3175 $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] :
"";
3176 if ( $groupedNumber === $number ) {
3178 return $sign . $groupedNumber;
3180 $start = $end = strlen( $integerPart[0] );
3181 while ( $start > 0 ) {
3182 $match =
$matches[0][$numMatches - 1];
3183 $matchLen = strlen( $match );
3184 $start = $end - $matchLen;
3188 $groupedNumber = substr( $number, $start, $end -$start ) . $groupedNumber;
3190 if ( $numMatches > 1 ) {
3195 $groupedNumber =
"," . $groupedNumber;
3198 return $sign . $groupedNumber;
3206 return self::$dataCache->getItem( $this->mCode,
'digitGroupingPattern' );
3213 return self::$dataCache->getItem( $this->mCode,
'digitTransformTable' );
3220 return self::$dataCache->getItem( $this->mCode,
'separatorTransformTable' );
3233 $m = count( $l ) - 1;
3245 for ( $i = $m - 1; $i >= 0; $i-- ) {
3246 if ( $i == $m - 1 ) {
3247 $s = $l[$i] . $and . $space .
$s;
3249 $s = $l[$i] . $comma .
$s;
3263 wfMessage(
'comma-separator' )->inLanguage( $this )->escaped(),
3276 wfMessage(
'semicolon-separator' )->inLanguage( $this )->escaped(),
3288 wfMessage(
'pipe-separator' )->inLanguage( $this )->escaped(),
3310 function truncate( $string, $length, $ellipsis =
'...', $adjustLength =
true ) {
3311 # Use the localized ellipsis character
3312 if ( $ellipsis ==
'...' ) {
3313 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3315 # Check if there is no need to truncate
3316 if ( $length == 0 ) {
3318 } elseif ( strlen( $string ) <= abs( $length ) ) {
3321 $stringOriginal = $string;
3322 # If ellipsis length is >= $length then we can't apply $adjustLength
3323 if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) {
3324 $string = $ellipsis;
3325 # Otherwise, truncate and add ellipsis...
3327 $eLength = $adjustLength ? strlen( $ellipsis ) : 0;
3328 if ( $length > 0 ) {
3329 $length -= $eLength;
3330 $string = substr( $string, 0, $length );
3332 $string = rtrim( $string );
3333 $string = $string . $ellipsis;
3335 $length += $eLength;
3336 $string = substr( $string, $length );
3338 $string = ltrim( $string );
3339 $string = $ellipsis . $string;
3342 # Do not truncate if the ellipsis makes the string longer/equal (bug 22181).
3343 # This check is *not* redundant if $adjustLength, due to the single case where
3344 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3345 if ( strlen( $string ) < strlen( $stringOriginal ) ) {
3348 return $stringOriginal;
3360 if ( $string !=
'' ) {
3361 $char = ord( $string[strlen( $string ) - 1] );
3363 if ( $char >= 0xc0 ) {
3364 # We got the first byte only of a multibyte char; remove it.
3365 $string = substr( $string, 0, -1 );
3366 } elseif ( $char >= 0x80 &&
3367 preg_match(
'/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3368 '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m )
3370 # We chopped in the middle of a character; remove it
3385 if ( $string !=
'' ) {
3386 $char = ord( $string[0] );
3387 if ( $char >= 0x80 && $char < 0xc0 ) {
3388 # We chopped in the middle of a character; remove the whole thing
3389 $string = preg_replace(
'/^[\x80-\xbf]+/',
'', $string );
3410 function truncateHtml( $text, $length, $ellipsis =
'...' ) {
3411 # Use the localized ellipsis character
3412 if ( $ellipsis ==
'...' ) {
3413 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3415 # Check if there is clearly no need to truncate
3416 if ( $length <= 0 ) {
3418 } elseif ( strlen( $text ) <= $length ) {
3423 $testingEllipsis =
false;
3428 $openTags =
array();
3431 $textLen = strlen( $text );
3432 $neLength = max( 0, $length - strlen( $ellipsis ) );
3433 for ( $pos = 0;
true; ++$pos ) {
3434 # Consider truncation once the display length has reached the maximim.
3435 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3436 # Check that we're not in the middle of a bracket/entity...
3437 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3438 if ( !$testingEllipsis ) {
3439 $testingEllipsis =
true;
3440 # Save where we are; we will truncate here unless there turn out to
3441 # be so few remaining characters that truncation is not necessary.
3442 if ( !$maybeState ) {
3445 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3446 # String in fact does need truncation, the truncation point was OK.
3447 list(
$ret, $openTags ) = $maybeState;
3453 if ( $pos >= $textLen ) {
3457 # Read the next char...
3459 $lastCh = $pos ? $text[$pos - 1] :
'';
3465 } elseif ( $ch ==
'>' ) {
3469 } elseif ( $bracketState == 1 ) {
3477 } elseif ( $bracketState == 2 ) {
3484 } elseif ( $bracketState == 0 ) {
3485 if ( $entityState ) {
3491 if ( $neLength == 0 && !$maybeState ) {
3494 $maybeState =
array( substr(
$ret, 0, -1 ), $openTags );
3501 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3503 $dispLen += $skipped;
3511 while ( count( $openTags ) > 0 ) {
3512 $ret .=
'</' . array_pop( $openTags ) .
'>';
3529 if ( $len ===
null ) {
3531 } elseif ( $len < 0 ) {
3535 if ( $start < strlen( $text ) ) {
3536 $skipCount = strcspn( $text, $search, $start, $len );
3537 $ret .= substr( $text, $start, $skipCount );
3552 $tag = ltrim( $tag );
3554 if ( $tagType == 0 && $lastCh !=
'/' ) {
3556 } elseif ( $tagType == 1 ) {
3557 if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3558 array_pop( $openTags );
3575 if ( isset( $wgGrammarForms[$this->
getCode()][$case][$word] ) ) {
3576 return $wgGrammarForms[$this->
getCode()][$case][$word];
3587 if ( isset( $wgGrammarForms[$this->
getCode()] ) && is_array( $wgGrammarForms[$this->
getCode()] ) ) {
3588 return $wgGrammarForms[$this->
getCode()];
3611 function gender( $gender, $forms ) {
3612 if ( !count( $forms ) ) {
3616 if ( $gender ===
'male' ) {
3619 if ( $gender ===
'female' ) {
3622 return isset( $forms[2] ) ? $forms[2] : $forms[0];
3643 if ( is_string( $forms ) ) {
3646 if ( !count( $forms ) ) {
3651 $pluralForm = min( $pluralForm, count( $forms ) - 1 );
3652 return $forms[$pluralForm];
3671 foreach ( $forms
as $index =>
$form ) {
3672 if ( preg_match(
'/\d+=/i',
$form ) ) {
3673 $pos = strpos(
$form,
'=' );
3674 if ( substr(
$form, 0, $pos ) === (
string)
$count ) {
3675 return substr(
$form, $pos + 1 );
3677 unset( $forms[$index] );
3680 return array_values( $forms );
3692 while ( count( $forms ) <
$count ) {
3693 $forms[] = $forms[count( $forms ) - 1];
3711 foreach ( $duration
as $show =>
$value ) {
3712 if ( strcmp( $str,
$value ) == 0 ) {
3713 return htmlspecialchars( trim( $show ) );
3719 $indefs =
array(
'infinite',
'infinity',
'indefinite' );
3720 if ( in_array( $str, $indefs ) ) {
3721 foreach ( $indefs
as $val ) {
3722 $show = array_search( $val, $duration,
true );
3723 if ( $show !==
false ) {
3724 return htmlspecialchars( trim( $show ) );
3730 $time = strtotime( $str, 0 );
3731 if (
$time ===
false ) {
3733 } elseif (
$time !== strtotime( $str, 1 ) ) {
3737 if (
$time === 0 ) {
3784 return $this->mConverter->autoConvertToAllVariants( $text );
3793 public function convert( $text ) {
3794 return $this->mConverter->convert( $text );
3804 return $this->mConverter->convertTitle(
$title );
3814 return $this->mConverter->convertNamespace( $ns );
3834 return (
bool)$this->mConverter->validateVariant( $variant );
3845 return $this->mConverter->armourMath( $text );
3855 public function convertHtml( $text, $isTitle =
false ) {
3856 return htmlspecialchars( $this->
convert( $text, $isTitle ) );
3864 return $this->mConverter->convertCategoryKey( $key );
3874 return $this->mConverter->getVariants();
3881 return $this->mConverter->getPreferredVariant();
3888 return $this->mConverter->getDefaultVariant();
3895 return $this->mConverter->getURLVariant();
3911 $this->mConverter->findVariantLink(
$link, $nt, $ignoreOtherCond );
3921 return $this->mConverter->getExtraHashOptions();
3932 return $this->mConverter->getParsedTitle();
3950 return $this->mConverter->markNoConversion( $text );
3963 return self::$dataCache->getItem( $this->mCode,
'linkTrail' );
3973 return self::$dataCache->getItem( $this->mCode,
'linkPrefixCharset' );
3991 if ( $this->mParentLanguage !==
false ) {
3995 $pieces = explode(
'-', $this->
getCode() );
3997 if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
3998 $this->mParentLanguage =
null;
4002 if ( !$lang->hasVariant( $this->getCode() ) ) {
4003 $this->mParentLanguage =
null;
4007 $this->mParentLanguage = $lang;
4034 if ( is_null( $this->mHtmlCode ) ) {
4043 public function setCode( $code ) {
4044 $this->mCode = $code;
4046 $this->mHtmlCode =
null;
4047 $this->mParentLanguage =
false;
4058 public static function getFileName( $prefix =
'Language', $code, $suffix =
'.php' ) {
4059 if ( !self::isValidBuiltInCode( $code ) ) {
4060 throw new MWException(
"Invalid language code \"$code\"" );
4063 return $prefix . str_replace(
'-',
'_',
ucfirst( $code ) ) . $suffix;
4073 public static function getCodeFromFileName( $filename, $prefix =
'Language', $suffix =
'.php' ) {
4075 preg_match(
'/' . preg_quote( $prefix,
'/' ) .
'([A-Z][a-z_]+)' .
4076 preg_quote( $suffix,
'/' ) .
'/', $filename, $m );
4077 if ( !count( $m ) ) {
4080 return str_replace(
'_',
'-', strtolower( $m[1] ) );
4102 if ( !self::isValidBuiltInCode( $code ) ) {
4103 throw new MWException(
"Invalid language code \"$code\"" );
4106 return "$IP/languages/i18n/$code.json" ;
4130 $first = array_shift( $fallbacks );
4147 $v = array_map(
'trim', explode(
',', $v ) );
4148 if ( $v[count( $v ) - 1] !==
'en' ) {
4168 $cacheKey =
"{$code}-{$wgLanguageCode}";
4170 if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4175 array_unshift( $siteFallbacks, $wgLanguageCode );
4178 $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4180 self::$fallbackLanguageCache[$cacheKey] =
array( $fallbacks, $siteFallbacks );
4182 return self::$fallbackLanguageCache[$cacheKey];
4227 if ( strpos( $talk,
'$1' ) ===
false ) {
4232 $talk = str_replace(
'$1', $wgMetaNamespace, $talk );
4234 # Allow grammar transformations
4235 # Allowing full message-style parsing would make simple requests
4236 # such as action=raw much more expensive than they need to be.
4237 # This will hopefully cover most cases.
4238 $talk = preg_replace_callback(
'/{{grammar:(.*?)\|(.*?)}}/i',
4239 array( &$this,
'replaceGrammarInNamespace' ), $talk );
4240 return str_replace(
' ',
'_', $talk );
4256 static $wikiUpperChars, $wikiLowerChars;
4257 if ( isset( $wikiUpperChars ) ) {
4258 return array( $wikiUpperChars, $wikiLowerChars );
4263 if ( $arr ===
false ) {
4265 "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" );
4267 $wikiUpperChars = $arr[
'wikiUpperChars'];
4268 $wikiLowerChars = $arr[
'wikiLowerChars'];
4270 return array( $wikiUpperChars, $wikiLowerChars );
4284 public function formatExpiry( $expiry, $format =
true ) {
4286 if ( $infinity ===
null ) {
4290 if ( $expiry ==
'' || $expiry == $infinity ) {
4291 return $format ===
true
4295 return $format ===
true
4312 if ( !is_array( $format ) ) {
4313 $format =
array(
'avoid' => $format );
4315 if ( !isset( $format[
'avoid'] ) ) {
4316 $format[
'avoid'] =
false;
4318 if ( !isset( $format[
'noabbrevs' ] ) ) {
4319 $format[
'noabbrevs'] =
false;
4322 $format[
'noabbrevs'] ?
'seconds' :
'seconds-abbrev' )->inLanguage( $this );
4324 $format[
'noabbrevs'] ?
'minutes' :
'minutes-abbrev' )->inLanguage( $this );
4326 $format[
'noabbrevs'] ?
'hours' :
'hours-abbrev' )->inLanguage( $this );
4328 $format[
'noabbrevs'] ?
'days' :
'days-abbrev' )->inLanguage( $this );
4330 if ( round( $seconds * 10 ) < 100 ) {
4331 $s = $this->
formatNum( sprintf(
"%.1f", round( $seconds * 10 ) / 10 ) );
4332 $s = $secondsMsg->params(
$s )->text();
4333 } elseif ( round( $seconds ) < 60 ) {
4335 $s = $secondsMsg->params(
$s )->text();
4336 } elseif ( round( $seconds ) < 3600 ) {
4337 $minutes = floor( $seconds / 60 );
4338 $secondsPart = round( fmod( $seconds, 60 ) );
4339 if ( $secondsPart == 60 ) {
4343 $s = $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4345 $s .= $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4346 } elseif ( round( $seconds ) <= 2 * 86400 ) {
4347 $hours = floor( $seconds / 3600 );
4348 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4349 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4350 if ( $secondsPart == 60 ) {
4354 if ( $minutes == 60 ) {
4358 $s = $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4360 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4361 if ( !in_array( $format[
'avoid'],
array(
'avoidseconds',
'avoidminutes' ) ) ) {
4362 $s .=
' ' . $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4365 $days = floor( $seconds / 86400 );
4366 if ( $format[
'avoid'] ===
'avoidminutes' ) {
4367 $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4368 if ( $hours == 24 ) {
4372 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4374 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4375 } elseif ( $format[
'avoid'] ===
'avoidseconds' ) {
4376 $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4377 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4378 if ( $minutes == 60 ) {
4382 if ( $hours == 24 ) {
4386 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4388 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4390 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4392 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4426 $sizes =
array(
'',
'kilo',
'mega',
'giga',
'tera',
'peta',
'exa',
'zeta',
'yotta' );
4429 $maxIndex = count( $sizes ) - 1;
4430 while (
$size >= $boundary && $index < $maxIndex ) {
4441 $msg = str_replace(
'$1', $sizes[$index], $messageKey );
4471 function specialList( $page, $details, $oppositedm =
true ) {
4472 $dirmark = ( $oppositedm ? $this->
getDirMark(
true ) :
'' ) .
4474 $details = $details ? $dirmark . $this->
getMessageFromDB(
'word-separator' ) .
4475 wfMessage(
'parentheses' )->rawParams( $details )->inLanguage( $this )->escaped() :
'';
4476 return $page . $details;
4492 # Make 'previous' link
4494 if ( $offset > 0 ) {
4496 $query, $prev,
'prevn-title',
'mw-prevlink' );
4498 $plink = htmlspecialchars( $prev );
4504 $nlink = htmlspecialchars( $next );
4507 $query, $next,
'nextn-title',
'mw-nextlink' );
4510 # Make links to set number of items per page
4511 $numLinks =
array();
4512 foreach (
array( 20, 50, 100, 250, 500 )
as $num ) {
4513 $numLinks[] = $this->
numLink( $title, $offset, $num,
4517 return wfMessage(
'viewprevnext' )->inLanguage( $this )->title(
$title
4518 )->rawParams( $plink, $nlink, $this->
pipeList( $numLinks ) )->escaped();
4535 $tooltip =
wfMessage( $tooltipMsg )->inLanguage( $this )->title(
$title )->numParams(
$limit )->text();
4537 'title' => $tooltip,
'class' => $class ),
$link );
4546 return $this->mConverter->getConvRuleTitle();
4555 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'compiledPluralRules' );
4557 if ( !$pluralRules ) {
4558 foreach ( $fallbacks
as $fallbackCode ) {
4559 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'compiledPluralRules' );
4560 if ( $pluralRules ) {
4565 return $pluralRules;
4574 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRules' );
4576 if ( !$pluralRules ) {
4577 foreach ( $fallbacks
as $fallbackCode ) {
4578 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRules' );
4579 if ( $pluralRules ) {
4584 return $pluralRules;
4593 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRuleTypes' );
4595 if ( !$pluralRuleTypes ) {
4596 foreach ( $fallbacks
as $fallbackCode ) {
4597 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRuleTypes' );
4598 if ( $pluralRuleTypes ) {
4603 return $pluralRuleTypes;
4626 if ( isset( $pluralRuleTypes[$index] ) ) {
4627 return $pluralRuleTypes[$index];