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 $mGeneralMessages; # General messages in English, divided to groups
00018 protected $mIgnoredMessages; # All the messages which should be exist only in the English file
00019 protected $mOptionalMessages; # All the messages which may be translated or not, depending on the language
00020
00021 protected $mNamespaceNames; # Namespace names
00022 protected $mNamespaceAliases; # Namespace aliases
00023 protected $mMagicWords; # Magic words
00024 protected $mSpecialPageAliases; # Special page aliases
00025
00032 function __construct( $exif = true ) {
00033 require( dirname(__FILE__) . '/messageTypes.inc' );
00034 $this->mIgnoredMessages = $wgIgnoredMessages;
00035 if ( $exif ) {
00036 $this->mOptionalMessages = array_merge( $wgOptionalMessages );
00037 } else {
00038 $this->mOptionalMessages = array_merge( $wgOptionalMessages, $wgEXIFMessages );
00039 }
00040
00041 $this->mLanguages = array_keys( Language::getLanguageNames( true ) );
00042 sort( $this->mLanguages );
00043 }
00044
00050 public function getLanguages() {
00051 return $this->mLanguages;
00052 }
00053
00059 public function getIgnoredMessages() {
00060 return $this->mIgnoredMessages;
00061 }
00062
00068 public function getOptionalMessages() {
00069 return $this->mOptionalMessages;
00070 }
00071
00077 protected function loadFile( $code ) {
00078 if ( isset( $this->mRawMessages[$code] ) &&
00079 isset( $this->mNamespaceNames[$code] ) &&
00080 isset( $this->mNamespaceAliases[$code] ) &&
00081 isset( $this->mMagicWords[$code] ) &&
00082 isset( $this->mSpecialPageAliases[$code] ) ) {
00083 return;
00084 }
00085 $this->mRawMessages[$code] = array();
00086 $this->mNamespaceNames[$code] = array();
00087 $this->mNamespaceAliases[$code] = array();
00088 $this->mMagicWords[$code] = array();
00089 $this->mSpecialPageAliases[$code] = array();
00090 $filename = Language::getMessagesFileName( $code );
00091 if ( file_exists( $filename ) ) {
00092 require( $filename );
00093 if ( isset( $messages ) ) {
00094 $this->mRawMessages[$code] = $messages;
00095 }
00096 if ( isset( $namespaceNames ) ) {
00097 $this->mNamespaceNames[$code] = $namespaceNames;
00098 }
00099 if ( isset( $namespaceAliases ) ) {
00100 $this->mNamespaceAliases[$code] = $namespaceAliases;
00101 }
00102 if ( isset( $magicWords ) ) {
00103 $this->mMagicWords[$code] = $magicWords;
00104 }
00105 if ( isset( $specialPageAliases ) ) {
00106 $this->mSpecialPageAliases[$code] = $specialPageAliases;
00107 }
00108 }
00109 }
00110
00121 private function loadMessages( $code ) {
00122 if ( isset( $this->mMessages[$code] ) ) {
00123 return;
00124 }
00125 $this->loadFile( $code );
00126 $this->loadGeneralMessages();
00127 $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
00128 $this->mMessages[$code]['required'] = array();
00129 $this->mMessages[$code]['optional'] = array();
00130 $this->mMessages[$code]['obsolete'] = array();
00131 $this->mMessages[$code]['translated'] = array();
00132 foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
00133 if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
00134 $this->mMessages[$code]['required'][$key] = $value;
00135 $this->mMessages[$code]['translated'][$key] = $value;
00136 } else if ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
00137 $this->mMessages[$code]['optional'][$key] = $value;
00138 $this->mMessages[$code]['translated'][$key] = $value;
00139 } else {
00140 $this->mMessages[$code]['obsolete'][$key] = $value;
00141 }
00142 }
00143 }
00144
00153 private function loadGeneralMessages() {
00154 if ( isset( $this->mGeneralMessages ) ) {
00155 return;
00156 }
00157 $this->loadFile( 'en' );
00158 $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
00159 $this->mGeneralMessages['required'] = array();
00160 $this->mGeneralMessages['optional'] = array();
00161 $this->mGeneralMessages['ignored'] = array();
00162 $this->mGeneralMessages['translatable'] = array();
00163 foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
00164 if ( in_array( $key, $this->mIgnoredMessages ) ) {
00165 $this->mGeneralMessages['ignored'][$key] = $value;
00166 } else if ( in_array( $key, $this->mOptionalMessages ) ) {
00167 $this->mGeneralMessages['optional'][$key] = $value;
00168 $this->mGeneralMessages['translatable'][$key] = $value;
00169 } else {
00170 $this->mGeneralMessages['required'][$key] = $value;
00171 $this->mGeneralMessages['translatable'][$key] = $value;
00172 }
00173 }
00174 }
00175
00189 public function getMessages( $code ) {
00190 $this->loadMessages( $code );
00191 return $this->mMessages[$code];
00192 }
00193
00204 public function getGeneralMessages() {
00205 $this->loadGeneralMessages();
00206 return $this->mGeneralMessages;
00207 }
00208
00216 public function getNamespaceNames( $code ) {
00217 $this->loadFile( $code );
00218 return $this->mNamespaceNames[$code];
00219 }
00220
00228 public function getNamespaceAliases( $code ) {
00229 $this->loadFile( $code );
00230 return $this->mNamespaceAliases[$code];
00231 }
00232
00240 public function getMagicWords( $code ) {
00241 $this->loadFile( $code );
00242 return $this->mMagicWords[$code];
00243 }
00244
00252 public function getSpecialPageAliases( $code ) {
00253 $this->loadFile( $code );
00254 return $this->mSpecialPageAliases[$code];
00255 }
00256
00264 public function getUntranslatedMessages( $code ) {
00265 $this->loadGeneralMessages();
00266 $this->loadMessages( $code );
00267 return array_diff_key( $this->mGeneralMessages['required'], $this->mMessages[$code]['required'] );
00268 }
00269
00277 public function getDuplicateMessages( $code ) {
00278 $this->loadGeneralMessages();
00279 $this->loadMessages( $code );
00280 $duplicateMessages = array();
00281 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00282 if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
00283 $duplicateMessages[$key] = $value;
00284 }
00285 }
00286 return $duplicateMessages;
00287 }
00288
00296 public function getObsoleteMessages( $code ) {
00297 $this->loadGeneralMessages();
00298 $this->loadMessages( $code );
00299 return $this->mMessages[$code]['obsolete'];
00300 }
00301
00309 public function getMessagesWithMismatchVariables( $code ) {
00310 $this->loadGeneralMessages();
00311 $this->loadMessages( $code );
00312 $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
00313 $mismatchMessages = array();
00314 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00315 $missing = false;
00316 foreach ( $variables as $var ) {
00317 if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
00318 !preg_match( "/$var/sU", $value ) ) {
00319 $missing = true;
00320 }
00321 if ( !preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
00322 preg_match( "/$var/sU", $value ) ) {
00323 $missing = true;
00324 }
00325 }
00326 if ( $missing ) {
00327 $mismatchMessages[$key] = $value;
00328 }
00329 }
00330 return $mismatchMessages;
00331 }
00332
00340 public function getMessagesWithoutPlural( $code ) {
00341 $this->loadGeneralMessages();
00342 $this->loadMessages( $code );
00343 $messagesWithoutPlural = array();
00344 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00345 if ( stripos( $this->mGeneralMessages['translatable'][$key], '{{plural:' ) !== false && stripos( $value, '{{plural:' ) === false ) {
00346 $messagesWithoutPlural[$key] = $value;
00347 }
00348 }
00349 return $messagesWithoutPlural;
00350 }
00351
00359 public function getEmptyMessages( $code ) {
00360 $this->loadGeneralMessages();
00361 $this->loadMessages( $code );
00362 $emptyMessages = array();
00363 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00364 if ( $value === '' || $value === '-' ) {
00365 $emptyMessages[$key] = $value;
00366 }
00367 }
00368 return $emptyMessages;
00369 }
00370
00378 public function getMessagesWithWhitespace( $code ) {
00379 $this->loadGeneralMessages();
00380 $this->loadMessages( $code );
00381 $messagesWithWhitespace = array();
00382 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00383 if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
00384 $messagesWithWhitespace[$key] = $value;
00385 }
00386 }
00387 return $messagesWithWhitespace;
00388 }
00389
00397 public function getNonXHTMLMessages( $code ) {
00398 $this->loadGeneralMessages();
00399 $this->loadMessages( $code );
00400 $wrongPhrases = array(
00401 '<hr *\\?>',
00402 '<br *\\?>',
00403 '<hr/>',
00404 '<br/>',
00405 '<hr>',
00406 '<br>',
00407 );
00408 $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
00409 $nonXHTMLMessages = array();
00410 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00411 if ( preg_match( $wrongPhrases, $value ) ) {
00412 $nonXHTMLMessages[$key] = $value;
00413 }
00414 }
00415 return $nonXHTMLMessages;
00416 }
00417
00425 public function getMessagesWithWrongChars( $code ) {
00426 $this->loadGeneralMessages();
00427 $this->loadMessages( $code );
00428 $wrongChars = array(
00429 '[LRM]' => "\xE2\x80\x8E",
00430 '[RLM]' => "\xE2\x80\x8F",
00431 '[LRE]' => "\xE2\x80\xAA",
00432 '[RLE]' => "\xE2\x80\xAB",
00433 '[POP]' => "\xE2\x80\xAC",
00434 '[LRO]' => "\xE2\x80\xAD",
00435 '[RLO]' => "\xE2\x80\xAB",
00436 '[ZWSP]'=> "\xE2\x80\x8B",
00437 '[NBSP]'=> "\xC2\xA0",
00438 '[WJ]' => "\xE2\x81\xA0",
00439 '[BOM]' => "\xEF\xBB\xBF",
00440 '[FFFD]'=> "\xEF\xBF\xBD",
00441 );
00442 $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
00443 $wrongCharsMessages = array();
00444 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00445 if ( preg_match( $wrongRegExp, $value ) ) {
00446 foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
00447 $value = str_replace( $hiddenChar, $viewableChar, $value );
00448 }
00449 $wrongCharsMessages[$key] = $value;
00450 }
00451 }
00452 return $wrongCharsMessages;
00453 }
00454
00462 public function getMessagesWithDubiousLinks( $code ) {
00463 $this->loadGeneralMessages();
00464 $this->loadMessages( $code );
00465 $tc = Title::legalChars() . '#%{}';
00466 $messages = array();
00467 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00468 $matches = array();
00469 preg_match_all( "/\[\[([{$tc}]+)(?:\\|(.+?))?]]/sDu", $value, $matches );
00470 for ($i = 0; $i < count($matches[0]); $i++ ) {
00471 if ( preg_match( "/.*project.*/isDu", $matches[1][$i] ) ) {
00472 $messages[$key][] = $matches[0][$i];
00473 }
00474 }
00475
00476
00477 if ( isset( $messages[$key] ) ) {
00478 $messages[$key] = implode( $messages[$key],", " );
00479 }
00480 }
00481 return $messages;
00482 }
00483
00491 public function getMessagesWithUnbalanced( $code ) {
00492 $this->loadGeneralMessages();
00493 $this->loadMessages( $code );
00494 $messages = array();
00495 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
00496 $a = $b = $c = $d = 0;
00497 foreach ( preg_split( '//', $value ) as $char ) {
00498 switch ( $char ) {
00499 case '[':
00500 $a++;
00501 break;
00502 case ']':
00503 $b++;
00504 break;
00505 case '{':
00506 $c++;
00507 break;
00508 case '}':
00509 $d++;
00510 break;
00511 }
00512 }
00513
00514 if ( $a !== $b || $c !== $d ) {
00515 $messages[$key] = "$a, $b, $c, $d";
00516 }
00517
00518 }
00519 return $messages;
00520 }
00521
00529 public function getUntranslatedNamespaces( $code ) {
00530 $this->loadFile( 'en' );
00531 $this->loadFile( $code );
00532 return array_flip( array_diff_key( $this->mNamespaceNames['en'], $this->mNamespaceNames[$code] ) );
00533 }
00534
00542 public function getProblematicProjectTalks( $code ) {
00543 $this->loadFile( $code );
00544 $namespaces = array();
00545
00546 # Check default namespace name
00547 if( isset( $this->mNamespaceNames[$code][NS_PROJECT_TALK] ) ) {
00548 $default = $this->mNamespaceNames[$code][NS_PROJECT_TALK];
00549 if ( strpos( $default, '$1' ) === FALSE ) {
00550 $namespaces[$default] = 'default';
00551 }
00552 }
00553
00554 # Check namespace aliases
00555 foreach( $this->mNamespaceAliases[$code] as $key => $value ) {
00556 if ( $value == NS_PROJECT_TALK && strpos( $key, '$1' ) === FALSE ) {
00557 $namespaces[$key] = '';
00558 }
00559 }
00560
00561 return $namespaces;
00562 }
00563
00571 public function getUntranslatedMagicWords( $code ) {
00572 $this->loadFile( 'en' );
00573 $this->loadFile( $code );
00574 $magicWords = array();
00575 foreach ( $this->mMagicWords['en'] as $key => $value ) {
00576 if ( !isset( $this->mMagicWords[$code][$key] ) ) {
00577 $magicWords[$key] = $value[1];
00578 }
00579 }
00580 return $magicWords;
00581 }
00582
00590 public function getObsoleteMagicWords( $code ) {
00591 $this->loadFile( 'en' );
00592 $this->loadFile( $code );
00593 $magicWords = array();
00594 foreach ( $this->mMagicWords[$code] as $key => $value ) {
00595 if ( !isset( $this->mMagicWords['en'][$key] ) ) {
00596 $magicWords[$key] = $value[1];
00597 }
00598 }
00599 return $magicWords;
00600 }
00601
00609 public function getOverridingMagicWords( $code ) {
00610 $this->loadFile( 'en' );
00611 $this->loadFile( $code );
00612 $magicWords = array();
00613 foreach ( $this->mMagicWords[$code] as $key => $local ) {
00614 if ( !isset( $this->mMagicWords['en'][$key] ) ) {
00615 # Unrecognized magic word
00616 continue;
00617 }
00618 $en = $this->mMagicWords['en'][$key];
00619 array_shift( $local );
00620 array_shift( $en );
00621 foreach ( $en as $word ) {
00622 if ( !in_array( $word, $local ) ) {
00623 $magicWords[$key] = $word;
00624 break;
00625 }
00626 }
00627 }
00628 return $magicWords;
00629 }
00630
00638 public function getCaseMismatchMagicWords( $code ) {
00639 $this->loadFile( 'en' );
00640 $this->loadFile( $code );
00641 $magicWords = array();
00642 foreach ( $this->mMagicWords[$code] as $key => $local ) {
00643 if ( !isset( $this->mMagicWords['en'][$key] ) ) {
00644 # Unrecognized magic word
00645 continue;
00646 }
00647 if ( $local[0] != $this->mMagicWords['en'][$key][0] ) {
00648 $magicWords[$key] = $local[0];
00649 }
00650 }
00651 return $magicWords;
00652 }
00653
00661 public function getUntraslatedSpecialPages( $code ) {
00662 $this->loadFile( 'en' );
00663 $this->loadFile( $code );
00664 $specialPageAliases = array();
00665 foreach ( $this->mSpecialPageAliases['en'] as $key => $value ) {
00666 if ( !isset( $this->mSpecialPageAliases[$code][$key] ) ) {
00667 $specialPageAliases[$key] = $value[0];
00668 }
00669 }
00670 return $specialPageAliases;
00671 }
00672
00680 public function getObsoleteSpecialPages( $code ) {
00681 $this->loadFile( 'en' );
00682 $this->loadFile( $code );
00683 $specialPageAliases = array();
00684 foreach ( $this->mSpecialPageAliases[$code] as $key => $value ) {
00685 if ( !isset( $this->mSpecialPageAliases['en'][$key] ) ) {
00686 $specialPageAliases[$key] = $value[0];
00687 }
00688 }
00689 return $specialPageAliases;
00690 }
00691 }
00692
00693 class extensionLanguages extends languages {
00694 private $mMessageGroup; # The message group
00695
00700 function __construct( MessageGroup $group ) {
00701 $this->mMessageGroup = $group;
00702
00703 $bools = $this->mMessageGroup->getBools();
00704 $this->mIgnoredMessages = $bools['ignored'];
00705 $this->mOptionalMessages = $bools['optional'];
00706 }
00707
00713 public function name() {
00714 return $this->mMessageGroup->getLabel();
00715 }
00716
00722 protected function loadFile( $code ) {
00723 if( !isset( $this->mRawMessages[$code] ) ) {
00724 $this->mRawMessages[$code] = $this->mMessageGroup->load( $code );
00725 if( empty( $this->mRawMessages[$code] ) ) {
00726 $this->mRawMessages[$code] = array();
00727 }
00728 }
00729 }
00730 }