00001 <?php
00010 define( 'MSG_LOAD_TIMEOUT', 60);
00011 define( 'MSG_LOCK_TIMEOUT', 10);
00012 define( 'MSG_WAIT_TIMEOUT', 10);
00013 define( 'MSG_CACHE_VERSION', 1 );
00014
00020 class MessageCache {
00021
00022 var $mCache;
00023
00024 var $mUseCache, $mDisable, $mExpiry;
00025 var $mKeys, $mParserOptions, $mParser;
00026 var $mExtensionMessages = array();
00027 var $mInitialised = false;
00028 var $mAllMessagesLoaded = array();
00029
00030
00031 var $mLoadedLanguages = array();
00032
00033 function __construct( &$memCached, $useDB, $expiry, $memcPrefix ) {
00034 $this->mUseCache = !is_null( $memCached );
00035 $this->mMemc = &$memCached;
00036 $this->mDisable = !$useDB;
00037 $this->mExpiry = $expiry;
00038 $this->mDisableTransform = false;
00039 $this->mKeys = false; # initialised on demand
00040 $this->mInitialised = true;
00041 $this->mParser = null;
00042 }
00043
00044
00048 function getParserOptions() {
00049 if ( !$this->mParserOptions ) {
00050 $this->mParserOptions = new ParserOptions;
00051 }
00052 return $this->mParserOptions;
00053 }
00054
00064 function loadFromLocal( $hash, $code ) {
00065 global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
00066
00067 $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code";
00068
00069 # Check file existence
00070 wfSuppressWarnings();
00071 $file = fopen( $filename, 'r' );
00072 wfRestoreWarnings();
00073 if ( !$file ) {
00074 return false;
00075 }
00076
00077 if ( $wgLocalMessageCacheSerialized ) {
00078
00079 $localHash = fread( $file, 32 );
00080 if ( $hash === $localHash ) {
00081
00082 $serialized = '';
00083 while ( !feof( $file ) ) {
00084 $serialized .= fread( $file, 100000 );
00085 }
00086 fclose( $file );
00087 return $this->setCache( unserialize( $serialized ), $code );
00088 } else {
00089 fclose( $file );
00090 return false;
00091 }
00092 } else {
00093 $localHash=substr(fread($file,40),8);
00094 fclose($file);
00095 if ($hash!=$localHash) {
00096 return false;
00097 }
00098
00099 # Require overwrites the member variable or just shadows it?
00100 require( $filename );
00101 return $this->setCache( $this->mCache, $code );
00102 }
00103 }
00104
00108 function saveToLocal( $serialized, $hash, $code ) {
00109 global $wgLocalMessageCache;
00110
00111 $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code";
00112 wfMkdirParents( $wgLocalMessageCache );
00113
00114 wfSuppressWarnings();
00115 $file = fopen( $filename, 'w' );
00116 wfRestoreWarnings();
00117
00118 if ( !$file ) {
00119 wfDebug( "Unable to open local cache file for writing\n" );
00120 return;
00121 }
00122
00123 fwrite( $file, $hash . $serialized );
00124 fclose( $file );
00125 @chmod( $filename, 0666 );
00126 }
00127
00128 function saveToScript( $array, $hash, $code ) {
00129 global $wgLocalMessageCache;
00130
00131 $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code";
00132 $tempFilename = $filename . '.tmp';
00133 wfMkdirParents( $wgLocalMessageCache );
00134
00135 wfSuppressWarnings();
00136 $file = fopen( $tempFilename, 'w');
00137 wfRestoreWarnings();
00138
00139 if ( !$file ) {
00140 wfDebug( "Unable to open local cache file for writing\n" );
00141 return;
00142 }
00143
00144 fwrite($file,"<?php\n//$hash\n\n \$this->mCache = array(");
00145
00146 foreach ($array as $key => $message) {
00147 $key = $this->escapeForScript($key);
00148 $messages = $this->escapeForScript($message);
00149 fwrite($file, "'$key' => '$message',\n");
00150 }
00151
00152 fwrite($file,");\n?>");
00153 fclose($file);
00154 rename($tempFilename, $filename);
00155 }
00156
00157 function escapeForScript($string) {
00158 $string = str_replace( '\\', '\\\\', $string );
00159 $string = str_replace( '\'', '\\\'', $string );
00160 return $string;
00161 }
00162
00166 function setCache( $cache, $code ) {
00167 if ( isset( $cache['VERSION'] ) && $cache['VERSION'] == MSG_CACHE_VERSION ) {
00168 $this->mCache[$code] = $cache;
00169 return true;
00170 } else {
00171 return false;
00172 }
00173 }
00174
00193 function load( $code = false ) {
00194 global $wgLocalMessageCache;
00195
00196 if ( !$this->mUseCache ) {
00197 return true;
00198 }
00199
00200 if( !is_string( $code ) ) {
00201 # This isn't really nice, so at least make a note about it and try to
00202 # fall back
00203 wfDebug( __METHOD__ . " called without providing a language code\n" );
00204 $code = 'en';
00205 }
00206
00207 # Don't do double loading...
00208 if ( isset($this->mLoadedLanguages[$code]) ) return true;
00209
00210 # 8 lines of code just to say (once) that message cache is disabled
00211 if ( $this->mDisable ) {
00212 static $shownDisabled = false;
00213 if ( !$shownDisabled ) {
00214 wfDebug( __METHOD__ . ": disabled\n" );
00215 $shownDisabled = true;
00216 }
00217 return true;
00218 }
00219
00220 # Loading code starts
00221 wfProfileIn( __METHOD__ );
00222 $success = false; # Keep track of success
00223 $where = array(); # Debug info, delayed to avoid spamming debug log too much
00224 $cacheKey = wfMemcKey( 'messages', $code ); # Key in memc for messages
00225
00226
00227 # (1) local cache
00228 # Hash of the contents is stored in memcache, to detect if local cache goes
00229 # out of date (due to update in other thread?)
00230 if ( $wgLocalMessageCache !== false ) {
00231 wfProfileIn( __METHOD__ . '-fromlocal' );
00232
00233 $hash = $this->mMemc->get( wfMemcKey( 'messages', $code, 'hash' ) );
00234 if ( $hash ) {
00235 $success = $this->loadFromLocal( $hash, $code );
00236 if ( $success ) $where[] = 'got from local cache';
00237 }
00238 wfProfileOut( __METHOD__ . '-fromlocal' );
00239 }
00240
00241 # (2) memcache
00242 # Fails if nothing in cache, or in the wrong version.
00243 if ( !$success ) {
00244 wfProfileIn( __METHOD__ . '-fromcache' );
00245 $cache = $this->mMemc->get( $cacheKey );
00246 $success = $this->setCache( $cache, $code );
00247 if ( $success ) {
00248 $where[] = 'got from global cache';
00249 $this->saveToCaches( $cache, false, $code );
00250 }
00251 wfProfileOut( __METHOD__ . '-fromcache' );
00252 }
00253
00254
00255 # (3)
00256 # Nothing in caches... so we need create one and store it in caches
00257 if ( !$success ) {
00258 $where[] = 'cache is empty';
00259 $where[] = 'loading from database';
00260
00261 $this->lock($cacheKey);
00262
00263 # Limit the concurrency of loadFromDB to a single process
00264 # This prevents the site from going down when the cache expires
00265 $statusKey = wfMemcKey( 'messages', $code, 'status' );
00266 $success = $this->mMemc->add( $statusKey, 'loading', MSG_LOAD_TIMEOUT );
00267 if ( $success ) {
00268 $cache = $this->loadFromDB( $code );
00269 $success = $this->setCache( $cache, $code );
00270 }
00271 if ( $success ) {
00272 $success = $this->saveToCaches( $cache, true, $code );
00273 if ( $success ) {
00274 $this->mMemc->delete( $statusKey );
00275 } else {
00276 $this->mMemc->set( $statusKey, 'error', 60*5 );
00277 wfDebug( "MemCached set error in MessageCache: restart memcached server!\n" );
00278 }
00279 }
00280 $this->unlock($cacheKey);
00281 }
00282
00283 if ( !$success ) {
00284 # Bad luck... this should not happen
00285 $where[] = 'loading FAILED - cache is disabled';
00286 $info = implode( ', ', $where );
00287 wfDebug( __METHOD__ . ": Loading $code... $info\n" );
00288 $this->mDisable = true;
00289 $this->mCache = false;
00290 } else {
00291 # All good, just record the success
00292 $info = implode( ', ', $where );
00293 wfDebug( __METHOD__ . ": Loading $code... $info\n" );
00294 $this->mLoadedLanguages[$code] = true;
00295 }
00296 wfProfileOut( __METHOD__ );
00297 return $success;
00298 }
00299
00308 function loadFromDB( $code = false ) {
00309 wfProfileIn( __METHOD__ );
00310 global $wgMaxMsgCacheEntrySize, $wgContLanguageCode;
00311 $dbr = wfGetDB( DB_SLAVE );
00312 $cache = array();
00313
00314 # Common conditions
00315 $conds = array(
00316 'page_is_redirect' => 0,
00317 'page_namespace' => NS_MEDIAWIKI,
00318 );
00319
00320 if ( $code ) {
00321 # Is this fast enough. Should not matter if the filtering is done in the
00322 # database or in code.
00323 if ( $code !== $wgContLanguageCode ) {
00324 # Messages for particular language
00325 $escapedCode = $dbr->escapeLike( $code );
00326 $conds[] = "page_title like '%%/$escapedCode'";
00327 } else {
00328 # Effectively disallows use of '/' character in NS_MEDIAWIKI for uses
00329 # other than language code.
00330 $conds[] = "page_title not like '%%/%%'";
00331 }
00332 }
00333
00334 # Conditions to fetch oversized pages to ignore them
00335 $bigConds = $conds;
00336 $bigConds[] = 'page_len > ' . intval( $wgMaxMsgCacheEntrySize );
00337
00338 # Load titles for all oversized pages in the MediaWiki namespace
00339 $res = $dbr->select( 'page', 'page_title', $bigConds, __METHOD__ );
00340 while ( $row = $dbr->fetchObject( $res ) ) {
00341 $cache[$row->page_title] = '!TOO BIG';
00342 }
00343 $dbr->freeResult( $res );
00344
00345 # Conditions to load the remaining pages with their contents
00346 $smallConds = $conds;
00347 $smallConds[] = 'page_latest=rev_id';
00348 $smallConds[] = 'rev_text_id=old_id';
00349 $smallConds[] = 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize );
00350
00351 $res = $dbr->select( array( 'page', 'revision', 'text' ),
00352 array( 'page_title', 'old_text', 'old_flags' ),
00353 $smallConds, __METHOD__ );
00354
00355 for ( $row = $dbr->fetchObject( $res ); $row; $row = $dbr->fetchObject( $res ) ) {
00356 $cache[$row->page_title] = ' ' . Revision::getRevisionText( $row );
00357 }
00358 $dbr->freeResult( $res );
00359
00360 $cache['VERSION'] = MSG_CACHE_VERSION;
00361 wfProfileOut( __METHOD__ );
00362 return $cache;
00363 }
00364
00371 public function replace( $title, $text ) {
00372 global $wgMaxMsgCacheEntrySize;
00373 wfProfileIn( __METHOD__ );
00374
00375
00376 list( , $code ) = $this->figureMessage( $title );
00377
00378 $cacheKey = wfMemcKey( 'messages', $code );
00379 $this->load($code);
00380 $this->lock($cacheKey);
00381
00382 if ( is_array($this->mCache[$code]) ) {
00383 $titleKey = wfMemcKey( 'messages', 'individual', $title );
00384
00385 if ( $text === false ) {
00386 # Article was deleted
00387 unset( $this->mCache[$code][$title] );
00388 $this->mMemc->delete( $titleKey );
00389
00390 } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
00391 # Check for size
00392 $this->mCache[$code][$title] = '!TOO BIG';
00393 $this->mMemc->set( $titleKey, ' ' . $text, $this->mExpiry );
00394
00395 } else {
00396 $this->mCache[$code][$title] = ' ' . $text;
00397 $this->mMemc->delete( $titleKey );
00398 }
00399
00400 # Update caches
00401 $this->saveToCaches( $this->mCache[$code], true, $code );
00402 }
00403 $this->unlock($cacheKey);
00404
00405
00406 global $parserMemc;
00407 $sidebarKey = wfMemcKey( 'sidebar', $code );
00408 $parserMemc->delete( $sidebarKey );
00409
00410 wfProfileOut( __METHOD__ );
00411 }
00412
00422 protected function saveToCaches( $cache, $memc = true, $code = false ) {
00423 wfProfileIn( __METHOD__ );
00424 global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
00425
00426 $cacheKey = wfMemcKey( 'messages', $code );
00427
00428 $i = 0;
00429 if ( $memc ) {
00430 # Save in memcached
00431 # Keep trying if it fails, this is kind of important
00432
00433 for ($i=0; $i<20 &&
00434 !$this->mMemc->set( $cacheKey, $cache, $this->mExpiry );
00435 $i++ ) {
00436 usleep(mt_rand(500000,1500000));
00437 }
00438 }
00439
00440 # Save to local cache
00441 if ( $wgLocalMessageCache !== false ) {
00442 $serialized = serialize( $cache );
00443 $hash = md5( $serialized );
00444 $this->mMemc->set( wfMemcKey( 'messages', $code, 'hash' ), $hash, $this->mExpiry );
00445 if ($wgLocalMessageCacheSerialized) {
00446 $this->saveToLocal( $serialized, $hash, $code );
00447 } else {
00448 $this->saveToScript( $cache, $hash, $code );
00449 }
00450 }
00451
00452 if ( $i == 20 ) {
00453 $success = false;
00454 } else {
00455 $success = true;
00456 }
00457 wfProfileOut( __METHOD__ );
00458 return $success;
00459 }
00460
00465 function lock($key) {
00466 if ( !$this->mUseCache ) {
00467 return true;
00468 }
00469
00470 $lockKey = $key . ':lock';
00471 for ($i=0; $i < MSG_WAIT_TIMEOUT && !$this->mMemc->add( $lockKey, 1, MSG_LOCK_TIMEOUT ); $i++ ) {
00472 sleep(1);
00473 }
00474
00475 return $i >= MSG_WAIT_TIMEOUT;
00476 }
00477
00478 function unlock($key) {
00479 if ( !$this->mUseCache ) {
00480 return;
00481 }
00482
00483 $lockKey = $key . ':lock';
00484 $this->mMemc->delete( $lockKey );
00485 }
00486
00503 function get( $key, $useDB = true, $langcode = true, $isFullKey = false ) {
00504 global $wgContLanguageCode, $wgContLang;
00505
00506 $lang = wfGetLangObj( $langcode );
00507 $langcode = $lang->getCode();
00508
00509 # If uninitialised, someone is trying to call this halfway through Setup.php
00510 if( !$this->mInitialised ) {
00511 return '<' . htmlspecialchars($key) . '>';
00512 }
00513
00514 $message = false;
00515
00516 # Normalise title-case input
00517 $lckey = $wgContLang->lcfirst( $key );
00518 $lckey = str_replace( ' ', '_', $lckey );
00519
00520 # Try the MediaWiki namespace
00521 if( !$this->mDisable && $useDB ) {
00522 $title = $wgContLang->ucfirst( $lckey );
00523 if(!$isFullKey && ($langcode != $wgContLanguageCode) ) {
00524 $title .= '/' . $langcode;
00525 }
00526 $message = $this->getMsgFromNamespace( $title, $langcode );
00527 }
00528
00529 # Try the extension array
00530 if ( $message === false && isset( $this->mExtensionMessages[$langcode][$lckey] ) ) {
00531 $message = $this->mExtensionMessages[$langcode][$lckey];
00532 }
00533 if ( $message === false && isset( $this->mExtensionMessages['en'][$lckey] ) ) {
00534 $message = $this->mExtensionMessages['en'][$lckey];
00535 }
00536
00537 # Try the array in the language object
00538 if ( $message === false ) {
00539 $message = $lang->getMessage( $lckey );
00540 if ( is_null( $message ) ) {
00541 $message = false;
00542 }
00543 }
00544
00545 # Try the array of another language
00546 $pos = strrpos( $lckey, '/' );
00547 if( $message === false && $pos !== false) {
00548 $mkey = substr( $lckey, 0, $pos );
00549 $code = substr( $lckey, $pos+1 );
00550 if ( $code ) {
00551 # We may get calls for things that are http-urls from sidebar
00552 # Let's not load nonexistent languages for those
00553 $validCodes = array_keys( Language::getLanguageNames() );
00554 if ( in_array( $code, $validCodes ) ) {
00555 $message = Language::getMessageFor( $mkey, $code );
00556 if ( is_null( $message ) ) {
00557 $message = false;
00558 }
00559 }
00560 }
00561 }
00562
00563 # Is this a custom message? Try the default language in the db...
00564 if( ($message === false || $message === '-' ) &&
00565 !$this->mDisable && $useDB &&
00566 !$isFullKey && ($langcode != $wgContLanguageCode) ) {
00567 $message = $this->getMsgFromNamespace( $wgContLang->ucfirst( $lckey ), $wgContLanguageCode );
00568 }
00569
00570 # Final fallback
00571 if( $message === false ) {
00572 return '<' . htmlspecialchars($key) . '>';
00573 }
00574 return $message;
00575 }
00576
00584 function getMsgFromNamespace( $title, $code ) {
00585 $type = false;
00586 $message = false;
00587
00588 if ( $this->mUseCache ) {
00589 $this->load( $code );
00590 if (isset( $this->mCache[$code][$title] ) ) {
00591 $entry = $this->mCache[$code][$title];
00592 $type = substr( $entry, 0, 1 );
00593 if ( $type == ' ' ) {
00594 return substr( $entry, 1 );
00595 }
00596 }
00597 }
00598
00599 # Call message hooks, in case they are defined
00600 wfRunHooks('MessagesPreLoad', array( $title, &$message ) );
00601 if ( $message !== false ) {
00602 return $message;
00603 }
00604
00605 # If there is no cache entry and no placeholder, it doesn't exist
00606 if ( $type !== '!' ) {
00607 return false;
00608 }
00609
00610 $titleKey = wfMemcKey( 'messages', 'individual', $title );
00611
00612 # Try the individual message cache
00613 if ( $this->mUseCache ) {
00614 $entry = $this->mMemc->get( $titleKey );
00615 if ( $entry ) {
00616 $type = substr( $entry, 0, 1 );
00617
00618 if ( $type === ' ' ) {
00619 # Ok!
00620 $message = substr( $entry, 1 );
00621 $this->mCache[$code][$title] = $entry;
00622 return $message;
00623 } elseif ( $entry === '!NONEXISTENT' ) {
00624 return false;
00625 } else {
00626 # Corrupt/obsolete entry, delete it
00627 $this->mMemc->delete( $titleKey );
00628 }
00629
00630 }
00631 }
00632
00633 # Try loading it from the DB
00634 $revision = Revision::newFromTitle( Title::makeTitle( NS_MEDIAWIKI, $title ) );
00635 if( $revision ) {
00636 $message = $revision->getText();
00637 if ($this->mUseCache) {
00638 $this->mCache[$code][$title] = ' ' . $message;
00639 $this->mMemc->set( $titleKey, $message, $this->mExpiry );
00640 }
00641 } else {
00642 # Negative caching
00643 # Use some special text instead of false, because false gets converted to '' somewhere
00644 $this->mMemc->set( $titleKey, '!NONEXISTENT', $this->mExpiry );
00645 $this->mCache[$code][$title] = false;
00646 }
00647 return $message;
00648 }
00649
00650 function transform( $message, $interface = false, $language = null ) {
00651
00652 if( strpos( $message, '{{' ) === false ) {
00653 return $message;
00654 }
00655
00656 global $wgParser, $wgParserConf;
00657 if ( !$this->mParser && isset( $wgParser ) ) {
00658 # Do some initialisation so that we don't have to do it twice
00659 $wgParser->firstCallInit();
00660 # Clone it and store it
00661 $class = $wgParserConf['class'];
00662 if ( $class == 'Parser_DiffTest' ) {
00663 # Uncloneable
00664 $this->mParser = new $class( $wgParserConf );
00665 } else {
00666 $this->mParser = clone $wgParser;
00667 }
00668 #wfDebug( __METHOD__ . ": following contents triggered transform: $message\n" );
00669 }
00670 if ( $this->mParser ) {
00671 $popts = $this->getParserOptions();
00672 $popts->setInterfaceMessage( $interface );
00673 $popts->setTargetLanguage( $language );
00674 $message = $this->mParser->transformMsg( $message, $popts );
00675 }
00676 return $message;
00677 }
00678
00679 function disable() { $this->mDisable = true; }
00680 function enable() { $this->mDisable = false; }
00681
00683 function disableTransform(){
00684 wfDeprecated( __METHOD__ );
00685 }
00686 function enableTransform() {
00687 wfDeprecated( __METHOD__ );
00688 }
00689 function setTransform( $x ) {
00690 wfDeprecated( __METHOD__ );
00691 }
00692 function getTransform() {
00693 wfDeprecated( __METHOD__ );
00694 return false;
00695 }
00696
00704 function addMessage( $key, $value, $lang = 'en' ) {
00705 global $wgContLang;
00706 # Normalise title-case input
00707 $lckey = str_replace( ' ', '_', $wgContLang->lcfirst( $key ) );
00708 $this->mExtensionMessages[$lang][$lckey] = $value;
00709 }
00710
00717 function addMessages( $messages, $lang = 'en' ) {
00718 wfProfileIn( __METHOD__ );
00719 if ( !is_array( $messages ) ) {
00720 throw new MWException( __METHOD__.': Invalid message array' );
00721 }
00722 if ( isset( $this->mExtensionMessages[$lang] ) ) {
00723 $this->mExtensionMessages[$lang] = $messages + $this->mExtensionMessages[$lang];
00724 } else {
00725 $this->mExtensionMessages[$lang] = $messages;
00726 }
00727 wfProfileOut( __METHOD__ );
00728 }
00729
00735 function addMessagesByLang( $messages ) {
00736 wfProfileIn( __METHOD__ );
00737 foreach ( $messages as $key => $value ) {
00738 $this->addMessages( $value, $key );
00739 }
00740 wfProfileOut( __METHOD__ );
00741 }
00742
00749 function getExtensionMessagesFor( $lang = 'en' ) {
00750 wfProfileIn( __METHOD__ );
00751 $messages = array();
00752 if ( isset( $this->mExtensionMessages[$lang] ) ) {
00753 $messages = $this->mExtensionMessages[$lang];
00754 }
00755 if ( $lang != 'en' ) {
00756 $messages = $messages + $this->mExtensionMessages['en'];
00757 }
00758 wfProfileOut( __METHOD__ );
00759 return $messages;
00760 }
00761
00765 function clear() {
00766 if( $this->mUseCache ) {
00767 $langs = Language::getLanguageNames( false );
00768 foreach ( array_keys($langs) as $code ) {
00769 # Global cache
00770 $this->mMemc->delete( wfMemcKey( 'messages', $code ) );
00771 # Invalidate all local caches
00772 $this->mMemc->delete( wfMemcKey( 'messages', $code, 'hash' ) );
00773 }
00774 }
00775 }
00776
00777 function loadAllMessages( $lang = false ) {
00778 global $wgExtensionMessagesFiles;
00779 $key = $lang === false ? '*' : $lang;
00780 if ( isset( $this->mAllMessagesLoaded[$key] ) ) {
00781 return;
00782 }
00783 $this->mAllMessagesLoaded[$key] = true;
00784
00785 # Some extensions will load their messages when you load their class file
00786 wfLoadAllExtensions();
00787 # Others will respond to this hook
00788 wfRunHooks( 'LoadAllMessages' );
00789 # Some register their messages in $wgExtensionMessagesFiles
00790 foreach ( $wgExtensionMessagesFiles as $name => $file ) {
00791 wfLoadExtensionMessages( $name, $lang );
00792 }
00793 # Still others will respond to neither, they are EVIL. We sometimes need to know!
00794 }
00795
00804 function loadMessagesFile( $filename, $langcode = false ) {
00805 global $wgLang, $wgContLang;
00806 wfProfileIn( __METHOD__ );
00807 $messages = $magicWords = false;
00808 require( $filename );
00809
00810 $validCodes = Language::getLanguageNames();
00811 if( is_string( $langcode ) && array_key_exists( $langcode, $validCodes ) ) {
00812 # Load messages for given language code.
00813 $this->processMessagesArray( $messages, $langcode );
00814 } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $validCodes ) ) {
00815 wfDebug( "Invalid language '$langcode' code passed to MessageCache::loadMessagesFile()" );
00816 } else {
00817 # Load only languages that are usually used, and merge all
00818 # fallbacks, except English.
00819 $langs = array_unique( array( 'en', $wgContLang->getCode(), $wgLang->getCode() ) );
00820 foreach( $langs as $code ) {
00821 $this->processMessagesArray( $messages, $code );
00822 }
00823 }
00824
00825 if ( $magicWords !== false ) {
00826 global $wgContLang;
00827 $wgContLang->addMagicWordsByLang( $magicWords );
00828 }
00829 wfProfileOut( __METHOD__ );
00830 }
00831
00838 function processMessagesArray( $messages, $langcode ) {
00839 wfProfileIn( __METHOD__ );
00840 $fallbackCode = $langcode;
00841 $mergedMessages = array();
00842 do {
00843 if ( isset($messages[$fallbackCode]) ) {
00844 $mergedMessages += $messages[$fallbackCode];
00845 }
00846 $fallbackCode = Language::getFallbackfor( $fallbackCode );
00847 } while( $fallbackCode && $fallbackCode !== 'en' );
00848
00849 if ( !empty($mergedMessages) )
00850 $this->addMessages( $mergedMessages, $langcode );
00851 wfProfileOut( __METHOD__ );
00852 }
00853
00854 public function figureMessage( $key ) {
00855 global $wgContLanguageCode;
00856 $pieces = explode( '/', $key );
00857 if( count( $pieces ) < 2 )
00858 return array( $key, $wgContLanguageCode );
00859
00860 $lang = array_pop( $pieces );
00861 $validCodes = Language::getLanguageNames();
00862 if( !array_key_exists( $lang, $validCodes ) )
00863 return array( $key, $wgContLanguageCode );
00864
00865 $message = implode( '/', $pieces );
00866 return array( $message, $lang );
00867 }
00868
00869 }