MediaWiki
REL1_24
|
00001 <?php 00027 class Languages { 00029 protected $mLanguages; 00030 00032 protected $mRawMessages; 00033 00035 protected $mMessages; 00036 00038 protected $mFallback; 00039 00041 protected $mGeneralMessages; 00042 00044 protected $mIgnoredMessages; 00045 00047 protected $mOptionalMessages; 00048 00050 protected $mNamespaceNames; 00051 00053 protected $mNamespaceAliases; 00054 00056 protected $mMagicWords; 00057 00059 protected $mSpecialPageAliases; 00060 00065 function __construct() { 00066 wfRunHooks( 'LocalisationIgnoredOptionalMessages', 00067 array( &$this->mIgnoredMessages, &$this->mOptionalMessages ) ); 00068 00069 $this->mLanguages = array_keys( Language::fetchLanguageNames( null, 'mwfile' ) ); 00070 sort( $this->mLanguages ); 00071 } 00072 00078 public function getLanguages() { 00079 return $this->mLanguages; 00080 } 00081 00087 public function getIgnoredMessages() { 00088 return $this->mIgnoredMessages; 00089 } 00090 00096 public function getOptionalMessages() { 00097 return $this->mOptionalMessages; 00098 } 00099 00105 protected function loadFile( $code ) { 00106 if ( isset( $this->mRawMessages[$code] ) && 00107 isset( $this->mFallback[$code] ) && 00108 isset( $this->mNamespaceNames[$code] ) && 00109 isset( $this->mNamespaceAliases[$code] ) && 00110 isset( $this->mMagicWords[$code] ) && 00111 isset( $this->mSpecialPageAliases[$code] ) 00112 ) { 00113 return; 00114 } 00115 $this->mRawMessages[$code] = array(); 00116 $this->mFallback[$code] = ''; 00117 $this->mNamespaceNames[$code] = array(); 00118 $this->mNamespaceAliases[$code] = array(); 00119 $this->mMagicWords[$code] = array(); 00120 $this->mSpecialPageAliases[$code] = array(); 00121 00122 $jsonfilename = Language::getJsonMessagesFileName( $code ); 00123 if ( file_exists( $jsonfilename ) ) { 00124 $json = Language::getLocalisationCache()->readJSONFile( $jsonfilename ); 00125 $this->mRawMessages[$code] = $json['messages']; 00126 } 00127 00128 $filename = Language::getMessagesFileName( $code ); 00129 if ( file_exists( $filename ) ) { 00130 require $filename; 00131 if ( isset( $fallback ) ) { 00132 $this->mFallback[$code] = $fallback; 00133 } 00134 if ( isset( $namespaceNames ) ) { 00135 $this->mNamespaceNames[$code] = $namespaceNames; 00136 } 00137 if ( isset( $namespaceAliases ) ) { 00138 $this->mNamespaceAliases[$code] = $namespaceAliases; 00139 } 00140 if ( isset( $magicWords ) ) { 00141 $this->mMagicWords[$code] = $magicWords; 00142 } 00143 if ( isset( $specialPageAliases ) ) { 00144 $this->mSpecialPageAliases[$code] = $specialPageAliases; 00145 } 00146 } 00147 } 00148 00163 private function loadMessages( $code ) { 00164 if ( isset( $this->mMessages[$code] ) ) { 00165 return; 00166 } 00167 $this->loadFile( $code ); 00168 $this->loadGeneralMessages(); 00169 $this->mMessages[$code]['all'] = $this->mRawMessages[$code]; 00170 $this->mMessages[$code]['required'] = array(); 00171 $this->mMessages[$code]['optional'] = array(); 00172 $this->mMessages[$code]['obsolete'] = array(); 00173 $this->mMessages[$code]['translated'] = array(); 00174 foreach ( $this->mMessages[$code]['all'] as $key => $value ) { 00175 if ( isset( $this->mGeneralMessages['required'][$key] ) ) { 00176 $this->mMessages[$code]['required'][$key] = $value; 00177 $this->mMessages[$code]['translated'][$key] = $value; 00178 } elseif ( isset( $this->mGeneralMessages['optional'][$key] ) ) { 00179 $this->mMessages[$code]['optional'][$key] = $value; 00180 $this->mMessages[$code]['translated'][$key] = $value; 00181 } else { 00182 $this->mMessages[$code]['obsolete'][$key] = $value; 00183 } 00184 } 00185 } 00186 00198 private function loadGeneralMessages() { 00199 if ( isset( $this->mGeneralMessages ) ) { 00200 return; 00201 } 00202 $this->loadFile( 'en' ); 00203 $this->mGeneralMessages['all'] = $this->mRawMessages['en']; 00204 $this->mGeneralMessages['required'] = array(); 00205 $this->mGeneralMessages['optional'] = array(); 00206 $this->mGeneralMessages['ignored'] = array(); 00207 $this->mGeneralMessages['translatable'] = array(); 00208 foreach ( $this->mGeneralMessages['all'] as $key => $value ) { 00209 if ( in_array( $key, $this->mIgnoredMessages ) ) { 00210 $this->mGeneralMessages['ignored'][$key] = $value; 00211 } elseif ( in_array( $key, $this->mOptionalMessages ) ) { 00212 $this->mGeneralMessages['optional'][$key] = $value; 00213 $this->mGeneralMessages['translatable'][$key] = $value; 00214 } else { 00215 $this->mGeneralMessages['required'][$key] = $value; 00216 $this->mGeneralMessages['translatable'][$key] = $value; 00217 } 00218 } 00219 } 00220 00237 public function getMessages( $code ) { 00238 $this->loadMessages( $code ); 00239 00240 return $this->mMessages[$code]; 00241 } 00242 00256 public function getGeneralMessages() { 00257 $this->loadGeneralMessages(); 00258 00259 return $this->mGeneralMessages; 00260 } 00261 00269 public function getFallback( $code ) { 00270 $this->loadFile( $code ); 00271 00272 return $this->mFallback[$code]; 00273 } 00274 00282 public function getNamespaceNames( $code ) { 00283 $this->loadFile( $code ); 00284 00285 return $this->mNamespaceNames[$code]; 00286 } 00287 00295 public function getNamespaceAliases( $code ) { 00296 $this->loadFile( $code ); 00297 00298 return $this->mNamespaceAliases[$code]; 00299 } 00300 00308 public function getMagicWords( $code ) { 00309 $this->loadFile( $code ); 00310 00311 return $this->mMagicWords[$code]; 00312 } 00313 00321 public function getSpecialPageAliases( $code ) { 00322 $this->loadFile( $code ); 00323 00324 return $this->mSpecialPageAliases[$code]; 00325 } 00326 00334 public function getUntranslatedMessages( $code ) { 00335 $this->loadGeneralMessages(); 00336 $this->loadMessages( $code ); 00337 00338 return array_diff_key( $this->mGeneralMessages['required'], $this->mMessages[$code]['required'] ); 00339 } 00340 00348 public function getDuplicateMessages( $code ) { 00349 $this->loadGeneralMessages(); 00350 $this->loadMessages( $code ); 00351 $duplicateMessages = array(); 00352 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00353 if ( $this->mGeneralMessages['translatable'][$key] == $value ) { 00354 $duplicateMessages[$key] = $value; 00355 } 00356 } 00357 00358 return $duplicateMessages; 00359 } 00360 00368 public function getObsoleteMessages( $code ) { 00369 $this->loadGeneralMessages(); 00370 $this->loadMessages( $code ); 00371 00372 return $this->mMessages[$code]['obsolete']; 00373 } 00374 00382 public function getMessagesWithMismatchVariables( $code ) { 00383 $this->loadGeneralMessages(); 00384 $this->loadMessages( $code ); 00385 $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' ); 00386 $mismatchMessages = array(); 00387 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00388 $missing = false; 00389 foreach ( $variables as $var ) { 00390 if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) && 00391 !preg_match( "/$var/sU", $value ) 00392 ) { 00393 $missing = true; 00394 } 00395 if ( !preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) && 00396 preg_match( "/$var/sU", $value ) 00397 ) { 00398 $missing = true; 00399 } 00400 } 00401 if ( $missing ) { 00402 $mismatchMessages[$key] = $value; 00403 } 00404 } 00405 00406 return $mismatchMessages; 00407 } 00408 00416 public function getMessagesWithoutPlural( $code ) { 00417 $this->loadGeneralMessages(); 00418 $this->loadMessages( $code ); 00419 $messagesWithoutPlural = array(); 00420 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00421 if ( stripos( $this->mGeneralMessages['translatable'][$key], '{{plural:' ) !== false && 00422 stripos( $value, '{{plural:' ) === false 00423 ) { 00424 $messagesWithoutPlural[$key] = $value; 00425 } 00426 } 00427 00428 return $messagesWithoutPlural; 00429 } 00430 00438 public function getEmptyMessages( $code ) { 00439 $this->loadGeneralMessages(); 00440 $this->loadMessages( $code ); 00441 $emptyMessages = array(); 00442 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00443 if ( $value === '' || $value === '-' ) { 00444 $emptyMessages[$key] = $value; 00445 } 00446 } 00447 00448 return $emptyMessages; 00449 } 00450 00458 public function getMessagesWithWhitespace( $code ) { 00459 $this->loadGeneralMessages(); 00460 $this->loadMessages( $code ); 00461 $messagesWithWhitespace = array(); 00462 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00463 if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) { 00464 $messagesWithWhitespace[$key] = $value; 00465 } 00466 } 00467 00468 return $messagesWithWhitespace; 00469 } 00470 00478 public function getNonXHTMLMessages( $code ) { 00479 $this->loadGeneralMessages(); 00480 $this->loadMessages( $code ); 00481 $wrongPhrases = array( 00482 '<hr *\\?>', 00483 '<br *\\?>', 00484 '<hr/>', 00485 '<br/>', 00486 '<hr>', 00487 '<br>', 00488 ); 00489 $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu'; 00490 $nonXHTMLMessages = array(); 00491 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00492 if ( preg_match( $wrongPhrases, $value ) ) { 00493 $nonXHTMLMessages[$key] = $value; 00494 } 00495 } 00496 00497 return $nonXHTMLMessages; 00498 } 00499 00507 public function getMessagesWithWrongChars( $code ) { 00508 $this->loadGeneralMessages(); 00509 $this->loadMessages( $code ); 00510 $wrongChars = array( 00511 '[LRM]' => "\xE2\x80\x8E", 00512 '[RLM]' => "\xE2\x80\x8F", 00513 '[LRE]' => "\xE2\x80\xAA", 00514 '[RLE]' => "\xE2\x80\xAB", 00515 '[POP]' => "\xE2\x80\xAC", 00516 '[LRO]' => "\xE2\x80\xAD", 00517 '[RLO]' => "\xE2\x80\xAB", 00518 '[ZWSP]' => "\xE2\x80\x8B", 00519 '[NBSP]' => "\xC2\xA0", 00520 '[WJ]' => "\xE2\x81\xA0", 00521 '[BOM]' => "\xEF\xBB\xBF", 00522 '[FFFD]' => "\xEF\xBF\xBD", 00523 ); 00524 $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu'; 00525 $wrongCharsMessages = array(); 00526 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00527 if ( preg_match( $wrongRegExp, $value ) ) { 00528 foreach ( $wrongChars as $viewableChar => $hiddenChar ) { 00529 $value = str_replace( $hiddenChar, $viewableChar, $value ); 00530 } 00531 $wrongCharsMessages[$key] = $value; 00532 } 00533 } 00534 00535 return $wrongCharsMessages; 00536 } 00537 00545 public function getMessagesWithDubiousLinks( $code ) { 00546 $this->loadGeneralMessages(); 00547 $this->loadMessages( $code ); 00548 $tc = Title::legalChars() . '#%{}'; 00549 $messages = array(); 00550 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00551 $matches = array(); 00552 preg_match_all( "/\[\[([{$tc}]+)(?:\\|(.+?))?]]/sDu", $value, $matches ); 00553 $numMatches = count( $matches[0] ); 00554 for ( $i = 0; $i < $numMatches; $i++ ) { 00555 if ( preg_match( "/.*project.*/isDu", $matches[1][$i] ) ) { 00556 $messages[$key][] = $matches[0][$i]; 00557 } 00558 } 00559 00560 if ( isset( $messages[$key] ) ) { 00561 $messages[$key] = implode( $messages[$key], ", " ); 00562 } 00563 } 00564 00565 return $messages; 00566 } 00567 00575 public function getMessagesWithUnbalanced( $code ) { 00576 $this->loadGeneralMessages(); 00577 $this->loadMessages( $code ); 00578 $messages = array(); 00579 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00580 $a = $b = $c = $d = 0; 00581 foreach ( preg_split( '//', $value ) as $char ) { 00582 switch ( $char ) { 00583 case '[': 00584 $a++; 00585 break; 00586 case ']': 00587 $b++; 00588 break; 00589 case '{': 00590 $c++; 00591 break; 00592 case '}': 00593 $d++; 00594 break; 00595 } 00596 } 00597 00598 if ( $a !== $b || $c !== $d ) { 00599 $messages[$key] = "$a, $b, $c, $d"; 00600 } 00601 } 00602 00603 return $messages; 00604 } 00605 00613 public function getUntranslatedNamespaces( $code ) { 00614 $this->loadFile( 'en' ); 00615 $this->loadFile( $code ); 00616 $namespacesDiff = array_diff_key( $this->mNamespaceNames['en'], $this->mNamespaceNames[$code] ); 00617 if ( isset( $namespacesDiff[NS_MAIN] ) ) { 00618 unset( $namespacesDiff[NS_MAIN] ); 00619 } 00620 00621 return $namespacesDiff; 00622 } 00623 00631 public function getProblematicProjectTalks( $code ) { 00632 $this->loadFile( $code ); 00633 $namespaces = array(); 00634 00635 # Check default namespace name 00636 if ( isset( $this->mNamespaceNames[$code][NS_PROJECT_TALK] ) ) { 00637 $default = $this->mNamespaceNames[$code][NS_PROJECT_TALK]; 00638 if ( strpos( $default, '$1' ) === false ) { 00639 $namespaces[$default] = 'default'; 00640 } 00641 } 00642 00643 # Check namespace aliases 00644 foreach ( $this->mNamespaceAliases[$code] as $key => $value ) { 00645 if ( $value == NS_PROJECT_TALK && strpos( $key, '$1' ) === false ) { 00646 $namespaces[$key] = ''; 00647 } 00648 } 00649 00650 return $namespaces; 00651 } 00652 00660 public function getUntranslatedMagicWords( $code ) { 00661 $this->loadFile( 'en' ); 00662 $this->loadFile( $code ); 00663 $magicWords = array(); 00664 foreach ( $this->mMagicWords['en'] as $key => $value ) { 00665 if ( !isset( $this->mMagicWords[$code][$key] ) ) { 00666 $magicWords[$key] = $value[1]; 00667 } 00668 } 00669 00670 return $magicWords; 00671 } 00672 00680 public function getObsoleteMagicWords( $code ) { 00681 $this->loadFile( 'en' ); 00682 $this->loadFile( $code ); 00683 $magicWords = array(); 00684 foreach ( $this->mMagicWords[$code] as $key => $value ) { 00685 if ( !isset( $this->mMagicWords['en'][$key] ) ) { 00686 $magicWords[$key] = $value[1]; 00687 } 00688 } 00689 00690 return $magicWords; 00691 } 00692 00700 public function getOverridingMagicWords( $code ) { 00701 $this->loadFile( 'en' ); 00702 $this->loadFile( $code ); 00703 $magicWords = array(); 00704 foreach ( $this->mMagicWords[$code] as $key => $local ) { 00705 if ( !isset( $this->mMagicWords['en'][$key] ) ) { 00706 # Unrecognized magic word 00707 continue; 00708 } 00709 $en = $this->mMagicWords['en'][$key]; 00710 array_shift( $local ); 00711 array_shift( $en ); 00712 foreach ( $en as $word ) { 00713 if ( !in_array( $word, $local ) ) { 00714 $magicWords[$key] = $word; 00715 break; 00716 } 00717 } 00718 } 00719 00720 return $magicWords; 00721 } 00722 00730 public function getCaseMismatchMagicWords( $code ) { 00731 $this->loadFile( 'en' ); 00732 $this->loadFile( $code ); 00733 $magicWords = array(); 00734 foreach ( $this->mMagicWords[$code] as $key => $local ) { 00735 if ( !isset( $this->mMagicWords['en'][$key] ) ) { 00736 # Unrecognized magic word 00737 continue; 00738 } 00739 if ( $local[0] != $this->mMagicWords['en'][$key][0] ) { 00740 $magicWords[$key] = $local[0]; 00741 } 00742 } 00743 00744 return $magicWords; 00745 } 00746 00754 public function getUntraslatedSpecialPages( $code ) { 00755 $this->loadFile( 'en' ); 00756 $this->loadFile( $code ); 00757 $specialPageAliases = array(); 00758 foreach ( $this->mSpecialPageAliases['en'] as $key => $value ) { 00759 if ( !isset( $this->mSpecialPageAliases[$code][$key] ) ) { 00760 $specialPageAliases[$key] = $value[0]; 00761 } 00762 } 00763 00764 return $specialPageAliases; 00765 } 00766 00774 public function getObsoleteSpecialPages( $code ) { 00775 $this->loadFile( 'en' ); 00776 $this->loadFile( $code ); 00777 $specialPageAliases = array(); 00778 foreach ( $this->mSpecialPageAliases[$code] as $key => $value ) { 00779 if ( !isset( $this->mSpecialPageAliases['en'][$key] ) ) { 00780 $specialPageAliases[$key] = $value[0]; 00781 } 00782 } 00783 00784 return $specialPageAliases; 00785 } 00786 } 00787 00788 class ExtensionLanguages extends Languages { 00792 private $mMessageGroup; 00793 00798 function __construct( MessageGroup $group ) { 00799 $this->mMessageGroup = $group; 00800 00801 $this->mIgnoredMessages = $this->mMessageGroup->getIgnored(); 00802 $this->mOptionalMessages = $this->mMessageGroup->getOptional(); 00803 } 00804 00810 public function name() { 00811 return $this->mMessageGroup->getLabel(); 00812 } 00813 00819 protected function loadFile( $code ) { 00820 if ( !isset( $this->mRawMessages[$code] ) ) { 00821 $this->mRawMessages[$code] = $this->mMessageGroup->load( $code ); 00822 if ( empty( $this->mRawMessages[$code] ) ) { 00823 $this->mRawMessages[$code] = array(); 00824 } 00825 } 00826 } 00827 }