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 }