]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - maintenance/language/languages.inc
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / maintenance / language / languages.inc
1 <?php
2 /**
3  * Handle messages in the language files.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup MaintenanceLanguage
22  */
23
24 /**
25  * @ingroup MaintenanceLanguage
26  */
27 class Languages {
28         /** @var array List of languages */
29         protected $mLanguages;
30
31         /** @var array Raw list of the messages in each language */
32         protected $mRawMessages;
33
34         /** @var array Messages in each language (except for English), divided to groups */
35         protected $mMessages;
36
37         /** @var array Fallback language in each language */
38         protected $mFallback;
39
40         /** @var array General messages in English, divided to groups */
41         protected $mGeneralMessages;
42
43         /** @var array All the messages which should be exist only in the English file */
44         protected $mIgnoredMessages;
45
46         /** @var array All the messages which may be translated or not, depending on the language */
47         protected $mOptionalMessages;
48
49         /** @var array Namespace names */
50         protected $mNamespaceNames;
51
52         /** @var array Namespace aliases */
53         protected $mNamespaceAliases;
54
55         /** @var array Magic words */
56         protected $mMagicWords;
57
58         /** @var array Special page aliases */
59         protected $mSpecialPageAliases;
60
61         /**
62          * Load the list of languages: all the Messages*.php
63          * files in the languages directory.
64          */
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
73         /**
74          * Get the language list.
75          *
76          * @return array The language list.
77          */
78         public function getLanguages() {
79                 return $this->mLanguages;
80         }
81
82         /**
83          * Get the ignored messages list.
84          *
85          * @return array The ignored messages list.
86          */
87         public function getIgnoredMessages() {
88                 return $this->mIgnoredMessages;
89         }
90
91         /**
92          * Get the optional messages list.
93          *
94          * @return array The  optional messages list.
95          */
96         public function getOptionalMessages() {
97                 return $this->mOptionalMessages;
98         }
99
100         /**
101          * Load the language file.
102          *
103          * @param string $code The language code.
104          */
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
149         /**
150          * Load the messages for a specific language (which is not English) and divide them to
151          * groups:
152          * all - all the messages.
153          * required - messages which should be translated in order to get a complete translation.
154          * optional - messages which can be translated, the fallback translation is used if not
155          *   translated.
156          * obsolete - messages which should not be translated, either because they do not exist,
157          *   or they are ignored messages.
158          * translated - messages which are either required or optional, but translated from
159          *   English and needed.
160          *
161          * @param string $code The language code.
162          */
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
187         /**
188          * Load the messages for English and divide them to groups:
189          * all - all the messages.
190          * required - messages which should be translated to other languages in order to get a
191          *   complete translation.
192          * optional - messages which can be translated to other languages, but it's not required
193          *   for a complete translation.
194          * ignored - messages which should not be translated to other languages.
195          * translatable - messages which are either required or optional, but can be translated
196          *   from English.
197          */
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
221         /**
222          * Get all the messages for a specific language (not English), without the
223          * fallback language messages, divided to groups:
224          * all - all the messages.
225          * required - messages which should be translated in order to get a complete translation.
226          * optional - messages which can be translated, the fallback translation is used if not
227          *   translated.
228          * obsolete - messages which should not be translated, either because they do not exist,
229          *   or they are ignored messages.
230          * translated - messages which are either required or optional, but translated from
231          *   English and needed.
232          *
233          * @param string $code The language code.
234          *
235          * @return string The messages in this language.
236          */
237         public function getMessages( $code ) {
238                 $this->loadMessages( $code );
239
240                 return $this->mMessages[$code];
241         }
242
243         /**
244          * Get all the general English messages, divided to groups:
245          * all - all the messages.
246          * required - messages which should be translated to other languages in
247          *   order to get a complete translation.
248          * optional - messages which can be translated to other languages, but it's
249          *   not required for a complete translation.
250          * ignored - messages which should not be translated to other languages.
251          * translatable - messages which are either required or optional, but can be
252          *   translated from English.
253          *
254          * @return array The general English messages.
255          */
256         public function getGeneralMessages() {
257                 $this->loadGeneralMessages();
258
259                 return $this->mGeneralMessages;
260         }
261
262         /**
263          * Get fallback language code for a specific language.
264          *
265          * @param string $code The language code.
266          *
267          * @return string Fallback code.
268          */
269         public function getFallback( $code ) {
270                 $this->loadFile( $code );
271
272                 return $this->mFallback[$code];
273         }
274
275         /**
276          * Get namespace names for a specific language.
277          *
278          * @param string $code The language code.
279          *
280          * @return array Namespace names.
281          */
282         public function getNamespaceNames( $code ) {
283                 $this->loadFile( $code );
284
285                 return $this->mNamespaceNames[$code];
286         }
287
288         /**
289          * Get namespace aliases for a specific language.
290          *
291          * @param string $code The language code.
292          *
293          * @return array Namespace aliases.
294          */
295         public function getNamespaceAliases( $code ) {
296                 $this->loadFile( $code );
297
298                 return $this->mNamespaceAliases[$code];
299         }
300
301         /**
302          * Get magic words for a specific language.
303          *
304          * @param string $code The language code.
305          *
306          * @return array Magic words.
307          */
308         public function getMagicWords( $code ) {
309                 $this->loadFile( $code );
310
311                 return $this->mMagicWords[$code];
312         }
313
314         /**
315          * Get special page aliases for a specific language.
316          *
317          * @param string $code The language code.
318          *
319          * @return array Special page aliases.
320          */
321         public function getSpecialPageAliases( $code ) {
322                 $this->loadFile( $code );
323
324                 return $this->mSpecialPageAliases[$code];
325         }
326
327         /**
328          * Get the untranslated messages for a specific language.
329          *
330          * @param string $code The language code.
331          *
332          * @return array The untranslated messages for this language.
333          */
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
341         /**
342          * Get the duplicate messages for a specific language.
343          *
344          * @param string $code The language code.
345          *
346          * @return array The duplicate messages for this language.
347          */
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
361         /**
362          * Get the obsolete messages for a specific language.
363          *
364          * @param string $code The language code.
365          *
366          * @return array The obsolete messages for this language.
367          */
368         public function getObsoleteMessages( $code ) {
369                 $this->loadGeneralMessages();
370                 $this->loadMessages( $code );
371
372                 return $this->mMessages[$code]['obsolete'];
373         }
374
375         /**
376          * Get the messages whose variables do not match the original ones.
377          *
378          * @param string $code The language code.
379          *
380          * @return array The messages whose variables do not match the original ones.
381          */
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
409         /**
410          * Get the messages which do not use plural.
411          *
412          * @param string $code The language code.
413          *
414          * @return array The messages which do not use plural in this language.
415          */
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
431         /**
432          * Get the empty messages.
433          *
434          * @param string $code The language code.
435          *
436          * @return array The empty messages for this language.
437          */
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
451         /**
452          * Get the messages with trailing whitespace.
453          *
454          * @param string $code The language code.
455          *
456          * @return array The messages with trailing whitespace in this language.
457          */
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
471         /**
472          * Get the non-XHTML messages.
473          *
474          * @param string $code The language code.
475          *
476          * @return array The non-XHTML messages for this language.
477          */
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
500         /**
501          * Get the messages which include wrong characters.
502          *
503          * @param string $code The language code.
504          *
505          * @return array The messages which include wrong characters in this language.
506          */
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
538         /**
539          * Get the messages which include dubious links.
540          *
541          * @param string $code The language code.
542          *
543          * @return array The messages which include dubious links in this language.
544          */
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
568         /**
569          * Get the messages which include unbalanced brackets.
570          *
571          * @param string $code The language code.
572          *
573          * @return array The messages which include unbalanced brackets in this language.
574          */
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
606         /**
607          * Get the untranslated namespace names.
608          *
609          * @param string $code The language code.
610          *
611          * @return array The untranslated namespace names in this language.
612          */
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
624         /**
625          * Get the project talk namespace names with no $1.
626          *
627          * @param string $code The language code.
628          *
629          * @return array The problematic project talk namespaces in this language.
630          */
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
653         /**
654          * Get the untranslated magic words.
655          *
656          * @param string $code The language code.
657          *
658          * @return array The untranslated magic words in this language.
659          */
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
673         /**
674          * Get the obsolete magic words.
675          *
676          * @param string $code The language code.
677          *
678          * @return array The obsolete magic words in this language.
679          */
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
693         /**
694          * Get the magic words that override the original English magic word.
695          *
696          * @param string $code The language code.
697          *
698          * @return array The overriding magic words in this language.
699          */
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
723         /**
724          * Get the magic words which do not match the case-sensitivity of the original words.
725          *
726          * @param string $code The language code.
727          *
728          * @return array The magic words whose case does not match in this language.
729          */
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
747         /**
748          * Get the untranslated special page names.
749          *
750          * @param string $code The language code.
751          *
752          * @return array The untranslated special page names in this language.
753          */
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
767         /**
768          * Get the obsolete special page names.
769          *
770          * @param string $code The language code.
771          *
772          * @return array The obsolete special page names in this language.
773          */
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
788 class ExtensionLanguages extends Languages {
789         /**
790          * @var MessageGroup
791          */
792         private $mMessageGroup;
793
794         /**
795          * Load the messages group.
796          * @param MessageGroup $group The messages group.
797          */
798         function __construct( MessageGroup $group ) {
799                 $this->mMessageGroup = $group;
800
801                 $this->mIgnoredMessages = $this->mMessageGroup->getIgnored();
802                 $this->mOptionalMessages = $this->mMessageGroup->getOptional();
803         }
804
805         /**
806          * Get the extension name.
807          *
808          * @return string The extension name.
809          */
810         public function name() {
811                 return $this->mMessageGroup->getLabel();
812         }
813
814         /**
815          * Load the language file.
816          *
817          * @param string $code The language code.
818          */
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 }