MediaWiki  REL1_19
Language.php
Go to the documentation of this file.
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. "&#160;")
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 }