MediaWiki REL1_31
languages.inc
Go to the documentation of this file.
1<?php
27class Languages {
29 protected $mLanguages;
30
32 protected $mRawMessages;
33
35 protected $mMessages;
36
38 protected $mFallback;
39
42
45
48
51
54
56 protected $mMagicWords;
57
60
65 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
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]' => "\xE2\x80\x8E",
512 '[RLM]' => "\xE2\x80\x8F",
513 '[LRE]' => "\xE2\x80\xAA",
514 '[RLE]' => "\xE2\x80\xAB",
515 '[POP]' => "\xE2\x80\xAC",
516 '[LRO]' => "\xE2\x80\xAD",
517 '[RLO]' => "\xE2\x80\xAB",
518 '[ZWSP]' => "\xE2\x80\x8B",
519 '[NBSP]' => "\xC2\xA0",
520 '[WJ]' => "\xE2\x81\xA0",
521 '[BOM]' => "\xEF\xBB\xBF",
522 '[FFFD]' => "\xEF\xBF\xBD",
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
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
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
755 $this->loadFile( 'en' );
756 $this->loadFile( $code );
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 );
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
793
798 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
$namespaceNames
$messages
$fallback
$namespaceAliases
__construct(MessageGroup $group)
Load the messages group.
loadFile( $code)
Load the language file.
name()
Get the extension name.
MessageGroup $mMessageGroup
array $mRawMessages
Raw list of the messages in each language.
Definition languages.inc:32
getMagicWords( $code)
Get magic words for a specific language.
loadMessages( $code)
Load the messages for a specific language (which is not English) and divide them to groups: all - all...
getUntranslatedMagicWords( $code)
Get the untranslated magic words.
getUntranslatedMessages( $code)
Get the untranslated messages for a specific language.
array $mIgnoredMessages
All the messages which should be exist only in the English file.
Definition languages.inc:44
getUntranslatedNamespaces( $code)
Get the untranslated namespace names.
getNamespaceAliases( $code)
Get namespace aliases for a specific language.
array $mSpecialPageAliases
Special page aliases.
Definition languages.inc:59
getMessagesWithUnbalanced( $code)
Get the messages which include unbalanced brackets.
getDuplicateMessages( $code)
Get the duplicate messages for a specific language.
loadGeneralMessages()
Load the messages for English and divide them to groups: all - all the messages.
getFallback( $code)
Get fallback language code for a specific language.
getOptionalMessages()
Get the optional messages list.
Definition languages.inc:96
array $mMagicWords
Magic words.
Definition languages.inc:56
getUntraslatedSpecialPages( $code)
Get the untranslated special page names.
array $mOptionalMessages
All the messages which may be translated or not, depending on the language.
Definition languages.inc:47
array $mLanguages
List of languages.
Definition languages.inc:29
array $mGeneralMessages
General messages in English, divided to groups.
Definition languages.inc:41
getOverridingMagicWords( $code)
Get the magic words that override the original English magic word.
getMessagesWithDubiousLinks( $code)
Get the messages which include dubious links.
getProblematicProjectTalks( $code)
Get the project talk namespace names with no $1.
getMessagesWithWrongChars( $code)
Get the messages which include wrong characters.
getCaseMismatchMagicWords( $code)
Get the magic words which do not match the case-sensitivity of the original words.
getObsoleteMagicWords( $code)
Get the obsolete magic words.
getNamespaceNames( $code)
Get namespace names for a specific language.
getMessagesWithWhitespace( $code)
Get the messages with trailing whitespace.
__construct()
Load the list of languages: all the Messages*.php files in the languages directory.
Definition languages.inc:65
array $mMessages
Messages in each language (except for English), divided to groups.
Definition languages.inc:35
getIgnoredMessages()
Get the ignored messages list.
Definition languages.inc:87
getLanguages()
Get the language list.
Definition languages.inc:78
array $mNamespaceNames
Namespace names.
Definition languages.inc:50
getMessagesWithMismatchVariables( $code)
Get the messages whose variables do not match the original ones.
getGeneralMessages()
Get all the general English messages, divided to groups: all - all the messages.
getEmptyMessages( $code)
Get the empty messages.
getSpecialPageAliases( $code)
Get special page aliases for a specific language.
getObsoleteMessages( $code)
Get the obsolete messages for a specific language.
getMessages( $code)
Get all the messages for a specific language (not English), without the fallback language messages,...
loadFile( $code)
Load the language file.
array $mNamespaceAliases
Namespace aliases.
Definition languages.inc:53
getMessagesWithoutPlural( $code)
Get the messages which do not use plural.
getNonXHTMLMessages( $code)
Get the non-XHTML messages.
getObsoleteSpecialPages( $code)
Get the obsolete special page names.
array $mFallback
Fallback language in each language.
Definition languages.inc:38
namespace and then decline to actually register it & $namespaces
Definition hooks.txt:934
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition hooks.txt:865
const NS_MAIN
Definition Defines.php:74
const NS_PROJECT_TALK
Definition Defines.php:79
magicword txt Magic Words are some phrases used in the wikitext They are used for two that looks like templates but that don t accept any parameter *Parser functions(like {{fullurl:...}}, {{#special:...}}) $magicWords['en']
Definition magicword.txt:33