00001 <?php 00012 class languages { 00013 protected $mLanguages; # List of languages 00014 00015 protected $mRawMessages; # Raw list of the messages in each language 00016 protected $mMessages; # Messages in each language (except for English), divided to groups 00017 protected $mFallback; # Fallback language in each language 00018 protected $mGeneralMessages; # General messages in English, divided to groups 00019 protected $mIgnoredMessages; # All the messages which should be exist only in the English file 00020 protected $mOptionalMessages; # All the messages which may be translated or not, depending on the language 00021 00022 protected $mNamespaceNames; # Namespace names 00023 protected $mNamespaceAliases; # Namespace aliases 00024 protected $mMagicWords; # Magic words 00025 protected $mSpecialPageAliases; # Special page aliases 00026 00033 function __construct( $exif = true ) { 00034 require( dirname(__FILE__) . '/messageTypes.inc' ); 00035 $this->mIgnoredMessages = $wgIgnoredMessages; 00036 if ( $exif ) { 00037 $this->mOptionalMessages = array_merge( $wgOptionalMessages ); 00038 } else { 00039 $this->mOptionalMessages = array_merge( $wgOptionalMessages, $wgEXIFMessages ); 00040 } 00041 00042 $this->mLanguages = array_keys( Language::getLanguageNames( true ) ); 00043 sort( $this->mLanguages ); 00044 } 00045 00051 public function getLanguages() { 00052 return $this->mLanguages; 00053 } 00054 00060 public function getIgnoredMessages() { 00061 return $this->mIgnoredMessages; 00062 } 00063 00069 public function getOptionalMessages() { 00070 return $this->mOptionalMessages; 00071 } 00072 00078 protected function loadFile( $code ) { 00079 if ( isset( $this->mRawMessages[$code] ) && 00080 isset( $this->mFallback[$code] ) && 00081 isset( $this->mNamespaceNames[$code] ) && 00082 isset( $this->mNamespaceAliases[$code] ) && 00083 isset( $this->mMagicWords[$code] ) && 00084 isset( $this->mSpecialPageAliases[$code] ) ) { 00085 return; 00086 } 00087 $this->mRawMessages[$code] = array(); 00088 $this->mFallback[$code] = ''; 00089 $this->mNamespaceNames[$code] = array(); 00090 $this->mNamespaceAliases[$code] = array(); 00091 $this->mMagicWords[$code] = array(); 00092 $this->mSpecialPageAliases[$code] = array(); 00093 $filename = Language::getMessagesFileName( $code ); 00094 if ( file_exists( $filename ) ) { 00095 require( $filename ); 00096 if ( isset( $messages ) ) { 00097 $this->mRawMessages[$code] = $messages; 00098 } 00099 if ( isset( $fallback ) ) { 00100 $this->mFallback[$code] = $fallback; 00101 } 00102 if ( isset( $namespaceNames ) ) { 00103 $this->mNamespaceNames[$code] = $namespaceNames; 00104 } 00105 if ( isset( $namespaceAliases ) ) { 00106 $this->mNamespaceAliases[$code] = $namespaceAliases; 00107 } 00108 if ( isset( $magicWords ) ) { 00109 $this->mMagicWords[$code] = $magicWords; 00110 } 00111 if ( isset( $specialPageAliases ) ) { 00112 $this->mSpecialPageAliases[$code] = $specialPageAliases; 00113 } 00114 } 00115 } 00116 00127 private function loadMessages( $code ) { 00128 if ( isset( $this->mMessages[$code] ) ) { 00129 return; 00130 } 00131 $this->loadFile( $code ); 00132 $this->loadGeneralMessages(); 00133 $this->mMessages[$code]['all'] = $this->mRawMessages[$code]; 00134 $this->mMessages[$code]['required'] = array(); 00135 $this->mMessages[$code]['optional'] = array(); 00136 $this->mMessages[$code]['obsolete'] = array(); 00137 $this->mMessages[$code]['translated'] = array(); 00138 foreach ( $this->mMessages[$code]['all'] as $key => $value ) { 00139 if ( isset( $this->mGeneralMessages['required'][$key] ) ) { 00140 $this->mMessages[$code]['required'][$key] = $value; 00141 $this->mMessages[$code]['translated'][$key] = $value; 00142 } else if ( isset( $this->mGeneralMessages['optional'][$key] ) ) { 00143 $this->mMessages[$code]['optional'][$key] = $value; 00144 $this->mMessages[$code]['translated'][$key] = $value; 00145 } else { 00146 $this->mMessages[$code]['obsolete'][$key] = $value; 00147 } 00148 } 00149 } 00150 00159 private function loadGeneralMessages() { 00160 if ( isset( $this->mGeneralMessages ) ) { 00161 return; 00162 } 00163 $this->loadFile( 'en' ); 00164 $this->mGeneralMessages['all'] = $this->mRawMessages['en']; 00165 $this->mGeneralMessages['required'] = array(); 00166 $this->mGeneralMessages['optional'] = array(); 00167 $this->mGeneralMessages['ignored'] = array(); 00168 $this->mGeneralMessages['translatable'] = array(); 00169 foreach ( $this->mGeneralMessages['all'] as $key => $value ) { 00170 if ( in_array( $key, $this->mIgnoredMessages ) ) { 00171 $this->mGeneralMessages['ignored'][$key] = $value; 00172 } else if ( in_array( $key, $this->mOptionalMessages ) ) { 00173 $this->mGeneralMessages['optional'][$key] = $value; 00174 $this->mGeneralMessages['translatable'][$key] = $value; 00175 } else { 00176 $this->mGeneralMessages['required'][$key] = $value; 00177 $this->mGeneralMessages['translatable'][$key] = $value; 00178 } 00179 } 00180 } 00181 00195 public function getMessages( $code ) { 00196 $this->loadMessages( $code ); 00197 return $this->mMessages[$code]; 00198 } 00199 00210 public function getGeneralMessages() { 00211 $this->loadGeneralMessages(); 00212 return $this->mGeneralMessages; 00213 } 00214 00222 public function getFallback( $code ) { 00223 $this->loadFile( $code ); 00224 return $this->mFallback[$code]; 00225 } 00226 00234 public function getNamespaceNames( $code ) { 00235 $this->loadFile( $code ); 00236 return $this->mNamespaceNames[$code]; 00237 } 00238 00246 public function getNamespaceAliases( $code ) { 00247 $this->loadFile( $code ); 00248 return $this->mNamespaceAliases[$code]; 00249 } 00250 00258 public function getMagicWords( $code ) { 00259 $this->loadFile( $code ); 00260 return $this->mMagicWords[$code]; 00261 } 00262 00270 public function getSpecialPageAliases( $code ) { 00271 $this->loadFile( $code ); 00272 return $this->mSpecialPageAliases[$code]; 00273 } 00274 00282 public function getUntranslatedMessages( $code ) { 00283 $this->loadGeneralMessages(); 00284 $this->loadMessages( $code ); 00285 return array_diff_key( $this->mGeneralMessages['required'], $this->mMessages[$code]['required'] ); 00286 } 00287 00295 public function getDuplicateMessages( $code ) { 00296 $this->loadGeneralMessages(); 00297 $this->loadMessages( $code ); 00298 $duplicateMessages = array(); 00299 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00300 if ( $this->mGeneralMessages['translatable'][$key] == $value ) { 00301 $duplicateMessages[$key] = $value; 00302 } 00303 } 00304 return $duplicateMessages; 00305 } 00306 00314 public function getObsoleteMessages( $code ) { 00315 $this->loadGeneralMessages(); 00316 $this->loadMessages( $code ); 00317 return $this->mMessages[$code]['obsolete']; 00318 } 00319 00327 public function getMessagesWithMismatchVariables( $code ) { 00328 $this->loadGeneralMessages(); 00329 $this->loadMessages( $code ); 00330 $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' ); 00331 $mismatchMessages = array(); 00332 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00333 $missing = false; 00334 foreach ( $variables as $var ) { 00335 if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) && 00336 !preg_match( "/$var/sU", $value ) ) { 00337 $missing = true; 00338 } 00339 if ( !preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) && 00340 preg_match( "/$var/sU", $value ) ) { 00341 $missing = true; 00342 } 00343 } 00344 if ( $missing ) { 00345 $mismatchMessages[$key] = $value; 00346 } 00347 } 00348 return $mismatchMessages; 00349 } 00350 00358 public function getMessagesWithoutPlural( $code ) { 00359 $this->loadGeneralMessages(); 00360 $this->loadMessages( $code ); 00361 $messagesWithoutPlural = array(); 00362 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00363 if ( stripos( $this->mGeneralMessages['translatable'][$key], '{{plural:' ) !== false && stripos( $value, '{{plural:' ) === false ) { 00364 $messagesWithoutPlural[$key] = $value; 00365 } 00366 } 00367 return $messagesWithoutPlural; 00368 } 00369 00377 public function getEmptyMessages( $code ) { 00378 $this->loadGeneralMessages(); 00379 $this->loadMessages( $code ); 00380 $emptyMessages = array(); 00381 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00382 if ( $value === '' || $value === '-' ) { 00383 $emptyMessages[$key] = $value; 00384 } 00385 } 00386 return $emptyMessages; 00387 } 00388 00396 public function getMessagesWithWhitespace( $code ) { 00397 $this->loadGeneralMessages(); 00398 $this->loadMessages( $code ); 00399 $messagesWithWhitespace = array(); 00400 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00401 if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) { 00402 $messagesWithWhitespace[$key] = $value; 00403 } 00404 } 00405 return $messagesWithWhitespace; 00406 } 00407 00415 public function getNonXHTMLMessages( $code ) { 00416 $this->loadGeneralMessages(); 00417 $this->loadMessages( $code ); 00418 $wrongPhrases = array( 00419 '<hr *\\?>', 00420 '<br *\\?>', 00421 '<hr/>', 00422 '<br/>', 00423 '<hr>', 00424 '<br>', 00425 ); 00426 $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu'; 00427 $nonXHTMLMessages = array(); 00428 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00429 if ( preg_match( $wrongPhrases, $value ) ) { 00430 $nonXHTMLMessages[$key] = $value; 00431 } 00432 } 00433 return $nonXHTMLMessages; 00434 } 00435 00443 public function getMessagesWithWrongChars( $code ) { 00444 $this->loadGeneralMessages(); 00445 $this->loadMessages( $code ); 00446 $wrongChars = array( 00447 '[LRM]' => "\xE2\x80\x8E", 00448 '[RLM]' => "\xE2\x80\x8F", 00449 '[LRE]' => "\xE2\x80\xAA", 00450 '[RLE]' => "\xE2\x80\xAB", 00451 '[POP]' => "\xE2\x80\xAC", 00452 '[LRO]' => "\xE2\x80\xAD", 00453 '[RLO]' => "\xE2\x80\xAB", 00454 '[ZWSP]'=> "\xE2\x80\x8B", 00455 '[NBSP]'=> "\xC2\xA0", 00456 '[WJ]' => "\xE2\x81\xA0", 00457 '[BOM]' => "\xEF\xBB\xBF", 00458 '[FFFD]'=> "\xEF\xBF\xBD", 00459 ); 00460 $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu'; 00461 $wrongCharsMessages = array(); 00462 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00463 if ( preg_match( $wrongRegExp, $value ) ) { 00464 foreach ( $wrongChars as $viewableChar => $hiddenChar ) { 00465 $value = str_replace( $hiddenChar, $viewableChar, $value ); 00466 } 00467 $wrongCharsMessages[$key] = $value; 00468 } 00469 } 00470 return $wrongCharsMessages; 00471 } 00472 00480 public function getMessagesWithDubiousLinks( $code ) { 00481 $this->loadGeneralMessages(); 00482 $this->loadMessages( $code ); 00483 $tc = Title::legalChars() . '#%{}'; 00484 $messages = array(); 00485 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00486 $matches = array(); 00487 preg_match_all( "/\[\[([{$tc}]+)(?:\\|(.+?))?]]/sDu", $value, $matches ); 00488 for ($i = 0; $i < count($matches[0]); $i++ ) { 00489 if ( preg_match( "/.*project.*/isDu", $matches[1][$i] ) ) { 00490 $messages[$key][] = $matches[0][$i]; 00491 } 00492 } 00493 00494 00495 if ( isset( $messages[$key] ) ) { 00496 $messages[$key] = implode( $messages[$key],", " ); 00497 } 00498 } 00499 return $messages; 00500 } 00501 00509 public function getMessagesWithUnbalanced( $code ) { 00510 $this->loadGeneralMessages(); 00511 $this->loadMessages( $code ); 00512 $messages = array(); 00513 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) { 00514 $a = $b = $c = $d = 0; 00515 foreach ( preg_split( '//', $value ) as $char ) { 00516 switch ( $char ) { 00517 case '[': 00518 $a++; 00519 break; 00520 case ']': 00521 $b++; 00522 break; 00523 case '{': 00524 $c++; 00525 break; 00526 case '}': 00527 $d++; 00528 break; 00529 } 00530 } 00531 00532 if ( $a !== $b || $c !== $d ) { 00533 $messages[$key] = "$a, $b, $c, $d"; 00534 } 00535 00536 } 00537 return $messages; 00538 } 00539 00547 public function getUntranslatedNamespaces( $code ) { 00548 $this->loadFile( 'en' ); 00549 $this->loadFile( $code ); 00550 return array_flip( array_diff_key( $this->mNamespaceNames['en'], $this->mNamespaceNames[$code] ) ); 00551 } 00552 00560 public function getProblematicProjectTalks( $code ) { 00561 $this->loadFile( $code ); 00562 $namespaces = array(); 00563 00564 # Check default namespace name 00565 if( isset( $this->mNamespaceNames[$code][NS_PROJECT_TALK] ) ) { 00566 $default = $this->mNamespaceNames[$code][NS_PROJECT_TALK]; 00567 if ( strpos( $default, '$1' ) === FALSE ) { 00568 $namespaces[$default] = 'default'; 00569 } 00570 } 00571 00572 # Check namespace aliases 00573 foreach( $this->mNamespaceAliases[$code] as $key => $value ) { 00574 if ( $value == NS_PROJECT_TALK && strpos( $key, '$1' ) === FALSE ) { 00575 $namespaces[$key] = ''; 00576 } 00577 } 00578 00579 return $namespaces; 00580 } 00581 00589 public function getUntranslatedMagicWords( $code ) { 00590 $this->loadFile( 'en' ); 00591 $this->loadFile( $code ); 00592 $magicWords = array(); 00593 foreach ( $this->mMagicWords['en'] as $key => $value ) { 00594 if ( !isset( $this->mMagicWords[$code][$key] ) ) { 00595 $magicWords[$key] = $value[1]; 00596 } 00597 } 00598 return $magicWords; 00599 } 00600 00608 public function getObsoleteMagicWords( $code ) { 00609 $this->loadFile( 'en' ); 00610 $this->loadFile( $code ); 00611 $magicWords = array(); 00612 foreach ( $this->mMagicWords[$code] as $key => $value ) { 00613 if ( !isset( $this->mMagicWords['en'][$key] ) ) { 00614 $magicWords[$key] = $value[1]; 00615 } 00616 } 00617 return $magicWords; 00618 } 00619 00627 public function getOverridingMagicWords( $code ) { 00628 $this->loadFile( 'en' ); 00629 $this->loadFile( $code ); 00630 $magicWords = array(); 00631 foreach ( $this->mMagicWords[$code] as $key => $local ) { 00632 if ( !isset( $this->mMagicWords['en'][$key] ) ) { 00633 # Unrecognized magic word 00634 continue; 00635 } 00636 $en = $this->mMagicWords['en'][$key]; 00637 array_shift( $local ); 00638 array_shift( $en ); 00639 foreach ( $en as $word ) { 00640 if ( !in_array( $word, $local ) ) { 00641 $magicWords[$key] = $word; 00642 break; 00643 } 00644 } 00645 } 00646 return $magicWords; 00647 } 00648 00656 public function getCaseMismatchMagicWords( $code ) { 00657 $this->loadFile( 'en' ); 00658 $this->loadFile( $code ); 00659 $magicWords = array(); 00660 foreach ( $this->mMagicWords[$code] as $key => $local ) { 00661 if ( !isset( $this->mMagicWords['en'][$key] ) ) { 00662 # Unrecognized magic word 00663 continue; 00664 } 00665 if ( $local[0] != $this->mMagicWords['en'][$key][0] ) { 00666 $magicWords[$key] = $local[0]; 00667 } 00668 } 00669 return $magicWords; 00670 } 00671 00679 public function getUntraslatedSpecialPages( $code ) { 00680 $this->loadFile( 'en' ); 00681 $this->loadFile( $code ); 00682 $specialPageAliases = array(); 00683 foreach ( $this->mSpecialPageAliases['en'] as $key => $value ) { 00684 if ( !isset( $this->mSpecialPageAliases[$code][$key] ) ) { 00685 $specialPageAliases[$key] = $value[0]; 00686 } 00687 } 00688 return $specialPageAliases; 00689 } 00690 00698 public function getObsoleteSpecialPages( $code ) { 00699 $this->loadFile( 'en' ); 00700 $this->loadFile( $code ); 00701 $specialPageAliases = array(); 00702 foreach ( $this->mSpecialPageAliases[$code] as $key => $value ) { 00703 if ( !isset( $this->mSpecialPageAliases['en'][$key] ) ) { 00704 $specialPageAliases[$key] = $value[0]; 00705 } 00706 } 00707 return $specialPageAliases; 00708 } 00709 } 00710 00711 class extensionLanguages extends languages { 00712 private $mMessageGroup; # The message group 00713 00718 function __construct( MessageGroup $group ) { 00719 $this->mMessageGroup = $group; 00720 00721 $bools = $this->mMessageGroup->getBools(); 00722 $this->mIgnoredMessages = $bools['ignored']; 00723 $this->mOptionalMessages = $bools['optional']; 00724 } 00725 00731 public function name() { 00732 return $this->mMessageGroup->getLabel(); 00733 } 00734 00740 protected function loadFile( $code ) { 00741 if( !isset( $this->mRawMessages[$code] ) ) { 00742 $this->mRawMessages[$code] = $this->mMessageGroup->load( $code ); 00743 if( empty( $this->mRawMessages[$code] ) ) { 00744 $this->mRawMessages[$code] = array(); 00745 } 00746 } 00747 } 00748 }