MediaWiki
REL1_19
|
00001 <?php 00013 if ( !defined( 'MEDIAWIKI' ) ) { 00014 echo "This file is part of MediaWiki, it is not a valid entry point.\n"; 00015 exit( 1 ); 00016 } 00017 00018 # Read language names 00019 global $wgLanguageNames; 00020 require_once( dirname( __FILE__ ) . '/Names.php' ); 00021 00022 if ( function_exists( 'mb_strtoupper' ) ) { 00023 mb_internal_encoding( 'UTF-8' ); 00024 } 00025 00031 class FakeConverter { 00032 var $mLang; 00033 function __construct( $langobj ) { $this->mLang = $langobj; } 00034 function autoConvertToAllVariants( $text ) { return array( $this->mLang->getCode() => $text ); } 00035 function convert( $t ) { return $t; } 00036 function convertTo( $text, $variant ) { return $text; } 00037 function convertTitle( $t ) { return $t->getPrefixedText(); } 00038 function getVariants() { return array( $this->mLang->getCode() ); } 00039 function getPreferredVariant() { return $this->mLang->getCode(); } 00040 function getDefaultVariant() { return $this->mLang->getCode(); } 00041 function getURLVariant() { return ''; } 00042 function getConvRuleTitle() { return false; } 00043 function findVariantLink( &$l, &$n, $ignoreOtherCond = false ) { } 00044 function getExtraHashOptions() { return ''; } 00045 function getParsedTitle() { return ''; } 00046 function markNoConversion( $text, $noParse = false ) { return $text; } 00047 function convertCategoryKey( $key ) { return $key; } 00048 function convertLinkToAllVariants( $text ) { return $this->autoConvertToAllVariants( $text ); } 00049 function armourMath( $text ) { return $text; } 00050 } 00051 00056 class Language { 00057 00061 var $mConverter; 00062 00063 var $mVariants, $mCode, $mLoaded = false; 00064 var $mMagicExtensions = array(), $mMagicHookDone = false; 00065 private $mHtmlCode = null; 00066 00067 var $dateFormatStrings = array(); 00068 var $mExtendedSpecialPageAliases; 00069 00070 protected $namespaceNames, $mNamespaceIds, $namespaceAliases; 00071 00075 var $transformData = array(); 00076 00080 static public $dataCache; 00081 00082 static public $mLangObjCache = array(); 00083 00084 static public $mWeekdayMsgs = array( 00085 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 00086 'friday', 'saturday' 00087 ); 00088 00089 static public $mWeekdayAbbrevMsgs = array( 00090 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' 00091 ); 00092 00093 static public $mMonthMsgs = array( 00094 'january', 'february', 'march', 'april', 'may_long', 'june', 00095 'july', 'august', 'september', 'october', 'november', 00096 'december' 00097 ); 00098 static public $mMonthGenMsgs = array( 00099 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen', 00100 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 00101 'december-gen' 00102 ); 00103 static public $mMonthAbbrevMsgs = array( 00104 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 00105 'sep', 'oct', 'nov', 'dec' 00106 ); 00107 00108 static public $mIranianCalendarMonthMsgs = array( 00109 'iranian-calendar-m1', 'iranian-calendar-m2', 'iranian-calendar-m3', 00110 'iranian-calendar-m4', 'iranian-calendar-m5', 'iranian-calendar-m6', 00111 'iranian-calendar-m7', 'iranian-calendar-m8', 'iranian-calendar-m9', 00112 'iranian-calendar-m10', 'iranian-calendar-m11', 'iranian-calendar-m12' 00113 ); 00114 00115 static public $mHebrewCalendarMonthMsgs = array( 00116 'hebrew-calendar-m1', 'hebrew-calendar-m2', 'hebrew-calendar-m3', 00117 'hebrew-calendar-m4', 'hebrew-calendar-m5', 'hebrew-calendar-m6', 00118 'hebrew-calendar-m7', 'hebrew-calendar-m8', 'hebrew-calendar-m9', 00119 'hebrew-calendar-m10', 'hebrew-calendar-m11', 'hebrew-calendar-m12', 00120 'hebrew-calendar-m6a', 'hebrew-calendar-m6b' 00121 ); 00122 00123 static public $mHebrewCalendarMonthGenMsgs = array( 00124 'hebrew-calendar-m1-gen', 'hebrew-calendar-m2-gen', 'hebrew-calendar-m3-gen', 00125 'hebrew-calendar-m4-gen', 'hebrew-calendar-m5-gen', 'hebrew-calendar-m6-gen', 00126 'hebrew-calendar-m7-gen', 'hebrew-calendar-m8-gen', 'hebrew-calendar-m9-gen', 00127 'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen', 00128 'hebrew-calendar-m6a-gen', 'hebrew-calendar-m6b-gen' 00129 ); 00130 00131 static public $mHijriCalendarMonthMsgs = array( 00132 'hijri-calendar-m1', 'hijri-calendar-m2', 'hijri-calendar-m3', 00133 'hijri-calendar-m4', 'hijri-calendar-m5', 'hijri-calendar-m6', 00134 'hijri-calendar-m7', 'hijri-calendar-m8', 'hijri-calendar-m9', 00135 'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12' 00136 ); 00137 00143 static function factory( $code ) { 00144 if ( !isset( self::$mLangObjCache[$code] ) ) { 00145 if ( count( self::$mLangObjCache ) > 10 ) { 00146 // Don't keep a billion objects around, that's stupid. 00147 self::$mLangObjCache = array(); 00148 } 00149 self::$mLangObjCache[$code] = self::newFromCode( $code ); 00150 } 00151 return self::$mLangObjCache[$code]; 00152 } 00153 00159 protected static function newFromCode( $code ) { 00160 // Protect against path traversal below 00161 if ( !Language::isValidCode( $code ) 00162 || strcspn( $code, ":/\\\000" ) !== strlen( $code ) ) 00163 { 00164 throw new MWException( "Invalid language code \"$code\"" ); 00165 } 00166 00167 if ( !Language::isValidBuiltInCode( $code ) ) { 00168 // It's not possible to customise this code with class files, so 00169 // just return a Language object. This is to support uselang= hacks. 00170 $lang = new Language; 00171 $lang->setCode( $code ); 00172 return $lang; 00173 } 00174 00175 // Check if there is a language class for the code 00176 $class = self::classFromCode( $code ); 00177 self::preloadLanguageClass( $class ); 00178 if ( MWInit::classExists( $class ) ) { 00179 $lang = new $class; 00180 return $lang; 00181 } 00182 00183 // Keep trying the fallback list until we find an existing class 00184 $fallbacks = Language::getFallbacksFor( $code ); 00185 foreach ( $fallbacks as $fallbackCode ) { 00186 if ( !Language::isValidBuiltInCode( $fallbackCode ) ) { 00187 throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" ); 00188 } 00189 00190 $class = self::classFromCode( $fallbackCode ); 00191 self::preloadLanguageClass( $class ); 00192 if ( MWInit::classExists( $class ) ) { 00193 $lang = Language::newFromCode( $fallbackCode ); 00194 $lang->setCode( $code ); 00195 return $lang; 00196 } 00197 } 00198 00199 throw new MWException( "Invalid fallback sequence for language '$code'" ); 00200 } 00201 00211 public static function isValidCode( $code ) { 00212 return 00213 // People think language codes are html safe, so enforce it. 00214 // Ideally we should only allow a-zA-Z0-9- 00215 // but, .+ and other chars are often used for {{int:}} hacks 00216 // see bugs 37564, 37587, 36938 00217 strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code ) 00218 && !preg_match( Title::getTitleInvalidRegex(), $code ); 00219 } 00220 00230 public static function isValidBuiltInCode( $code ) { 00231 return preg_match( '/^[a-z0-9-]+$/i', $code ); 00232 } 00233 00238 public static function classFromCode( $code ) { 00239 if ( $code == 'en' ) { 00240 return 'Language'; 00241 } else { 00242 return 'Language' . str_replace( '-', '_', ucfirst( $code ) ); 00243 } 00244 } 00245 00251 public static function preloadLanguageClass( $class ) { 00252 global $IP; 00253 00254 if ( $class === 'Language' ) { 00255 return; 00256 } 00257 00258 if ( !defined( 'MW_COMPILED' ) ) { 00259 // Preload base classes to work around APC/PHP5 bug 00260 if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) { 00261 include_once( "$IP/languages/classes/$class.deps.php" ); 00262 } 00263 if ( file_exists( "$IP/languages/classes/$class.php" ) ) { 00264 include_once( "$IP/languages/classes/$class.php" ); 00265 } 00266 } 00267 } 00268 00274 public static function getLocalisationCache() { 00275 if ( is_null( self::$dataCache ) ) { 00276 global $wgLocalisationCacheConf; 00277 $class = $wgLocalisationCacheConf['class']; 00278 self::$dataCache = new $class( $wgLocalisationCacheConf ); 00279 } 00280 return self::$dataCache; 00281 } 00282 00283 function __construct() { 00284 $this->mConverter = new FakeConverter( $this ); 00285 // Set the code to the name of the descendant 00286 if ( get_class( $this ) == 'Language' ) { 00287 $this->mCode = 'en'; 00288 } else { 00289 $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) ); 00290 } 00291 self::getLocalisationCache(); 00292 } 00293 00297 function __destruct() { 00298 foreach ( $this as $name => $value ) { 00299 unset( $this->$name ); 00300 } 00301 } 00302 00307 function initContLang() { } 00308 00314 function getFallbackLanguageCode() { 00315 wfDeprecated( __METHOD__ ); 00316 return self::getFallbackFor( $this->mCode ); 00317 } 00318 00323 function getFallbackLanguages() { 00324 return self::getFallbacksFor( $this->mCode ); 00325 } 00326 00331 function getBookstoreList() { 00332 return self::$dataCache->getItem( $this->mCode, 'bookstoreList' ); 00333 } 00334 00338 public function getNamespaces() { 00339 if ( is_null( $this->namespaceNames ) ) { 00340 global $wgMetaNamespace, $wgMetaNamespaceTalk, $wgExtraNamespaces; 00341 00342 $this->namespaceNames = self::$dataCache->getItem( $this->mCode, 'namespaceNames' ); 00343 $validNamespaces = MWNamespace::getCanonicalNamespaces(); 00344 00345 $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames + $validNamespaces; 00346 00347 $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace; 00348 if ( $wgMetaNamespaceTalk ) { 00349 $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk; 00350 } else { 00351 $talk = $this->namespaceNames[NS_PROJECT_TALK]; 00352 $this->namespaceNames[NS_PROJECT_TALK] = 00353 $this->fixVariableInNamespace( $talk ); 00354 } 00355 00356 # Sometimes a language will be localised but not actually exist on this wiki. 00357 foreach ( $this->namespaceNames as $key => $text ) { 00358 if ( !isset( $validNamespaces[$key] ) ) { 00359 unset( $this->namespaceNames[$key] ); 00360 } 00361 } 00362 00363 # The above mixing may leave namespaces out of canonical order. 00364 # Re-order by namespace ID number... 00365 ksort( $this->namespaceNames ); 00366 00367 wfRunHooks( 'LanguageGetNamespaces', array( &$this->namespaceNames ) ); 00368 } 00369 return $this->namespaceNames; 00370 } 00371 00376 public function setNamespaces( array $namespaces ) { 00377 $this->namespaceNames = $namespaces; 00378 } 00379 00388 function getFormattedNamespaces() { 00389 $ns = $this->getNamespaces(); 00390 foreach ( $ns as $k => $v ) { 00391 $ns[$k] = strtr( $v, '_', ' ' ); 00392 } 00393 return $ns; 00394 } 00395 00406 function getNsText( $index ) { 00407 $ns = $this->getNamespaces(); 00408 return isset( $ns[$index] ) ? $ns[$index] : false; 00409 } 00410 00420 function getFormattedNsText( $index ) { 00421 $ns = $this->getNsText( $index ); 00422 return strtr( $ns, '_', ' ' ); 00423 } 00424 00432 function getGenderNsText( $index, $gender ) { 00433 global $wgExtraGenderNamespaces; 00434 00435 $ns = $wgExtraGenderNamespaces + self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' ); 00436 return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->getNsText( $index ); 00437 } 00438 00445 function needsGenderDistinction() { 00446 global $wgExtraGenderNamespaces, $wgExtraNamespaces; 00447 if ( count( $wgExtraGenderNamespaces ) > 0 ) { 00448 // $wgExtraGenderNamespaces overrides everything 00449 return true; 00450 } elseif ( isset( $wgExtraNamespaces[NS_USER] ) && isset( $wgExtraNamespaces[NS_USER_TALK] ) ) { 00452 // $wgExtraNamespaces overrides any gender aliases specified in i18n files 00453 return false; 00454 } else { 00455 // Check what is in i18n files 00456 $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' ); 00457 return count( $aliases ) > 0; 00458 } 00459 } 00460 00469 function getLocalNsIndex( $text ) { 00470 $lctext = $this->lc( $text ); 00471 $ids = $this->getNamespaceIds(); 00472 return isset( $ids[$lctext] ) ? $ids[$lctext] : false; 00473 } 00474 00478 function getNamespaceAliases() { 00479 if ( is_null( $this->namespaceAliases ) ) { 00480 $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceAliases' ); 00481 if ( !$aliases ) { 00482 $aliases = array(); 00483 } else { 00484 foreach ( $aliases as $name => $index ) { 00485 if ( $index === NS_PROJECT_TALK ) { 00486 unset( $aliases[$name] ); 00487 $name = $this->fixVariableInNamespace( $name ); 00488 $aliases[$name] = $index; 00489 } 00490 } 00491 } 00492 00493 global $wgExtraGenderNamespaces; 00494 $genders = $wgExtraGenderNamespaces + (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' ); 00495 foreach ( $genders as $index => $forms ) { 00496 foreach ( $forms as $alias ) { 00497 $aliases[$alias] = $index; 00498 } 00499 } 00500 00501 $this->namespaceAliases = $aliases; 00502 } 00503 return $this->namespaceAliases; 00504 } 00505 00509 function getNamespaceIds() { 00510 if ( is_null( $this->mNamespaceIds ) ) { 00511 global $wgNamespaceAliases; 00512 # Put namespace names and aliases into a hashtable. 00513 # If this is too slow, then we should arrange it so that it is done 00514 # before caching. The catch is that at pre-cache time, the above 00515 # class-specific fixup hasn't been done. 00516 $this->mNamespaceIds = array(); 00517 foreach ( $this->getNamespaces() as $index => $name ) { 00518 $this->mNamespaceIds[$this->lc( $name )] = $index; 00519 } 00520 foreach ( $this->getNamespaceAliases() as $name => $index ) { 00521 $this->mNamespaceIds[$this->lc( $name )] = $index; 00522 } 00523 if ( $wgNamespaceAliases ) { 00524 foreach ( $wgNamespaceAliases as $name => $index ) { 00525 $this->mNamespaceIds[$this->lc( $name )] = $index; 00526 } 00527 } 00528 } 00529 return $this->mNamespaceIds; 00530 } 00531 00539 function getNsIndex( $text ) { 00540 $lctext = $this->lc( $text ); 00541 $ns = MWNamespace::getCanonicalIndex( $lctext ); 00542 if ( $ns !== null ) { 00543 return $ns; 00544 } 00545 $ids = $this->getNamespaceIds(); 00546 return isset( $ids[$lctext] ) ? $ids[$lctext] : false; 00547 } 00548 00556 function getVariantname( $code, $usemsg = true ) { 00557 $msg = "variantname-$code"; 00558 list( $rootCode ) = explode( '-', $code ); 00559 if ( $usemsg && wfMessage( $msg )->exists() ) { 00560 return $this->getMessageFromDB( $msg ); 00561 } 00562 $name = self::getLanguageName( $code ); 00563 if ( $name ) { 00564 return $name; # if it's defined as a language name, show that 00565 } else { 00566 # otherwise, output the language code 00567 return $code; 00568 } 00569 } 00570 00575 function specialPage( $name ) { 00576 $aliases = $this->getSpecialPageAliases(); 00577 if ( isset( $aliases[$name][0] ) ) { 00578 $name = $aliases[$name][0]; 00579 } 00580 return $this->getNsText( NS_SPECIAL ) . ':' . $name; 00581 } 00582 00586 function getQuickbarSettings() { 00587 return array( 00588 $this->getMessage( 'qbsettings-none' ), 00589 $this->getMessage( 'qbsettings-fixedleft' ), 00590 $this->getMessage( 'qbsettings-fixedright' ), 00591 $this->getMessage( 'qbsettings-floatingleft' ), 00592 $this->getMessage( 'qbsettings-floatingright' ), 00593 $this->getMessage( 'qbsettings-directionality' ) 00594 ); 00595 } 00596 00600 function getDatePreferences() { 00601 return self::$dataCache->getItem( $this->mCode, 'datePreferences' ); 00602 } 00603 00607 function getDateFormats() { 00608 return self::$dataCache->getItem( $this->mCode, 'dateFormats' ); 00609 } 00610 00614 function getDefaultDateFormat() { 00615 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat' ); 00616 if ( $df === 'dmy or mdy' ) { 00617 global $wgAmericanDates; 00618 return $wgAmericanDates ? 'mdy' : 'dmy'; 00619 } else { 00620 return $df; 00621 } 00622 } 00623 00627 function getDatePreferenceMigrationMap() { 00628 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' ); 00629 } 00630 00635 function getImageFile( $image ) { 00636 return self::$dataCache->getSubitem( $this->mCode, 'imageFiles', $image ); 00637 } 00638 00642 function getExtraUserToggles() { 00643 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles' ); 00644 } 00645 00650 function getUserToggle( $tog ) { 00651 return $this->getMessageFromDB( "tog-$tog" ); 00652 } 00653 00663 public static function getLanguageNames( $customisedOnly = false ) { 00664 global $wgExtraLanguageNames; 00665 static $coreLanguageNames; 00666 00667 if ( $coreLanguageNames === null ) { 00668 include( MWInit::compiledPath( 'languages/Names.php' ) ); 00669 } 00670 00671 $allNames = $wgExtraLanguageNames + $coreLanguageNames; 00672 if ( !$customisedOnly ) { 00673 return $allNames; 00674 } 00675 00676 $names = array(); 00677 // We do this using a foreach over the codes instead of a directory 00678 // loop so that messages files in extensions will work correctly. 00679 foreach ( $allNames as $code => $value ) { 00680 if ( is_readable( self::getMessagesFileName( $code ) ) ) { 00681 $names[$code] = $allNames[$code]; 00682 } 00683 } 00684 return $names; 00685 } 00686 00695 public static function getTranslatedLanguageNames( $code ) { 00696 $names = array(); 00697 wfRunHooks( 'LanguageGetTranslatedLanguageNames', array( &$names, $code ) ); 00698 00699 foreach ( self::getLanguageNames() as $code => $name ) { 00700 if ( !isset( $names[$code] ) ) $names[$code] = $name; 00701 } 00702 00703 return $names; 00704 } 00705 00712 function getMessageFromDB( $msg ) { 00713 return wfMsgExt( $msg, array( 'parsemag', 'language' => $this ) ); 00714 } 00715 00722 function getLanguageName( $code ) { 00723 $names = self::getLanguageNames(); 00724 if ( !array_key_exists( $code, $names ) ) { 00725 return ''; 00726 } 00727 return $names[$code]; 00728 } 00729 00734 function getMonthName( $key ) { 00735 return $this->getMessageFromDB( self::$mMonthMsgs[$key - 1] ); 00736 } 00737 00741 function getMonthNamesArray() { 00742 $monthNames = array( '' ); 00743 for ( $i = 1; $i < 13; $i++ ) { 00744 $monthNames[] = $this->getMonthName( $i ); 00745 } 00746 return $monthNames; 00747 } 00748 00753 function getMonthNameGen( $key ) { 00754 return $this->getMessageFromDB( self::$mMonthGenMsgs[$key - 1] ); 00755 } 00756 00761 function getMonthAbbreviation( $key ) { 00762 return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key - 1] ); 00763 } 00764 00768 function getMonthAbbreviationsArray() { 00769 $monthNames = array( '' ); 00770 for ( $i = 1; $i < 13; $i++ ) { 00771 $monthNames[] = $this->getMonthAbbreviation( $i ); 00772 } 00773 return $monthNames; 00774 } 00775 00780 function getWeekdayName( $key ) { 00781 return $this->getMessageFromDB( self::$mWeekdayMsgs[$key - 1] ); 00782 } 00783 00788 function getWeekdayAbbreviation( $key ) { 00789 return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key - 1] ); 00790 } 00791 00796 function getIranianCalendarMonthName( $key ) { 00797 return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] ); 00798 } 00799 00804 function getHebrewCalendarMonthName( $key ) { 00805 return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] ); 00806 } 00807 00812 function getHebrewCalendarMonthNameGen( $key ) { 00813 return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] ); 00814 } 00815 00820 function getHijriCalendarMonthName( $key ) { 00821 return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] ); 00822 } 00823 00886 function sprintfDate( $format, $ts ) { 00887 $s = ''; 00888 $raw = false; 00889 $roman = false; 00890 $hebrewNum = false; 00891 $unix = false; 00892 $rawToggle = false; 00893 $iranian = false; 00894 $hebrew = false; 00895 $hijri = false; 00896 $thai = false; 00897 $minguo = false; 00898 $tenno = false; 00899 for ( $p = 0; $p < strlen( $format ); $p++ ) { 00900 $num = false; 00901 $code = $format[$p]; 00902 if ( $code == 'x' && $p < strlen( $format ) - 1 ) { 00903 $code .= $format[++$p]; 00904 } 00905 00906 if ( ( $code === 'xi' || $code == 'xj' || $code == 'xk' || $code == 'xm' || $code == 'xo' || $code == 'xt' ) && $p < strlen( $format ) - 1 ) { 00907 $code .= $format[++$p]; 00908 } 00909 00910 switch ( $code ) { 00911 case 'xx': 00912 $s .= 'x'; 00913 break; 00914 case 'xn': 00915 $raw = true; 00916 break; 00917 case 'xN': 00918 $rawToggle = !$rawToggle; 00919 break; 00920 case 'xr': 00921 $roman = true; 00922 break; 00923 case 'xh': 00924 $hebrewNum = true; 00925 break; 00926 case 'xg': 00927 $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) ); 00928 break; 00929 case 'xjx': 00930 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts ); 00931 $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] ); 00932 break; 00933 case 'd': 00934 $num = substr( $ts, 6, 2 ); 00935 break; 00936 case 'D': 00937 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts ); 00938 $s .= $this->getWeekdayAbbreviation( gmdate( 'w', $unix ) + 1 ); 00939 break; 00940 case 'j': 00941 $num = intval( substr( $ts, 6, 2 ) ); 00942 break; 00943 case 'xij': 00944 if ( !$iranian ) { 00945 $iranian = self::tsToIranian( $ts ); 00946 } 00947 $num = $iranian[2]; 00948 break; 00949 case 'xmj': 00950 if ( !$hijri ) { 00951 $hijri = self::tsToHijri( $ts ); 00952 } 00953 $num = $hijri[2]; 00954 break; 00955 case 'xjj': 00956 if ( !$hebrew ) { 00957 $hebrew = self::tsToHebrew( $ts ); 00958 } 00959 $num = $hebrew[2]; 00960 break; 00961 case 'l': 00962 if ( !$unix ) { 00963 $unix = wfTimestamp( TS_UNIX, $ts ); 00964 } 00965 $s .= $this->getWeekdayName( gmdate( 'w', $unix ) + 1 ); 00966 break; 00967 case 'N': 00968 if ( !$unix ) { 00969 $unix = wfTimestamp( TS_UNIX, $ts ); 00970 } 00971 $w = gmdate( 'w', $unix ); 00972 $num = $w ? $w : 7; 00973 break; 00974 case 'w': 00975 if ( !$unix ) { 00976 $unix = wfTimestamp( TS_UNIX, $ts ); 00977 } 00978 $num = gmdate( 'w', $unix ); 00979 break; 00980 case 'z': 00981 if ( !$unix ) { 00982 $unix = wfTimestamp( TS_UNIX, $ts ); 00983 } 00984 $num = gmdate( 'z', $unix ); 00985 break; 00986 case 'W': 00987 if ( !$unix ) { 00988 $unix = wfTimestamp( TS_UNIX, $ts ); 00989 } 00990 $num = gmdate( 'W', $unix ); 00991 break; 00992 case 'F': 00993 $s .= $this->getMonthName( substr( $ts, 4, 2 ) ); 00994 break; 00995 case 'xiF': 00996 if ( !$iranian ) { 00997 $iranian = self::tsToIranian( $ts ); 00998 } 00999 $s .= $this->getIranianCalendarMonthName( $iranian[1] ); 01000 break; 01001 case 'xmF': 01002 if ( !$hijri ) { 01003 $hijri = self::tsToHijri( $ts ); 01004 } 01005 $s .= $this->getHijriCalendarMonthName( $hijri[1] ); 01006 break; 01007 case 'xjF': 01008 if ( !$hebrew ) { 01009 $hebrew = self::tsToHebrew( $ts ); 01010 } 01011 $s .= $this->getHebrewCalendarMonthName( $hebrew[1] ); 01012 break; 01013 case 'm': 01014 $num = substr( $ts, 4, 2 ); 01015 break; 01016 case 'M': 01017 $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) ); 01018 break; 01019 case 'n': 01020 $num = intval( substr( $ts, 4, 2 ) ); 01021 break; 01022 case 'xin': 01023 if ( !$iranian ) { 01024 $iranian = self::tsToIranian( $ts ); 01025 } 01026 $num = $iranian[1]; 01027 break; 01028 case 'xmn': 01029 if ( !$hijri ) { 01030 $hijri = self::tsToHijri ( $ts ); 01031 } 01032 $num = $hijri[1]; 01033 break; 01034 case 'xjn': 01035 if ( !$hebrew ) { 01036 $hebrew = self::tsToHebrew( $ts ); 01037 } 01038 $num = $hebrew[1]; 01039 break; 01040 case 't': 01041 if ( !$unix ) { 01042 $unix = wfTimestamp( TS_UNIX, $ts ); 01043 } 01044 $num = gmdate( 't', $unix ); 01045 break; 01046 case 'xjt': 01047 if ( !$hebrew ) { 01048 $hebrew = self::tsToHebrew( $ts ); 01049 } 01050 $num = $hebrew[3]; 01051 break; 01052 case 'L': 01053 if ( !$unix ) { 01054 $unix = wfTimestamp( TS_UNIX, $ts ); 01055 } 01056 $num = gmdate( 'L', $unix ); 01057 break; 01058 case 'o': 01059 if ( !$unix ) { 01060 $unix = wfTimestamp( TS_UNIX, $ts ); 01061 } 01062 $num = gmdate( 'o', $unix ); 01063 break; 01064 case 'Y': 01065 $num = substr( $ts, 0, 4 ); 01066 break; 01067 case 'xiY': 01068 if ( !$iranian ) { 01069 $iranian = self::tsToIranian( $ts ); 01070 } 01071 $num = $iranian[0]; 01072 break; 01073 case 'xmY': 01074 if ( !$hijri ) { 01075 $hijri = self::tsToHijri( $ts ); 01076 } 01077 $num = $hijri[0]; 01078 break; 01079 case 'xjY': 01080 if ( !$hebrew ) { 01081 $hebrew = self::tsToHebrew( $ts ); 01082 } 01083 $num = $hebrew[0]; 01084 break; 01085 case 'xkY': 01086 if ( !$thai ) { 01087 $thai = self::tsToYear( $ts, 'thai' ); 01088 } 01089 $num = $thai[0]; 01090 break; 01091 case 'xoY': 01092 if ( !$minguo ) { 01093 $minguo = self::tsToYear( $ts, 'minguo' ); 01094 } 01095 $num = $minguo[0]; 01096 break; 01097 case 'xtY': 01098 if ( !$tenno ) { 01099 $tenno = self::tsToYear( $ts, 'tenno' ); 01100 } 01101 $num = $tenno[0]; 01102 break; 01103 case 'y': 01104 $num = substr( $ts, 2, 2 ); 01105 break; 01106 case 'xiy': 01107 if ( !$iranian ) { 01108 $iranian = self::tsToIranian( $ts ); 01109 } 01110 $num = substr( $iranian[0], -2 ); 01111 break; 01112 case 'a': 01113 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm'; 01114 break; 01115 case 'A': 01116 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM'; 01117 break; 01118 case 'g': 01119 $h = substr( $ts, 8, 2 ); 01120 $num = $h % 12 ? $h % 12 : 12; 01121 break; 01122 case 'G': 01123 $num = intval( substr( $ts, 8, 2 ) ); 01124 break; 01125 case 'h': 01126 $h = substr( $ts, 8, 2 ); 01127 $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 ); 01128 break; 01129 case 'H': 01130 $num = substr( $ts, 8, 2 ); 01131 break; 01132 case 'i': 01133 $num = substr( $ts, 10, 2 ); 01134 break; 01135 case 's': 01136 $num = substr( $ts, 12, 2 ); 01137 break; 01138 case 'c': 01139 if ( !$unix ) { 01140 $unix = wfTimestamp( TS_UNIX, $ts ); 01141 } 01142 $s .= gmdate( 'c', $unix ); 01143 break; 01144 case 'r': 01145 if ( !$unix ) { 01146 $unix = wfTimestamp( TS_UNIX, $ts ); 01147 } 01148 $s .= gmdate( 'r', $unix ); 01149 break; 01150 case 'U': 01151 if ( !$unix ) { 01152 $unix = wfTimestamp( TS_UNIX, $ts ); 01153 } 01154 $num = $unix; 01155 break; 01156 case '\\': 01157 # Backslash escaping 01158 if ( $p < strlen( $format ) - 1 ) { 01159 $s .= $format[++$p]; 01160 } else { 01161 $s .= '\\'; 01162 } 01163 break; 01164 case '"': 01165 # Quoted literal 01166 if ( $p < strlen( $format ) - 1 ) { 01167 $endQuote = strpos( $format, '"', $p + 1 ); 01168 if ( $endQuote === false ) { 01169 # No terminating quote, assume literal " 01170 $s .= '"'; 01171 } else { 01172 $s .= substr( $format, $p + 1, $endQuote - $p - 1 ); 01173 $p = $endQuote; 01174 } 01175 } else { 01176 # Quote at end of string, assume literal " 01177 $s .= '"'; 01178 } 01179 break; 01180 default: 01181 $s .= $format[$p]; 01182 } 01183 if ( $num !== false ) { 01184 if ( $rawToggle || $raw ) { 01185 $s .= $num; 01186 $raw = false; 01187 } elseif ( $roman ) { 01188 $s .= self::romanNumeral( $num ); 01189 $roman = false; 01190 } elseif ( $hebrewNum ) { 01191 $s .= self::hebrewNumeral( $num ); 01192 $hebrewNum = false; 01193 } else { 01194 $s .= $this->formatNum( $num, true ); 01195 } 01196 } 01197 } 01198 return $s; 01199 } 01200 01201 private static $GREG_DAYS = array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); 01202 private static $IRANIAN_DAYS = array( 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 ); 01203 01216 private static function tsToIranian( $ts ) { 01217 $gy = substr( $ts, 0, 4 ) -1600; 01218 $gm = substr( $ts, 4, 2 ) -1; 01219 $gd = substr( $ts, 6, 2 ) -1; 01220 01221 # Days passed from the beginning (including leap years) 01222 $gDayNo = 365 * $gy 01223 + floor( ( $gy + 3 ) / 4 ) 01224 - floor( ( $gy + 99 ) / 100 ) 01225 + floor( ( $gy + 399 ) / 400 ); 01226 01227 // Add days of the past months of this year 01228 for ( $i = 0; $i < $gm; $i++ ) { 01229 $gDayNo += self::$GREG_DAYS[$i]; 01230 } 01231 01232 // Leap years 01233 if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) { 01234 $gDayNo++; 01235 } 01236 01237 // Days passed in current month 01238 $gDayNo += (int)$gd; 01239 01240 $jDayNo = $gDayNo - 79; 01241 01242 $jNp = floor( $jDayNo / 12053 ); 01243 $jDayNo %= 12053; 01244 01245 $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 ); 01246 $jDayNo %= 1461; 01247 01248 if ( $jDayNo >= 366 ) { 01249 $jy += floor( ( $jDayNo - 1 ) / 365 ); 01250 $jDayNo = floor( ( $jDayNo - 1 ) % 365 ); 01251 } 01252 01253 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) { 01254 $jDayNo -= self::$IRANIAN_DAYS[$i]; 01255 } 01256 01257 $jm = $i + 1; 01258 $jd = $jDayNo + 1; 01259 01260 return array( $jy, $jm, $jd ); 01261 } 01262 01274 private static function tsToHijri( $ts ) { 01275 $year = substr( $ts, 0, 4 ); 01276 $month = substr( $ts, 4, 2 ); 01277 $day = substr( $ts, 6, 2 ); 01278 01279 $zyr = $year; 01280 $zd = $day; 01281 $zm = $month; 01282 $zy = $zyr; 01283 01284 if ( 01285 ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) || 01286 ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) ) 01287 ) 01288 { 01289 $zjd = (int)( ( 1461 * ( $zy + 4800 + (int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) + 01290 (int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) - 01291 (int)( ( 3 * (int)( ( ( $zy + 4900 + (int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) + 01292 $zd - 32075; 01293 } else { 01294 $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) + 01295 (int)( ( 275 * $zm ) / 9 ) + $zd + 1729777; 01296 } 01297 01298 $zl = $zjd -1948440 + 10632; 01299 $zn = (int)( ( $zl - 1 ) / 10631 ); 01300 $zl = $zl - 10631 * $zn + 354; 01301 $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) + ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) ); 01302 $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) - ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29; 01303 $zm = (int)( ( 24 * $zl ) / 709 ); 01304 $zd = $zl - (int)( ( 709 * $zm ) / 24 ); 01305 $zy = 30 * $zn + $zj - 30; 01306 01307 return array( $zy, $zm, $zd ); 01308 } 01309 01325 private static function tsToHebrew( $ts ) { 01326 # Parse date 01327 $year = substr( $ts, 0, 4 ); 01328 $month = substr( $ts, 4, 2 ); 01329 $day = substr( $ts, 6, 2 ); 01330 01331 # Calculate Hebrew year 01332 $hebrewYear = $year + 3760; 01333 01334 # Month number when September = 1, August = 12 01335 $month += 4; 01336 if ( $month > 12 ) { 01337 # Next year 01338 $month -= 12; 01339 $year++; 01340 $hebrewYear++; 01341 } 01342 01343 # Calculate day of year from 1 September 01344 $dayOfYear = $day; 01345 for ( $i = 1; $i < $month; $i++ ) { 01346 if ( $i == 6 ) { 01347 # February 01348 $dayOfYear += 28; 01349 # Check if the year is leap 01350 if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) { 01351 $dayOfYear++; 01352 } 01353 } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) { 01354 $dayOfYear += 30; 01355 } else { 01356 $dayOfYear += 31; 01357 } 01358 } 01359 01360 # Calculate the start of the Hebrew year 01361 $start = self::hebrewYearStart( $hebrewYear ); 01362 01363 # Calculate next year's start 01364 if ( $dayOfYear <= $start ) { 01365 # Day is before the start of the year - it is the previous year 01366 # Next year's start 01367 $nextStart = $start; 01368 # Previous year 01369 $year--; 01370 $hebrewYear--; 01371 # Add days since previous year's 1 September 01372 $dayOfYear += 365; 01373 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) { 01374 # Leap year 01375 $dayOfYear++; 01376 } 01377 # Start of the new (previous) year 01378 $start = self::hebrewYearStart( $hebrewYear ); 01379 } else { 01380 # Next year's start 01381 $nextStart = self::hebrewYearStart( $hebrewYear + 1 ); 01382 } 01383 01384 # Calculate Hebrew day of year 01385 $hebrewDayOfYear = $dayOfYear - $start; 01386 01387 # Difference between year's days 01388 $diff = $nextStart - $start; 01389 # Add 12 (or 13 for leap years) days to ignore the difference between 01390 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the 01391 # difference is only about the year type 01392 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) { 01393 $diff += 13; 01394 } else { 01395 $diff += 12; 01396 } 01397 01398 # Check the year pattern, and is leap year 01399 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year 01400 # This is mod 30, to work on both leap years (which add 30 days of Adar I) 01401 # and non-leap years 01402 $yearPattern = $diff % 30; 01403 # Check if leap year 01404 $isLeap = $diff >= 30; 01405 01406 # Calculate day in the month from number of day in the Hebrew year 01407 # Don't check Adar - if the day is not in Adar, we will stop before; 01408 # if it is in Adar, we will use it to check if it is Adar I or Adar II 01409 $hebrewDay = $hebrewDayOfYear; 01410 $hebrewMonth = 1; 01411 $days = 0; 01412 while ( $hebrewMonth <= 12 ) { 01413 # Calculate days in this month 01414 if ( $isLeap && $hebrewMonth == 6 ) { 01415 # Adar in a leap year 01416 if ( $isLeap ) { 01417 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days 01418 $days = 30; 01419 if ( $hebrewDay <= $days ) { 01420 # Day in Adar I 01421 $hebrewMonth = 13; 01422 } else { 01423 # Subtract the days of Adar I 01424 $hebrewDay -= $days; 01425 # Try Adar II 01426 $days = 29; 01427 if ( $hebrewDay <= $days ) { 01428 # Day in Adar II 01429 $hebrewMonth = 14; 01430 } 01431 } 01432 } 01433 } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) { 01434 # Cheshvan in a complete year (otherwise as the rule below) 01435 $days = 30; 01436 } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) { 01437 # Kislev in an incomplete year (otherwise as the rule below) 01438 $days = 29; 01439 } else { 01440 # Odd months have 30 days, even have 29 01441 $days = 30 - ( $hebrewMonth - 1 ) % 2; 01442 } 01443 if ( $hebrewDay <= $days ) { 01444 # In the current month 01445 break; 01446 } else { 01447 # Subtract the days of the current month 01448 $hebrewDay -= $days; 01449 # Try in the next month 01450 $hebrewMonth++; 01451 } 01452 } 01453 01454 return array( $hebrewYear, $hebrewMonth, $hebrewDay, $days ); 01455 } 01456 01466 private static function hebrewYearStart( $year ) { 01467 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 ); 01468 $b = intval( ( $year - 1 ) % 4 ); 01469 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 ); 01470 if ( $m < 0 ) { 01471 $m--; 01472 } 01473 $Mar = intval( $m ); 01474 if ( $m < 0 ) { 01475 $m++; 01476 } 01477 $m -= $Mar; 01478 01479 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 ); 01480 if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) { 01481 $Mar++; 01482 } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) { 01483 $Mar += 2; 01484 } elseif ( $c == 2 || $c == 4 || $c == 6 ) { 01485 $Mar++; 01486 } 01487 01488 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24; 01489 return $Mar; 01490 } 01491 01504 private static function tsToYear( $ts, $cName ) { 01505 $gy = substr( $ts, 0, 4 ); 01506 $gm = substr( $ts, 4, 2 ); 01507 $gd = substr( $ts, 6, 2 ); 01508 01509 if ( !strcmp( $cName, 'thai' ) ) { 01510 # Thai solar dates 01511 # Add 543 years to the Gregorian calendar 01512 # Months and days are identical 01513 $gy_offset = $gy + 543; 01514 } elseif ( ( !strcmp( $cName, 'minguo' ) ) || !strcmp( $cName, 'juche' ) ) { 01515 # Minguo dates 01516 # Deduct 1911 years from the Gregorian calendar 01517 # Months and days are identical 01518 $gy_offset = $gy - 1911; 01519 } elseif ( !strcmp( $cName, 'tenno' ) ) { 01520 # Nengō dates up to Meiji period 01521 # Deduct years from the Gregorian calendar 01522 # depending on the nengo periods 01523 # Months and days are identical 01524 if ( ( $gy < 1912 ) || ( ( $gy == 1912 ) && ( $gm < 7 ) ) || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) ) ) { 01525 # Meiji period 01526 $gy_gannen = $gy - 1868 + 1; 01527 $gy_offset = $gy_gannen; 01528 if ( $gy_gannen == 1 ) { 01529 $gy_offset = '元'; 01530 } 01531 $gy_offset = '明治' . $gy_offset; 01532 } elseif ( 01533 ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) || 01534 ( ( $gy == 1912 ) && ( $gm >= 8 ) ) || 01535 ( ( $gy > 1912 ) && ( $gy < 1926 ) ) || 01536 ( ( $gy == 1926 ) && ( $gm < 12 ) ) || 01537 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) ) 01538 ) 01539 { 01540 # Taishō period 01541 $gy_gannen = $gy - 1912 + 1; 01542 $gy_offset = $gy_gannen; 01543 if ( $gy_gannen == 1 ) { 01544 $gy_offset = '元'; 01545 } 01546 $gy_offset = '大正' . $gy_offset; 01547 } elseif ( 01548 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) || 01549 ( ( $gy > 1926 ) && ( $gy < 1989 ) ) || 01550 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) ) 01551 ) 01552 { 01553 # Shōwa period 01554 $gy_gannen = $gy - 1926 + 1; 01555 $gy_offset = $gy_gannen; 01556 if ( $gy_gannen == 1 ) { 01557 $gy_offset = '元'; 01558 } 01559 $gy_offset = '昭和' . $gy_offset; 01560 } else { 01561 # Heisei period 01562 $gy_gannen = $gy - 1989 + 1; 01563 $gy_offset = $gy_gannen; 01564 if ( $gy_gannen == 1 ) { 01565 $gy_offset = '元'; 01566 } 01567 $gy_offset = '平成' . $gy_offset; 01568 } 01569 } else { 01570 $gy_offset = $gy; 01571 } 01572 01573 return array( $gy_offset, $gm, $gd ); 01574 } 01575 01583 static function romanNumeral( $num ) { 01584 static $table = array( 01585 array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ), 01586 array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ), 01587 array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ), 01588 array( '', 'M', 'MM', 'MMM' ) 01589 ); 01590 01591 $num = intval( $num ); 01592 if ( $num > 3000 || $num <= 0 ) { 01593 return $num; 01594 } 01595 01596 $s = ''; 01597 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) { 01598 if ( $num >= $pow10 ) { 01599 $s .= $table[$i][(int)floor( $num / $pow10 )]; 01600 } 01601 $num = $num % $pow10; 01602 } 01603 return $s; 01604 } 01605 01613 static function hebrewNumeral( $num ) { 01614 static $table = array( 01615 array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ), 01616 array( '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ), 01617 array( '', 'ק', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק', 'תתר' ), 01618 array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ) 01619 ); 01620 01621 $num = intval( $num ); 01622 if ( $num > 9999 || $num <= 0 ) { 01623 return $num; 01624 } 01625 01626 $s = ''; 01627 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) { 01628 if ( $num >= $pow10 ) { 01629 if ( $num == 15 || $num == 16 ) { 01630 $s .= $table[0][9] . $table[0][$num - 9]; 01631 $num = 0; 01632 } else { 01633 $s .= $table[$i][intval( ( $num / $pow10 ) )]; 01634 if ( $pow10 == 1000 ) { 01635 $s .= "'"; 01636 } 01637 } 01638 } 01639 $num = $num % $pow10; 01640 } 01641 if ( strlen( $s ) == 2 ) { 01642 $str = $s . "'"; 01643 } else { 01644 $str = substr( $s, 0, strlen( $s ) - 2 ) . '"'; 01645 $str .= substr( $s, strlen( $s ) - 2, 2 ); 01646 } 01647 $start = substr( $str, 0, strlen( $str ) - 2 ); 01648 $end = substr( $str, strlen( $str ) - 2 ); 01649 switch( $end ) { 01650 case 'כ': 01651 $str = $start . 'ך'; 01652 break; 01653 case 'מ': 01654 $str = $start . 'ם'; 01655 break; 01656 case 'נ': 01657 $str = $start . 'ן'; 01658 break; 01659 case 'פ': 01660 $str = $start . 'ף'; 01661 break; 01662 case 'צ': 01663 $str = $start . 'ץ'; 01664 break; 01665 } 01666 return $str; 01667 } 01668 01677 function userAdjust( $ts, $tz = false ) { 01678 global $wgUser, $wgLocalTZoffset; 01679 01680 if ( $tz === false ) { 01681 $tz = $wgUser->getOption( 'timecorrection' ); 01682 } 01683 01684 $data = explode( '|', $tz, 3 ); 01685 01686 if ( $data[0] == 'ZoneInfo' ) { 01687 wfSuppressWarnings(); 01688 $userTZ = timezone_open( $data[2] ); 01689 wfRestoreWarnings(); 01690 if ( $userTZ !== false ) { 01691 $date = date_create( $ts, timezone_open( 'UTC' ) ); 01692 date_timezone_set( $date, $userTZ ); 01693 $date = date_format( $date, 'YmdHis' ); 01694 return $date; 01695 } 01696 # Unrecognized timezone, default to 'Offset' with the stored offset. 01697 $data[0] = 'Offset'; 01698 } 01699 01700 $minDiff = 0; 01701 if ( $data[0] == 'System' || $tz == '' ) { 01702 # Global offset in minutes. 01703 if ( isset( $wgLocalTZoffset ) ) { 01704 $minDiff = $wgLocalTZoffset; 01705 } 01706 } elseif ( $data[0] == 'Offset' ) { 01707 $minDiff = intval( $data[1] ); 01708 } else { 01709 $data = explode( ':', $tz ); 01710 if ( count( $data ) == 2 ) { 01711 $data[0] = intval( $data[0] ); 01712 $data[1] = intval( $data[1] ); 01713 $minDiff = abs( $data[0] ) * 60 + $data[1]; 01714 if ( $data[0] < 0 ) { 01715 $minDiff = -$minDiff; 01716 } 01717 } else { 01718 $minDiff = intval( $data[0] ) * 60; 01719 } 01720 } 01721 01722 # No difference ? Return time unchanged 01723 if ( 0 == $minDiff ) { 01724 return $ts; 01725 } 01726 01727 wfSuppressWarnings(); // E_STRICT system time bitching 01728 # Generate an adjusted date; take advantage of the fact that mktime 01729 # will normalize out-of-range values so we don't have to split $minDiff 01730 # into hours and minutes. 01731 $t = mktime( ( 01732 (int)substr( $ts, 8, 2 ) ), # Hours 01733 (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes 01734 (int)substr( $ts, 12, 2 ), # Seconds 01735 (int)substr( $ts, 4, 2 ), # Month 01736 (int)substr( $ts, 6, 2 ), # Day 01737 (int)substr( $ts, 0, 4 ) ); # Year 01738 01739 $date = date( 'YmdHis', $t ); 01740 wfRestoreWarnings(); 01741 01742 return $date; 01743 } 01744 01762 function dateFormat( $usePrefs = true ) { 01763 global $wgUser; 01764 01765 if ( is_bool( $usePrefs ) ) { 01766 if ( $usePrefs ) { 01767 $datePreference = $wgUser->getDatePreference(); 01768 } else { 01769 $datePreference = (string)User::getDefaultOption( 'date' ); 01770 } 01771 } else { 01772 $datePreference = (string)$usePrefs; 01773 } 01774 01775 // return int 01776 if ( $datePreference == '' ) { 01777 return 'default'; 01778 } 01779 01780 return $datePreference; 01781 } 01782 01790 function getDateFormatString( $type, $pref ) { 01791 if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) { 01792 if ( $pref == 'default' ) { 01793 $pref = $this->getDefaultDateFormat(); 01794 $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" ); 01795 } else { 01796 $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" ); 01797 if ( is_null( $df ) ) { 01798 $pref = $this->getDefaultDateFormat(); 01799 $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" ); 01800 } 01801 } 01802 $this->dateFormatStrings[$type][$pref] = $df; 01803 } 01804 return $this->dateFormatStrings[$type][$pref]; 01805 } 01806 01817 function date( $ts, $adj = false, $format = true, $timecorrection = false ) { 01818 $ts = wfTimestamp( TS_MW, $ts ); 01819 if ( $adj ) { 01820 $ts = $this->userAdjust( $ts, $timecorrection ); 01821 } 01822 $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) ); 01823 return $this->sprintfDate( $df, $ts ); 01824 } 01825 01836 function time( $ts, $adj = false, $format = true, $timecorrection = false ) { 01837 $ts = wfTimestamp( TS_MW, $ts ); 01838 if ( $adj ) { 01839 $ts = $this->userAdjust( $ts, $timecorrection ); 01840 } 01841 $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) ); 01842 return $this->sprintfDate( $df, $ts ); 01843 } 01844 01856 function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) { 01857 $ts = wfTimestamp( TS_MW, $ts ); 01858 if ( $adj ) { 01859 $ts = $this->userAdjust( $ts, $timecorrection ); 01860 } 01861 $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) ); 01862 return $this->sprintfDate( $df, $ts ); 01863 } 01864 01884 private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) { 01885 $ts = wfTimestamp( TS_MW, $ts ); 01886 $options += array( 'timecorrection' => true, 'format' => true ); 01887 if ( $options['timecorrection'] !== false ) { 01888 if ( $options['timecorrection'] === true ) { 01889 $offset = $user->getOption( 'timecorrection' ); 01890 } else { 01891 $offset = $options['timecorrection']; 01892 } 01893 $ts = $this->userAdjust( $ts, $offset ); 01894 } 01895 if ( $options['format'] === true ) { 01896 $format = $user->getDatePreference(); 01897 } else { 01898 $format = $options['format']; 01899 } 01900 $df = $this->getDateFormatString( $type, $this->dateFormat( $format ) ); 01901 return $this->sprintfDate( $df, $ts ); 01902 } 01903 01923 public function userDate( $ts, User $user, array $options = array() ) { 01924 return $this->internalUserTimeAndDate( 'date', $ts, $user, $options ); 01925 } 01926 01946 public function userTime( $ts, User $user, array $options = array() ) { 01947 return $this->internalUserTimeAndDate( 'time', $ts, $user, $options ); 01948 } 01949 01969 public function userTimeAndDate( $ts, User $user, array $options = array() ) { 01970 return $this->internalUserTimeAndDate( 'both', $ts, $user, $options ); 01971 } 01972 01977 function getMessage( $key ) { 01978 return self::$dataCache->getSubitem( $this->mCode, 'messages', $key ); 01979 } 01980 01984 function getAllMessages() { 01985 return self::$dataCache->getItem( $this->mCode, 'messages' ); 01986 } 01987 01994 function iconv( $in, $out, $string ) { 01995 # This is a wrapper for iconv in all languages except esperanto, 01996 # which does some nasty x-conversions beforehand 01997 01998 # Even with //IGNORE iconv can whine about illegal characters in 01999 # *input* string. We just ignore those too. 02000 # REF: http://bugs.php.net/bug.php?id=37166 02001 # REF: https://bugzilla.wikimedia.org/show_bug.cgi?id=16885 02002 wfSuppressWarnings(); 02003 $text = iconv( $in, $out . '//IGNORE', $string ); 02004 wfRestoreWarnings(); 02005 return $text; 02006 } 02007 02008 // callback functions for uc(), lc(), ucwords(), ucwordbreaks() 02009 02014 function ucwordbreaksCallbackAscii( $matches ) { 02015 return $this->ucfirst( $matches[1] ); 02016 } 02017 02022 function ucwordbreaksCallbackMB( $matches ) { 02023 return mb_strtoupper( $matches[0] ); 02024 } 02025 02030 function ucCallback( $matches ) { 02031 list( $wikiUpperChars ) = self::getCaseMaps(); 02032 return strtr( $matches[1], $wikiUpperChars ); 02033 } 02034 02039 function lcCallback( $matches ) { 02040 list( , $wikiLowerChars ) = self::getCaseMaps(); 02041 return strtr( $matches[1], $wikiLowerChars ); 02042 } 02043 02048 function ucwordsCallbackMB( $matches ) { 02049 return mb_strtoupper( $matches[0] ); 02050 } 02051 02056 function ucwordsCallbackWiki( $matches ) { 02057 list( $wikiUpperChars ) = self::getCaseMaps(); 02058 return strtr( $matches[0], $wikiUpperChars ); 02059 } 02060 02068 function ucfirst( $str ) { 02069 $o = ord( $str ); 02070 if ( $o < 96 ) { // if already uppercase... 02071 return $str; 02072 } elseif ( $o < 128 ) { 02073 return ucfirst( $str ); // use PHP's ucfirst() 02074 } else { 02075 // fall back to more complex logic in case of multibyte strings 02076 return $this->uc( $str, true ); 02077 } 02078 } 02079 02088 function uc( $str, $first = false ) { 02089 if ( function_exists( 'mb_strtoupper' ) ) { 02090 if ( $first ) { 02091 if ( $this->isMultibyte( $str ) ) { 02092 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 ); 02093 } else { 02094 return ucfirst( $str ); 02095 } 02096 } else { 02097 return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str ); 02098 } 02099 } else { 02100 if ( $this->isMultibyte( $str ) ) { 02101 $x = $first ? '^' : ''; 02102 return preg_replace_callback( 02103 "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/", 02104 array( $this, 'ucCallback' ), 02105 $str 02106 ); 02107 } else { 02108 return $first ? ucfirst( $str ) : strtoupper( $str ); 02109 } 02110 } 02111 } 02112 02117 function lcfirst( $str ) { 02118 $o = ord( $str ); 02119 if ( !$o ) { 02120 return strval( $str ); 02121 } elseif ( $o >= 128 ) { 02122 return $this->lc( $str, true ); 02123 } elseif ( $o > 96 ) { 02124 return $str; 02125 } else { 02126 $str[0] = strtolower( $str[0] ); 02127 return $str; 02128 } 02129 } 02130 02136 function lc( $str, $first = false ) { 02137 if ( function_exists( 'mb_strtolower' ) ) { 02138 if ( $first ) { 02139 if ( $this->isMultibyte( $str ) ) { 02140 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 ); 02141 } else { 02142 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ); 02143 } 02144 } else { 02145 return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str ); 02146 } 02147 } else { 02148 if ( $this->isMultibyte( $str ) ) { 02149 $x = $first ? '^' : ''; 02150 return preg_replace_callback( 02151 "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/", 02152 array( $this, 'lcCallback' ), 02153 $str 02154 ); 02155 } else { 02156 return $first ? strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str ); 02157 } 02158 } 02159 } 02160 02165 function isMultibyte( $str ) { 02166 return (bool)preg_match( '/[\x80-\xff]/', $str ); 02167 } 02168 02173 function ucwords( $str ) { 02174 if ( $this->isMultibyte( $str ) ) { 02175 $str = $this->lc( $str ); 02176 02177 // regexp to find first letter in each word (i.e. after each space) 02178 $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/"; 02179 02180 // function to use to capitalize a single char 02181 if ( function_exists( 'mb_strtoupper' ) ) { 02182 return preg_replace_callback( 02183 $replaceRegexp, 02184 array( $this, 'ucwordsCallbackMB' ), 02185 $str 02186 ); 02187 } else { 02188 return preg_replace_callback( 02189 $replaceRegexp, 02190 array( $this, 'ucwordsCallbackWiki' ), 02191 $str 02192 ); 02193 } 02194 } else { 02195 return ucwords( strtolower( $str ) ); 02196 } 02197 } 02198 02205 function ucwordbreaks( $str ) { 02206 if ( $this->isMultibyte( $str ) ) { 02207 $str = $this->lc( $str ); 02208 02209 // since \b doesn't work for UTF-8, we explicitely define word break chars 02210 $breaks = "[ \-\(\)\}\{\.,\?!]"; 02211 02212 // find first letter after word break 02213 $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/"; 02214 02215 if ( function_exists( 'mb_strtoupper' ) ) { 02216 return preg_replace_callback( 02217 $replaceRegexp, 02218 array( $this, 'ucwordbreaksCallbackMB' ), 02219 $str 02220 ); 02221 } else { 02222 return preg_replace_callback( 02223 $replaceRegexp, 02224 array( $this, 'ucwordsCallbackWiki' ), 02225 $str 02226 ); 02227 } 02228 } else { 02229 return preg_replace_callback( 02230 '/\b([\w\x80-\xff]+)\b/', 02231 array( $this, 'ucwordbreaksCallbackAscii' ), 02232 $str 02233 ); 02234 } 02235 } 02236 02252 function caseFold( $s ) { 02253 return $this->uc( $s ); 02254 } 02255 02260 function checkTitleEncoding( $s ) { 02261 if ( is_array( $s ) ) { 02262 wfDebugDieBacktrace( 'Given array to checkTitleEncoding.' ); 02263 } 02264 # Check for non-UTF-8 URLs 02265 $ishigh = preg_match( '/[\x80-\xff]/', $s ); 02266 if ( !$ishigh ) { 02267 return $s; 02268 } 02269 02270 $isutf8 = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' . 02271 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); 02272 if ( $isutf8 ) { 02273 return $s; 02274 } 02275 02276 return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s ); 02277 } 02278 02282 function fallback8bitEncoding() { 02283 return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' ); 02284 } 02285 02294 function hasWordBreaks() { 02295 return true; 02296 } 02297 02305 function segmentByWord( $string ) { 02306 return $string; 02307 } 02308 02316 function normalizeForSearch( $string ) { 02317 return self::convertDoubleWidth( $string ); 02318 } 02319 02328 protected static function convertDoubleWidth( $string ) { 02329 static $full = null; 02330 static $half = null; 02331 02332 if ( $full === null ) { 02333 $fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 02334 $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 02335 $full = str_split( $fullWidth, 3 ); 02336 $half = str_split( $halfWidth ); 02337 } 02338 02339 $string = str_replace( $full, $half, $string ); 02340 return $string; 02341 } 02342 02348 protected static function insertSpace( $string, $pattern ) { 02349 $string = preg_replace( $pattern, " $1 ", $string ); 02350 $string = preg_replace( '/ +/', ' ', $string ); 02351 return $string; 02352 } 02353 02358 function convertForSearchResult( $termsArray ) { 02359 # some languages, e.g. Chinese, need to do a conversion 02360 # in order for search results to be displayed correctly 02361 return $termsArray; 02362 } 02363 02370 function firstChar( $s ) { 02371 $matches = array(); 02372 preg_match( 02373 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' . 02374 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/', 02375 $s, 02376 $matches 02377 ); 02378 02379 if ( isset( $matches[1] ) ) { 02380 if ( strlen( $matches[1] ) != 3 ) { 02381 return $matches[1]; 02382 } 02383 02384 // Break down Hangul syllables to grab the first jamo 02385 $code = utf8ToCodepoint( $matches[1] ); 02386 if ( $code < 0xac00 || 0xd7a4 <= $code ) { 02387 return $matches[1]; 02388 } elseif ( $code < 0xb098 ) { 02389 return "\xe3\x84\xb1"; 02390 } elseif ( $code < 0xb2e4 ) { 02391 return "\xe3\x84\xb4"; 02392 } elseif ( $code < 0xb77c ) { 02393 return "\xe3\x84\xb7"; 02394 } elseif ( $code < 0xb9c8 ) { 02395 return "\xe3\x84\xb9"; 02396 } elseif ( $code < 0xbc14 ) { 02397 return "\xe3\x85\x81"; 02398 } elseif ( $code < 0xc0ac ) { 02399 return "\xe3\x85\x82"; 02400 } elseif ( $code < 0xc544 ) { 02401 return "\xe3\x85\x85"; 02402 } elseif ( $code < 0xc790 ) { 02403 return "\xe3\x85\x87"; 02404 } elseif ( $code < 0xcc28 ) { 02405 return "\xe3\x85\x88"; 02406 } elseif ( $code < 0xce74 ) { 02407 return "\xe3\x85\x8a"; 02408 } elseif ( $code < 0xd0c0 ) { 02409 return "\xe3\x85\x8b"; 02410 } elseif ( $code < 0xd30c ) { 02411 return "\xe3\x85\x8c"; 02412 } elseif ( $code < 0xd558 ) { 02413 return "\xe3\x85\x8d"; 02414 } else { 02415 return "\xe3\x85\x8e"; 02416 } 02417 } else { 02418 return ''; 02419 } 02420 } 02421 02422 function initEncoding() { 02423 # Some languages may have an alternate char encoding option 02424 # (Esperanto X-coding, Japanese furigana conversion, etc) 02425 # If this language is used as the primary content language, 02426 # an override to the defaults can be set here on startup. 02427 } 02428 02433 function recodeForEdit( $s ) { 02434 # For some languages we'll want to explicitly specify 02435 # which characters make it into the edit box raw 02436 # or are converted in some way or another. 02437 global $wgEditEncoding; 02438 if ( $wgEditEncoding == '' || $wgEditEncoding == 'UTF-8' ) { 02439 return $s; 02440 } else { 02441 return $this->iconv( 'UTF-8', $wgEditEncoding, $s ); 02442 } 02443 } 02444 02449 function recodeInput( $s ) { 02450 # Take the previous into account. 02451 global $wgEditEncoding; 02452 if ( $wgEditEncoding != '' ) { 02453 $enc = $wgEditEncoding; 02454 } else { 02455 $enc = 'UTF-8'; 02456 } 02457 if ( $enc == 'UTF-8' ) { 02458 return $s; 02459 } else { 02460 return $this->iconv( $enc, 'UTF-8', $s ); 02461 } 02462 } 02463 02475 function normalize( $s ) { 02476 global $wgAllUnicodeFixes; 02477 $s = UtfNormal::cleanUp( $s ); 02478 if ( $wgAllUnicodeFixes ) { 02479 $s = $this->transformUsingPairFile( 'normalize-ar.ser', $s ); 02480 $s = $this->transformUsingPairFile( 'normalize-ml.ser', $s ); 02481 } 02482 02483 return $s; 02484 } 02485 02499 function transformUsingPairFile( $file, $string ) { 02500 if ( !isset( $this->transformData[$file] ) ) { 02501 $data = wfGetPrecompiledData( $file ); 02502 if ( $data === false ) { 02503 throw new MWException( __METHOD__ . ": The transformation file $file is missing" ); 02504 } 02505 $this->transformData[$file] = new ReplacementArray( $data ); 02506 } 02507 return $this->transformData[$file]->replace( $string ); 02508 } 02509 02515 function isRTL() { 02516 return self::$dataCache->getItem( $this->mCode, 'rtl' ); 02517 } 02518 02523 function getDir() { 02524 return $this->isRTL() ? 'rtl' : 'ltr'; 02525 } 02526 02535 function alignStart() { 02536 return $this->isRTL() ? 'right' : 'left'; 02537 } 02538 02547 function alignEnd() { 02548 return $this->isRTL() ? 'left' : 'right'; 02549 } 02550 02557 function getDirMark( $opposite = false ) { 02558 $rtl = "\xE2\x80\x8F"; 02559 $ltr = "\xE2\x80\x8E"; 02560 if ( $opposite ) { return $this->isRTL() ? $ltr : $rtl; } 02561 return $this->isRTL() ? $rtl : $ltr; 02562 } 02563 02567 function capitalizeAllNouns() { 02568 return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' ); 02569 } 02570 02576 function getArrow() { 02577 return $this->isRTL() ? '←' : '→'; 02578 } 02579 02585 function linkPrefixExtension() { 02586 return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' ); 02587 } 02588 02592 function getMagicWords() { 02593 return self::$dataCache->getItem( $this->mCode, 'magicWords' ); 02594 } 02595 02596 protected function doMagicHook() { 02597 if ( $this->mMagicHookDone ) { 02598 return; 02599 } 02600 $this->mMagicHookDone = true; 02601 wfProfileIn( 'LanguageGetMagic' ); 02602 wfRunHooks( 'LanguageGetMagic', array( &$this->mMagicExtensions, $this->getCode() ) ); 02603 wfProfileOut( 'LanguageGetMagic' ); 02604 } 02605 02611 function getMagic( $mw ) { 02612 $this->doMagicHook(); 02613 02614 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) { 02615 $rawEntry = $this->mMagicExtensions[$mw->mId]; 02616 } else { 02617 $magicWords = $this->getMagicWords(); 02618 if ( isset( $magicWords[$mw->mId] ) ) { 02619 $rawEntry = $magicWords[$mw->mId]; 02620 } else { 02621 $rawEntry = false; 02622 } 02623 } 02624 02625 if ( !is_array( $rawEntry ) ) { 02626 error_log( "\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" ); 02627 } else { 02628 $mw->mCaseSensitive = $rawEntry[0]; 02629 $mw->mSynonyms = array_slice( $rawEntry, 1 ); 02630 } 02631 } 02632 02638 function addMagicWordsByLang( $newWords ) { 02639 $fallbackChain = $this->getFallbackLanguages(); 02640 $fallbackChain = array_reverse( $fallbackChain ); 02641 foreach ( $fallbackChain as $code ) { 02642 if ( isset( $newWords[$code] ) ) { 02643 $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions; 02644 } 02645 } 02646 } 02647 02652 function getSpecialPageAliases() { 02653 // Cache aliases because it may be slow to load them 02654 if ( is_null( $this->mExtendedSpecialPageAliases ) ) { 02655 // Initialise array 02656 $this->mExtendedSpecialPageAliases = 02657 self::$dataCache->getItem( $this->mCode, 'specialPageAliases' ); 02658 wfRunHooks( 'LanguageGetSpecialPageAliases', 02659 array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) ); 02660 } 02661 02662 return $this->mExtendedSpecialPageAliases; 02663 } 02664 02671 function emphasize( $text ) { 02672 return "<em>$text</em>"; 02673 } 02674 02699 public function formatNum( $number, $nocommafy = false ) { 02700 global $wgTranslateNumerals; 02701 if ( !$nocommafy ) { 02702 $number = $this->commafy( $number ); 02703 $s = $this->separatorTransformTable(); 02704 if ( $s ) { 02705 $number = strtr( $number, $s ); 02706 } 02707 } 02708 02709 if ( $wgTranslateNumerals ) { 02710 $s = $this->digitTransformTable(); 02711 if ( $s ) { 02712 $number = strtr( $number, $s ); 02713 } 02714 } 02715 02716 return $number; 02717 } 02718 02723 function parseFormattedNumber( $number ) { 02724 $s = $this->digitTransformTable(); 02725 if ( $s ) { 02726 $number = strtr( $number, array_flip( $s ) ); 02727 } 02728 02729 $s = $this->separatorTransformTable(); 02730 if ( $s ) { 02731 $number = strtr( $number, array_flip( $s ) ); 02732 } 02733 02734 $number = strtr( $number, array( ',' => '' ) ); 02735 return $number; 02736 } 02737 02744 function commafy( $_ ) { 02745 $digitGroupingPattern = $this->digitGroupingPattern(); 02746 if ( $_ === null ) { 02747 return ''; 02748 } 02749 02750 if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) { 02751 // default grouping is at thousands, use the same for ###,###,### pattern too. 02752 return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) ); 02753 } else { 02754 // Ref: http://cldr.unicode.org/translation/number-patterns 02755 $sign = ""; 02756 if ( intval( $_ ) < 0 ) { 02757 // For negative numbers apply the algorithm like positive number and add sign. 02758 $sign = "-"; 02759 $_ = substr( $_, 1 ); 02760 } 02761 $numberpart = array(); 02762 $decimalpart = array(); 02763 $numMatches = preg_match_all( "/(#+)/", $digitGroupingPattern, $matches ); 02764 preg_match( "/\d+/", $_, $numberpart ); 02765 preg_match( "/\.\d*/", $_, $decimalpart ); 02766 $groupedNumber = ( count( $decimalpart ) > 0 ) ? $decimalpart[0]:""; 02767 if ( $groupedNumber === $_ ) { 02768 // the string does not have any number part. Eg: .12345 02769 return $sign . $groupedNumber; 02770 } 02771 $start = $end = strlen( $numberpart[0] ); 02772 while ( $start > 0 ) { 02773 $match = $matches[0][$numMatches -1] ; 02774 $matchLen = strlen( $match ); 02775 $start = $end - $matchLen; 02776 if ( $start < 0 ) { 02777 $start = 0; 02778 } 02779 $groupedNumber = substr( $_ , $start, $end -$start ) . $groupedNumber ; 02780 $end = $start; 02781 if ( $numMatches > 1 ) { 02782 // use the last pattern for the rest of the number 02783 $numMatches--; 02784 } 02785 if ( $start > 0 ) { 02786 $groupedNumber = "," . $groupedNumber; 02787 } 02788 } 02789 return $sign . $groupedNumber; 02790 } 02791 } 02795 function digitGroupingPattern() { 02796 return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' ); 02797 } 02798 02802 function digitTransformTable() { 02803 return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' ); 02804 } 02805 02809 function separatorTransformTable() { 02810 return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' ); 02811 } 02812 02821 function listToText( array $l ) { 02822 $s = ''; 02823 $m = count( $l ) - 1; 02824 if ( $m == 1 ) { 02825 return $l[0] . $this->getMessageFromDB( 'and' ) . $this->getMessageFromDB( 'word-separator' ) . $l[1]; 02826 } else { 02827 for ( $i = $m; $i >= 0; $i-- ) { 02828 if ( $i == $m ) { 02829 $s = $l[$i]; 02830 } elseif ( $i == $m - 1 ) { 02831 $s = $l[$i] . $this->getMessageFromDB( 'and' ) . $this->getMessageFromDB( 'word-separator' ) . $s; 02832 } else { 02833 $s = $l[$i] . $this->getMessageFromDB( 'comma-separator' ) . $s; 02834 } 02835 } 02836 return $s; 02837 } 02838 } 02839 02846 function commaList( array $list ) { 02847 return implode( 02848 wfMsgExt( 02849 'comma-separator', 02850 array( 'parsemag', 'escapenoentities', 'language' => $this ) 02851 ), 02852 $list 02853 ); 02854 } 02855 02862 function semicolonList( array $list ) { 02863 return implode( 02864 wfMsgExt( 02865 'semicolon-separator', 02866 array( 'parsemag', 'escapenoentities', 'language' => $this ) 02867 ), 02868 $list 02869 ); 02870 } 02871 02877 function pipeList( array $list ) { 02878 return implode( 02879 wfMsgExt( 02880 'pipe-separator', 02881 array( 'escapenoentities', 'language' => $this ) 02882 ), 02883 $list 02884 ); 02885 } 02886 02904 function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) { 02905 # Use the localized ellipsis character 02906 if ( $ellipsis == '...' ) { 02907 $ellipsis = wfMsgExt( 'ellipsis', array( 'escapenoentities', 'language' => $this ) ); 02908 } 02909 # Check if there is no need to truncate 02910 if ( $length == 0 ) { 02911 return $ellipsis; // convention 02912 } elseif ( strlen( $string ) <= abs( $length ) ) { 02913 return $string; // no need to truncate 02914 } 02915 $stringOriginal = $string; 02916 # If ellipsis length is >= $length then we can't apply $adjustLength 02917 if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) { 02918 $string = $ellipsis; // this can be slightly unexpected 02919 # Otherwise, truncate and add ellipsis... 02920 } else { 02921 $eLength = $adjustLength ? strlen( $ellipsis ) : 0; 02922 if ( $length > 0 ) { 02923 $length -= $eLength; 02924 $string = substr( $string, 0, $length ); // xyz... 02925 $string = $this->removeBadCharLast( $string ); 02926 $string = $string . $ellipsis; 02927 } else { 02928 $length += $eLength; 02929 $string = substr( $string, $length ); // ...xyz 02930 $string = $this->removeBadCharFirst( $string ); 02931 $string = $ellipsis . $string; 02932 } 02933 } 02934 # Do not truncate if the ellipsis makes the string longer/equal (bug 22181). 02935 # This check is *not* redundant if $adjustLength, due to the single case where 02936 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string. 02937 if ( strlen( $string ) < strlen( $stringOriginal ) ) { 02938 return $string; 02939 } else { 02940 return $stringOriginal; 02941 } 02942 } 02943 02951 protected function removeBadCharLast( $string ) { 02952 if ( $string != '' ) { 02953 $char = ord( $string[strlen( $string ) - 1] ); 02954 $m = array(); 02955 if ( $char >= 0xc0 ) { 02956 # We got the first byte only of a multibyte char; remove it. 02957 $string = substr( $string, 0, -1 ); 02958 } elseif ( $char >= 0x80 && 02959 preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' . 02960 '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m ) ) 02961 { 02962 # We chopped in the middle of a character; remove it 02963 $string = $m[1]; 02964 } 02965 } 02966 return $string; 02967 } 02968 02976 protected function removeBadCharFirst( $string ) { 02977 if ( $string != '' ) { 02978 $char = ord( $string[0] ); 02979 if ( $char >= 0x80 && $char < 0xc0 ) { 02980 # We chopped in the middle of a character; remove the whole thing 02981 $string = preg_replace( '/^[\x80-\xbf]+/', '', $string ); 02982 } 02983 } 02984 return $string; 02985 } 02986 03002 function truncateHtml( $text, $length, $ellipsis = '...' ) { 03003 # Use the localized ellipsis character 03004 if ( $ellipsis == '...' ) { 03005 $ellipsis = wfMsgExt( 'ellipsis', array( 'escapenoentities', 'language' => $this ) ); 03006 } 03007 # Check if there is clearly no need to truncate 03008 if ( $length <= 0 ) { 03009 return $ellipsis; // no text shown, nothing to format (convention) 03010 } elseif ( strlen( $text ) <= $length ) { 03011 return $text; // string short enough even *with* HTML (short-circuit) 03012 } 03013 03014 $dispLen = 0; // innerHTML legth so far 03015 $testingEllipsis = false; // checking if ellipses will make string longer/equal? 03016 $tagType = 0; // 0-open, 1-close 03017 $bracketState = 0; // 1-tag start, 2-tag name, 0-neither 03018 $entityState = 0; // 0-not entity, 1-entity 03019 $tag = $ret = ''; // accumulated tag name, accumulated result string 03020 $openTags = array(); // open tag stack 03021 $maybeState = null; // possible truncation state 03022 03023 $textLen = strlen( $text ); 03024 $neLength = max( 0, $length - strlen( $ellipsis ) ); // non-ellipsis len if truncated 03025 for ( $pos = 0; true; ++$pos ) { 03026 # Consider truncation once the display length has reached the maximim. 03027 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case. 03028 # Check that we're not in the middle of a bracket/entity... 03029 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) { 03030 if ( !$testingEllipsis ) { 03031 $testingEllipsis = true; 03032 # Save where we are; we will truncate here unless there turn out to 03033 # be so few remaining characters that truncation is not necessary. 03034 if ( !$maybeState ) { // already saved? ($neLength = 0 case) 03035 $maybeState = array( $ret, $openTags ); // save state 03036 } 03037 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) { 03038 # String in fact does need truncation, the truncation point was OK. 03039 list( $ret, $openTags ) = $maybeState; // reload state 03040 $ret = $this->removeBadCharLast( $ret ); // multi-byte char fix 03041 $ret .= $ellipsis; // add ellipsis 03042 break; 03043 } 03044 } 03045 if ( $pos >= $textLen ) break; // extra iteration just for above checks 03046 03047 # Read the next char... 03048 $ch = $text[$pos]; 03049 $lastCh = $pos ? $text[$pos - 1] : ''; 03050 $ret .= $ch; // add to result string 03051 if ( $ch == '<' ) { 03052 $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); // for bad HTML 03053 $entityState = 0; // for bad HTML 03054 $bracketState = 1; // tag started (checking for backslash) 03055 } elseif ( $ch == '>' ) { 03056 $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); 03057 $entityState = 0; // for bad HTML 03058 $bracketState = 0; // out of brackets 03059 } elseif ( $bracketState == 1 ) { 03060 if ( $ch == '/' ) { 03061 $tagType = 1; // close tag (e.g. "</span>") 03062 } else { 03063 $tagType = 0; // open tag (e.g. "<span>") 03064 $tag .= $ch; 03065 } 03066 $bracketState = 2; // building tag name 03067 } elseif ( $bracketState == 2 ) { 03068 if ( $ch != ' ' ) { 03069 $tag .= $ch; 03070 } else { 03071 // Name found (e.g. "<a href=..."), add on tag attributes... 03072 $pos += $this->truncate_skip( $ret, $text, "<>", $pos + 1 ); 03073 } 03074 } elseif ( $bracketState == 0 ) { 03075 if ( $entityState ) { 03076 if ( $ch == ';' ) { 03077 $entityState = 0; 03078 $dispLen++; // entity is one displayed char 03079 } 03080 } else { 03081 if ( $neLength == 0 && !$maybeState ) { 03082 // Save state without $ch. We want to *hit* the first 03083 // display char (to get tags) but not *use* it if truncating. 03084 $maybeState = array( substr( $ret, 0, -1 ), $openTags ); 03085 } 03086 if ( $ch == '&' ) { 03087 $entityState = 1; // entity found, (e.g. " ") 03088 } else { 03089 $dispLen++; // this char is displayed 03090 // Add the next $max display text chars after this in one swoop... 03091 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen; 03092 $skipped = $this->truncate_skip( $ret, $text, "<>&", $pos + 1, $max ); 03093 $dispLen += $skipped; 03094 $pos += $skipped; 03095 } 03096 } 03097 } 03098 } 03099 // Close the last tag if left unclosed by bad HTML 03100 $this->truncate_endBracket( $tag, $text[$textLen - 1], $tagType, $openTags ); 03101 while ( count( $openTags ) > 0 ) { 03102 $ret .= '</' . array_pop( $openTags ) . '>'; // close open tags 03103 } 03104 return $ret; 03105 } 03106 03118 private function truncate_skip( &$ret, $text, $search, $start, $len = null ) { 03119 if ( $len === null ) { 03120 $len = -1; // -1 means "no limit" for strcspn 03121 } elseif ( $len < 0 ) { 03122 $len = 0; // sanity 03123 } 03124 $skipCount = 0; 03125 if ( $start < strlen( $text ) ) { 03126 $skipCount = strcspn( $text, $search, $start, $len ); 03127 $ret .= substr( $text, $start, $skipCount ); 03128 } 03129 return $skipCount; 03130 } 03131 03141 private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) { 03142 $tag = ltrim( $tag ); 03143 if ( $tag != '' ) { 03144 if ( $tagType == 0 && $lastCh != '/' ) { 03145 $openTags[] = $tag; // tag opened (didn't close itself) 03146 } elseif ( $tagType == 1 ) { 03147 if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) { 03148 array_pop( $openTags ); // tag closed 03149 } 03150 } 03151 $tag = ''; 03152 } 03153 } 03154 03163 function convertGrammar( $word, $case ) { 03164 global $wgGrammarForms; 03165 if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) { 03166 return $wgGrammarForms[$this->getCode()][$case][$word]; 03167 } 03168 return $word; 03169 } 03170 03190 function gender( $gender, $forms ) { 03191 if ( !count( $forms ) ) { 03192 return ''; 03193 } 03194 $forms = $this->preConvertPlural( $forms, 2 ); 03195 if ( $gender === 'male' ) { 03196 return $forms[0]; 03197 } 03198 if ( $gender === 'female' ) { 03199 return $forms[1]; 03200 } 03201 return isset( $forms[2] ) ? $forms[2] : $forms[0]; 03202 } 03203 03219 function convertPlural( $count, $forms ) { 03220 if ( !count( $forms ) ) { 03221 return ''; 03222 } 03223 $forms = $this->preConvertPlural( $forms, 2 ); 03224 03225 return ( $count == 1 ) ? $forms[0] : $forms[1]; 03226 } 03227 03236 protected function preConvertPlural( /* Array */ $forms, $count ) { 03237 while ( count( $forms ) < $count ) { 03238 $forms[] = $forms[count( $forms ) - 1]; 03239 } 03240 return $forms; 03241 } 03242 03254 function translateBlockExpiry( $str ) { 03255 $duration = SpecialBlock::getSuggestedDurations( $this ); 03256 foreach ( $duration as $show => $value ) { 03257 if ( strcmp( $str, $value ) == 0 ) { 03258 return htmlspecialchars( trim( $show ) ); 03259 } 03260 } 03261 03262 // Since usually only infinite or indefinite is only on list, so try 03263 // equivalents if still here. 03264 $indefs = array( 'infinite', 'infinity', 'indefinite' ); 03265 if ( in_array( $str, $indefs ) ) { 03266 foreach ( $indefs as $val ) { 03267 $show = array_search( $val, $duration, true ); 03268 if ( $show !== false ) { 03269 return htmlspecialchars( trim( $show ) ); 03270 } 03271 } 03272 } 03273 // If all else fails, return the original string. 03274 return $str; 03275 } 03276 03284 public function segmentForDiff( $text ) { 03285 return $text; 03286 } 03287 03294 public function unsegmentForDiff( $text ) { 03295 return $text; 03296 } 03297 03304 public function getConverter() { 03305 return $this->mConverter; 03306 } 03307 03314 public function autoConvertToAllVariants( $text ) { 03315 return $this->mConverter->autoConvertToAllVariants( $text ); 03316 } 03317 03324 public function convert( $text ) { 03325 return $this->mConverter->convert( $text ); 03326 } 03327 03334 public function convertTitle( $title ) { 03335 return $this->mConverter->convertTitle( $title ); 03336 } 03337 03343 public function hasVariants() { 03344 return sizeof( $this->getVariants() ) > 1; 03345 } 03346 03354 public function hasVariant( $variant ) { 03355 return (bool)$this->mConverter->validateVariant( $variant ); 03356 } 03357 03364 public function armourMath( $text ) { 03365 return $this->mConverter->armourMath( $text ); 03366 } 03367 03375 public function convertHtml( $text, $isTitle = false ) { 03376 return htmlspecialchars( $this->convert( $text, $isTitle ) ); 03377 } 03378 03383 public function convertCategoryKey( $key ) { 03384 return $this->mConverter->convertCategoryKey( $key ); 03385 } 03386 03393 public function getVariants() { 03394 return $this->mConverter->getVariants(); 03395 } 03396 03400 public function getPreferredVariant() { 03401 return $this->mConverter->getPreferredVariant(); 03402 } 03403 03407 public function getDefaultVariant() { 03408 return $this->mConverter->getDefaultVariant(); 03409 } 03410 03414 public function getURLVariant() { 03415 return $this->mConverter->getURLVariant(); 03416 } 03417 03430 public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) { 03431 $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond ); 03432 } 03433 03445 public function convertLinkToAllVariants( $text ) { 03446 return $this->mConverter->convertLinkToAllVariants( $text ); 03447 } 03448 03455 function getExtraHashOptions() { 03456 return $this->mConverter->getExtraHashOptions(); 03457 } 03458 03466 public function getParsedTitle() { 03467 return $this->mConverter->getParsedTitle(); 03468 } 03469 03478 public function markNoConversion( $text, $noParse = false ) { 03479 return $this->mConverter->markNoConversion( $text, $noParse ); 03480 } 03481 03488 public function linkTrail() { 03489 return self::$dataCache->getItem( $this->mCode, 'linkTrail' ); 03490 } 03491 03495 function getLangObj() { 03496 return $this; 03497 } 03498 03507 public function getCode() { 03508 return $this->mCode; 03509 } 03510 03521 public function getHtmlCode() { 03522 if ( is_null( $this->mHtmlCode ) ) { 03523 $this->mHtmlCode = wfBCP47( $this->getCode() ); 03524 } 03525 return $this->mHtmlCode; 03526 } 03527 03531 public function setCode( $code ) { 03532 $this->mCode = $code; 03533 // Ensure we don't leave an incorrect html code lying around 03534 $this->mHtmlCode = null; 03535 } 03536 03544 public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) { 03545 // Protect against path traversal 03546 if ( !Language::isValidCode( $code ) 03547 || strcspn( $code, ":/\\\000" ) !== strlen( $code ) ) 03548 { 03549 throw new MWException( "Invalid language code \"$code\"" ); 03550 } 03551 03552 return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix; 03553 } 03554 03562 public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) { 03563 $m = null; 03564 preg_match( '/' . preg_quote( $prefix, '/' ) . '([A-Z][a-z_]+)' . 03565 preg_quote( $suffix, '/' ) . '/', $filename, $m ); 03566 if ( !count( $m ) ) { 03567 return false; 03568 } 03569 return str_replace( '_', '-', strtolower( $m[1] ) ); 03570 } 03571 03576 public static function getMessagesFileName( $code ) { 03577 global $IP; 03578 $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' ); 03579 wfRunHooks( 'Language::getMessagesFileName', array( $code, &$file ) ); 03580 return $file; 03581 } 03582 03587 public static function getClassFileName( $code ) { 03588 global $IP; 03589 return self::getFileName( "$IP/languages/classes/Language", $code, '.php' ); 03590 } 03591 03599 public static function getFallbackFor( $code ) { 03600 if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) { 03601 return false; 03602 } else { 03603 $fallbacks = self::getFallbacksFor( $code ); 03604 $first = array_shift( $fallbacks ); 03605 return $first; 03606 } 03607 } 03608 03616 public static function getFallbacksFor( $code ) { 03617 if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) { 03618 return array(); 03619 } else { 03620 $v = self::getLocalisationCache()->getItem( $code, 'fallback' ); 03621 $v = array_map( 'trim', explode( ',', $v ) ); 03622 if ( $v[count( $v ) - 1] !== 'en' ) { 03623 $v[] = 'en'; 03624 } 03625 return $v; 03626 } 03627 } 03628 03638 public static function getMessagesFor( $code ) { 03639 return self::getLocalisationCache()->getItem( $code, 'messages' ); 03640 } 03641 03650 public static function getMessageFor( $key, $code ) { 03651 return self::getLocalisationCache()->getSubitem( $code, 'messages', $key ); 03652 } 03653 03662 public static function getMessageKeysFor( $code ) { 03663 return self::getLocalisationCache()->getSubItemList( $code, 'messages' ); 03664 } 03665 03670 function fixVariableInNamespace( $talk ) { 03671 if ( strpos( $talk, '$1' ) === false ) { 03672 return $talk; 03673 } 03674 03675 global $wgMetaNamespace; 03676 $talk = str_replace( '$1', $wgMetaNamespace, $talk ); 03677 03678 # Allow grammar transformations 03679 # Allowing full message-style parsing would make simple requests 03680 # such as action=raw much more expensive than they need to be. 03681 # This will hopefully cover most cases. 03682 $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i', 03683 array( &$this, 'replaceGrammarInNamespace' ), $talk ); 03684 return str_replace( ' ', '_', $talk ); 03685 } 03686 03691 function replaceGrammarInNamespace( $m ) { 03692 return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) ); 03693 } 03694 03699 static function getCaseMaps() { 03700 static $wikiUpperChars, $wikiLowerChars; 03701 if ( isset( $wikiUpperChars ) ) { 03702 return array( $wikiUpperChars, $wikiLowerChars ); 03703 } 03704 03705 wfProfileIn( __METHOD__ ); 03706 $arr = wfGetPrecompiledData( 'Utf8Case.ser' ); 03707 if ( $arr === false ) { 03708 throw new MWException( 03709 "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" ); 03710 } 03711 $wikiUpperChars = $arr['wikiUpperChars']; 03712 $wikiLowerChars = $arr['wikiLowerChars']; 03713 wfProfileOut( __METHOD__ ); 03714 return array( $wikiUpperChars, $wikiLowerChars ); 03715 } 03716 03725 public function formatExpiry( $expiry, $format = true ) { 03726 static $infinity, $infinityMsg; 03727 if ( $infinity === null ) { 03728 $infinityMsg = wfMessage( 'infiniteblock' ); 03729 $infinity = wfGetDB( DB_SLAVE )->getInfinity(); 03730 } 03731 03732 if ( $expiry == '' || $expiry == $infinity ) { 03733 return $format === true 03734 ? $infinityMsg 03735 : $infinity; 03736 } else { 03737 return $format === true 03738 ? $this->timeanddate( $expiry, /* User preference timezone */ true ) 03739 : wfTimestamp( $format, $expiry ); 03740 } 03741 } 03742 03753 function formatTimePeriod( $seconds, $format = array() ) { 03754 if ( !is_array( $format ) ) { 03755 $format = array( 'avoid' => $format ); // For backwards compatibility 03756 } 03757 if ( !isset( $format['avoid'] ) ) { 03758 $format['avoid'] = false; 03759 } 03760 if ( !isset( $format['noabbrevs' ] ) ) { 03761 $format['noabbrevs'] = false; 03762 } 03763 $secondsMsg = wfMessage( 03764 $format['noabbrevs'] ? 'seconds' : 'seconds-abbrev' )->inLanguage( $this ); 03765 $minutesMsg = wfMessage( 03766 $format['noabbrevs'] ? 'minutes' : 'minutes-abbrev' )->inLanguage( $this ); 03767 $hoursMsg = wfMessage( 03768 $format['noabbrevs'] ? 'hours' : 'hours-abbrev' )->inLanguage( $this ); 03769 $daysMsg = wfMessage( 03770 $format['noabbrevs'] ? 'days' : 'days-abbrev' )->inLanguage( $this ); 03771 03772 if ( round( $seconds * 10 ) < 100 ) { 03773 $s = $this->formatNum( sprintf( "%.1f", round( $seconds * 10 ) / 10 ) ); 03774 $s = $secondsMsg->params( $s )->text(); 03775 } elseif ( round( $seconds ) < 60 ) { 03776 $s = $this->formatNum( round( $seconds ) ); 03777 $s = $secondsMsg->params( $s )->text(); 03778 } elseif ( round( $seconds ) < 3600 ) { 03779 $minutes = floor( $seconds / 60 ); 03780 $secondsPart = round( fmod( $seconds, 60 ) ); 03781 if ( $secondsPart == 60 ) { 03782 $secondsPart = 0; 03783 $minutes++; 03784 } 03785 $s = $minutesMsg->params( $this->formatNum( $minutes ) )->text(); 03786 $s .= ' '; 03787 $s .= $secondsMsg->params( $this->formatNum( $secondsPart ) )->text(); 03788 } elseif ( round( $seconds ) <= 2 * 86400 ) { 03789 $hours = floor( $seconds / 3600 ); 03790 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 ); 03791 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 ); 03792 if ( $secondsPart == 60 ) { 03793 $secondsPart = 0; 03794 $minutes++; 03795 } 03796 if ( $minutes == 60 ) { 03797 $minutes = 0; 03798 $hours++; 03799 } 03800 $s = $hoursMsg->params( $this->formatNum( $hours ) )->text(); 03801 $s .= ' '; 03802 $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text(); 03803 if ( !in_array( $format['avoid'], array( 'avoidseconds', 'avoidminutes' ) ) ) { 03804 $s .= ' ' . $secondsMsg->params( $this->formatNum( $secondsPart ) )->text(); 03805 } 03806 } else { 03807 $days = floor( $seconds / 86400 ); 03808 if ( $format['avoid'] === 'avoidminutes' ) { 03809 $hours = round( ( $seconds - $days * 86400 ) / 3600 ); 03810 if ( $hours == 24 ) { 03811 $hours = 0; 03812 $days++; 03813 } 03814 $s = $daysMsg->params( $this->formatNum( $days ) )->text(); 03815 $s .= ' '; 03816 $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text(); 03817 } elseif ( $format['avoid'] === 'avoidseconds' ) { 03818 $hours = floor( ( $seconds - $days * 86400 ) / 3600 ); 03819 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 ); 03820 if ( $minutes == 60 ) { 03821 $minutes = 0; 03822 $hours++; 03823 } 03824 if ( $hours == 24 ) { 03825 $hours = 0; 03826 $days++; 03827 } 03828 $s = $daysMsg->params( $this->formatNum( $days ) )->text(); 03829 $s .= ' '; 03830 $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text(); 03831 $s .= ' '; 03832 $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text(); 03833 } else { 03834 $s = $daysMsg->params( $this->formatNum( $days ) )->text(); 03835 $s .= ' '; 03836 $s .= $this->formatTimePeriod( $seconds - $days * 86400, $format ); 03837 } 03838 } 03839 return $s; 03840 } 03841 03852 function formatBitrate( $bps ) { 03853 return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" ); 03854 } 03855 03862 function formatComputingNumbers( $size, $boundary, $messageKey ) { 03863 if ( $size <= 0 ) { 03864 return str_replace( '$1', $this->formatNum( $size ), 03865 $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) ) 03866 ); 03867 } 03868 $sizes = array( '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' ); 03869 $index = 0; 03870 03871 $maxIndex = count( $sizes ) - 1; 03872 while ( $size >= $boundary && $index < $maxIndex ) { 03873 $index++; 03874 $size /= $boundary; 03875 } 03876 03877 // For small sizes no decimal places necessary 03878 $round = 0; 03879 if ( $index > 1 ) { 03880 // For MB and bigger two decimal places are smarter 03881 $round = 2; 03882 } 03883 $msg = str_replace( '$1', $sizes[$index], $messageKey ); 03884 03885 $size = round( $size, $round ); 03886 $text = $this->getMessageFromDB( $msg ); 03887 return str_replace( '$1', $this->formatNum( $size ), $text ); 03888 } 03889 03900 function formatSize( $size ) { 03901 return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" ); 03902 } 03903 03913 function specialList( $page, $details, $oppositedm = true ) { 03914 $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) . 03915 $this->getDirMark(); 03916 $details = $details ? $dirmark . $this->getMessageFromDB( 'word-separator' ) . 03917 wfMsgExt( 'parentheses', array( 'escape', 'replaceafter', 'language' => $this ), $details ) : ''; 03918 return $page . $details; 03919 } 03920 03931 public function viewPrevNext( Title $title, $offset, $limit, array $query = array(), $atend = false ) { 03932 // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip? 03933 03934 # Make 'previous' link 03935 $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text(); 03936 if ( $offset > 0 ) { 03937 $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit, 03938 $query, $prev, 'prevn-title', 'mw-prevlink' ); 03939 } else { 03940 $plink = htmlspecialchars( $prev ); 03941 } 03942 03943 # Make 'next' link 03944 $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text(); 03945 if ( $atend ) { 03946 $nlink = htmlspecialchars( $next ); 03947 } else { 03948 $nlink = $this->numLink( $title, $offset + $limit, $limit, 03949 $query, $next, 'prevn-title', 'mw-nextlink' ); 03950 } 03951 03952 # Make links to set number of items per page 03953 $numLinks = array(); 03954 foreach ( array( 20, 50, 100, 250, 500 ) as $num ) { 03955 $numLinks[] = $this->numLink( $title, $offset, $num, 03956 $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' ); 03957 } 03958 03959 return wfMessage( 'viewprevnext' )->inLanguage( $this )->title( $title 03960 )->rawParams( $plink, $nlink, $this->pipeList( $numLinks ) )->escaped(); 03961 } 03962 03975 private function numLink( Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class ) { 03976 $query = array( 'limit' => $limit, 'offset' => $offset ) + $query; 03977 $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )->numParams( $limit )->text(); 03978 return Html::element( 'a', array( 'href' => $title->getLocalURL( $query ), 03979 'title' => $tooltip, 'class' => $class ), $link ); 03980 } 03981 03987 public function getConvRuleTitle() { 03988 return $this->mConverter->getConvRuleTitle(); 03989 } 03990 }