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