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