3 * Handle messages in the language files.
6 * @subpackage Maintenance
10 private $mLanguages; # List of languages
11 private $mRawMessages; # Raw list of the messages in each language
12 private $mMessages; # Messages in each language (except for English), divided to groups
13 private $mGeneralMessages; # General messages in English, divided to groups
14 private $mIgnoredMessages = array(
22 'exif-software-value',
27 'markaspatrolledlink',
30 'number_of_watching_users_RCview',
35 'shareddescriptionfollows',
44 ); # All the messages which should be exist only in the English file
45 private $mOptionalMessages = array(
49 'booksources-summary',
50 'ipblocklist-summary',
53 'preferences-summary',
54 'specialpages-summary',
55 'whatlinkshere-summary',
70 ); # All the messages which may be translated or not, depending on the language
71 private $mEXIFMessages = array(
76 'exif-photometricinterpretation',
78 'exif-samplesperpixel',
79 'exif-planarconfiguration',
80 'exif-ycbcrsubsampling',
81 'exif-ycbcrpositioning',
84 'exif-resolutionunit',
87 'exif-stripbytecounts',
88 'exif-jpeginterchangeformat',
89 'exif-jpeginterchangeformatlength',
90 'exif-transferfunction',
92 'exif-primarychromaticities',
93 'exif-ycbcrcoefficients',
94 'exif-referenceblackwhite',
96 'exif-imagedescription',
103 'exif-flashpixversion',
105 'exif-componentsconfiguration',
106 'exif-compressedbitsperpixel',
107 'exif-pixelydimension',
108 'exif-pixelxdimension',
111 'exif-relatedsoundfile',
112 'exif-datetimeoriginal',
113 'exif-datetimedigitized',
115 'exif-subsectimeoriginal',
116 'exif-subsectimedigitized',
118 'exif-exposuretime-format',
120 'exif-fnumber-format',
121 'exif-exposureprogram',
122 'exif-spectralsensitivity',
123 'exif-isospeedratings',
125 'exif-shutterspeedvalue',
126 'exif-aperturevalue',
127 'exif-brightnessvalue',
128 'exif-exposurebiasvalue',
129 'exif-maxaperturevalue',
130 'exif-subjectdistance',
135 'exif-focallength-format',
138 'exif-spatialfrequencyresponse',
139 'exif-focalplanexresolution',
140 'exif-focalplaneyresolution',
141 'exif-focalplaneresolutionunit',
142 'exif-subjectlocation',
143 'exif-exposureindex',
144 'exif-sensingmethod',
148 'exif-customrendered',
151 'exif-digitalzoomratio',
152 'exif-focallengthin35mmfilm',
153 'exif-scenecapturetype',
158 'exif-devicesettingdescription',
159 'exif-subjectdistancerange',
160 'exif-imageuniqueid',
162 'exif-gpslatituderef',
164 'exif-gpslongituderef',
166 'exif-gpsaltituderef',
169 'exif-gpssatellites',
171 'exif-gpsmeasuremode',
177 'exif-gpsimgdirectionref',
178 'exif-gpsimgdirection',
180 'exif-gpsdestlatituderef',
181 'exif-gpsdestlatitude',
182 'exif-gpsdestlongituderef',
183 'exif-gpsdestlongitude',
184 'exif-gpsdestbearingref',
185 'exif-gpsdestbearing',
186 'exif-gpsdestdistanceref',
187 'exif-gpsdestdistance',
188 'exif-gpsprocessingmethod',
189 'exif-gpsareainformation',
191 'exif-gpsdifferential',
192 'exif-compression-1',
193 'exif-compression-6',
194 'exif-photometricinterpretation-2',
195 'exif-photometricinterpretation-6',
196 'exif-orientation-1',
197 'exif-orientation-2',
198 'exif-orientation-3',
199 'exif-orientation-4',
200 'exif-orientation-5',
201 'exif-orientation-6',
202 'exif-orientation-7',
203 'exif-orientation-8',
204 'exif-planarconfiguration-1',
205 'exif-planarconfiguration-2',
206 'exif-xyresolution-i',
207 'exif-xyresolution-c',
209 'exif-colorspace-ffff.h',
210 'exif-componentsconfiguration-0',
211 'exif-componentsconfiguration-1',
212 'exif-componentsconfiguration-2',
213 'exif-componentsconfiguration-3',
214 'exif-componentsconfiguration-4',
215 'exif-componentsconfiguration-5',
216 'exif-componentsconfiguration-6',
217 'exif-exposureprogram-0',
218 'exif-exposureprogram-1',
219 'exif-exposureprogram-2',
220 'exif-exposureprogram-3',
221 'exif-exposureprogram-4',
222 'exif-exposureprogram-5',
223 'exif-exposureprogram-6',
224 'exif-exposureprogram-7',
225 'exif-exposureprogram-8',
226 'exif-subjectdistance-value',
227 'exif-meteringmode-0',
228 'exif-meteringmode-1',
229 'exif-meteringmode-2',
230 'exif-meteringmode-3',
231 'exif-meteringmode-4',
232 'exif-meteringmode-5',
233 'exif-meteringmode-6',
234 'exif-meteringmode-255',
235 'exif-lightsource-0',
236 'exif-lightsource-1',
237 'exif-lightsource-2',
238 'exif-lightsource-3',
239 'exif-lightsource-4',
240 'exif-lightsource-9',
241 'exif-lightsource-10',
242 'exif-lightsource-11',
243 'exif-lightsource-12',
244 'exif-lightsource-13',
245 'exif-lightsource-14',
246 'exif-lightsource-15',
247 'exif-lightsource-17',
248 'exif-lightsource-18',
249 'exif-lightsource-19',
250 'exif-lightsource-20',
251 'exif-lightsource-21',
252 'exif-lightsource-22',
253 'exif-lightsource-23',
254 'exif-lightsource-24',
255 'exif-lightsource-255',
256 'exif-focalplaneresolutionunit-2',
257 'exif-sensingmethod-1',
258 'exif-sensingmethod-2',
259 'exif-sensingmethod-3',
260 'exif-sensingmethod-4',
261 'exif-sensingmethod-5',
262 'exif-sensingmethod-7',
263 'exif-sensingmethod-8',
266 'exif-customrendered-0',
267 'exif-customrendered-1',
268 'exif-exposuremode-0',
269 'exif-exposuremode-1',
270 'exif-exposuremode-2',
271 'exif-whitebalance-0',
272 'exif-whitebalance-1',
273 'exif-scenecapturetype-0',
274 'exif-scenecapturetype-1',
275 'exif-scenecapturetype-2',
276 'exif-scenecapturetype-3',
277 'exif-gaincontrol-0',
278 'exif-gaincontrol-1',
279 'exif-gaincontrol-2',
280 'exif-gaincontrol-3',
281 'exif-gaincontrol-4',
291 'exif-subjectdistancerange-0',
292 'exif-subjectdistancerange-1',
293 'exif-subjectdistancerange-2',
294 'exif-subjectdistancerange-3',
295 'exif-gpslatitude-n',
296 'exif-gpslatitude-s',
297 'exif-gpslongitude-e',
298 'exif-gpslongitude-w',
301 'exif-gpsmeasuremode-2',
302 'exif-gpsmeasuremode-3',
306 'exif-gpsdirection-t',
307 'exif-gpsdirection-m',
308 ); # All the EXIF messages, may be set as optional if defined as such
311 * Load the list of languages: all the Messages*.php
312 * files in the languages directory.
314 * @param $exif Treat the EXIF messages?
316 function __construct( $exif = true ) {
317 $this->mLanguages = array_keys( Language::getLanguageNames( true ) );
318 sort( $this->mLanguages );
320 $this->mOptionalMessages = array_merge( $this->mOptionalMessages, $this->mEXIFMessages );
325 * Get the language list.
327 * @return The language list.
329 public function getLanguages() {
330 return $this->mLanguages;
334 * Load the raw messages for a specific langauge from the messages file.
336 * @param $code The langauge code.
338 private function loadRawMessages( $code ) {
339 if ( isset( $this->mRawMessages[$code] ) ) {
342 $filename = Language::getMessagesFileName( $code );
343 if ( file_exists( $filename ) ) {
344 require( $filename );
345 if ( isset( $messages ) ) {
346 $this->mRawMessages[$code] = $messages;
348 $this->mRawMessages[$code] = array();
351 $this->mRawMessages[$code] = array();
356 * Load the messages for a specific language (which is not English) and divide them to groups:
357 * all - all the messages.
358 * required - messages which should be translated in order to get a complete translation.
359 * optional - messages which can be translated, the fallback translation is used if not translated.
360 * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
361 * translated - messages which are either required or optional, but translated from English and needed.
363 * @param $code The language code.
365 private function loadMessages( $code ) {
366 if ( isset( $this->mMessages[$code] ) ) {
369 $this->loadRawMessages( $code );
370 $this->loadGeneralMessages();
371 $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
372 $this->mMessages[$code]['required'] = array();
373 $this->mMessages[$code]['optional'] = array();
374 $this->mMessages[$code]['obsolete'] = array();
375 $this->mMessages[$code]['translated'] = array();
376 foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
377 if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
378 $this->mMessages[$code]['required'][$key] = $value;
379 $this->mMessages[$code]['translated'][$key] = $value;
380 } else if ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
381 $this->mMessages[$code]['optional'][$key] = $value;
382 $this->mMessages[$code]['translated'][$key] = $value;
384 $this->mMessages[$code]['obsolete'][$key] = $value;
390 * Load the messages for English and divide them to groups:
391 * all - all the messages.
392 * required - messages which should be translated to other languages in order to get a complete translation.
393 * optional - messages which can be translated to other languages, but it's not required for a complete translation.
394 * ignored - messages which should not be translated to other languages.
395 * translatable - messages which are either required or optional, but can be translated from English.
397 private function loadGeneralMessages() {
398 if ( isset( $this->mGeneralMessages ) ) {
401 $this->loadRawMessages( 'en' );
402 $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
403 $this->mGeneralMessages['required'] = array();
404 $this->mGeneralMessages['optional'] = array();
405 $this->mGeneralMessages['ignored'] = array();
406 $this->mGeneralMessages['translatable'] = array();
407 foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
408 if ( in_array( $key, $this->mIgnoredMessages ) ) {
409 $this->mGeneralMessages['ignored'][$key] = $value;
410 } else if ( in_array( $key, $this->mOptionalMessages ) ) {
411 $this->mGeneralMessages['optional'][$key] = $value;
412 $this->mGeneralMessages['translatable'][$key] = $value;
414 $this->mGeneralMessages['required'][$key] = $value;
415 $this->mGeneralMessages['translatable'][$key] = $value;
421 * Get all the messages for a specific langauge (not English), without the
422 * fallback language messages, divided to groups:
423 * all - all the messages.
424 * required - messages which should be translated in order to get a complete translation.
425 * optional - messages which can be translated, the fallback translation is used if not translated.
426 * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
427 * translated - messages which are either required or optional, but translated from English and needed.
429 * @param $code The langauge code.
431 * @return The messages in this language.
433 public function getMessages( $code ) {
434 $this->loadMessages( $code );
435 return $this->mMessages[$code];
439 * Get all the general English messages, divided to groups:
440 * all - all the messages.
441 * required - messages which should be translated to other languages in order to get a complete translation.
442 * optional - messages which can be translated to other languages, but it's not required for a complete translation.
443 * ignored - messages which should not be translated to other languages.
444 * translatable - messages which are either required or optional, but can be translated from English.
446 * @return The general English messages.
448 public function getGeneralMessages() {
449 $this->loadGeneralMessages();
450 return $this->mGeneralMessages;
454 * Get the untranslated messages for a specific language.
456 * @param $code The langauge code.
458 * @return The untranslated messages for this language.
460 public function getUntranslatedMessages( $code ) {
461 $this->loadGeneralMessages();
462 $this->loadMessages( $code );
463 $requiredGeneralMessages = array_keys( $this->mGeneralMessages['required'] );
464 $requiredMessages = array_keys( $this->mMessages[$code]['required'] );
465 $untranslatedMessages = array();
466 foreach ( array_diff( $requiredGeneralMessages, $requiredMessages ) as $key ) {
467 $untranslatedMessages[$key] = $this->mGeneralMessages['required'][$key];
469 return $untranslatedMessages;
473 * Get the duplicate messages for a specific language.
475 * @param $code The langauge code.
477 * @return The duplicate messages for this language.
479 public function getDuplicateMessages( $code ) {
480 $this->loadGeneralMessages();
481 $this->loadMessages( $code );
482 $duplicateMessages = array();
483 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
484 if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
485 $duplicateMessages[$key] = $value;
488 return $duplicateMessages;
492 * Get the messages which do not use some variables.
494 * @param $code The langauge code.
496 * @return The messages which do not use some variables in this language.
498 public function getMessagesWithoutVariables( $code ) {
499 $this->loadGeneralMessages();
500 $this->loadMessages( $code );
501 $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
502 $messagesWithoutVariables = array();
503 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
505 foreach ( $variables as $var ) {
506 if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
507 !preg_match( "/$var/sU", $value ) ) {
512 $messagesWithoutVariables[$key] = $value;
515 return $messagesWithoutVariables;
519 * Get the empty messages.
521 * @param $code The langauge code.
523 * @return The empty messages for this language.
525 public function getEmptyMessages( $code ) {
526 $this->loadGeneralMessages();
527 $this->loadMessages( $code );
528 $emptyMessages = array();
529 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
530 if ( $value === '' || $value === '-' ) {
531 $emptyMessages[$key] = $value;
534 return $emptyMessages;
538 * Get the messages with trailing whitespace.
540 * @param $code The langauge code.
542 * @return The messages with trailing whitespace in this language.
544 public function getMessagesWithWhitespace( $code ) {
545 $this->loadGeneralMessages();
546 $this->loadMessages( $code );
547 $messagesWithWhitespace = array();
548 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
549 if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
550 $messagesWithWhitespace[$key] = $value;
553 return $messagesWithWhitespace;
557 * Get the non-XHTML messages.
559 * @param $code The langauge code.
561 * @return The non-XHTML messages for this language.
563 public function getNonXHTMLMessages( $code ) {
564 $this->loadGeneralMessages();
565 $this->loadMessages( $code );
566 $wrongPhrases = array(
572 $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
573 $nonXHTMLMessages = array();
574 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
575 if ( preg_match( $wrongPhrases, $value ) ) {
576 $nonXHTMLMessages[$key] = $value;
579 return $nonXHTMLMessages;
583 * Get the messages which include wrong characters.
585 * @param $code The langauge code.
587 * @return The messages which include wrong characters in this language.
589 public function getMessagesWithWrongChars( $code ) {
590 $this->loadGeneralMessages();
591 $this->loadMessages( $code );
593 '[LRM]' => "\xE2\x80\x8E",
594 '[RLM]' => "\xE2\x80\x8F",
595 '[LRE]' => "\xE2\x80\xAA",
596 '[RLE]' => "\xE2\x80\xAB",
597 '[POP]' => "\xE2\x80\xAC",
598 '[LRO]' => "\xE2\x80\xAD",
599 '[RLO]' => "\xE2\x80\xAB",
600 '[ZWSP]'=> "\xE2\x80\x8B",
601 '[NBSP]'=> "\xC2\xA0",
602 '[WJ]' => "\xE2\x81\xA0",
603 '[BOM]' => "\xEF\xBB\xBF",
604 '[FFFD]'=> "\xEF\xBF\xBD",
606 $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
607 $wrongCharsMessages = array();
608 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
609 if ( preg_match( $wrongRegExp, $value ) ) {
610 foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
611 $value = str_replace( $hiddenChar, $viewableChar, $value );
613 $wrongCharsMessages[$key] = $value;
616 return $wrongCharsMessages;
620 * Output a messages list
622 * @param $messages The messages list
623 * @param $code The language code
624 * @param $text The text to show before the list (optional)
625 * @param $level The display level (optional)
626 * @param $links Show links (optional)
627 * @param $wikilang The langauge of the wiki to display the list in, for the links (optional)
629 public function outputMessagesList( $messages, $code, $text = '', $level = 2, $links = false, $wikilang = null ) {
630 if ( count( $messages ) == 0 ) {
637 echo "[messages are hidden]\n";
639 foreach ( $messages as $key => $value ) {
641 $displayKey = ucfirst( $key );
642 if ( !isset( $wikilang ) ) {
644 $wikilang = $wgContLang->getCode();
646 if ( $code == $wikilang ) {
647 $displayKey = "[[MediaWiki:$displayKey|$key]]";
649 $displayKey = "[[MediaWiki:$displayKey/$code|$key]]";
655 echo "* $displayKey\n";
657 echo "* $displayKey: '$value'\n";