00001 <?php
00009 if( !defined( 'MEDIAWIKI' ) ) {
00010 echo "This file is part of MediaWiki, it is not a valid entry point.\n";
00011 exit( 1 );
00012 }
00013
00014 # Read language names
00015 global $wgLanguageNames;
00016 require_once( dirname(__FILE__) . '/Names.php' ) ;
00017
00018 global $wgInputEncoding, $wgOutputEncoding;
00019
00023 $wgInputEncoding = "UTF-8";
00024 $wgOutputEncoding = "UTF-8";
00025
00026 if( function_exists( 'mb_strtoupper' ) ) {
00027 mb_internal_encoding('UTF-8');
00028 }
00029
00035 class FakeConverter {
00036 var $mLang;
00037 function FakeConverter($langobj) {$this->mLang = $langobj;}
00038 function convert($t, $i) {return $t;}
00039 function parserConvert($t, $p) {return $t;}
00040 function getVariants() { return array( $this->mLang->getCode() ); }
00041 function getPreferredVariant() {return $this->mLang->getCode(); }
00042 function findVariantLink(&$l, &$n, $ignoreOtherCond = false) {}
00043 function getExtraHashOptions() {return '';}
00044 function getParsedTitle() {return '';}
00045 function markNoConversion($text, $noParse=false) {return $text;}
00046 function convertCategoryKey( $key ) {return $key; }
00047 function convertLinkToAllVariants($text){ return array( $this->mLang->getCode() => $text); }
00048 function armourMath($text){ return $text; }
00049 }
00050
00055 class Language {
00056 var $mConverter, $mVariants, $mCode, $mLoaded = false;
00057 var $mMagicExtensions = array(), $mMagicHookDone = false;
00058
00059 static public $mLocalisationKeys = array(
00060 'fallback', 'namespaceNames', 'mathNames', 'bookstoreList',
00061 'magicWords', 'messages', 'rtl', 'digitTransformTable',
00062 'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
00063 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
00064 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
00065 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
00066 'imageFiles'
00067 );
00068
00069 static public $mMergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
00070 'dateFormats', 'defaultUserOptionOverrides', 'magicWords', 'imageFiles' );
00071
00072 static public $mMergeableListKeys = array( 'extraUserToggles' );
00073
00074 static public $mMergeableAliasListKeys = array( 'specialPageAliases' );
00075
00076 static public $mLocalisationCache = array();
00077 static public $mLangObjCache = array();
00078
00079 static public $mWeekdayMsgs = array(
00080 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
00081 'friday', 'saturday'
00082 );
00083
00084 static public $mWeekdayAbbrevMsgs = array(
00085 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
00086 );
00087
00088 static public $mMonthMsgs = array(
00089 'january', 'february', 'march', 'april', 'may_long', 'june',
00090 'july', 'august', 'september', 'october', 'november',
00091 'december'
00092 );
00093 static public $mMonthGenMsgs = array(
00094 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
00095 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen',
00096 'december-gen'
00097 );
00098 static public $mMonthAbbrevMsgs = array(
00099 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
00100 'sep', 'oct', 'nov', 'dec'
00101 );
00102
00103 static public $mIranianCalendarMonthMsgs = array(
00104 'iranian-calendar-m1', 'iranian-calendar-m2', 'iranian-calendar-m3',
00105 'iranian-calendar-m4', 'iranian-calendar-m5', 'iranian-calendar-m6',
00106 'iranian-calendar-m7', 'iranian-calendar-m8', 'iranian-calendar-m9',
00107 'iranian-calendar-m10', 'iranian-calendar-m11', 'iranian-calendar-m12'
00108 );
00109
00110 static public $mHebrewCalendarMonthMsgs = array(
00111 'hebrew-calendar-m1', 'hebrew-calendar-m2', 'hebrew-calendar-m3',
00112 'hebrew-calendar-m4', 'hebrew-calendar-m5', 'hebrew-calendar-m6',
00113 'hebrew-calendar-m7', 'hebrew-calendar-m8', 'hebrew-calendar-m9',
00114 'hebrew-calendar-m10', 'hebrew-calendar-m11', 'hebrew-calendar-m12',
00115 'hebrew-calendar-m6a', 'hebrew-calendar-m6b'
00116 );
00117
00118 static public $mHebrewCalendarMonthGenMsgs = array(
00119 'hebrew-calendar-m1-gen', 'hebrew-calendar-m2-gen', 'hebrew-calendar-m3-gen',
00120 'hebrew-calendar-m4-gen', 'hebrew-calendar-m5-gen', 'hebrew-calendar-m6-gen',
00121 'hebrew-calendar-m7-gen', 'hebrew-calendar-m8-gen', 'hebrew-calendar-m9-gen',
00122 'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen',
00123 'hebrew-calendar-m6a-gen', 'hebrew-calendar-m6b-gen'
00124 );
00125
00126 static public $mHijriCalendarMonthMsgs = array(
00127 'hijri-calendar-m1', 'hijri-calendar-m2', 'hijri-calendar-m3',
00128 'hijri-calendar-m4', 'hijri-calendar-m5', 'hijri-calendar-m6',
00129 'hijri-calendar-m7', 'hijri-calendar-m8', 'hijri-calendar-m9',
00130 'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12'
00131 );
00132
00136 static function factory( $code ) {
00137 if ( !isset( self::$mLangObjCache[$code] ) ) {
00138 if( count( self::$mLangObjCache ) > 10 ) {
00139
00140 self::$mLangObjCache = array();
00141 }
00142 self::$mLangObjCache[$code] = self::newFromCode( $code );
00143 }
00144 return self::$mLangObjCache[$code];
00145 }
00146
00150 protected static function newFromCode( $code ) {
00151 global $IP;
00152 static $recursionLevel = 0;
00153 if ( $code == 'en' ) {
00154 $class = 'Language';
00155 } else {
00156 $class = 'Language' . str_replace( '-', '_', ucfirst( $code ) );
00157
00158 if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) {
00159 include_once("$IP/languages/classes/$class.deps.php");
00160 }
00161 if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
00162 include_once("$IP/languages/classes/$class.php");
00163 }
00164 }
00165
00166 if ( $recursionLevel > 5 ) {
00167 throw new MWException( "Language fallback loop detected when creating class $class\n" );
00168 }
00169
00170 if( ! class_exists( $class ) ) {
00171 $fallback = Language::getFallbackFor( $code );
00172 ++$recursionLevel;
00173 $lang = Language::newFromCode( $fallback );
00174 --$recursionLevel;
00175 $lang->setCode( $code );
00176 } else {
00177 $lang = new $class;
00178 }
00179 return $lang;
00180 }
00181
00182 function __construct() {
00183 $this->mConverter = new FakeConverter($this);
00184
00185 if ( get_class( $this ) == 'Language' ) {
00186 $this->mCode = 'en';
00187 } else {
00188 $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) );
00189 }
00190 }
00191
00195 function __destruct() {
00196 foreach ( $this as $name => $value ) {
00197 unset( $this->$name );
00198 }
00199 }
00200
00205 function initContLang() {}
00206
00211 function getDefaultUserOptions() {
00212 wfDeprecated( __METHOD__ );
00213 return User::getDefaultOptions();
00214 }
00215
00216 function getFallbackLanguageCode() {
00217 return self::getFallbackFor( $this->mCode );
00218 }
00219
00224 function getBookstoreList() {
00225 $this->load();
00226 return $this->bookstoreList;
00227 }
00228
00232 function getNamespaces() {
00233 $this->load();
00234 return $this->namespaceNames;
00235 }
00236
00245 function getFormattedNamespaces() {
00246 $ns = $this->getNamespaces();
00247 foreach($ns as $k => $v) {
00248 $ns[$k] = strtr($v, '_', ' ');
00249 }
00250 return $ns;
00251 }
00252
00263 function getNsText( $index ) {
00264 $ns = $this->getNamespaces();
00265 return isset( $ns[$index] ) ? $ns[$index] : false;
00266 }
00267
00275 function getFormattedNsText( $index ) {
00276 $ns = $this->getNsText( $index );
00277 return strtr($ns, '_', ' ');
00278 }
00279
00288 function getLocalNsIndex( $text ) {
00289 $this->load();
00290 $lctext = $this->lc($text);
00291 return isset( $this->mNamespaceIds[$lctext] ) ? $this->mNamespaceIds[$lctext] : false;
00292 }
00293
00301 function getNsIndex( $text ) {
00302 $this->load();
00303 $lctext = $this->lc($text);
00304 if( ( $ns = MWNamespace::getCanonicalIndex( $lctext ) ) !== null ) return $ns;
00305 return isset( $this->mNamespaceIds[$lctext] ) ? $this->mNamespaceIds[$lctext] : false;
00306 }
00307
00314 function getVariantname( $code ) {
00315 return $this->getMessageFromDB( "variantname-$code" );
00316 }
00317
00318 function specialPage( $name ) {
00319 $aliases = $this->getSpecialPageAliases();
00320 if ( isset( $aliases[$name][0] ) ) {
00321 $name = $aliases[$name][0];
00322 }
00323 return $this->getNsText( NS_SPECIAL ) . ':' . $name;
00324 }
00325
00326 function getQuickbarSettings() {
00327 return array(
00328 $this->getMessage( 'qbsettings-none' ),
00329 $this->getMessage( 'qbsettings-fixedleft' ),
00330 $this->getMessage( 'qbsettings-fixedright' ),
00331 $this->getMessage( 'qbsettings-floatingleft' ),
00332 $this->getMessage( 'qbsettings-floatingright' )
00333 );
00334 }
00335
00336 function getMathNames() {
00337 $this->load();
00338 return $this->mathNames;
00339 }
00340
00341 function getDatePreferences() {
00342 $this->load();
00343 return $this->datePreferences;
00344 }
00345
00346 function getDateFormats() {
00347 $this->load();
00348 return $this->dateFormats;
00349 }
00350
00351 function getDefaultDateFormat() {
00352 $this->load();
00353 return $this->defaultDateFormat;
00354 }
00355
00356 function getDatePreferenceMigrationMap() {
00357 $this->load();
00358 return $this->datePreferenceMigrationMap;
00359 }
00360
00361 function getImageFile( $image ) {
00362 $this->load();
00363 return $this->imageFiles[$image];
00364 }
00365
00366 function getDefaultUserOptionOverrides() {
00367 $this->load();
00368 # XXX - apparently some languageas get empty arrays, didn't get to it yet -- midom
00369 if (is_array($this->defaultUserOptionOverrides)) {
00370 return $this->defaultUserOptionOverrides;
00371 } else {
00372 return array();
00373 }
00374 }
00375
00376 function getExtraUserToggles() {
00377 $this->load();
00378 return $this->extraUserToggles;
00379 }
00380
00381 function getUserToggle( $tog ) {
00382 return $this->getMessageFromDB( "tog-$tog" );
00383 }
00384
00389 public static function getLanguageNames( $customisedOnly = false ) {
00390 global $wgLanguageNames, $wgExtraLanguageNames;
00391 $allNames = $wgExtraLanguageNames + $wgLanguageNames;
00392 if ( !$customisedOnly ) {
00393 return $allNames;
00394 }
00395
00396 global $IP;
00397 $names = array();
00398 $dir = opendir( "$IP/languages/messages" );
00399 while( false !== ( $file = readdir( $dir ) ) ) {
00400 $m = array();
00401 if( preg_match( '/Messages([A-Z][a-z_]+)\.php$/', $file, $m ) ) {
00402 $code = str_replace( '_', '-', strtolower( $m[1] ) );
00403 if ( isset( $allNames[$code] ) ) {
00404 $names[$code] = $allNames[$code];
00405 }
00406 }
00407 }
00408 closedir( $dir );
00409 return $names;
00410 }
00411
00418 function getMessageFromDB( $msg ) {
00419 return wfMsgExt( $msg, array( 'parsemag', 'language' => $this ) );
00420 }
00421
00422 function getLanguageName( $code ) {
00423 $names = self::getLanguageNames();
00424 if ( !array_key_exists( $code, $names ) ) {
00425 return '';
00426 }
00427 return $names[$code];
00428 }
00429
00430 function getMonthName( $key ) {
00431 return $this->getMessageFromDB( self::$mMonthMsgs[$key-1] );
00432 }
00433
00434 function getMonthNameGen( $key ) {
00435 return $this->getMessageFromDB( self::$mMonthGenMsgs[$key-1] );
00436 }
00437
00438 function getMonthAbbreviation( $key ) {
00439 return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key-1] );
00440 }
00441
00442 function getWeekdayName( $key ) {
00443 return $this->getMessageFromDB( self::$mWeekdayMsgs[$key-1] );
00444 }
00445
00446 function getWeekdayAbbreviation( $key ) {
00447 return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key-1] );
00448 }
00449
00450 function getIranianCalendarMonthName( $key ) {
00451 return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key-1] );
00452 }
00453
00454 function getHebrewCalendarMonthName( $key ) {
00455 return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key-1] );
00456 }
00457
00458 function getHebrewCalendarMonthNameGen( $key ) {
00459 return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key-1] );
00460 }
00461
00462 function getHijriCalendarMonthName( $key ) {
00463 return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key-1] );
00464 }
00465
00474 function userAdjust( $ts, $tz = false ) {
00475 global $wgUser, $wgLocalTZoffset;
00476
00477 if ( $tz === false ) {
00478 $tz = $wgUser->getOption( 'timecorrection' );
00479 }
00480
00481 $data = explode( '|', $tz, 3 );
00482
00483 if ( $data[0] == 'ZoneInfo' ) {
00484 if ( function_exists( 'timezone_open' ) && @timezone_open( $data[2] ) !== false ) {
00485 $date = date_create( $ts, timezone_open( 'UTC' ) );
00486 date_timezone_set( $date, timezone_open( $data[2] ) );
00487 $date = date_format( $date, 'YmdHis' );
00488 return $date;
00489 }
00490 # Unrecognized timezone, default to 'Offset' with the stored offset.
00491 $data[0] = 'Offset';
00492 }
00493
00494 $minDiff = 0;
00495 if ( $data[0] == 'System' || $tz == '' ) {
00496 # Global offset in minutes.
00497 if( isset($wgLocalTZoffset) ) $minDiff = $wgLocalTZoffset;
00498 } else if ( $data[0] == 'Offset' ) {
00499 $minDiff = intval( $data[1] );
00500 } else {
00501 $data = explode( ':', $tz );
00502 if( count( $data ) == 2 ) {
00503 $data[0] = intval( $data[0] );
00504 $data[1] = intval( $data[1] );
00505 $minDiff = abs( $data[0] ) * 60 + $data[1];
00506 if ( $data[0] < 0 ) $minDiff = -$minDiff;
00507 } else {
00508 $minDiff = intval( $data[0] ) * 60;
00509 }
00510 }
00511
00512 # No difference ? Return time unchanged
00513 if ( 0 == $minDiff ) return $ts;
00514
00515 wfSuppressWarnings();
00516 # Generate an adjusted date; take advantage of the fact that mktime
00517 # will normalize out-of-range values so we don't have to split $minDiff
00518 # into hours and minutes.
00519 $t = mktime( (
00520 (int)substr( $ts, 8, 2) ), # Hours
00521 (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
00522 (int)substr( $ts, 12, 2 ), # Seconds
00523 (int)substr( $ts, 4, 2 ), # Month
00524 (int)substr( $ts, 6, 2 ), # Day
00525 (int)substr( $ts, 0, 4 ) ); #Year
00526
00527 $date = date( 'YmdHis', $t );
00528 wfRestoreWarnings();
00529
00530 return $date;
00531 }
00532
00589 function sprintfDate( $format, $ts ) {
00590 $s = '';
00591 $raw = false;
00592 $roman = false;
00593 $hebrewNum = false;
00594 $unix = false;
00595 $rawToggle = false;
00596 $iranian = false;
00597 $hebrew = false;
00598 $hijri = false;
00599 $thai = false;
00600 for ( $p = 0; $p < strlen( $format ); $p++ ) {
00601 $num = false;
00602 $code = $format[$p];
00603 if ( $code == 'x' && $p < strlen( $format ) - 1 ) {
00604 $code .= $format[++$p];
00605 }
00606
00607 if ( ( $code === 'xi' || $code == 'xj' || $code == 'xk' || $code == 'xm' ) && $p < strlen( $format ) - 1 ) {
00608 $code .= $format[++$p];
00609 }
00610
00611 switch ( $code ) {
00612 case 'xx':
00613 $s .= 'x';
00614 break;
00615 case 'xn':
00616 $raw = true;
00617 break;
00618 case 'xN':
00619 $rawToggle = !$rawToggle;
00620 break;
00621 case 'xr':
00622 $roman = true;
00623 break;
00624 case 'xh':
00625 $hebrewNum = true;
00626 break;
00627 case 'xg':
00628 $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
00629 break;
00630 case 'xjx':
00631 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
00632 $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
00633 break;
00634 case 'd':
00635 $num = substr( $ts, 6, 2 );
00636 break;
00637 case 'D':
00638 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00639 $s .= $this->getWeekdayAbbreviation( gmdate( 'w', $unix ) + 1 );
00640 break;
00641 case 'j':
00642 $num = intval( substr( $ts, 6, 2 ) );
00643 break;
00644 case 'xij':
00645 if ( !$iranian ) $iranian = self::tsToIranian( $ts );
00646 $num = $iranian[2];
00647 break;
00648 case 'xmj':
00649 if ( !$hijri ) $hijri = self::tsToHijri( $ts );
00650 $num = $hijri[2];
00651 break;
00652 case 'xjj':
00653 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
00654 $num = $hebrew[2];
00655 break;
00656 case 'l':
00657 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00658 $s .= $this->getWeekdayName( gmdate( 'w', $unix ) + 1 );
00659 break;
00660 case 'N':
00661 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00662 $w = gmdate( 'w', $unix );
00663 $num = $w ? $w : 7;
00664 break;
00665 case 'w':
00666 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00667 $num = gmdate( 'w', $unix );
00668 break;
00669 case 'z':
00670 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00671 $num = gmdate( 'z', $unix );
00672 break;
00673 case 'W':
00674 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00675 $num = gmdate( 'W', $unix );
00676 break;
00677 case 'F':
00678 $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
00679 break;
00680 case 'xiF':
00681 if ( !$iranian ) $iranian = self::tsToIranian( $ts );
00682 $s .= $this->getIranianCalendarMonthName( $iranian[1] );
00683 break;
00684 case 'xmF':
00685 if ( !$hijri ) $hijri = self::tsToHijri( $ts );
00686 $s .= $this->getHijriCalendarMonthName( $hijri[1] );
00687 break;
00688 case 'xjF':
00689 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
00690 $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
00691 break;
00692 case 'm':
00693 $num = substr( $ts, 4, 2 );
00694 break;
00695 case 'M':
00696 $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
00697 break;
00698 case 'n':
00699 $num = intval( substr( $ts, 4, 2 ) );
00700 break;
00701 case 'xin':
00702 if ( !$iranian ) $iranian = self::tsToIranian( $ts );
00703 $num = $iranian[1];
00704 break;
00705 case 'xmn':
00706 if ( !$hijri ) $hijri = self::tsToHijri ( $ts );
00707 $num = $hijri[1];
00708 break;
00709 case 'xjn':
00710 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
00711 $num = $hebrew[1];
00712 break;
00713 case 't':
00714 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00715 $num = gmdate( 't', $unix );
00716 break;
00717 case 'xjt':
00718 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
00719 $num = $hebrew[3];
00720 break;
00721 case 'L':
00722 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00723 $num = gmdate( 'L', $unix );
00724 break;
00725 # 'o' is supported since PHP 5.1.0
00726 # return literal if not supported
00727 # TODO: emulation for pre 5.1.0 versions
00728 case 'o':
00729 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00730 if ( version_compare(PHP_VERSION, '5.1.0') === 1 )
00731 $num = date( 'o', $unix );
00732 else
00733 $s .= 'o';
00734 break;
00735 case 'Y':
00736 $num = substr( $ts, 0, 4 );
00737 break;
00738 case 'xiY':
00739 if ( !$iranian ) $iranian = self::tsToIranian( $ts );
00740 $num = $iranian[0];
00741 break;
00742 case 'xmY':
00743 if ( !$hijri ) $hijri = self::tsToHijri( $ts );
00744 $num = $hijri[0];
00745 break;
00746 case 'xjY':
00747 if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
00748 $num = $hebrew[0];
00749 break;
00750 case 'xkY':
00751 if ( !$thai ) $thai = self::tsToThai( $ts );
00752 $num = $thai[0];
00753 break;
00754 case 'y':
00755 $num = substr( $ts, 2, 2 );
00756 break;
00757 case 'a':
00758 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
00759 break;
00760 case 'A':
00761 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
00762 break;
00763 case 'g':
00764 $h = substr( $ts, 8, 2 );
00765 $num = $h % 12 ? $h % 12 : 12;
00766 break;
00767 case 'G':
00768 $num = intval( substr( $ts, 8, 2 ) );
00769 break;
00770 case 'h':
00771 $h = substr( $ts, 8, 2 );
00772 $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
00773 break;
00774 case 'H':
00775 $num = substr( $ts, 8, 2 );
00776 break;
00777 case 'i':
00778 $num = substr( $ts, 10, 2 );
00779 break;
00780 case 's':
00781 $num = substr( $ts, 12, 2 );
00782 break;
00783 case 'c':
00784 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00785 $s .= gmdate( 'c', $unix );
00786 break;
00787 case 'r':
00788 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00789 $s .= gmdate( 'r', $unix );
00790 break;
00791 case 'U':
00792 if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
00793 $num = $unix;
00794 break;
00795 case '\\':
00796 # Backslash escaping
00797 if ( $p < strlen( $format ) - 1 ) {
00798 $s .= $format[++$p];
00799 } else {
00800 $s .= '\\';
00801 }
00802 break;
00803 case '"':
00804 # Quoted literal
00805 if ( $p < strlen( $format ) - 1 ) {
00806 $endQuote = strpos( $format, '"', $p + 1 );
00807 if ( $endQuote === false ) {
00808 # No terminating quote, assume literal "
00809 $s .= '"';
00810 } else {
00811 $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
00812 $p = $endQuote;
00813 }
00814 } else {
00815 # Quote at end of string, assume literal "
00816 $s .= '"';
00817 }
00818 break;
00819 default:
00820 $s .= $format[$p];
00821 }
00822 if ( $num !== false ) {
00823 if ( $rawToggle || $raw ) {
00824 $s .= $num;
00825 $raw = false;
00826 } elseif ( $roman ) {
00827 $s .= self::romanNumeral( $num );
00828 $roman = false;
00829 } elseif( $hebrewNum ) {
00830 $s .= self::hebrewNumeral( $num );
00831 $hebrewNum = false;
00832 } else {
00833 $s .= $this->formatNum( $num, true );
00834 }
00835 $num = false;
00836 }
00837 }
00838 return $s;
00839 }
00840
00841 private static $GREG_DAYS = array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
00842 private static $IRANIAN_DAYS = array( 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 );
00851 private static function tsToIranian( $ts ) {
00852 $gy = substr( $ts, 0, 4 ) -1600;
00853 $gm = substr( $ts, 4, 2 ) -1;
00854 $gd = substr( $ts, 6, 2 ) -1;
00855
00856 # Days passed from the beginning (including leap years)
00857 $gDayNo = 365*$gy
00858 + floor(($gy+3) / 4)
00859 - floor(($gy+99) / 100)
00860 + floor(($gy+399) / 400);
00861
00862
00863
00864 for( $i = 0; $i < $gm; $i++ ) {
00865 $gDayNo += self::$GREG_DAYS[$i];
00866 }
00867
00868
00869 if ( $gm > 1 && (($gy%4===0 && $gy%100!==0 || ($gy%400==0)))) {
00870 $gDayNo++;
00871 }
00872
00873
00874 $gDayNo += $gd;
00875
00876 $jDayNo = $gDayNo - 79;
00877
00878 $jNp = floor($jDayNo / 12053);
00879 $jDayNo %= 12053;
00880
00881 $jy = 979 + 33*$jNp + 4*floor($jDayNo/1461);
00882 $jDayNo %= 1461;
00883
00884 if ( $jDayNo >= 366 ) {
00885 $jy += floor(($jDayNo-1)/365);
00886 $jDayNo = floor(($jDayNo-1)%365);
00887 }
00888
00889 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
00890 $jDayNo -= self::$IRANIAN_DAYS[$i];
00891 }
00892
00893 $jm= $i+1;
00894 $jd= $jDayNo+1;
00895
00896 return array($jy, $jm, $jd);
00897 }
00905 private static function tsToHijri ( $ts ) {
00906 $year = substr( $ts, 0, 4 );
00907 $month = substr( $ts, 4, 2 );
00908 $day = substr( $ts, 6, 2 );
00909
00910 $zyr = $year;
00911 $zd=$day;
00912 $zm=$month;
00913 $zy=$zyr;
00914
00915
00916
00917 if (($zy>1582)||(($zy==1582)&&($zm>10))||(($zy==1582)&&($zm==10)&&($zd>14)))
00918 {
00919
00920
00921 $zjd=(int)((1461*($zy + 4800 + (int)( ($zm-14) /12) ))/4) + (int)((367*($zm-2-12*((int)(($zm-14)/12))))/12)-(int)((3*(int)(( ($zy+4900+(int)(($zm-14)/12))/100)))/4)+$zd-32075;
00922 }
00923 else
00924 {
00925 $zjd = 367*$zy-(int)((7*($zy+5001+(int)(($zm-9)/7)))/4)+(int)((275*$zm)/9)+$zd+1729777;
00926 }
00927
00928 $zl=$zjd-1948440+10632;
00929 $zn=(int)(($zl-1)/10631);
00930 $zl=$zl-10631*$zn+354;
00931 $zj=((int)((10985-$zl)/5316))*((int)((50*$zl)/17719))+((int)($zl/5670))*((int)((43*$zl)/15238));
00932 $zl=$zl-((int)((30-$zj)/15))*((int)((17719*$zj)/50))-((int)($zj/16))*((int)((15238*$zj)/43))+29;
00933 $zm=(int)((24*$zl)/709);
00934 $zd=$zl-(int)((709*$zm)/24);
00935 $zy=30*$zn+$zj-30;
00936
00937 return array ($zy, $zm, $zd);
00938 }
00939
00951 private static function tsToHebrew( $ts ) {
00952 # Parse date
00953 $year = substr( $ts, 0, 4 );
00954 $month = substr( $ts, 4, 2 );
00955 $day = substr( $ts, 6, 2 );
00956
00957 # Calculate Hebrew year
00958 $hebrewYear = $year + 3760;
00959
00960 # Month number when September = 1, August = 12
00961 $month += 4;
00962 if( $month > 12 ) {
00963 # Next year
00964 $month -= 12;
00965 $year++;
00966 $hebrewYear++;
00967 }
00968
00969 # Calculate day of year from 1 September
00970 $dayOfYear = $day;
00971 for( $i = 1; $i < $month; $i++ ) {
00972 if( $i == 6 ) {
00973 # February
00974 $dayOfYear += 28;
00975 # Check if the year is leap
00976 if( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
00977 $dayOfYear++;
00978 }
00979 } elseif( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
00980 $dayOfYear += 30;
00981 } else {
00982 $dayOfYear += 31;
00983 }
00984 }
00985
00986 # Calculate the start of the Hebrew year
00987 $start = self::hebrewYearStart( $hebrewYear );
00988
00989 # Calculate next year's start
00990 if( $dayOfYear <= $start ) {
00991 # Day is before the start of the year - it is the previous year
00992 # Next year's start
00993 $nextStart = $start;
00994 # Previous year
00995 $year--;
00996 $hebrewYear--;
00997 # Add days since previous year's 1 September
00998 $dayOfYear += 365;
00999 if( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
01000 # Leap year
01001 $dayOfYear++;
01002 }
01003 # Start of the new (previous) year
01004 $start = self::hebrewYearStart( $hebrewYear );
01005 } else {
01006 # Next year's start
01007 $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
01008 }
01009
01010 # Calculate Hebrew day of year
01011 $hebrewDayOfYear = $dayOfYear - $start;
01012
01013 # Difference between year's days
01014 $diff = $nextStart - $start;
01015 # Add 12 (or 13 for leap years) days to ignore the difference between
01016 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
01017 # difference is only about the year type
01018 if( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
01019 $diff += 13;
01020 } else {
01021 $diff += 12;
01022 }
01023
01024 # Check the year pattern, and is leap year
01025 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
01026 # This is mod 30, to work on both leap years (which add 30 days of Adar I)
01027 # and non-leap years
01028 $yearPattern = $diff % 30;
01029 # Check if leap year
01030 $isLeap = $diff >= 30;
01031
01032 # Calculate day in the month from number of day in the Hebrew year
01033 # Don't check Adar - if the day is not in Adar, we will stop before;
01034 # if it is in Adar, we will use it to check if it is Adar I or Adar II
01035 $hebrewDay = $hebrewDayOfYear;
01036 $hebrewMonth = 1;
01037 $days = 0;
01038 while( $hebrewMonth <= 12 ) {
01039 # Calculate days in this month
01040 if( $isLeap && $hebrewMonth == 6 ) {
01041 # Adar in a leap year
01042 if( $isLeap ) {
01043 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
01044 $days = 30;
01045 if( $hebrewDay <= $days ) {
01046 # Day in Adar I
01047 $hebrewMonth = 13;
01048 } else {
01049 # Subtract the days of Adar I
01050 $hebrewDay -= $days;
01051 # Try Adar II
01052 $days = 29;
01053 if( $hebrewDay <= $days ) {
01054 # Day in Adar II
01055 $hebrewMonth = 14;
01056 }
01057 }
01058 }
01059 } elseif( $hebrewMonth == 2 && $yearPattern == 2 ) {
01060 # Cheshvan in a complete year (otherwise as the rule below)
01061 $days = 30;
01062 } elseif( $hebrewMonth == 3 && $yearPattern == 0 ) {
01063 # Kislev in an incomplete year (otherwise as the rule below)
01064 $days = 29;
01065 } else {
01066 # Odd months have 30 days, even have 29
01067 $days = 30 - ( $hebrewMonth - 1 ) % 2;
01068 }
01069 if( $hebrewDay <= $days ) {
01070 # In the current month
01071 break;
01072 } else {
01073 # Subtract the days of the current month
01074 $hebrewDay -= $days;
01075 # Try in the next month
01076 $hebrewMonth++;
01077 }
01078 }
01079
01080 return array( $hebrewYear, $hebrewMonth, $hebrewDay, $days );
01081 }
01082
01088 private static function hebrewYearStart( $year ) {
01089 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
01090 $b = intval( ( $year - 1 ) % 4 );
01091 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
01092 if( $m < 0 ) {
01093 $m--;
01094 }
01095 $Mar = intval( $m );
01096 if( $m < 0 ) {
01097 $m++;
01098 }
01099 $m -= $Mar;
01100
01101 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7);
01102 if( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
01103 $Mar++;
01104 } else if( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
01105 $Mar += 2;
01106 } else if( $c == 2 || $c == 4 || $c == 6 ) {
01107 $Mar++;
01108 }
01109
01110 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
01111 return $Mar;
01112 }
01113
01122 private static function tsToThai( $ts ) {
01123 $gy = substr( $ts, 0, 4 );
01124 $gm = substr( $ts, 4, 2 );
01125 $gd = substr( $ts, 6, 2 );
01126
01127 # Add 543 years to the Gregorian calendar
01128 # Months and days are identical
01129 $gy_thai = $gy + 543;
01130
01131 return array( $gy_thai, $gm, $gd );
01132 }
01133
01134
01138 static function romanNumeral( $num ) {
01139 static $table = array(
01140 array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ),
01141 array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ),
01142 array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ),
01143 array( '', 'M', 'MM', 'MMM' )
01144 );
01145
01146 $num = intval( $num );
01147 if ( $num > 3000 || $num <= 0 ) {
01148 return $num;
01149 }
01150
01151 $s = '';
01152 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
01153 if ( $num >= $pow10 ) {
01154 $s .= $table[$i][floor($num / $pow10)];
01155 }
01156 $num = $num % $pow10;
01157 }
01158 return $s;
01159 }
01160
01164 static function hebrewNumeral( $num ) {
01165 static $table = array(
01166 array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ),
01167 array( '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ),
01168 array( '', 'ק', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק', 'תתר' ),
01169 array( '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' )
01170 );
01171
01172 $num = intval( $num );
01173 if ( $num > 9999 || $num <= 0 ) {
01174 return $num;
01175 }
01176
01177 $s = '';
01178 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
01179 if ( $num >= $pow10 ) {
01180 if ( $num == 15 || $num == 16 ) {
01181 $s .= $table[0][9] . $table[0][$num - 9];
01182 $num = 0;
01183 } else {
01184 $s .= $table[$i][intval( ( $num / $pow10 ) )];
01185 if( $pow10 == 1000 ) {
01186 $s .= "'";
01187 }
01188 }
01189 }
01190 $num = $num % $pow10;
01191 }
01192 if( strlen( $s ) == 2 ) {
01193 $str = $s . "'";
01194 } else {
01195 $str = substr( $s, 0, strlen( $s ) - 2 ) . '"';
01196 $str .= substr( $s, strlen( $s ) - 2, 2 );
01197 }
01198 $start = substr( $str, 0, strlen( $str ) - 2 );
01199 $end = substr( $str, strlen( $str ) - 2 );
01200 switch( $end ) {
01201 case 'כ':
01202 $str = $start . 'ך';
01203 break;
01204 case 'מ':
01205 $str = $start . 'ם';
01206 break;
01207 case 'נ':
01208 $str = $start . 'ן';
01209 break;
01210 case 'פ':
01211 $str = $start . 'ף';
01212 break;
01213 case 'צ':
01214 $str = $start . 'ץ';
01215 break;
01216 }
01217 return $str;
01218 }
01219
01237 function dateFormat( $usePrefs = true ) {
01238 global $wgUser;
01239
01240 if( is_bool( $usePrefs ) ) {
01241 if( $usePrefs ) {
01242 $datePreference = $wgUser->getDatePreference();
01243 } else {
01244 $options = User::getDefaultOptions();
01245 $datePreference = (string)$options['date'];
01246 }
01247 } else {
01248 $datePreference = (string)$usePrefs;
01249 }
01250
01251
01252 if( $datePreference == '' ) {
01253 return 'default';
01254 }
01255
01256 return $datePreference;
01257 }
01258
01269 function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
01270 $this->load();
01271 if ( $adj ) {
01272 $ts = $this->userAdjust( $ts, $timecorrection );
01273 }
01274
01275 $pref = $this->dateFormat( $format );
01276 if( $pref == 'default' || !isset( $this->dateFormats["$pref date"] ) ) {
01277 $pref = $this->defaultDateFormat;
01278 }
01279 return $this->sprintfDate( $this->dateFormats["$pref date"], $ts );
01280 }
01281
01292 function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
01293 $this->load();
01294 if ( $adj ) {
01295 $ts = $this->userAdjust( $ts, $timecorrection );
01296 }
01297
01298 $pref = $this->dateFormat( $format );
01299 if( $pref == 'default' || !isset( $this->dateFormats["$pref time"] ) ) {
01300 $pref = $this->defaultDateFormat;
01301 }
01302 return $this->sprintfDate( $this->dateFormats["$pref time"], $ts );
01303 }
01304
01316 function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false) {
01317 $this->load();
01318
01319 $ts = wfTimestamp( TS_MW, $ts );
01320
01321 if ( $adj ) {
01322 $ts = $this->userAdjust( $ts, $timecorrection );
01323 }
01324
01325 $pref = $this->dateFormat( $format );
01326 if( $pref == 'default' || !isset( $this->dateFormats["$pref both"] ) ) {
01327 $pref = $this->defaultDateFormat;
01328 }
01329
01330 return $this->sprintfDate( $this->dateFormats["$pref both"], $ts );
01331 }
01332
01333 function getMessage( $key ) {
01334 $this->load();
01335 return isset( $this->messages[$key] ) ? $this->messages[$key] : null;
01336 }
01337
01338 function getAllMessages() {
01339 $this->load();
01340 return $this->messages;
01341 }
01342
01343 function iconv( $in, $out, $string ) {
01344 # For most languages, this is a wrapper for iconv
01345 return iconv( $in, $out . '//IGNORE', $string );
01346 }
01347
01348
01349 function ucwordbreaksCallbackAscii($matches){
01350 return $this->ucfirst($matches[1]);
01351 }
01352
01353 function ucwordbreaksCallbackMB($matches){
01354 return mb_strtoupper($matches[0]);
01355 }
01356
01357 function ucCallback($matches){
01358 list( $wikiUpperChars ) = self::getCaseMaps();
01359 return strtr( $matches[1], $wikiUpperChars );
01360 }
01361
01362 function lcCallback($matches){
01363 list( , $wikiLowerChars ) = self::getCaseMaps();
01364 return strtr( $matches[1], $wikiLowerChars );
01365 }
01366
01367 function ucwordsCallbackMB($matches){
01368 return mb_strtoupper($matches[0]);
01369 }
01370
01371 function ucwordsCallbackWiki($matches){
01372 list( $wikiUpperChars ) = self::getCaseMaps();
01373 return strtr( $matches[0], $wikiUpperChars );
01374 }
01375
01376 function ucfirst( $str ) {
01377 if ( empty($str) ) return $str;
01378 if ( ord($str[0]) < 128 ) return ucfirst($str);
01379 else return self::uc($str,true);
01380 }
01381
01382 function uc( $str, $first = false ) {
01383 if ( function_exists( 'mb_strtoupper' ) ) {
01384 if ( $first ) {
01385 if ( self::isMultibyte( $str ) ) {
01386 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
01387 } else {
01388 return ucfirst( $str );
01389 }
01390 } else {
01391 return self::isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
01392 }
01393 } else {
01394 if ( self::isMultibyte( $str ) ) {
01395 list( $wikiUpperChars ) = $this->getCaseMaps();
01396 $x = $first ? '^' : '';
01397 return preg_replace_callback(
01398 "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
01399 array($this,"ucCallback"),
01400 $str
01401 );
01402 } else {
01403 return $first ? ucfirst( $str ) : strtoupper( $str );
01404 }
01405 }
01406 }
01407
01408 function lcfirst( $str ) {
01409 if ( empty($str) ) return $str;
01410 if ( is_string( $str ) && ord($str[0]) < 128 ) {
01411
01412 $str[0]=strtolower($str[0]);
01413 return $str;
01414 }
01415 else return self::lc( $str, true );
01416 }
01417
01418 function lc( $str, $first = false ) {
01419 if ( function_exists( 'mb_strtolower' ) )
01420 if ( $first )
01421 if ( self::isMultibyte( $str ) )
01422 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
01423 else
01424 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
01425 else
01426 return self::isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
01427 else
01428 if ( self::isMultibyte( $str ) ) {
01429 list( , $wikiLowerChars ) = self::getCaseMaps();
01430 $x = $first ? '^' : '';
01431 return preg_replace_callback(
01432 "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
01433 array($this,"lcCallback"),
01434 $str
01435 );
01436 } else
01437 return $first ? strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str );
01438 }
01439
01440 function isMultibyte( $str ) {
01441 return (bool)preg_match( '/[\x80-\xff]/', $str );
01442 }
01443
01444 function ucwords($str) {
01445 if ( self::isMultibyte( $str ) ) {
01446 $str = self::lc($str);
01447
01448
01449 $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
01450
01451
01452 if ( function_exists( 'mb_strtoupper' ) )
01453 return preg_replace_callback(
01454 $replaceRegexp,
01455 array($this,"ucwordsCallbackMB"),
01456 $str
01457 );
01458 else
01459 return preg_replace_callback(
01460 $replaceRegexp,
01461 array($this,"ucwordsCallbackWiki"),
01462 $str
01463 );
01464 }
01465 else
01466 return ucwords( strtolower( $str ) );
01467 }
01468
01469 # capitalize words at word breaks
01470 function ucwordbreaks($str){
01471 if (self::isMultibyte( $str ) ) {
01472 $str = self::lc($str);
01473
01474
01475 $breaks= "[ \-\(\)\}\{\.,\?!]";
01476
01477
01478 $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
01479
01480 if ( function_exists( 'mb_strtoupper' ) )
01481 return preg_replace_callback(
01482 $replaceRegexp,
01483 array($this,"ucwordbreaksCallbackMB"),
01484 $str
01485 );
01486 else
01487 return preg_replace_callback(
01488 $replaceRegexp,
01489 array($this,"ucwordsCallbackWiki"),
01490 $str
01491 );
01492 }
01493 else
01494 return preg_replace_callback(
01495 '/\b([\w\x80-\xff]+)\b/',
01496 array($this,"ucwordbreaksCallbackAscii"),
01497 $str );
01498 }
01499
01511 function caseFold( $s ) {
01512 return $this->uc( $s );
01513 }
01514
01515 function checkTitleEncoding( $s ) {
01516 if( is_array( $s ) ) {
01517 wfDebugDieBacktrace( 'Given array to checkTitleEncoding.' );
01518 }
01519 # Check for non-UTF-8 URLs
01520 $ishigh = preg_match( '/[\x80-\xff]/', $s);
01521 if(!$ishigh) return $s;
01522
01523 $isutf8 = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
01524 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s );
01525 if( $isutf8 ) return $s;
01526
01527 return $this->iconv( $this->fallback8bitEncoding(), "utf-8", $s );
01528 }
01529
01530 function fallback8bitEncoding() {
01531 $this->load();
01532 return $this->fallback8bitEncoding;
01533 }
01534
01543 function stripForSearch( $string ) {
01544 global $wgDBtype;
01545 if ( $wgDBtype != 'mysql' ) {
01546 return $string;
01547 }
01548
01549
01550 wfProfileIn( __METHOD__ );
01551
01552
01553
01554 $out = preg_replace_callback(
01555 "/([\\xc0-\\xff][\\x80-\\xbf]*)/",
01556 array( $this, 'stripForSearchCallback' ),
01557 $this->lc( $string ) );
01558
01559
01560
01561
01562 $minLength = $this->minSearchLength();
01563 if( $minLength > 1 ) {
01564 $n = $minLength-1;
01565 $out = preg_replace(
01566 "/\b(\w{1,$n})\b/",
01567 "$1u800",
01568 $out );
01569 }
01570
01571
01572
01573
01574
01575
01576
01577 $out = preg_replace(
01578 "/(\w)\.(\w|\*)/u",
01579 "$1u82e$2",
01580 $out );
01581
01582 wfProfileOut( __METHOD__ );
01583 return $out;
01584 }
01585
01591 protected function stripForSearchCallback( $matches ) {
01592 return 'u8' . bin2hex( $matches[1] );
01593 }
01594
01599 protected function minSearchLength() {
01600 if( !isset( $this->minSearchLength ) ) {
01601 $sql = "show global variables like 'ft\\_min\\_word\\_len'";
01602 $dbr = wfGetDB( DB_SLAVE );
01603 $result = $dbr->query( $sql );
01604 $row = $result->fetchObject();
01605 $result->free();
01606
01607 if( $row && $row->Variable_name == 'ft_min_word_len' ) {
01608 $this->minSearchLength = intval( $row->Value );
01609 } else {
01610 $this->minSearchLength = 0;
01611 }
01612 }
01613 return $this->minSearchLength;
01614 }
01615
01616 function convertForSearchResult( $termsArray ) {
01617 # some languages, e.g. Chinese, need to do a conversion
01618 # in order for search results to be displayed correctly
01619 return $termsArray;
01620 }
01621
01628 function firstChar( $s ) {
01629 $matches = array();
01630 preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
01631 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/', $s, $matches);
01632
01633 if ( isset( $matches[1] ) ) {
01634 if ( strlen( $matches[1] ) != 3 ) {
01635 return $matches[1];
01636 }
01637
01638
01639 $code = utf8ToCodepoint( $matches[1] );
01640 if ( $code < 0xac00 || 0xd7a4 <= $code) {
01641 return $matches[1];
01642 } elseif ( $code < 0xb098 ) {
01643 return "\xe3\x84\xb1";
01644 } elseif ( $code < 0xb2e4 ) {
01645 return "\xe3\x84\xb4";
01646 } elseif ( $code < 0xb77c ) {
01647 return "\xe3\x84\xb7";
01648 } elseif ( $code < 0xb9c8 ) {
01649 return "\xe3\x84\xb9";
01650 } elseif ( $code < 0xbc14 ) {
01651 return "\xe3\x85\x81";
01652 } elseif ( $code < 0xc0ac ) {
01653 return "\xe3\x85\x82";
01654 } elseif ( $code < 0xc544 ) {
01655 return "\xe3\x85\x85";
01656 } elseif ( $code < 0xc790 ) {
01657 return "\xe3\x85\x87";
01658 } elseif ( $code < 0xcc28 ) {
01659 return "\xe3\x85\x88";
01660 } elseif ( $code < 0xce74 ) {
01661 return "\xe3\x85\x8a";
01662 } elseif ( $code < 0xd0c0 ) {
01663 return "\xe3\x85\x8b";
01664 } elseif ( $code < 0xd30c ) {
01665 return "\xe3\x85\x8c";
01666 } elseif ( $code < 0xd558 ) {
01667 return "\xe3\x85\x8d";
01668 } else {
01669 return "\xe3\x85\x8e";
01670 }
01671 } else {
01672 return "";
01673 }
01674 }
01675
01676 function initEncoding() {
01677 # Some languages may have an alternate char encoding option
01678 # (Esperanto X-coding, Japanese furigana conversion, etc)
01679 # If this language is used as the primary content language,
01680 # an override to the defaults can be set here on startup.
01681 }
01682
01683 function recodeForEdit( $s ) {
01684 # For some languages we'll want to explicitly specify
01685 # which characters make it into the edit box raw
01686 # or are converted in some way or another.
01687 # Note that if wgOutputEncoding is different from
01688 # wgInputEncoding, this text will be further converted
01689 # to wgOutputEncoding.
01690 global $wgEditEncoding;
01691 if( $wgEditEncoding == '' or
01692 $wgEditEncoding == 'UTF-8' ) {
01693 return $s;
01694 } else {
01695 return $this->iconv( 'UTF-8', $wgEditEncoding, $s );
01696 }
01697 }
01698
01699 function recodeInput( $s ) {
01700 # Take the previous into account.
01701 global $wgEditEncoding;
01702 if($wgEditEncoding != "") {
01703 $enc = $wgEditEncoding;
01704 } else {
01705 $enc = 'UTF-8';
01706 }
01707 if( $enc == 'UTF-8' ) {
01708 return $s;
01709 } else {
01710 return $this->iconv( $enc, 'UTF-8', $s );
01711 }
01712 }
01713
01719 function isRTL() {
01720 $this->load();
01721 return $this->rtl;
01722 }
01723
01729 function getDirMark() {
01730 return $this->isRTL() ? "\xE2\x80\x8F" : "\xE2\x80\x8E";
01731 }
01732
01738 function getArrow() {
01739 return $this->isRTL() ? '←' : '→';
01740 }
01741
01747 function linkPrefixExtension() {
01748 $this->load();
01749 return $this->linkPrefixExtension;
01750 }
01751
01752 function &getMagicWords() {
01753 $this->load();
01754 return $this->magicWords;
01755 }
01756
01757 # Fill a MagicWord object with data from here
01758 function getMagic( &$mw ) {
01759 if ( !$this->mMagicHookDone ) {
01760 $this->mMagicHookDone = true;
01761 wfRunHooks( 'LanguageGetMagic', array( &$this->mMagicExtensions, $this->getCode() ) );
01762 }
01763 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
01764 $rawEntry = $this->mMagicExtensions[$mw->mId];
01765 } else {
01766 $magicWords =& $this->getMagicWords();
01767 if ( isset( $magicWords[$mw->mId] ) ) {
01768 $rawEntry = $magicWords[$mw->mId];
01769 } else {
01770 # Fall back to English if local list is incomplete
01771 $magicWords =& Language::getMagicWords();
01772 if ( !isset($magicWords[$mw->mId]) ) {
01773 throw new MWException("Magic word '{$mw->mId}' not found" );
01774 }
01775 $rawEntry = $magicWords[$mw->mId];
01776 }
01777 }
01778
01779 if( !is_array( $rawEntry ) ) {
01780 error_log( "\"$rawEntry\" is not a valid magic thingie for \"$mw->mId\"" );
01781 } else {
01782 $mw->mCaseSensitive = $rawEntry[0];
01783 $mw->mSynonyms = array_slice( $rawEntry, 1 );
01784 }
01785 }
01786
01790 function addMagicWordsByLang( $newWords ) {
01791 $code = $this->getCode();
01792 $fallbackChain = array();
01793 while ( $code && !in_array( $code, $fallbackChain ) ) {
01794 $fallbackChain[] = $code;
01795 $code = self::getFallbackFor( $code );
01796 }
01797 if ( !in_array( 'en', $fallbackChain ) ) {
01798 $fallbackChain[] = 'en';
01799 }
01800 $fallbackChain = array_reverse( $fallbackChain );
01801 foreach ( $fallbackChain as $code ) {
01802 if ( isset( $newWords[$code] ) ) {
01803 $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
01804 }
01805 }
01806 }
01807
01812 function getSpecialPageAliases() {
01813 $this->load();
01814
01815
01816 if ( !isset( $this->mExtendedSpecialPageAliases ) ) {
01817
01818
01819 $this->mExtendedSpecialPageAliases = $this->specialPageAliases;
01820
01821 global $wgExtensionAliasesFiles;
01822 foreach ( $wgExtensionAliasesFiles as $file ) {
01823
01824
01825 if ( !file_exists($file) )
01826 throw new MWException( "Aliases file does not exist: $file" );
01827
01828 $aliases = array();
01829 require($file);
01830
01831
01832 if ( !isset($aliases['en']) )
01833 throw new MWException( "Malformed aliases file: $file" );
01834
01835
01836 $code = $this->getCode();
01837 do {
01838 if ( !isset($aliases[$code]) ) continue;
01839
01840 $aliases[$code] = $this->fixSpecialPageAliases( $aliases[$code] );
01841
01842
01843
01844 $this->mExtendedSpecialPageAliases = array_merge_recursive(
01845 $this->mExtendedSpecialPageAliases, $aliases[$code] );
01846
01847 } while ( $code = self::getFallbackFor( $code ) );
01848 }
01849
01850 wfRunHooks( 'LanguageGetSpecialPageAliases',
01851 array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
01852 }
01853
01854 return $this->mExtendedSpecialPageAliases;
01855 }
01856
01862 public function fixSpecialPageAliases( $mixed ) {
01863
01864 if ( is_array($mixed) ) {
01865 $callback = array( $this, 'fixSpecialPageAliases' );
01866 return array_map( $callback, $mixed );
01867 }
01868 return str_replace( ' ', '_', $this->ucfirst( $mixed ) );
01869 }
01870
01877 function emphasize( $text ) {
01878 return "<em>$text</em>";
01879 }
01880
01905 function formatNum( $number, $nocommafy = false ) {
01906 global $wgTranslateNumerals;
01907 if (!$nocommafy) {
01908 $number = $this->commafy($number);
01909 $s = $this->separatorTransformTable();
01910 if ($s) { $number = strtr($number, $s); }
01911 }
01912
01913 if ($wgTranslateNumerals) {
01914 $s = $this->digitTransformTable();
01915 if ($s) { $number = strtr($number, $s); }
01916 }
01917
01918 return $number;
01919 }
01920
01921 function parseFormattedNumber( $number ) {
01922 $s = $this->digitTransformTable();
01923 if ($s) { $number = strtr($number, array_flip($s)); }
01924
01925 $s = $this->separatorTransformTable();
01926 if ($s) { $number = strtr($number, array_flip($s)); }
01927
01928 $number = strtr( $number, array (',' => '') );
01929 return $number;
01930 }
01931
01938 function commafy($_) {
01939 return strrev((string)preg_replace('/(\d{3})(?=\d)(?!\d*\.)/','$1,',strrev($_)));
01940 }
01941
01942 function digitTransformTable() {
01943 $this->load();
01944 return $this->digitTransformTable;
01945 }
01946
01947 function separatorTransformTable() {
01948 $this->load();
01949 return $this->separatorTransformTable;
01950 }
01951
01952
01961 function listToText( $l ) {
01962 $s = '';
01963 $m = count( $l ) - 1;
01964 if( $m == 1 ) {
01965 return $l[0] . $this->getMessageFromDB( 'and' ) . $this->getMessageFromDB( 'word-separator' ) . $l[1];
01966 }
01967 else {
01968 for ( $i = $m; $i >= 0; $i-- ) {
01969 if ( $i == $m ) {
01970 $s = $l[$i];
01971 } else if( $i == $m - 1 ) {
01972 $s = $l[$i] . $this->getMessageFromDB( 'and' ) . $this->getMessageFromDB( 'word-separator' ) . $s;
01973 } else {
01974 $s = $l[$i] . $this->getMessageFromDB( 'comma-separator' ) . $s;
01975 }
01976 }
01977 return $s;
01978 }
01979 }
01980
01987 function commaList( $list ) {
01988 return implode(
01989 $list,
01990 wfMsgExt( 'comma-separator', array( 'escapenoentities', 'language' => $this ) ) );
01991 }
01992
01999 function semicolonList( $list ) {
02000 return implode(
02001 $list,
02002 wfMsgExt( 'semicolon-separator', array( 'escapenoentities', 'language' => $this ) ) );
02003 }
02004
02010 function pipeList( $list ) {
02011 return implode(
02012 $list,
02013 wfMsgExt( 'pipe-separator', array( 'escapenoentities', 'language' => $this ) ) );
02014 }
02015
02031 function truncate( $string, $length, $ellipsis = '...' ) {
02032 # Use the localized ellipsis character
02033 if( $ellipsis == '...' ) {
02034 $ellipsis = wfMsgExt( 'ellipsis', array( 'escapenoentities', 'language' => $this ) );
02035 }
02036
02037 if( $length == 0 ) {
02038 return $ellipsis;
02039 }
02040 if ( strlen( $string ) <= abs( $length ) ) {
02041 return $string;
02042 }
02043 if( $length > 0 ) {
02044 $string = substr( $string, 0, $length );
02045 $char = ord( $string[strlen( $string ) - 1] );
02046 $m = array();
02047 if ($char >= 0xc0) {
02048 # We got the first byte only of a multibyte char; remove it.
02049 $string = substr( $string, 0, -1 );
02050 } elseif( $char >= 0x80 &&
02051 preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
02052 '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m ) ) {
02053 # We chopped in the middle of a character; remove it
02054 $string = $m[1];
02055 }
02056 return $string . $ellipsis;
02057 } else {
02058 $string = substr( $string, $length );
02059 $char = ord( $string[0] );
02060 if( $char >= 0x80 && $char < 0xc0 ) {
02061 # We chopped in the middle of a character; remove the whole thing
02062 $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
02063 }
02064 return $ellipsis . $string;
02065 }
02066 }
02067
02076 function convertGrammar( $word, $case ) {
02077 global $wgGrammarForms;
02078 if ( isset($wgGrammarForms[$this->getCode()][$case][$word]) ) {
02079 return $wgGrammarForms[$this->getCode()][$case][$word];
02080 }
02081 return $word;
02082 }
02083
02092 function gender( $gender, $forms ) {
02093 if ( !count($forms) ) { return ''; }
02094 $forms = $this->preConvertPlural( $forms, 2 );
02095 if ( $gender === 'male' ) return $forms[0];
02096 if ( $gender === 'female' ) return $forms[1];
02097 return isset($forms[2]) ? $forms[2] : $forms[0];
02098 }
02099
02115 function convertPlural( $count, $forms ) {
02116 if ( !count($forms) ) { return ''; }
02117 $forms = $this->preConvertPlural( $forms, 2 );
02118
02119 return ( $count == 1 ) ? $forms[0] : $forms[1];
02120 }
02121
02130 protected function preConvertPlural( $forms, $count ) {
02131 while ( count($forms) < $count ) {
02132 $forms[] = $forms[count($forms)-1];
02133 }
02134 return $forms;
02135 }
02136
02143 function translateBlockExpiry( $str ) {
02144
02145 $scBlockExpiryOptions = $this->getMessageFromDB( 'ipboptions' );
02146
02147 if ( $scBlockExpiryOptions == '-') {
02148 return $str;
02149 }
02150
02151 foreach (explode(',', $scBlockExpiryOptions) as $option) {
02152 if ( strpos($option, ":") === false )
02153 continue;
02154 list($show, $value) = explode(":", $option);
02155 if ( strcmp ( $str, $value) == 0 ) {
02156 return htmlspecialchars( trim( $show ) );
02157 }
02158 }
02159
02160 return $str;
02161 }
02162
02170 function segmentForDiff( $text ) {
02171 return $text;
02172 }
02173
02180 function unsegmentForDiff( $text ) {
02181 return $text;
02182 }
02183
02184 # convert text to different variants of a language.
02185 function convert( $text, $isTitle = false) {
02186 return $this->mConverter->convert($text, $isTitle);
02187 }
02188
02189 # Convert text from within Parser
02190 function parserConvert( $text, &$parser ) {
02191 return $this->mConverter->parserConvert( $text, $parser );
02192 }
02193
02194 # Check if this is a language with variants
02195 function hasVariants(){
02196 return sizeof($this->getVariants())>1;
02197 }
02198
02199 # Put custom tags (e.g. -{ }-) around math to prevent conversion
02200 function armourMath($text){
02201 return $this->mConverter->armourMath($text);
02202 }
02203
02204
02212 function convertHtml( $text, $isTitle = false ) {
02213 return htmlspecialchars( $this->convert( $text, $isTitle ) );
02214 }
02215
02216 function convertCategoryKey( $key ) {
02217 return $this->mConverter->convertCategoryKey( $key );
02218 }
02219
02226 function getVariants() {
02227 return $this->mConverter->getVariants();
02228 }
02229
02230
02231 function getPreferredVariant( $fromUser = true ) {
02232 return $this->mConverter->getPreferredVariant( $fromUser );
02233 }
02234
02247 function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
02248 $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond );
02249 }
02250
02256 function convertLinkToAllVariants($text){
02257 return $this->mConverter->convertLinkToAllVariants($text);
02258 }
02259
02260
02267 function getExtraHashOptions() {
02268 return $this->mConverter->getExtraHashOptions();
02269 }
02270
02278 function getParsedTitle() {
02279 return $this->mConverter->getParsedTitle();
02280 }
02281
02290 function markNoConversion( $text, $noParse=false ) {
02291 return $this->mConverter->markNoConversion( $text, $noParse );
02292 }
02293
02300 function linkTrail() {
02301 $this->load();
02302 return $this->linkTrail;
02303 }
02304
02305 function getLangObj() {
02306 return $this;
02307 }
02308
02312 function getCode() {
02313 return $this->mCode;
02314 }
02315
02316 function setCode( $code ) {
02317 $this->mCode = $code;
02318 }
02319
02320 static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
02321 return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
02322 }
02323
02324 static function getMessagesFileName( $code ) {
02325 global $IP;
02326 return self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
02327 }
02328
02329 static function getClassFileName( $code ) {
02330 global $IP;
02331 return self::getFileName( "$IP/languages/classes/Language", $code, '.php' );
02332 }
02333
02334 static function getLocalisationArray( $code, $disableCache = false ) {
02335 self::loadLocalisation( $code, $disableCache );
02336 return self::$mLocalisationCache[$code];
02337 }
02338
02344 static function loadLocalisation( $code, $disableCache = false ) {
02345 static $recursionGuard = array();
02346 global $wgMemc, $wgEnableSerializedMessages, $wgCheckSerialized;
02347
02348 if ( !$code ) {
02349 throw new MWException( "Invalid language code requested" );
02350 }
02351
02352 if ( !$disableCache ) {
02353 # Try the per-process cache
02354 if ( isset( self::$mLocalisationCache[$code] ) ) {
02355 return self::$mLocalisationCache[$code]['deps'];
02356 }
02357
02358 wfProfileIn( __METHOD__ );
02359
02360 # Try the serialized directory
02361 if( $wgEnableSerializedMessages ) {
02362 $cache = wfGetPrecompiledData( self::getFileName( "Messages", $code, '.ser' ) );
02363 if ( $cache ) {
02364 if ( $wgCheckSerialized && self::isLocalisationOutOfDate( $cache ) ) {
02365 $cache = false;
02366 wfDebug( "Language::loadLocalisation(): precompiled data file for $code is out of date\n" );
02367 } else {
02368 self::$mLocalisationCache[$code] = $cache;
02369 wfDebug( "Language::loadLocalisation(): got localisation for $code from precompiled data file\n" );
02370 wfProfileOut( __METHOD__ );
02371 return self::$mLocalisationCache[$code]['deps'];
02372 }
02373 }
02374 }
02375
02376 # Try the global cache
02377 $memcKey = wfMemcKey('localisation', $code );
02378 $fbMemcKey = wfMemcKey('fallback', $cache['fallback'] );
02379 $cache = $wgMemc->get( $memcKey );
02380 if ( $cache ) {
02381 if ( self::isLocalisationOutOfDate( $cache ) ) {
02382 $wgMemc->delete( $memcKey );
02383 $wgMemc->delete( $fbMemcKey );
02384 $cache = false;
02385 wfDebug( "Language::loadLocalisation(): localisation cache for $code had expired\n" );
02386 } else {
02387 self::$mLocalisationCache[$code] = $cache;
02388 wfDebug( "Language::loadLocalisation(): got localisation for $code from cache\n" );
02389 wfProfileOut( __METHOD__ );
02390 return $cache['deps'];
02391 }
02392 }
02393 } else {
02394 wfProfileIn( __METHOD__ );
02395 }
02396
02397 # Default fallback, may be overridden when the messages file is included
02398 if ( $code != 'en' ) {
02399 $fallback = 'en';
02400 } else {
02401 $fallback = false;
02402 }
02403
02404 # Load the primary localisation from the source file
02405 $filename = self::getMessagesFileName( $code );
02406 if ( !file_exists( $filename ) ) {
02407 wfDebug( "Language::loadLocalisation(): no localisation file for $code, using implicit fallback to en\n" );
02408 $cache = compact( self::$mLocalisationKeys );
02409 $deps = array();
02410 } else {
02411 $deps = array( $filename => filemtime( $filename ) );
02412 require( $filename );
02413 $cache = compact( self::$mLocalisationKeys );
02414 wfDebug( "Language::loadLocalisation(): got localisation for $code from source\n" );
02415 }
02416
02417 # Load magic word source file
02418 global $IP;
02419 $filename = "$IP/includes/MagicWord.php";
02420 $newDeps = array( $filename => filemtime( $filename ) );
02421 $deps = array_merge( $deps, $newDeps );
02422
02423 if ( !empty( $fallback ) ) {
02424 # Load the fallback localisation, with a circular reference guard
02425 if ( isset( $recursionGuard[$code] ) ) {
02426 throw new MWException( "Error: Circular fallback reference in language code $code" );
02427 }
02428 $recursionGuard[$code] = true;
02429 $newDeps = self::loadLocalisation( $fallback, $disableCache );
02430 unset( $recursionGuard[$code] );
02431
02432 $secondary = self::$mLocalisationCache[$fallback];
02433 $deps = array_merge( $deps, $newDeps );
02434
02435 # Merge the fallback localisation with the current localisation
02436 foreach ( self::$mLocalisationKeys as $key ) {
02437 if ( isset( $cache[$key] ) ) {
02438 if ( isset( $secondary[$key] ) ) {
02439 if ( in_array( $key, self::$mMergeableMapKeys ) ) {
02440 $cache[$key] = $cache[$key] + $secondary[$key];
02441 } elseif ( in_array( $key, self::$mMergeableListKeys ) ) {
02442 $cache[$key] = array_merge( $secondary[$key], $cache[$key] );
02443 } elseif ( in_array( $key, self::$mMergeableAliasListKeys ) ) {
02444 $cache[$key] = array_merge_recursive( $cache[$key], $secondary[$key] );
02445 }
02446 }
02447 } else {
02448 $cache[$key] = $secondary[$key];
02449 }
02450 }
02451
02452 # Merge bookstore lists if requested
02453 if ( !empty( $cache['bookstoreList']['inherit'] ) ) {
02454 $cache['bookstoreList'] = array_merge( $cache['bookstoreList'], $secondary['bookstoreList'] );
02455 }
02456 if ( isset( $cache['bookstoreList']['inherit'] ) ) {
02457 unset( $cache['bookstoreList']['inherit'] );
02458 }
02459 }
02460
02461 # Add dependencies to the cache entry
02462 $cache['deps'] = $deps;
02463
02464 # Replace spaces with underscores in namespace names
02465 $cache['namespaceNames'] = str_replace( ' ', '_', $cache['namespaceNames'] );
02466
02467 # And do the same for specialpage aliases. $page is an array.
02468 foreach ( $cache['specialPageAliases'] as &$page ) {
02469 $page = str_replace( ' ', '_', $page );
02470 }
02471 # Decouple the reference to prevent accidental damage
02472 unset($page);
02473
02474 # Save to both caches
02475 self::$mLocalisationCache[$code] = $cache;
02476 if ( !$disableCache ) {
02477 $wgMemc->set( $memcKey, $cache );
02478 $wgMemc->set( $fbMemcKey, (string) $cache['fallback'] );
02479 }
02480
02481 wfProfileOut( __METHOD__ );
02482 return $deps;
02483 }
02484
02493 static function isLocalisationOutOfDate( $cache ) {
02494 if ( !is_array( $cache ) ) {
02495 self::loadLocalisation( $cache );
02496 $cache = self::$mLocalisationCache[$cache];
02497 }
02498
02499 if( count($cache['deps']) < 2 ) {
02500 return true;
02501 }
02502 $expired = false;
02503 foreach ( $cache['deps'] as $file => $mtime ) {
02504 if ( !file_exists( $file ) || filemtime( $file ) > $mtime ) {
02505 $expired = true;
02506 break;
02507 }
02508 }
02509 return $expired;
02510 }
02511
02515 static function getFallbackFor( $code ) {
02516
02517 if ( $code === 'en' ) return false;
02518
02519
02520 static $cache = array();
02521
02522 if ( isset($cache[$code]) ) return $cache[$code];
02523
02524
02525 global $wgMemc;
02526 $memcKey = wfMemcKey( 'fallback', $code );
02527 $fbcode = $wgMemc->get( $memcKey );
02528
02529 if ( is_string($fbcode) ) {
02530
02531 if ( $fbcode === '' ) $fbcode = false;
02532
02533
02534 $cache[$code] = $fbcode;
02535 return $fbcode;
02536 }
02537
02538
02539 self::loadLocalisation( $code );
02540 $fbcode = self::$mLocalisationCache[$code]['fallback'];
02541
02542 $cache[$code] = $fbcode;
02543 $wgMemc->set( $memcKey, (string) $fbcode );
02544
02545 return $fbcode;
02546 }
02547
02551 static function getMessagesFor( $code ) {
02552 self::loadLocalisation( $code );
02553 return self::$mLocalisationCache[$code]['messages'];
02554 }
02555
02559 static function getMessageFor( $key, $code ) {
02560 self::loadLocalisation( $code );
02561 return isset( self::$mLocalisationCache[$code]['messages'][$key] ) ? self::$mLocalisationCache[$code]['messages'][$key] : null;
02562 }
02563
02567 function load() {
02568 if ( !$this->mLoaded ) {
02569 self::loadLocalisation( $this->getCode() );
02570 $cache =& self::$mLocalisationCache[$this->getCode()];
02571 foreach ( self::$mLocalisationKeys as $key ) {
02572 $this->$key = $cache[$key];
02573 }
02574 $this->mLoaded = true;
02575
02576 $this->fixUpSettings();
02577 }
02578 }
02579
02583 function fixUpSettings() {
02584 global $wgExtraNamespaces, $wgMetaNamespace, $wgMetaNamespaceTalk,
02585 $wgNamespaceAliases, $wgAmericanDates;
02586 wfProfileIn( __METHOD__ );
02587 if ( $wgExtraNamespaces ) {
02588 $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames;
02589 }
02590
02591 $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
02592 if ( $wgMetaNamespaceTalk ) {
02593 $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
02594 } else {
02595 $talk = $this->namespaceNames[NS_PROJECT_TALK];
02596 $this->namespaceNames[NS_PROJECT_TALK] =
02597 $this->fixVariableInNamespace( $talk );
02598 }
02599
02600 # The above mixing may leave namespaces out of canonical order.
02601 # Re-order by namespace ID number...
02602 ksort( $this->namespaceNames );
02603
02604 # Put namespace names and aliases into a hashtable.
02605 # If this is too slow, then we should arrange it so that it is done
02606 # before caching. The catch is that at pre-cache time, the above
02607 # class-specific fixup hasn't been done.
02608 $this->mNamespaceIds = array();
02609 foreach ( $this->namespaceNames as $index => $name ) {
02610 $this->mNamespaceIds[$this->lc($name)] = $index;
02611 }
02612 if ( $this->namespaceAliases ) {
02613 foreach ( $this->namespaceAliases as $name => $index ) {
02614 if ( $index === NS_PROJECT_TALK ) {
02615 unset( $this->namespaceAliases[$name] );
02616 $name = $this->fixVariableInNamespace( $name );
02617 $this->namespaceAliases[$name] = $index;
02618 }
02619 $this->mNamespaceIds[$this->lc($name)] = $index;
02620 }
02621 }
02622 if ( $wgNamespaceAliases ) {
02623 foreach ( $wgNamespaceAliases as $name => $index ) {
02624 $this->mNamespaceIds[$this->lc($name)] = $index;
02625 }
02626 }
02627
02628 if ( $this->defaultDateFormat == 'dmy or mdy' ) {
02629 $this->defaultDateFormat = $wgAmericanDates ? 'mdy' : 'dmy';
02630 }
02631 wfProfileOut( __METHOD__ );
02632 }
02633
02634 function fixVariableInNamespace( $talk ) {
02635 if ( strpos( $talk, '$1' ) === false ) return $talk;
02636
02637 global $wgMetaNamespace;
02638 $talk = str_replace( '$1', $wgMetaNamespace, $talk );
02639
02640 # Allow grammar transformations
02641 # Allowing full message-style parsing would make simple requests
02642 # such as action=raw much more expensive than they need to be.
02643 # This will hopefully cover most cases.
02644 $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
02645 array( &$this, 'replaceGrammarInNamespace' ), $talk );
02646 return str_replace( ' ', '_', $talk );
02647 }
02648
02649 function replaceGrammarInNamespace( $m ) {
02650 return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
02651 }
02652
02653 static function getCaseMaps() {
02654 static $wikiUpperChars, $wikiLowerChars;
02655 if ( isset( $wikiUpperChars ) ) {
02656 return array( $wikiUpperChars, $wikiLowerChars );
02657 }
02658
02659 wfProfileIn( __METHOD__ );
02660 $arr = wfGetPrecompiledData( 'Utf8Case.ser' );
02661 if ( $arr === false ) {
02662 throw new MWException(
02663 "Utf8Case.ser is missing, please run \"make\" in the serialized directory\n" );
02664 }
02665 extract( $arr );
02666 wfProfileOut( __METHOD__ );
02667 return array( $wikiUpperChars, $wikiLowerChars );
02668 }
02669
02670 function formatTimePeriod( $seconds ) {
02671 if ( $seconds < 10 ) {
02672 return $this->formatNum( sprintf( "%.1f", $seconds ) ) . wfMsg( 'seconds-abbrev' );
02673 } elseif ( $seconds < 60 ) {
02674 return $this->formatNum( round( $seconds ) ) . wfMsg( 'seconds-abbrev' );
02675 } elseif ( $seconds < 3600 ) {
02676 return $this->formatNum( floor( $seconds / 60 ) ) . wfMsg( 'minutes-abbrev' ) .
02677 $this->formatNum( round( fmod( $seconds, 60 ) ) ) . wfMsg( 'seconds-abbrev' );
02678 } else {
02679 $hours = floor( $seconds / 3600 );
02680 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
02681 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
02682 return $this->formatNum( $hours ) . wfMsg( 'hours-abbrev' ) .
02683 $this->formatNum( $minutes ) . wfMsg( 'minutes-abbrev' ) .
02684 $this->formatNum( $secondsPart ) . wfMsg( 'seconds-abbrev' );
02685 }
02686 }
02687
02688 function formatBitrate( $bps ) {
02689 $units = array( 'bps', 'kbps', 'Mbps', 'Gbps' );
02690 if ( $bps <= 0 ) {
02691 return $this->formatNum( $bps ) . $units[0];
02692 }
02693 $unitIndex = floor( log10( $bps ) / 3 );
02694 $mantissa = $bps / pow( 1000, $unitIndex );
02695 if ( $mantissa < 10 ) {
02696 $mantissa = round( $mantissa, 1 );
02697 } else {
02698 $mantissa = round( $mantissa );
02699 }
02700 return $this->formatNum( $mantissa ) . $units[$unitIndex];
02701 }
02702
02710 function formatSize( $size ) {
02711
02712 $round = 0;
02713 if( $size > 1024 ) {
02714 $size = $size / 1024;
02715 if( $size > 1024 ) {
02716 $size = $size / 1024;
02717
02718 $round = 2;
02719 if( $size > 1024 ) {
02720 $size = $size / 1024;
02721 $msg = 'size-gigabytes';
02722 } else {
02723 $msg = 'size-megabytes';
02724 }
02725 } else {
02726 $msg = 'size-kilobytes';
02727 }
02728 } else {
02729 $msg = 'size-bytes';
02730 }
02731 $size = round( $size, $round );
02732 $text = $this->getMessageFromDB( $msg );
02733 return str_replace( '$1', $this->formatNum( $size ), $text );
02734 }
02735 }