MediaWiki  master
languages.inc
Go to the documentation of this file.
1 <?php
27 class Languages {
29  protected $mLanguages;
30 
32  protected $mRawMessages;
33 
35  protected $mMessages;
36 
38  protected $mFallback;
39 
41  protected $mGeneralMessages;
42 
44  protected $mIgnoredMessages;
45 
47  protected $mOptionalMessages;
48 
50  protected $mNamespaceNames;
51 
53  protected $mNamespaceAliases;
54 
56  protected $mMagicWords;
57 
60 
65  public function __construct() {
66  Hooks::run( 'LocalisationIgnoredOptionalMessages',
67  [ &$this->mIgnoredMessages, &$this->mOptionalMessages ] );
68 
69  $this->mLanguages = array_keys( Language::fetchLanguageNames( null, 'mwfile' ) );
70  sort( $this->mLanguages );
71  }
72 
78  public function getLanguages() {
79  return $this->mLanguages;
80  }
81 
87  public function getIgnoredMessages() {
89  }
90 
96  public function getOptionalMessages() {
98  }
99 
105  protected function loadFile( $code ) {
106  if ( isset( $this->mRawMessages[$code] ) &&
107  isset( $this->mFallback[$code] ) &&
108  isset( $this->mNamespaceNames[$code] ) &&
109  isset( $this->mNamespaceAliases[$code] ) &&
110  isset( $this->mMagicWords[$code] ) &&
111  isset( $this->mSpecialPageAliases[$code] )
112  ) {
113  return;
114  }
115  $this->mRawMessages[$code] = [];
116  $this->mFallback[$code] = '';
117  $this->mNamespaceNames[$code] = [];
118  $this->mNamespaceAliases[$code] = [];
119  $this->mMagicWords[$code] = [];
120  $this->mSpecialPageAliases[$code] = [];
121 
122  $jsonfilename = Language::getJsonMessagesFileName( $code );
123  if ( file_exists( $jsonfilename ) ) {
124  $json = Language::getLocalisationCache()->readJSONFile( $jsonfilename );
125  $this->mRawMessages[$code] = $json['messages'];
126  }
127 
128  $filename = Language::getMessagesFileName( $code );
129  if ( file_exists( $filename ) ) {
130  require $filename;
131  if ( isset( $fallback ) ) {
132  $this->mFallback[$code] = $fallback;
133  }
134  if ( isset( $namespaceNames ) ) {
135  $this->mNamespaceNames[$code] = $namespaceNames;
136  }
137  if ( isset( $namespaceAliases ) ) {
138  $this->mNamespaceAliases[$code] = $namespaceAliases;
139  }
140  if ( isset( $magicWords ) ) {
141  $this->mMagicWords[$code] = $magicWords;
142  }
143  if ( isset( $specialPageAliases ) ) {
144  $this->mSpecialPageAliases[$code] = $specialPageAliases;
145  }
146  }
147  }
148 
163  private function loadMessages( $code ) {
164  if ( isset( $this->mMessages[$code] ) ) {
165  return;
166  }
167  $this->loadFile( $code );
168  $this->loadGeneralMessages();
169  $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
170  $this->mMessages[$code]['required'] = [];
171  $this->mMessages[$code]['optional'] = [];
172  $this->mMessages[$code]['obsolete'] = [];
173  $this->mMessages[$code]['translated'] = [];
174  foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
175  if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
176  $this->mMessages[$code]['required'][$key] = $value;
177  $this->mMessages[$code]['translated'][$key] = $value;
178  } elseif ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
179  $this->mMessages[$code]['optional'][$key] = $value;
180  $this->mMessages[$code]['translated'][$key] = $value;
181  } else {
182  $this->mMessages[$code]['obsolete'][$key] = $value;
183  }
184  }
185  }
186 
198  private function loadGeneralMessages() {
199  if ( isset( $this->mGeneralMessages ) ) {
200  return;
201  }
202  $this->loadFile( 'en' );
203  $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
204  $this->mGeneralMessages['required'] = [];
205  $this->mGeneralMessages['optional'] = [];
206  $this->mGeneralMessages['ignored'] = [];
207  $this->mGeneralMessages['translatable'] = [];
208  foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
209  if ( in_array( $key, $this->mIgnoredMessages ) ) {
210  $this->mGeneralMessages['ignored'][$key] = $value;
211  } elseif ( in_array( $key, $this->mOptionalMessages ) ) {
212  $this->mGeneralMessages['optional'][$key] = $value;
213  $this->mGeneralMessages['translatable'][$key] = $value;
214  } else {
215  $this->mGeneralMessages['required'][$key] = $value;
216  $this->mGeneralMessages['translatable'][$key] = $value;
217  }
218  }
219  }
220 
237  public function getMessages( $code ) {
238  $this->loadMessages( $code );
239 
240  return $this->mMessages[$code];
241  }
242 
256  public function getGeneralMessages() {
257  $this->loadGeneralMessages();
258 
260  }
261 
269  public function getFallback( $code ) {
270  $this->loadFile( $code );
271 
272  return $this->mFallback[$code];
273  }
274 
282  public function getNamespaceNames( $code ) {
283  $this->loadFile( $code );
284 
285  return $this->mNamespaceNames[$code];
286  }
287 
295  public function getNamespaceAliases( $code ) {
296  $this->loadFile( $code );
297 
298  return $this->mNamespaceAliases[$code];
299  }
300 
308  public function getMagicWords( $code ) {
309  $this->loadFile( $code );
310 
311  return $this->mMagicWords[$code];
312  }
313 
321  public function getSpecialPageAliases( $code ) {
322  $this->loadFile( $code );
323 
324  return $this->mSpecialPageAliases[$code];
325  }
326 
334  public function getUntranslatedMessages( $code ) {
335  $this->loadGeneralMessages();
336  $this->loadMessages( $code );
337 
338  return array_diff_key( $this->mGeneralMessages['required'], $this->mMessages[$code]['required'] );
339  }
340 
348  public function getDuplicateMessages( $code ) {
349  $this->loadGeneralMessages();
350  $this->loadMessages( $code );
351  $duplicateMessages = [];
352  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
353  if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
354  $duplicateMessages[$key] = $value;
355  }
356  }
357 
358  return $duplicateMessages;
359  }
360 
368  public function getObsoleteMessages( $code ) {
369  $this->loadGeneralMessages();
370  $this->loadMessages( $code );
371 
372  return $this->mMessages[$code]['obsolete'];
373  }
374 
382  public function getMessagesWithMismatchVariables( $code ) {
383  $this->loadGeneralMessages();
384  $this->loadMessages( $code );
385  $variables = [ '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' ];
386  $mismatchMessages = [];
387  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
388  $missing = false;
389  foreach ( $variables as $var ) {
390  if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
391  !preg_match( "/$var/sU", $value )
392  ) {
393  $missing = true;
394  }
395  if ( !preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
396  preg_match( "/$var/sU", $value )
397  ) {
398  $missing = true;
399  }
400  }
401  if ( $missing ) {
402  $mismatchMessages[$key] = $value;
403  }
404  }
405 
406  return $mismatchMessages;
407  }
408 
416  public function getMessagesWithoutPlural( $code ) {
417  $this->loadGeneralMessages();
418  $this->loadMessages( $code );
419  $messagesWithoutPlural = [];
420  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
421  if ( stripos( $this->mGeneralMessages['translatable'][$key], '{{plural:' ) !== false &&
422  stripos( $value, '{{plural:' ) === false
423  ) {
424  $messagesWithoutPlural[$key] = $value;
425  }
426  }
427 
428  return $messagesWithoutPlural;
429  }
430 
438  public function getEmptyMessages( $code ) {
439  $this->loadGeneralMessages();
440  $this->loadMessages( $code );
441  $emptyMessages = [];
442  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
443  if ( $value === '' || $value === '-' ) {
444  $emptyMessages[$key] = $value;
445  }
446  }
447 
448  return $emptyMessages;
449  }
450 
458  public function getMessagesWithWhitespace( $code ) {
459  $this->loadGeneralMessages();
460  $this->loadMessages( $code );
461  $messagesWithWhitespace = [];
462  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
463  if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
464  $messagesWithWhitespace[$key] = $value;
465  }
466  }
467 
468  return $messagesWithWhitespace;
469  }
470 
478  public function getNonXHTMLMessages( $code ) {
479  $this->loadGeneralMessages();
480  $this->loadMessages( $code );
481  $wrongPhrases = [
482  '<hr *\\?>',
483  '<br *\\?>',
484  '<hr/>',
485  '<br/>',
486  '<hr>',
487  '<br>',
488  ];
489  $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
490  $nonXHTMLMessages = [];
491  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
492  if ( preg_match( $wrongPhrases, $value ) ) {
493  $nonXHTMLMessages[$key] = $value;
494  }
495  }
496 
497  return $nonXHTMLMessages;
498  }
499 
507  public function getMessagesWithWrongChars( $code ) {
508  $this->loadGeneralMessages();
509  $this->loadMessages( $code );
510  $wrongChars = [
511  '[LRM]' => "\u{200E}",
512  '[RLM]' => "\u{200F}",
513  '[LRE]' => "\u{202A}",
514  '[RLE]' => "\u{202B}",
515  '[POP]' => "\u{202C}",
516  '[LRO]' => "\u{202D}",
517  '[RLO]' => "\u{202B}",
518  '[ZWSP]' => "\u{200B}",
519  '[NBSP]' => "\u{00A0}",
520  '[WJ]' => "\u{2060}",
521  '[BOM]' => "\u{FEFF}",
522  '[FFFD]' => "\u{FFFD}",
523  ];
524  $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
525  $wrongCharsMessages = [];
526  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
527  if ( preg_match( $wrongRegExp, $value ) ) {
528  foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
529  $value = str_replace( $hiddenChar, $viewableChar, $value );
530  }
531  $wrongCharsMessages[$key] = $value;
532  }
533  }
534 
535  return $wrongCharsMessages;
536  }
537 
545  public function getMessagesWithDubiousLinks( $code ) {
546  $this->loadGeneralMessages();
547  $this->loadMessages( $code );
548  $tc = Title::legalChars() . '#%{}';
549  $messages = [];
550  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
551  $matches = [];
552  preg_match_all( "/\[\[([{$tc}]+)(?:\\|(.+?))?]]/sDu", $value, $matches );
553  $numMatches = count( $matches[0] );
554  for ( $i = 0; $i < $numMatches; $i++ ) {
555  if ( preg_match( "/.*project.*/isDu", $matches[1][$i] ) ) {
556  $messages[$key][] = $matches[0][$i];
557  }
558  }
559 
560  if ( isset( $messages[$key] ) ) {
561  $messages[$key] = implode( ", ", $messages[$key] );
562  }
563  }
564 
565  return $messages;
566  }
567 
575  public function getMessagesWithUnbalanced( $code ) {
576  $this->loadGeneralMessages();
577  $this->loadMessages( $code );
578  $messages = [];
579  foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
580  $a = $b = $c = $d = 0;
581  foreach ( preg_split( '//', $value ) as $char ) {
582  switch ( $char ) {
583  case '[':
584  $a++;
585  break;
586  case ']':
587  $b++;
588  break;
589  case '{':
590  $c++;
591  break;
592  case '}':
593  $d++;
594  break;
595  }
596  }
597 
598  if ( $a !== $b || $c !== $d ) {
599  $messages[$key] = "$a, $b, $c, $d";
600  }
601  }
602 
603  return $messages;
604  }
605 
613  public function getUntranslatedNamespaces( $code ) {
614  $this->loadFile( 'en' );
615  $this->loadFile( $code );
616  $namespacesDiff = array_diff_key( $this->mNamespaceNames['en'], $this->mNamespaceNames[$code] );
617  if ( isset( $namespacesDiff[NS_MAIN] ) ) {
618  unset( $namespacesDiff[NS_MAIN] );
619  }
620 
621  return $namespacesDiff;
622  }
623 
631  public function getProblematicProjectTalks( $code ) {
632  $this->loadFile( $code );
633  $namespaces = [];
634 
635  # Check default namespace name
636  if ( isset( $this->mNamespaceNames[$code][NS_PROJECT_TALK] ) ) {
637  $default = $this->mNamespaceNames[$code][NS_PROJECT_TALK];
638  if ( strpos( $default, '$1' ) === false ) {
639  $namespaces[$default] = 'default';
640  }
641  }
642 
643  # Check namespace aliases
644  foreach ( $this->mNamespaceAliases[$code] as $key => $value ) {
645  if ( $value == NS_PROJECT_TALK && strpos( $key, '$1' ) === false ) {
646  $namespaces[$key] = '';
647  }
648  }
649 
650  return $namespaces;
651  }
652 
660  public function getUntranslatedMagicWords( $code ) {
661  $this->loadFile( 'en' );
662  $this->loadFile( $code );
663  $magicWords = [];
664  foreach ( $this->mMagicWords['en'] as $key => $value ) {
665  if ( !isset( $this->mMagicWords[$code][$key] ) ) {
666  $magicWords[$key] = $value[1];
667  }
668  }
669 
670  return $magicWords;
671  }
672 
680  public function getObsoleteMagicWords( $code ) {
681  $this->loadFile( 'en' );
682  $this->loadFile( $code );
683  $magicWords = [];
684  foreach ( $this->mMagicWords[$code] as $key => $value ) {
685  if ( !isset( $this->mMagicWords['en'][$key] ) ) {
686  $magicWords[$key] = $value[1];
687  }
688  }
689 
690  return $magicWords;
691  }
692 
700  public function getOverridingMagicWords( $code ) {
701  $this->loadFile( 'en' );
702  $this->loadFile( $code );
703  $magicWords = [];
704  foreach ( $this->mMagicWords[$code] as $key => $local ) {
705  if ( !isset( $this->mMagicWords['en'][$key] ) ) {
706  # Unrecognized magic word
707  continue;
708  }
709  $en = $this->mMagicWords['en'][$key];
710  array_shift( $local );
711  array_shift( $en );
712  foreach ( $en as $word ) {
713  if ( !in_array( $word, $local ) ) {
714  $magicWords[$key] = $word;
715  break;
716  }
717  }
718  }
719 
720  return $magicWords;
721  }
722 
730  public function getCaseMismatchMagicWords( $code ) {
731  $this->loadFile( 'en' );
732  $this->loadFile( $code );
733  $magicWords = [];
734  foreach ( $this->mMagicWords[$code] as $key => $local ) {
735  if ( !isset( $this->mMagicWords['en'][$key] ) ) {
736  # Unrecognized magic word
737  continue;
738  }
739  if ( $local[0] != $this->mMagicWords['en'][$key][0] ) {
740  $magicWords[$key] = $local[0];
741  }
742  }
743 
744  return $magicWords;
745  }
746 
754  public function getUntraslatedSpecialPages( $code ) {
755  $this->loadFile( 'en' );
756  $this->loadFile( $code );
757  $specialPageAliases = [];
758  foreach ( $this->mSpecialPageAliases['en'] as $key => $value ) {
759  if ( !isset( $this->mSpecialPageAliases[$code][$key] ) ) {
760  $specialPageAliases[$key] = $value[0];
761  }
762  }
763 
764  return $specialPageAliases;
765  }
766 
774  public function getObsoleteSpecialPages( $code ) {
775  $this->loadFile( 'en' );
776  $this->loadFile( $code );
777  $specialPageAliases = [];
778  foreach ( $this->mSpecialPageAliases[$code] as $key => $value ) {
779  if ( !isset( $this->mSpecialPageAliases['en'][$key] ) ) {
780  $specialPageAliases[$key] = $value[0];
781  }
782  }
783 
784  return $specialPageAliases;
785  }
786 }
787 
792  private $mMessageGroup;
793 
798  public function __construct( MessageGroup $group ) {
799  $this->mMessageGroup = $group;
800 
801  $this->mIgnoredMessages = $this->mMessageGroup->getIgnored();
802  $this->mOptionalMessages = $this->mMessageGroup->getOptional();
803  }
804 
810  public function name() {
811  return $this->mMessageGroup->getLabel();
812  }
813 
819  protected function loadFile( $code ) {
820  if ( !isset( $this->mRawMessages[$code] ) ) {
821  $this->mRawMessages[$code] = $this->mMessageGroup->load( $code );
822  if ( empty( $this->mRawMessages[$code] ) ) {
823  $this->mRawMessages[$code] = [];
824  }
825  }
826  }
827 }
$specialPageAliases
Definition: MessagesAb.php:57
getNonXHTMLMessages( $code)
Get the non-XHTML messages.
Definition: languages.inc:478
getDuplicateMessages( $code)
Get the duplicate messages for a specific language.
Definition: languages.inc:348
static getLocalisationCache()
Get the LocalisationCache instance.
Definition: Language.php:446
array $mFallback
Fallback language in each language.
Definition: languages.inc:38
static fetchLanguageNames( $inLanguage=self::AS_AUTONYMS, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:854
getEmptyMessages( $code)
Get the empty messages.
Definition: languages.inc:438
__construct(MessageGroup $group)
Load the messages group.
Definition: languages.inc:798
$namespaceNames
Definition: MessagesAb.php:13
array $mNamespaceAliases
Namespace aliases.
Definition: languages.inc:53
const NS_MAIN
Definition: Defines.php:60
getMessagesWithoutPlural( $code)
Get the messages which do not use plural.
Definition: languages.inc:416
array $mRawMessages
Raw list of the messages in each language.
Definition: languages.inc:32
getNamespaceNames( $code)
Get namespace names for a specific language.
Definition: languages.inc:282
getMessages( $code)
Get all the messages for a specific language (not English), without the fallback language messages...
Definition: languages.inc:237
getCaseMismatchMagicWords( $code)
Get the magic words which do not match the case-sensitivity of the original words.
Definition: languages.inc:730
array $mOptionalMessages
All the messages which may be translated or not, depending on the language.
Definition: languages.inc:47
loadFile( $code)
Load the language file.
Definition: languages.inc:819
getGeneralMessages()
Get all the general English messages, divided to groups: all - all the messages.
Definition: languages.inc:256
$namespaceAliases
Definition: MessagesAb.php:32
getMessagesWithMismatchVariables( $code)
Get the messages whose variables do not match the original ones.
Definition: languages.inc:382
loadFile( $code)
Load the language file.
Definition: languages.inc:105
getUntranslatedMagicWords( $code)
Get the untranslated magic words.
Definition: languages.inc:660
getLanguages()
Get the language list.
Definition: languages.inc:78
getMessagesWithUnbalanced( $code)
Get the messages which include unbalanced brackets.
Definition: languages.inc:575
array $mSpecialPageAliases
Special page aliases.
Definition: languages.inc:59
getIgnoredMessages()
Get the ignored messages list.
Definition: languages.inc:87
getFallback( $code)
Get fallback language code for a specific language.
Definition: languages.inc:269
getUntranslatedNamespaces( $code)
Get the untranslated namespace names.
Definition: languages.inc:613
$magicWords
Definition: MessagesAb.php:71
getUntraslatedSpecialPages( $code)
Get the untranslated special page names.
Definition: languages.inc:754
getOptionalMessages()
Get the optional messages list.
Definition: languages.inc:96
__construct()
Load the list of languages: all the Messages*.php files in the languages directory.
Definition: languages.inc:65
getObsoleteSpecialPages( $code)
Get the obsolete special page names.
Definition: languages.inc:774
const NS_PROJECT_TALK
Definition: Defines.php:65
MessageGroup $mMessageGroup
Definition: languages.inc:792
getNamespaceAliases( $code)
Get namespace aliases for a specific language.
Definition: languages.inc:295
array $mMessages
Messages in each language (except for English), divided to groups.
Definition: languages.inc:35
getMagicWords( $code)
Get magic words for a specific language.
Definition: languages.inc:308
static getMessagesFileName( $code)
Definition: Language.php:4337
getObsoleteMessages( $code)
Get the obsolete messages for a specific language.
Definition: languages.inc:368
static getJsonMessagesFileName( $code)
Definition: Language.php:4349
getSpecialPageAliases( $code)
Get special page aliases for a specific language.
Definition: languages.inc:321
array $mIgnoredMessages
All the messages which should be exist only in the English file.
Definition: languages.inc:44
$fallback
Definition: MessagesAb.php:11
getObsoleteMagicWords( $code)
Get the obsolete magic words.
Definition: languages.inc:680
array $mGeneralMessages
General messages in English, divided to groups.
Definition: languages.inc:41
getUntranslatedMessages( $code)
Get the untranslated messages for a specific language.
Definition: languages.inc:334
getMessagesWithDubiousLinks( $code)
Get the messages which include dubious links.
Definition: languages.inc:545
getMessagesWithWhitespace( $code)
Get the messages with trailing whitespace.
Definition: languages.inc:458
getOverridingMagicWords( $code)
Get the magic words that override the original English magic word.
Definition: languages.inc:700
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:692
name()
Get the extension name.
Definition: languages.inc:810
array $mLanguages
List of languages.
Definition: languages.inc:29
getMessagesWithWrongChars( $code)
Get the messages which include wrong characters.
Definition: languages.inc:507
loadMessages( $code)
Load the messages for a specific language (which is not English) and divide them to groups: all - all...
Definition: languages.inc:163
array $mNamespaceNames
Namespace names.
Definition: languages.inc:50
array $mMagicWords
Magic words.
Definition: languages.inc:56
getProblematicProjectTalks( $code)
Get the project talk namespace names with no $1.
Definition: languages.inc:631
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
loadGeneralMessages()
Load the messages for English and divide them to groups: all - all the messages.
Definition: languages.inc:198
$matches