Splitting the lists of ignored, optional and EXIF messages to a separate file.
[mediawiki.git] / maintenance / language / languages.inc
blob8093e58daf356d56f30b43c0de8307eb937fb2f7
1 <?php
2 /**
3  * Handle messages in the language files.
4  *
5  * @package MediaWiki
6  * @subpackage Maintenance
7  */
9 require_once( 'messageTypes.inc' );
11 class languages {
12         private $mLanguages; # List of languages
13         private $mRawMessages; # Raw list of the messages in each language
14         private $mMessages; # Messages in each language (except for English), divided to groups
15         private $mGeneralMessages; # General messages in English, divided to groups
16         private $mIgnoredMessages; # All the messages which should be exist only in the English file
17         private $mOptionalMessages; # All the messages which may be translated or not, depending on the language
19         /**
20          * Load the list of languages: all the Messages*.php
21          * files in the languages directory.
22          *
23          * @param $exif Treat the EXIF messages?
24          */
25         function __construct( $exif = true ) {
26                 global $wgIgnoredMessages, $wgOptionalMessages, $wgEXIFMessages;
27                 $this->mIgnoredMessages = $wgIgnoredMessages;
28                 if ( $exif ) {
29                         $this->mOptionalMessages = array_merge( $wgOptionalMessages );
30                 } else {
31                         $this->mOptionalMessages = array_merge( $wgOptionalMessages, $wgEXIFMessages );
32                 }
34                 $this->mLanguages = array_keys( Language::getLanguageNames( true ) );
35                 sort( $this->mLanguages );
36         }
38         /**
39          * Get the language list.
40          *
41          * @return The language list.
42          */
43         public function getLanguages() {
44                 return $this->mLanguages;
45         }
47         /**
48          * Load the raw messages for a specific langauge from the messages file.
49          *
50          * @param $code The langauge code.
51          */
52         private function loadRawMessages( $code ) {
53                 if ( isset( $this->mRawMessages[$code] ) ) {
54                         return;
55                 }
56                 $filename = Language::getMessagesFileName( $code );
57                 if ( file_exists( $filename ) ) {
58                         require( $filename );
59                         if ( isset( $messages ) ) {
60                                 $this->mRawMessages[$code] = $messages;
61                         } else {
62                                 $this->mRawMessages[$code] = array();
63                         }
64                 } else {
65                         $this->mRawMessages[$code] = array();
66                 }
67         }
69         /**
70          * Load the messages for a specific language (which is not English) and divide them to groups:
71          * all - all the messages.
72          * required - messages which should be translated in order to get a complete translation.
73          * optional - messages which can be translated, the fallback translation is used if not translated.
74          * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
75          * translated - messages which are either required or optional, but translated from English and needed.
76          *
77          * @param $code The language code.
78          */
79         private function loadMessages( $code ) {
80                 if ( isset( $this->mMessages[$code] ) ) {
81                         return;
82                 }
83                 $this->loadRawMessages( $code );
84                 $this->loadGeneralMessages();
85                 $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
86                 $this->mMessages[$code]['required'] = array();
87                 $this->mMessages[$code]['optional'] = array();
88                 $this->mMessages[$code]['obsolete'] = array();
89                 $this->mMessages[$code]['translated'] = array();
90                 foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
91                         if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
92                                 $this->mMessages[$code]['required'][$key] = $value;
93                                 $this->mMessages[$code]['translated'][$key] = $value;
94                         } else if ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
95                                 $this->mMessages[$code]['optional'][$key] = $value;
96                                 $this->mMessages[$code]['translated'][$key] = $value;
97                         } else {
98                                 $this->mMessages[$code]['obsolete'][$key] = $value;
99                         }
100                 }
101         }
103         /**
104          * Load the messages for English and divide them to groups:
105          * all - all the messages.
106          * required - messages which should be translated to other languages in order to get a complete translation.
107          * optional - messages which can be translated to other languages, but it's not required for a complete translation.
108          * ignored - messages which should not be translated to other languages.
109          * translatable - messages which are either required or optional, but can be translated from English.
110          */
111         private function loadGeneralMessages() {
112                 if ( isset( $this->mGeneralMessages ) ) {
113                         return;
114                 }
115                 $this->loadRawMessages( 'en' );
116                 $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
117                 $this->mGeneralMessages['required'] = array();
118                 $this->mGeneralMessages['optional'] = array();
119                 $this->mGeneralMessages['ignored'] = array();
120                 $this->mGeneralMessages['translatable'] = array();
121                 foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
122                         if ( in_array( $key, $this->mIgnoredMessages ) ) {
123                                 $this->mGeneralMessages['ignored'][$key] = $value;
124                         } else if ( in_array( $key, $this->mOptionalMessages ) ) {
125                                 $this->mGeneralMessages['optional'][$key] = $value;
126                                 $this->mGeneralMessages['translatable'][$key] = $value;
127                         } else {
128                                 $this->mGeneralMessages['required'][$key] = $value;
129                                 $this->mGeneralMessages['translatable'][$key] = $value;
130                         }
131                 }
132         }
134         /**
135          * Get all the messages for a specific langauge (not English), without the
136          * fallback language messages, divided to groups:
137          * all - all the messages.
138          * required - messages which should be translated in order to get a complete translation.
139          * optional - messages which can be translated, the fallback translation is used if not translated.
140          * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
141          * translated - messages which are either required or optional, but translated from English and needed.
142          *
143          * @param $code The langauge code.
144          *
145          * @return The messages in this language.
146          */
147         public function getMessages( $code ) {
148                 $this->loadMessages( $code );
149                 return $this->mMessages[$code];
150         }
152         /**
153          * Get all the general English messages, divided to groups:
154          * all - all the messages.
155          * required - messages which should be translated to other languages in order to get a complete translation.
156          * optional - messages which can be translated to other languages, but it's not required for a complete translation.
157          * ignored - messages which should not be translated to other languages.
158          * translatable - messages which are either required or optional, but can be translated from English.
159          *
160          * @return The general English messages.
161          */
162         public function getGeneralMessages() {
163                 $this->loadGeneralMessages();
164                 return $this->mGeneralMessages;
165         }
167         /**
168          * Get the untranslated messages for a specific language.
169          *
170          * @param $code The langauge code.
171          *
172          * @return The untranslated messages for this language.
173          */
174         public function getUntranslatedMessages( $code ) {
175                 $this->loadGeneralMessages();
176                 $this->loadMessages( $code );
177                 $requiredGeneralMessages = array_keys( $this->mGeneralMessages['required'] );
178                 $requiredMessages = array_keys( $this->mMessages[$code]['required'] );
179                 $untranslatedMessages = array();
180                 foreach ( array_diff( $requiredGeneralMessages, $requiredMessages ) as $key ) {
181                         $untranslatedMessages[$key] = $this->mGeneralMessages['required'][$key];
182                 }
183                 return $untranslatedMessages;
184         }
186         /**
187          * Get the duplicate messages for a specific language.
188          *
189          * @param $code The langauge code.
190          *
191          * @return The duplicate messages for this language.
192          */
193         public function getDuplicateMessages( $code ) {
194                 $this->loadGeneralMessages();
195                 $this->loadMessages( $code );
196                 $duplicateMessages = array();
197                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
198                         if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
199                                 $duplicateMessages[$key] = $value;
200                         }
201                 }
202                 return $duplicateMessages;
203         }
205         /**
206          * Get the messages which do not use some variables.
207          *
208          * @param $code The langauge code.
209          *
210          * @return The messages which do not use some variables in this language.
211          */
212         public function getMessagesWithoutVariables( $code ) {
213                 $this->loadGeneralMessages();
214                 $this->loadMessages( $code );
215                 $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
216                 $messagesWithoutVariables = array();
217                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
218                         $missing = false;
219                         foreach ( $variables as $var ) {
220                                 if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
221                                         !preg_match( "/$var/sU", $value ) ) {
222                                         $missing = true;
223                                 }
224                         }
225                         if ( $missing ) {
226                                 $messagesWithoutVariables[$key] = $value;
227                         }
228                 }
229                 return $messagesWithoutVariables;
230         }
232         /**
233          * Get the empty messages.
234          *
235          * @param $code The langauge code.
236          *
237          * @return The empty messages for this language.
238          */
239         public function getEmptyMessages( $code ) {
240                 $this->loadGeneralMessages();
241                 $this->loadMessages( $code );
242                 $emptyMessages = array();
243                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
244                         if ( $value === '' || $value === '-' ) {
245                                 $emptyMessages[$key] = $value;
246                         }
247                 }
248                 return $emptyMessages;
249         }
251         /**
252          * Get the messages with trailing whitespace.
253          *
254          * @param $code The langauge code.
255          *
256          * @return The messages with trailing whitespace in this language.
257          */
258         public function getMessagesWithWhitespace( $code ) {
259                 $this->loadGeneralMessages();
260                 $this->loadMessages( $code );
261                 $messagesWithWhitespace = array();
262                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
263                         if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
264                                 $messagesWithWhitespace[$key] = $value;
265                         }
266                 }
267                 return $messagesWithWhitespace;
268         }
270         /**
271          * Get the non-XHTML messages.
272          *
273          * @param $code The langauge code.
274          *
275          * @return The non-XHTML messages for this language.
276          */
277         public function getNonXHTMLMessages( $code ) {
278                 $this->loadGeneralMessages();
279                 $this->loadMessages( $code );
280                 $wrongPhrases = array(
281                         '<hr *\\?>',
282                         '<br *\\?>',
283                         '<hr/>',
284                         '<br/>',
285                 );
286                 $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
287                 $nonXHTMLMessages = array();
288                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
289                         if ( preg_match( $wrongPhrases, $value ) ) {
290                                 $nonXHTMLMessages[$key] = $value;
291                         }
292                 }
293                 return $nonXHTMLMessages;
294         }
296         /**
297          * Get the messages which include wrong characters.
298          *
299          * @param $code The langauge code.
300          *
301          * @return The messages which include wrong characters in this language.
302          */
303         public function getMessagesWithWrongChars( $code ) {
304                 $this->loadGeneralMessages();
305                 $this->loadMessages( $code );
306                 $wrongChars = array(
307                         '[LRM]' => "\xE2\x80\x8E",
308                         '[RLM]' => "\xE2\x80\x8F",
309                         '[LRE]' => "\xE2\x80\xAA",
310                         '[RLE]' => "\xE2\x80\xAB",
311                         '[POP]' => "\xE2\x80\xAC",
312                         '[LRO]' => "\xE2\x80\xAD",
313                         '[RLO]' => "\xE2\x80\xAB",
314                         '[ZWSP]'=> "\xE2\x80\x8B",
315                         '[NBSP]'=> "\xC2\xA0",
316                         '[WJ]'  => "\xE2\x81\xA0",
317                         '[BOM]' => "\xEF\xBB\xBF",
318                         '[FFFD]'=> "\xEF\xBF\xBD",
319                 );
320                 $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
321                 $wrongCharsMessages = array();
322                 foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
323                         if ( preg_match( $wrongRegExp, $value ) ) {
324                                 foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
325                                         $value = str_replace( $hiddenChar, $viewableChar, $value );
326                                 }
327                                 $wrongCharsMessages[$key] = $value;
328                         }
329                 }
330                 return $wrongCharsMessages;
331         }
333         /**
334          * Output a messages list
335          *
336          * @param $messages The messages list
337          * @param $code The language code
338          * @param $text The text to show before the list (optional)
339          * @param $level The display level (optional)
340          * @param $links Show links (optional)
341          * @param $wikilang The langauge of the wiki to display the list in, for the links (optional)
342          */
343         public function outputMessagesList( $messages, $code, $text = '', $level = 2, $links = false, $wikilang = null ) {
344                 if ( count( $messages ) == 0 ) {
345                         return;
346                 }
347                 if ( $text ) {
348                         echo "$text\n";
349                 }
350                 if ( $level == 1 ) {
351                         echo "[messages are hidden]\n";
352                 } else {
353                         foreach ( $messages as $key => $value ) {
354                                 if ( $links ) {
355                                         $displayKey = ucfirst( $key );
356                                         if ( !isset( $wikilang ) ) {
357                                                 global $wgContLang;
358                                                 $wikilang = $wgContLang->getCode();
359                                         }
360                                         if ( $code == $wikilang ) {
361                                                 $displayKey = "[[MediaWiki:$displayKey|$key]]";
362                                         } else {
363                                                 $displayKey = "[[MediaWiki:$displayKey/$code|$key]]";
364                                         }
365                                 } else {
366                                         $displayKey = $key;
367                                 }
368                                 if ( $level == 2 ) {
369                                         echo "* $displayKey\n";
370                                 } else {
371                                         echo "* $displayKey:            '$value'\n";
372                                 }
373                         }
374                 }
375         }