Merge "Make update.php file executable"
[mediawiki.git] / maintenance / language / languages.inc
blob6e5b29d86dbc1fc09af498f64623a5b09dd59d6b
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  */
24 /**
25  * @ingroup MaintenanceLanguage
26  */
27 class Languages {
28         /** @var array List of languages */
29         protected $mLanguages;
31         /** @var array Raw list of the messages in each language */
32         protected $mRawMessages;
34         /** @var array Messages in each language (except for English), divided to groups */
35         protected $mMessages;
37         /** @var array Fallback language in each language */
38         protected $mFallback;
40         /** @var array General messages in English, divided to groups */
41         protected $mGeneralMessages;
43         /** @var array All the messages which should be exist only in the English file */
44         protected $mIgnoredMessages;
46         /** @var array All the messages which may be translated or not, depending on the language */
47         protected $mOptionalMessages;
49         /** @var array Namespace names */
50         protected $mNamespaceNames;
52         /** @var array Namespace aliases */
53         protected $mNamespaceAliases;
55         /** @var array Magic words */
56         protected $mMagicWords;
58         /** @var  array Special page aliases */
59         protected $mSpecialPageAliases;
61         /**
62          * Load the list of languages: all the Messages*.php
63          * files in the languages directory.
64          *
65          * @param bool $exif Treat the Exif messages?
66          */
67         function __construct( $exif = true ) {
68                 require __DIR__ . '/messageTypes.inc';
69                 $this->mIgnoredMessages = $wgIgnoredMessages;
70                 if ( $exif ) {
71                         $this->mOptionalMessages = array_merge( $wgOptionalMessages );
72                 } else {
73                         $this->mOptionalMessages = array_merge( $wgOptionalMessages, $wgEXIFMessages );
74                 }
76                 $this->mLanguages = array_keys( Language::fetchLanguageNames( null, 'mwfile' ) );
77                 sort( $this->mLanguages );
78         }
80         /**
81          * Get the language list.
82          *
83          * @return array The language list.
84          */
85         public function getLanguages() {
86                 return $this->mLanguages;
87         }
89         /**
90          * Get the ignored messages list.
91          *
92          * @return array The ignored messages list.
93          */
94         public function getIgnoredMessages() {
95                 return $this->mIgnoredMessages;
96         }
98         /**
99          * Get the optional messages list.
100          *
101          * @return array The  optional messages list.
102          */
103         public function getOptionalMessages() {
104                 return $this->mOptionalMessages;
105         }
107         /**
108          * Load the language file.
109          *
110          * @param string $code The language code.
111          */
112         protected function loadFile( $code ) {
113                 if ( isset( $this->mRawMessages[$code] ) &&
114                         isset( $this->mFallback[$code] ) &&
115                         isset( $this->mNamespaceNames[$code] ) &&
116                         isset( $this->mNamespaceAliases[$code] ) &&
117                         isset( $this->mMagicWords[$code] ) &&
118                         isset( $this->mSpecialPageAliases[$code] )
119                 ) {
120                         return;
121                 }
122                 $this->mRawMessages[$code] = array();
123                 $this->mFallback[$code] = '';
124                 $this->mNamespaceNames[$code] = array();
125                 $this->mNamespaceAliases[$code] = array();
126                 $this->mMagicWords[$code] = array();
127                 $this->mSpecialPageAliases[$code] = array();
129                 $jsonfilename = Language::getJsonMessagesFileName( $code );
130                 if ( file_exists( $jsonfilename ) ) {
131                         $json = Language::getLocalisationCache()->readJSONFile( $jsonfilename );
132                         $this->mRawMessages[$code] = $json['messages'];
133                 }
135                 $filename = Language::getMessagesFileName( $code );
136                 if ( file_exists( $filename ) ) {
137                         require $filename;
138                         if ( isset( $fallback ) ) {
139                                 $this->mFallback[$code] = $fallback;
140                         }
141                         if ( isset( $namespaceNames ) ) {
142                                 $this->mNamespaceNames[$code] = $namespaceNames;
143                         }
144                         if ( isset( $namespaceAliases ) ) {
145                                 $this->mNamespaceAliases[$code] = $namespaceAliases;
146                         }
147                         if ( isset( $magicWords ) ) {
148                                 $this->mMagicWords[$code] = $magicWords;
149                         }
150                         if ( isset( $specialPageAliases ) ) {
151                                 $this->mSpecialPageAliases[$code] = $specialPageAliases;
152                         }
153                 }
154         }
156         /**
157          * Load the messages for a specific language (which is not English) and divide them to
158          * groups:
159          * all - all the messages.
160          * required - messages which should be translated in order to get a complete translation.
161          * optional - messages which can be translated, the fallback translation is used if not
162          *   translated.
163          * obsolete - messages which should not be translated, either because they do not exist,
164          *   or they are ignored messages.
165          * translated - messages which are either required or optional, but translated from
166          *   English and needed.
167          *
168          * @param string $code The language code.
169          */
170         private function loadMessages( $code ) {
171                 if ( isset( $this->mMessages[$code] ) ) {
172                         return;
173                 }
174                 $this->loadFile( $code );
175                 $this->loadGeneralMessages();
176                 $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
177                 $this->mMessages[$code]['required'] = array();
178                 $this->mMessages[$code]['optional'] = array();
179                 $this->mMessages[$code]['obsolete'] = array();
180                 $this->mMessages[$code]['translated'] = array();
181                 foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
182                         if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
183                                 $this->mMessages[$code]['required'][$key] = $value;
184                                 $this->mMessages[$code]['translated'][$key] = $value;
185                         } elseif ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
186                                 $this->mMessages[$code]['optional'][$key] = $value;
187                                 $this->mMessages[$code]['translated'][$key] = $value;
188                         } else {
189                                 $this->mMessages[$code]['obsolete'][$key] = $value;
190                         }
191                 }
192         }
194         /**
195          * Load the messages for English and divide them to groups:
196          * all - all the messages.
197          * required - messages which should be translated to other languages in order to get a
198          *   complete translation.
199          * optional - messages which can be translated to other languages, but it's not required
200          *   for a complete translation.
201          * ignored - messages which should not be translated to other languages.
202          * translatable - messages which are either required or optional, but can be translated
203          *   from English.
204          */
205         private function loadGeneralMessages() {
206                 if ( isset( $this->mGeneralMessages ) ) {
207                         return;
208                 }
209                 $this->loadFile( 'en' );
210                 $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
211                 $this->mGeneralMessages['required'] = array();
212                 $this->mGeneralMessages['optional'] = array();
213                 $this->mGeneralMessages['ignored'] = array();
214                 $this->mGeneralMessages['translatable'] = array();
215                 foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
216                         if ( in_array( $key, $this->mIgnoredMessages ) ) {
217                                 $this->mGeneralMessages['ignored'][$key] = $value;
218                         } elseif ( in_array( $key, $this->mOptionalMessages ) ) {
219                                 $this->mGeneralMessages['optional'][$key] = $value;
220                                 $this->mGeneralMessages['translatable'][$key] = $value;
221                         } else {
222                                 $this->mGeneralMessages['required'][$key] = $value;
223                                 $this->mGeneralMessages['translatable'][$key] = $value;
224                         }
225                 }
226         }
228         /**
229          * Get all the messages for a specific language (not English), without the
230          * fallback language messages, divided to groups:
231          * all - all the messages.
232          * required - messages which should be translated in order to get a complete translation.
233          * optional - messages which can be translated, the fallback translation is used if not
234          *   translated.
235          * obsolete - messages which should not be translated, either because they do not exist,
236          *   or they are ignored messages.
237          * translated - messages which are either required or optional, but translated from
238          *   English and needed.
239          *
240          * @param string $code The language code.
241          *
242          * @return string The messages in this language.
243          */
244         public function getMessages( $code ) {
245                 $this->loadMessages( $code );
247                 return $this->mMessages[$code];
248         }
250         /**
251          * Get all the general English messages, divided to groups:
252          * all - all the messages.
253          * required - messages which should be translated to other languages in
254          *   order to get a complete translation.
255          * optional - messages which can be translated to other languages, but it's
256          *   not required for a complete translation.
257          * ignored - messages which should not be translated to other languages.
258          * translatable - messages which are either required or optional, but can be
259          *   translated from English.
260          *
261          * @return array The general English messages.
262          */
263         public function getGeneralMessages() {
264                 $this->loadGeneralMessages();
266                 return $this->mGeneralMessages;
267         }
269         /**
270          * Get fallback language code for a specific language.
271          *
272          * @param string $code The language code.
273          *
274          * @return string Fallback code.
275          */
276         public function getFallback( $code ) {
277                 $this->loadFile( $code );
279                 return $this->mFallback[$code];
280         }
282         /**
283          * Get namespace names for a specific language.
284          *
285          * @param string $code The language code.
286          *
287          * @return array Namespace names.
288          */
289         public function getNamespaceNames( $code ) {
290                 $this->loadFile( $code );
292                 return $this->mNamespaceNames[$code];
293         }
295         /**
296          * Get namespace aliases for a specific language.
297          *
298          * @param string $code The language code.
299          *
300          * @return array Namespace aliases.
301          */
302         public function getNamespaceAliases( $code ) {
303                 $this->loadFile( $code );
305                 return $this->mNamespaceAliases[$code];
306         }
308         /**
309          * Get magic words for a specific language.
310          *
311          * @param string $code The language code.
312          *
313          * @return array Magic words.
314          */
315         public function getMagicWords( $code ) {
316                 $this->loadFile( $code );
318                 return $this->mMagicWords[$code];
319         }
321         /**
322          * Get special page aliases for a specific language.
323          *
324          * @param string $code The language code.
325          *
326          * @return array Special page aliases.
327          */
328         public function getSpecialPageAliases( $code ) {
329                 $this->loadFile( $code );
331                 return $this->mSpecialPageAliases[$code];
332         }
334         /**
335          * Get the untranslated messages for a specific language.
336          *
337          * @param string $code The language code.
338          *
339          * @return array The untranslated messages for this language.
340          */
341         public function getUntranslatedMessages( $code ) {
342                 $this->loadGeneralMessages();
343                 $this->loadMessages( $code );
345                 return array_diff_key( $this->mGeneralMessages['required'], $this->mMessages[$code]['required'] );
346         }
348         /**
349          * Get the duplicate messages for a specific language.
350          *
351          * @param string $code The language code.
352          *
353          * @return array The duplicate messages for this language.
354          */
355         public function getDuplicateMessages( $code ) {
356                 $this->loadGeneralMessages();
357                 $this->loadMessages( $code );
358                 $duplicateMessages = array();
359                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
360                         if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
361                                 $duplicateMessages[$key] = $value;
362                         }
363                 }
365                 return $duplicateMessages;
366         }
368         /**
369          * Get the obsolete messages for a specific language.
370          *
371          * @param string $code The language code.
372          *
373          * @return array The obsolete messages for this language.
374          */
375         public function getObsoleteMessages( $code ) {
376                 $this->loadGeneralMessages();
377                 $this->loadMessages( $code );
379                 return $this->mMessages[$code]['obsolete'];
380         }
382         /**
383          * Get the messages whose variables do not match the original ones.
384          *
385          * @param string $code The language code.
386          *
387          * @return array The messages whose variables do not match the original ones.
388          */
389         public function getMessagesWithMismatchVariables( $code ) {
390                 $this->loadGeneralMessages();
391                 $this->loadMessages( $code );
392                 $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
393                 $mismatchMessages = array();
394                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
395                         $missing = false;
396                         foreach ( $variables as $var ) {
397                                 if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
398                                         !preg_match( "/$var/sU", $value )
399                                 ) {
400                                         $missing = true;
401                                 }
402                                 if ( !preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
403                                         preg_match( "/$var/sU", $value )
404                                 ) {
405                                         $missing = true;
406                                 }
407                         }
408                         if ( $missing ) {
409                                 $mismatchMessages[$key] = $value;
410                         }
411                 }
413                 return $mismatchMessages;
414         }
416         /**
417          * Get the messages which do not use plural.
418          *
419          * @param string $code The language code.
420          *
421          * @return array The messages which do not use plural in this language.
422          */
423         public function getMessagesWithoutPlural( $code ) {
424                 $this->loadGeneralMessages();
425                 $this->loadMessages( $code );
426                 $messagesWithoutPlural = array();
427                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
428                         if ( stripos( $this->mGeneralMessages['translatable'][$key], '{{plural:' ) !== false &&
429                                 stripos( $value, '{{plural:' ) === false
430                         ) {
431                                 $messagesWithoutPlural[$key] = $value;
432                         }
433                 }
435                 return $messagesWithoutPlural;
436         }
438         /**
439          * Get the empty messages.
440          *
441          * @param string $code The language code.
442          *
443          * @return array The empty messages for this language.
444          */
445         public function getEmptyMessages( $code ) {
446                 $this->loadGeneralMessages();
447                 $this->loadMessages( $code );
448                 $emptyMessages = array();
449                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
450                         if ( $value === '' || $value === '-' ) {
451                                 $emptyMessages[$key] = $value;
452                         }
453                 }
455                 return $emptyMessages;
456         }
458         /**
459          * Get the messages with trailing whitespace.
460          *
461          * @param string $code The language code.
462          *
463          * @return array The messages with trailing whitespace in this language.
464          */
465         public function getMessagesWithWhitespace( $code ) {
466                 $this->loadGeneralMessages();
467                 $this->loadMessages( $code );
468                 $messagesWithWhitespace = array();
469                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
470                         if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
471                                 $messagesWithWhitespace[$key] = $value;
472                         }
473                 }
475                 return $messagesWithWhitespace;
476         }
478         /**
479          * Get the non-XHTML messages.
480          *
481          * @param string $code The language code.
482          *
483          * @return array The non-XHTML messages for this language.
484          */
485         public function getNonXHTMLMessages( $code ) {
486                 $this->loadGeneralMessages();
487                 $this->loadMessages( $code );
488                 $wrongPhrases = array(
489                         '<hr *\\?>',
490                         '<br *\\?>',
491                         '<hr/>',
492                         '<br/>',
493                         '<hr>',
494                         '<br>',
495                 );
496                 $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
497                 $nonXHTMLMessages = array();
498                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
499                         if ( preg_match( $wrongPhrases, $value ) ) {
500                                 $nonXHTMLMessages[$key] = $value;
501                         }
502                 }
504                 return $nonXHTMLMessages;
505         }
507         /**
508          * Get the messages which include wrong characters.
509          *
510          * @param string $code The language code.
511          *
512          * @return array The messages which include wrong characters in this language.
513          */
514         public function getMessagesWithWrongChars( $code ) {
515                 $this->loadGeneralMessages();
516                 $this->loadMessages( $code );
517                 $wrongChars = array(
518                         '[LRM]' => "\xE2\x80\x8E",
519                         '[RLM]' => "\xE2\x80\x8F",
520                         '[LRE]' => "\xE2\x80\xAA",
521                         '[RLE]' => "\xE2\x80\xAB",
522                         '[POP]' => "\xE2\x80\xAC",
523                         '[LRO]' => "\xE2\x80\xAD",
524                         '[RLO]' => "\xE2\x80\xAB",
525                         '[ZWSP]' => "\xE2\x80\x8B",
526                         '[NBSP]' => "\xC2\xA0",
527                         '[WJ]' => "\xE2\x81\xA0",
528                         '[BOM]' => "\xEF\xBB\xBF",
529                         '[FFFD]' => "\xEF\xBF\xBD",
530                 );
531                 $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
532                 $wrongCharsMessages = array();
533                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
534                         if ( preg_match( $wrongRegExp, $value ) ) {
535                                 foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
536                                         $value = str_replace( $hiddenChar, $viewableChar, $value );
537                                 }
538                                 $wrongCharsMessages[$key] = $value;
539                         }
540                 }
542                 return $wrongCharsMessages;
543         }
545         /**
546          * Get the messages which include dubious links.
547          *
548          * @param string $code The language code.
549          *
550          * @return array The messages which include dubious links in this language.
551          */
552         public function getMessagesWithDubiousLinks( $code ) {
553                 $this->loadGeneralMessages();
554                 $this->loadMessages( $code );
555                 $tc = Title::legalChars() . '#%{}';
556                 $messages = array();
557                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
558                         $matches = array();
559                         preg_match_all( "/\[\[([{$tc}]+)(?:\\|(.+?))?]]/sDu", $value, $matches );
560                         $numMatches = count( $matches[0] );
561                         for ( $i = 0; $i < $numMatches; $i++ ) {
562                                 if ( preg_match( "/.*project.*/isDu", $matches[1][$i] ) ) {
563                                         $messages[$key][] = $matches[0][$i];
564                                 }
565                         }
567                         if ( isset( $messages[$key] ) ) {
568                                 $messages[$key] = implode( $messages[$key], ", " );
569                         }
570                 }
572                 return $messages;
573         }
575         /**
576          * Get the messages which include unbalanced brackets.
577          *
578          * @param string $code The language code.
579          *
580          * @return array The messages which include unbalanced brackets in this language.
581          */
582         public function getMessagesWithUnbalanced( $code ) {
583                 $this->loadGeneralMessages();
584                 $this->loadMessages( $code );
585                 $messages = array();
586                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
587                         $a = $b = $c = $d = 0;
588                         foreach ( preg_split( '//', $value ) as $char ) {
589                                 switch ( $char ) {
590                                         case '[':
591                                                 $a++;
592                                                 break;
593                                         case ']':
594                                                 $b++;
595                                                 break;
596                                         case '{':
597                                                 $c++;
598                                                 break;
599                                         case '}':
600                                                 $d++;
601                                                 break;
602                                 }
603                         }
605                         if ( $a !== $b || $c !== $d ) {
606                                 $messages[$key] = "$a, $b, $c, $d";
607                         }
608                 }
610                 return $messages;
611         }
613         /**
614          * Get the untranslated namespace names.
615          *
616          * @param string $code The language code.
617          *
618          * @return array The untranslated namespace names in this language.
619          */
620         public function getUntranslatedNamespaces( $code ) {
621                 $this->loadFile( 'en' );
622                 $this->loadFile( $code );
623                 $namespacesDiff = array_diff_key( $this->mNamespaceNames['en'], $this->mNamespaceNames[$code] );
624                 if ( isset( $namespacesDiff[NS_MAIN] ) ) {
625                         unset( $namespacesDiff[NS_MAIN] );
626                 }
628                 return $namespacesDiff;
629         }
631         /**
632          * Get the project talk namespace names with no $1.
633          *
634          * @param string $code The language code.
635          *
636          * @return array The problematic project talk namespaces in this language.
637          */
638         public function getProblematicProjectTalks( $code ) {
639                 $this->loadFile( $code );
640                 $namespaces = array();
642                 # Check default namespace name
643                 if ( isset( $this->mNamespaceNames[$code][NS_PROJECT_TALK] ) ) {
644                         $default = $this->mNamespaceNames[$code][NS_PROJECT_TALK];
645                         if ( strpos( $default, '$1' ) === false ) {
646                                 $namespaces[$default] = 'default';
647                         }
648                 }
650                 # Check namespace aliases
651                 foreach ( $this->mNamespaceAliases[$code] as $key => $value ) {
652                         if ( $value == NS_PROJECT_TALK && strpos( $key, '$1' ) === false ) {
653                                 $namespaces[$key] = '';
654                         }
655                 }
657                 return $namespaces;
658         }
660         /**
661          * Get the untranslated magic words.
662          *
663          * @param string $code The language code.
664          *
665          * @return array The untranslated magic words in this language.
666          */
667         public function getUntranslatedMagicWords( $code ) {
668                 $this->loadFile( 'en' );
669                 $this->loadFile( $code );
670                 $magicWords = array();
671                 foreach ( $this->mMagicWords['en'] as $key => $value ) {
672                         if ( !isset( $this->mMagicWords[$code][$key] ) ) {
673                                 $magicWords[$key] = $value[1];
674                         }
675                 }
677                 return $magicWords;
678         }
680         /**
681          * Get the obsolete magic words.
682          *
683          * @param string $code The language code.
684          *
685          * @return array The obsolete magic words in this language.
686          */
687         public function getObsoleteMagicWords( $code ) {
688                 $this->loadFile( 'en' );
689                 $this->loadFile( $code );
690                 $magicWords = array();
691                 foreach ( $this->mMagicWords[$code] as $key => $value ) {
692                         if ( !isset( $this->mMagicWords['en'][$key] ) ) {
693                                 $magicWords[$key] = $value[1];
694                         }
695                 }
697                 return $magicWords;
698         }
700         /**
701          * Get the magic words that override the original English magic word.
702          *
703          * @param string $code The language code.
704          *
705          * @return array The overriding magic words in this language.
706          */
707         public function getOverridingMagicWords( $code ) {
708                 $this->loadFile( 'en' );
709                 $this->loadFile( $code );
710                 $magicWords = array();
711                 foreach ( $this->mMagicWords[$code] as $key => $local ) {
712                         if ( !isset( $this->mMagicWords['en'][$key] ) ) {
713                                 # Unrecognized magic word
714                                 continue;
715                         }
716                         $en = $this->mMagicWords['en'][$key];
717                         array_shift( $local );
718                         array_shift( $en );
719                         foreach ( $en as $word ) {
720                                 if ( !in_array( $word, $local ) ) {
721                                         $magicWords[$key] = $word;
722                                         break;
723                                 }
724                         }
725                 }
727                 return $magicWords;
728         }
730         /**
731          * Get the magic words which do not match the case-sensitivity of the original words.
732          *
733          * @param string $code The language code.
734          *
735          * @return array The magic words whose case does not match in this language.
736          */
737         public function getCaseMismatchMagicWords( $code ) {
738                 $this->loadFile( 'en' );
739                 $this->loadFile( $code );
740                 $magicWords = array();
741                 foreach ( $this->mMagicWords[$code] as $key => $local ) {
742                         if ( !isset( $this->mMagicWords['en'][$key] ) ) {
743                                 # Unrecognized magic word
744                                 continue;
745                         }
746                         if ( $local[0] != $this->mMagicWords['en'][$key][0] ) {
747                                 $magicWords[$key] = $local[0];
748                         }
749                 }
751                 return $magicWords;
752         }
754         /**
755          * Get the untranslated special page names.
756          *
757          * @param string $code The language code.
758          *
759          * @return array The untranslated special page names in this language.
760          */
761         public function getUntraslatedSpecialPages( $code ) {
762                 $this->loadFile( 'en' );
763                 $this->loadFile( $code );
764                 $specialPageAliases = array();
765                 foreach ( $this->mSpecialPageAliases['en'] as $key => $value ) {
766                         if ( !isset( $this->mSpecialPageAliases[$code][$key] ) ) {
767                                 $specialPageAliases[$key] = $value[0];
768                         }
769                 }
771                 return $specialPageAliases;
772         }
774         /**
775          * Get the obsolete special page names.
776          *
777          * @param string $code The language code.
778          *
779          * @return array The obsolete special page names in this language.
780          */
781         public function getObsoleteSpecialPages( $code ) {
782                 $this->loadFile( 'en' );
783                 $this->loadFile( $code );
784                 $specialPageAliases = array();
785                 foreach ( $this->mSpecialPageAliases[$code] as $key => $value ) {
786                         if ( !isset( $this->mSpecialPageAliases['en'][$key] ) ) {
787                                 $specialPageAliases[$key] = $value[0];
788                         }
789                 }
791                 return $specialPageAliases;
792         }
795 class ExtensionLanguages extends Languages {
796         /**
797          * @var MessageGroup
798          */
799         private $mMessageGroup;
801         /**
802          * Load the messages group.
803          * @param MessageGroup $group The messages group.
804          */
805         function __construct( MessageGroup $group ) {
806                 $this->mMessageGroup = $group;
808                 $this->mIgnoredMessages = $this->mMessageGroup->getIgnored();
809                 $this->mOptionalMessages = $this->mMessageGroup->getOptional();
810         }
812         /**
813          * Get the extension name.
814          *
815          * @return string The extension name.
816          */
817         public function name() {
818                 return $this->mMessageGroup->getLabel();
819         }
821         /**
822          * Load the language file.
823          *
824          * @param string $code The language code.
825          */
826         protected function loadFile( $code ) {
827                 if ( !isset( $this->mRawMessages[$code] ) ) {
828                         $this->mRawMessages[$code] = $this->mMessageGroup->load( $code );
829                         if ( empty( $this->mRawMessages[$code] ) ) {
830                                 $this->mRawMessages[$code] = array();
831                         }
832                 }
833         }