r98323 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r98322‎ | r98323 | r98324 >
Date:16:19, 28 September 2011
Author:siebrand
Status:resolved (Comments)
Tags:
Comment:
Started taking PHP code out of the groups/ folder anticipating Wikimedia review. Later we will move all content of the groups folder elsewhere to reduce the number of changes of changes to the Translate extension considerably.
Modified paths:
  • /trunk/extensions/Translate/README (modified) (history)
  • /trunk/extensions/Translate/Translate.php (modified) (history)
  • /trunk/extensions/Translate/_autoload.php (modified) (history)
  • /trunk/extensions/Translate/ffs/MediaWikiChecker.php (added) (history)
  • /trunk/extensions/Translate/ffs/MediaWikiComplexMessages.php (added) (history)
  • /trunk/extensions/Translate/ffs/MediaWikiExtensions.php (added) (history)
  • /trunk/extensions/Translate/ffs/ToolserverTextdomains.php (added) (history)
  • /trunk/extensions/Translate/ffs/Voctrain.php (added) (history)
  • /trunk/extensions/Translate/ffs/WikiaExtensions.php (added) (history)
  • /trunk/extensions/Translate/groups/ComplexMessages.php (deleted) (history)
  • /trunk/extensions/Translate/groups/MediaWiki/Checker.php (deleted) (history)
  • /trunk/extensions/Translate/groups/MediaWikiExtensions.php (deleted) (history)
  • /trunk/extensions/Translate/groups/Toolserver/ToolserverTextdomains.php (deleted) (history)
  • /trunk/extensions/Translate/groups/Voctrain.php (deleted) (history)
  • /trunk/extensions/Translate/groups/Wikia/WikiaExtensions.php (deleted) (history)

Diff [purge]

Index: trunk/extensions/Translate/groups/ComplexMessages.php
@@ -1,708 +0,0 @@
2 -<?php
3 -/**
4 - * Classes for complex messages (%MediaWiki special page aliases, namespace names, magic words).
5 - *
6 - * @file
7 - * @author Niklas Laxström
8 - * @copyright Copyright © 2008-2010, Niklas Laxström
9 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
10 - */
11 -
12 -/**
13 - * Base class which implements handling and translation interface of
14 - * non-message %MediaWiki items.
15 - * @todo Needs documentation.
16 - */
17 -abstract class ComplexMessages {
18 -
19 - const LANG_MASTER = 0;
20 - const LANG_CHAIN = 1;
21 - const LANG_CURRENT = 2;
22 -
23 - protected $language = null;
24 - protected $id = '__BUG__';
25 - protected $variable = '__BUG__';
26 - protected $data = null;
27 - protected $elementsInArray = true;
28 - protected $databaseMsg = '__BUG__';
29 - protected $chainable = false;
30 - protected $firstMagic = false;
31 - protected $constants = array();
32 -
33 - protected $tableAttributes = array(
34 - 'class' => 'wikitable',
35 - 'border' => '2',
36 - 'cellpadding' => '4',
37 - 'cellspacing' => '0',
38 - 'style' => 'background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse;',
39 - );
40 -
41 - public function __construct( $language ) {
42 - $this->language = $language;
43 - }
44 -
45 - public function getTitle() {
46 - return wfMsg( 'translate-magic-' . $this->id );
47 - }
48 -
49 - #
50 - # Data retrieval
51 - #
52 - protected $init = false;
53 - public function getGroups() {
54 - if ( !$this->init ) {
55 - $saved = $this->getSavedData();
56 - foreach ( $this->data as &$group ) {
57 - $this->getData( $group, $saved );
58 - }
59 - $this->init = true;
60 - }
61 -
62 - return $this->data;
63 -
64 - }
65 -
66 - public function cleanData( $defs, $current ) {
67 - foreach ( $current as $item => $values ) {
68 - if ( !$this->elementsInArray ) {
69 - break;
70 - }
71 -
72 - if ( !isset( $defs[$item] ) ) {
73 - unset( $current[$item] );
74 - continue;
75 - }
76 -
77 - foreach ( $values as $index => $value )
78 - if ( in_array( $value, $defs[$item], true ) ) {
79 - unset( $current[$item][$index] );
80 - }
81 - }
82 - return $current;
83 - }
84 -
85 - public function mergeMagic( $defs, $current ) {
86 - foreach ( $current as $item => &$values ) {
87 - $newchain = $defs[$item];
88 - array_splice( $newchain, 1, 0, $values );
89 - $values = $newchain;
90 -
91 - }
92 - return $current;
93 - }
94 -
95 - public function getData( &$group, $savedData ) {
96 - $defs = $this->readVariable( $group, 'en' );
97 - $code = $this->language;
98 -
99 - $current = wfArrayMerge( $this->readVariable( $group, $code ), $savedData );
100 -
101 - // Clean up duplicates to definitions from saved data
102 - $current = $this->cleanData( $defs, $current );
103 -
104 - $chain = $current;
105 - if ( $this->chainable ) {
106 - foreach ( Language::getFallbacksFor( $code ) as $code ) {
107 - $fbdata = $this->readVariable( $group, $code );
108 - if ( $this->firstMagic ) {
109 - $fbdata = $this->cleanData( $defs, $fbdata );
110 - }
111 -
112 - $chain = array_merge_recursive( $chain, $fbdata );
113 - }
114 - }
115 -
116 - if ( $this->firstMagic ) {
117 - $chain = $this->mergeMagic( $defs, $chain );
118 - }
119 -
120 - $data = $group['data'] = array( $defs, $chain, $current );
121 -
122 - return $data;
123 - }
124 -
125 - /**
126 - * Gets data from request. Needs to be run before the form is displayed and
127 - * validation. Not needed for export, which uses request directly.
128 - */
129 - public function loadFromRequest( WebRequest $request ) {
130 - $saved = $this->parse( $this->formatForSave( $request ) );
131 - foreach ( $this->data as &$group ) {
132 - $this->getData( $group, $saved );
133 - }
134 - }
135 -
136 - /**
137 - * Gets saved data from Mediawiki namespace
138 - * @return Array
139 - */
140 - protected function getSavedData() {
141 - $data = TranslateUtils::getMessageContent( $this->databaseMsg, $this->language );
142 -
143 - if ( !$data ) {
144 - return array();
145 - } else {
146 - return $this->parse( $data );
147 - }
148 - }
149 -
150 - protected function parse( $data ) {
151 - $lines = array_map( 'trim', explode( "\n", $data ) );
152 - $array = array();
153 - foreach ( $lines as $line ) {
154 - if ( $line === '' || $line[0] === '#' || $line[0] === '<' ) {
155 - continue;
156 - }
157 -
158 - if ( strpos( $line, '=' ) === false ) {
159 - continue;
160 - }
161 -
162 - list( $name, $values ) = array_map( 'trim', explode( '=', $line, 2 ) );
163 - if ( $name === '' || $values === '' ) {
164 - continue;
165 - }
166 -
167 - $data = array_map( 'trim', explode( ',', $values ) );
168 - $array[$name] = $data;
169 - }
170 -
171 - return $array;
172 - }
173 -
174 - /**
175 - * Return an array of keys that can be used to iterate over all keys
176 - * @return Array of keys for data
177 - */
178 - protected function getIterator( $group ) {
179 - $groups = $this->getGroups();
180 - return array_keys( $groups[$group]['data'][self::LANG_MASTER] );
181 - }
182 -
183 - protected function val( $group, $type, $key ) {
184 - $array = $this->getGroups();
185 - $subarray = @$array[$group]['data'][$type][$key];
186 - if ( $this->elementsInArray ) {
187 - if ( !$subarray || !count( $subarray ) ) {
188 - return array();
189 - }
190 - } else {
191 - if ( !$subarray ) {
192 - return array();
193 - }
194 - }
195 -
196 - if ( !is_array( $subarray ) ) {
197 - $subarray = array( $subarray );
198 - }
199 -
200 - return $subarray;
201 - }
202 -
203 - /**
204 - */
205 - protected function readVariable( $group, $code ) {
206 - $file = $group['file'];
207 - if ( !$group['code'] ) {
208 - $file = str_replace( '%CODE%', str_replace( '-', '_', ucfirst( $code ) ), $file );
209 - }
210 -
211 - $ { $group['var'] } = array(); # Initialize
212 - if ( file_exists( $file ) ) {
213 - require( $file ); # Include
214 - }
215 -
216 - if ( $group['code'] ) {
217 - $data = (array) @$ { $group['var'] } [$code];
218 - } else {
219 - $data = $ { $group['var'] } ;
220 - }
221 -
222 - return self::arrayMapRecursive( 'strval', $data );
223 - }
224 -
225 - public static function arrayMapRecursive( $callback, $data ) {
226 - foreach ( $data as $index => $values ) {
227 - if ( is_array( $values ) ) {
228 - $data[$index] = self::arrayMapRecursive( $callback, $values );
229 - } else {
230 - $data[$index] = call_user_func( $callback, $values );
231 - }
232 - }
233 - return $data;
234 - }
235 -
236 - #
237 - # /Data retrieval
238 - #
239 -
240 - #
241 - # Output
242 - #
243 - public function header( $title ) {
244 - $colspan = array( 'colspan' => 3 );
245 - $header = Xml::element( 'th', $colspan, $this->getTitle() . ' - ' . $title );
246 - $subheading[] = '<th>' . wfMsgHtml( 'translate-magic-cm-original' ) . '</th>';
247 - $subheading[] = '<th>' . wfMsgHtml( 'translate-magic-cm-current' ) . '</th>';
248 - $subheading[] = '<th>' . wfMsgHtml( 'translate-magic-cm-to-be' ) . '</th>';
249 - return '<tr>' . $header . '</tr>' .
250 - '<tr>' . implode( "\n", $subheading ) . '</tr>';
251 - }
252 -
253 - public function output() {
254 - global $wgRequest;
255 -
256 - $colspan = array( 'colspan' => 3 );
257 -
258 - $s = Xml::openElement( 'table', $this->tableAttributes );
259 -
260 - foreach ( array_keys( $this->data ) as $group ) {
261 - $s .= $this->header( $this->data[$group]['label'] );
262 -
263 - foreach ( $this->getIterator( $group ) as $key ) {
264 - $rowContents = '';
265 -
266 - $value = $this->val( $group, self::LANG_MASTER, $key );
267 - if ( $this->firstMagic ) {
268 - array_shift( $value );
269 - }
270 -
271 - $value = array_map( 'htmlspecialchars', $value );
272 - $rowContents .= '<td>' . $this->formatElement( $value ) . '</td>';
273 -
274 - $value = $this->val( $group, self::LANG_CHAIN, $key );
275 - if ( $this->firstMagic ) {
276 - array_shift( $value );
277 - }
278 -
279 - $value = array_map( 'htmlspecialchars', $value );
280 - $value = $this->highlight( $key, $value );
281 - $rowContents .= '<td>' . $this->formatElement( $value ) . '</td>';
282 -
283 - $value = $this->val( $group, self::LANG_CURRENT, $key );
284 - $rowContents .= '<td>' . $this->editElement( $key, $this->formatElement( $value ) ) . '</td>';
285 -
286 - $s .= Xml::tags( 'tr', array( 'id' => "mw-sp-magic-$key" ), $rowContents );
287 - }
288 - }
289 -
290 - global $wgUser;
291 - if ( $wgUser->isAllowed( 'translate' ) ) {
292 - $s .= '<tr>' . Xml::tags( 'td', $colspan, $this->getButtons() ) . '<tr>';
293 - }
294 -
295 - $s .= Xml::closeElement( 'table' );
296 -
297 - return Xml::tags(
298 - 'form',
299 - array( 'method' => 'post', 'action' => $wgRequest->getRequestURL() ),
300 - $s
301 - );
302 - }
303 -
304 - public function getButtons() {
305 - return
306 - Xml::inputLabel( wfMsg( 'translate-magic-cm-comment' ), 'comment', 'sp-translate-magic-comment' ) .
307 - Xml::submitButton( wfMsg( 'translate-magic-cm-save' ), array( 'name' => 'savetodb' ) );
308 - }
309 -
310 - public function formatElement( $element ) {
311 - if ( !count( $element ) ) {
312 - return '';
313 - }
314 -
315 - if ( is_array( $element ) ) {
316 - $element = array_map( 'trim', $element );
317 - $element = implode( ', ', $element );
318 - }
319 - return trim( $element );
320 - }
321 -
322 - function getKeyForEdit( $key ) {
323 - return Sanitizer::escapeId( 'sp-translate-magic-cm-' . $this->id . $key );
324 - }
325 -
326 - public function editElement( $key, $contents ) {
327 - return Xml::input( $this->getKeyForEdit( $key ), 40, $contents );
328 - }
329 -
330 - #
331 - # /Output
332 - #
333 -
334 - #
335 - # Save to database
336 - #
337 -
338 - function getKeyForSave() {
339 - return $this->databaseMsg . '/' . $this->language;
340 - }
341 -
342 - function formatForSave( $request ) {
343 - $text = '';
344 - foreach ( array_keys( $this->data ) as $group ) {
345 - foreach ( $this->getIterator( $group ) as $key ) {
346 - $data = $request->getText( $this->getKeyForEdit( $key ) );
347 - // Make a nice array out of the submit with trimmed values.
348 - $data = array_map( 'trim', explode( ',', $data ) );
349 - // Normalise: Replace spaces with underscores.
350 - $data = str_replace( ' ', '_', $data );
351 - // Create final format.
352 - $data = implode( ', ', $data );
353 - if ( $data !== '' ) {
354 - $text .= "$key = $data\n" ;
355 - }
356 - }
357 - }
358 - return $text;
359 - }
360 -
361 - public function save( $request ) {
362 - $title = Title::newFromText( 'MediaWiki:' . $this->getKeyForSave() );
363 - $article = new Article( $title );
364 -
365 - $data = "# DO NOT EDIT THIS PAGE DIRECTLY! Use [[Special:AdvancedTranslate]].\n<pre>\n" . $this->formatForSave( $request ) . "\n</pre>";
366 -
367 - $comment = $request->getText( 'comment', wfMsgForContent( 'translate-magic-cm-updatedusing' ) );
368 - $status = $article->doEdit( $data, $comment, 0 );
369 -
370 - if ( $status === false || ( is_object( $status ) && !$status->isOK() ) ) {
371 - throw new MWException( wfMsg( 'translate-magic-cm-savefailed' ) );
372 - }
373 -
374 - /* Reset outdated array */
375 - $this->init = false;
376 - }
377 -
378 - #
379 - # !Save to database
380 - #
381 -
382 - #
383 - # Export
384 - #
385 - public function validate( &$errors = array(), $filter = false ) {
386 - $used = array();
387 - foreach ( array_keys( $this->data ) as $group ) {
388 - if ( $filter !== false && !in_array( $group, (array) $filter, true ) ) {
389 - continue;
390 - }
391 -
392 - $this->validateEach( $errors, $group, $used );
393 - }
394 - }
395 -
396 - protected function validateEach( &$errors = array(), $group, &$used ) {
397 - foreach ( $this->getIterator( $group ) as $key ) {
398 - $values = $this->val( $group, self::LANG_CURRENT, $key );
399 - $link = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$key" ), $key );
400 -
401 - if ( count( $values ) !== count( array_filter( $values ) ) ) {
402 - $errors[] = "There is empty value in $link.";
403 - }
404 -
405 - foreach ( $values as $v ) {
406 - if ( isset( $used[$v] ) ) {
407 - $otherkey = $used[$v];
408 - $first = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$otherkey" ), $otherkey );
409 - $errors[] = "Translation <b>$v</b> is used more than once for $first and $link.";
410 - } else {
411 - $used[$v] = $key;
412 - }
413 - }
414 - }
415 - }
416 -
417 - public function export( $filter = false ) {
418 - $text = '';
419 - $errors = array();
420 - $this->validate( $errors, $filter );
421 - foreach ( $errors as $_ ) $text .= "#!!# $_\n";
422 -
423 - foreach ( $this->getGroups() as $group => $data ) {
424 - if ( $filter !== false && !in_array( $group, (array) $filter, true ) ) {
425 - continue;
426 - }
427 -
428 - $text .= $this->exportEach( $group, $data );
429 - }
430 -
431 - return $text;
432 - }
433 -
434 - protected function exportEach( $group, $data ) {
435 - $var = $data['var'];
436 - $items = $data['data'];
437 -
438 - $extra = $data['code'] ? "['{$this->language}']" : '';
439 -
440 - $out = '';
441 -
442 - $indexKeys = array();
443 - foreach ( array_keys( $items[self::LANG_MASTER] ) as $key ) {
444 - $indexKeys[$key] = isset( $this->constants[$key] ) ? $this->constants[$key] : "'$key'";
445 - }
446 -
447 - $padTo = max( array_map( 'strlen', $indexKeys ) ) + 3;
448 -
449 - foreach ( $this->getIterator( $group ) as $key ) {
450 - $temp = "\t{$indexKeys[$key]}";
451 -
452 - while ( strlen( $temp ) <= $padTo ) {
453 - $temp .= ' ';
454 - }
455 -
456 - $from = self::LANG_CURRENT;
457 - // Abuse of the firstMagic property, should use something proper
458 - if ( $this->firstMagic ) {
459 - $from = self::LANG_CHAIN;
460 - }
461 -
462 - // Check for translations
463 - $val = $this->val( $group, self::LANG_CURRENT, $key );
464 - if ( !$val || !count( $val ) ) {
465 - continue;
466 - }
467 -
468 - // Then get the data we really want
469 - $val = $this->val( $group, $from, $key );
470 -
471 - // Remove duplicated entries, causes problems with magic words
472 - // Just to be sure, it should not be possible to save invalid data anymore
473 - $val = array_unique( $val /* @todo SORT_REGULAR */ );
474 -
475 - // So do empty elements...
476 - foreach ( $val as $k => $v ) {
477 - if ( $v === '' ) {
478 - unset( $val[$k] );
479 - }
480 - }
481 -
482 - // Another check
483 - if ( !count( $val ) ) {
484 - continue;
485 - }
486 -
487 - $normalized = array_map( array( $this, 'normalize' ), $val );
488 - if ( $this->elementsInArray ) {
489 - $temp .= "=> array( " . implode( ', ', $normalized ) . " ),";
490 - } else {
491 - $temp .= "=> " . implode( ', ', $normalized ) . ",";
492 - }
493 - $out .= $temp . "\n";
494 - }
495 -
496 - if ( $out !== '' ) {
497 - $text = "# {$data['label']} \n";
498 - $text .= "\$$var$extra = array(\n" . $out . ");\n\n";
499 - return $text;
500 - } else {
501 - return '';
502 - }
503 - }
504 -
505 - /**
506 - * Returns string with quotes that should be valid php
507 - */
508 - protected function normalize( $data ) {
509 - # Escape quotes
510 - if ( !is_string( $data ) ) {
511 - throw new MWException();
512 - }
513 - $data = preg_replace( "/(?<!\\\\)'/", "\'", trim( $data ) );
514 - return "'$data'";
515 - }
516 -
517 - #
518 - # /Export
519 - #
520 - public function highlight( $key, $values ) {
521 - return $values;
522 - }
523 -}
524 -
525 -/**
526 - * Adds support for translating special page aliases via Special:AdvancedTranslate.
527 - * @todo Needs documentation.
528 - */
529 -class SpecialPageAliasesCM extends ComplexMessages {
530 - protected $id = SpecialMagic::MODULE_SPECIAL;
531 - protected $databaseMsg = 'sp-translate-data-SpecialPageAliases';
532 - protected $chainable = true;
533 -
534 -
535 - public function __construct( $code ) {
536 - parent::__construct( $code );
537 - $this->data['core'] = array(
538 - 'label' => 'MediaWiki Core',
539 - 'var' => 'specialPageAliases',
540 - 'file' => Language::getMessagesFileName( '%CODE%' ),
541 - 'code' => false,
542 - );
543 -
544 - global $wgTranslateExtensionDirectory;
545 - $groups = MessageGroups::singleton()->getGroups();
546 - foreach ( $groups as $g ) {
547 - if ( !$g instanceof ExtensionMessageGroup ) {
548 - continue;
549 - }
550 -
551 - $file = $g->getAliasFile();
552 - if ( $file === null ) {
553 - continue;
554 - }
555 -
556 - $file = "$wgTranslateExtensionDirectory/$file";
557 - if ( file_exists( $file ) ) {
558 - $this->data[$g->getId()] = array(
559 - 'label' => $g->getLabel(),
560 - 'var' => $g->getVariableNameAlias(),
561 - 'file' => $file,
562 - 'code' => $code,
563 - );
564 - }
565 - }
566 - }
567 -
568 - public function highlight( $key, $values ) {
569 - if ( count( $values ) ) {
570 - if ( !isset( $values[0] ) ) {
571 - throw new MWException( "Something missing from values: " . print_r( $values, true ) );
572 - }
573 -
574 - $values[0] = "<b>$values[0]</b>";
575 - }
576 - return $values;
577 - }
578 -
579 - protected function validateEach( &$errors = array(), $group, &$used ) {
580 - parent::validateEach( $errors, $group, $used );
581 - foreach ( $this->getIterator( $group ) as $key ) {
582 - $values = $this->val( $group, self::LANG_CURRENT, $key );
583 -
584 - foreach ( $values as $_ ) {
585 - wfSuppressWarnings();
586 - $title = SpecialPage::getTitleFor( $_ );
587 - wfRestoreWarnings();
588 - $link = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$key" ), $key );
589 - if ( $title === null ) {
590 - if ( $_ !== '' ) {
591 - // Empty values checked elsewhere
592 - $errors[] = "Translation <b>$_</b> is invalid title in $link.";
593 - }
594 - } else {
595 - $text = $title->getText();
596 - $dbkey = $title->getDBkey();
597 - if ( $text !== $_ && $dbkey !== $_ ) {
598 - $errors[] = "Translation <b>$_</b> for $link is not in normalised form, which is <b>$text</b>";
599 - }
600 - }
601 - }
602 - }
603 - }
604 -}
605 -
606 -/**
607 - * Adds support for translating magic words via Special:AdvancedTranslate.
608 - * @todo Needs documentation.
609 - */
610 -class MagicWordsCM extends ComplexMessages {
611 - protected $id = SpecialMagic::MODULE_MAGIC;
612 - protected $firstMagic = true;
613 - protected $chainable = true;
614 - protected $databaseMsg = 'sp-translate-data-MagicWords';
615 -
616 - public function __construct( $code ) {
617 - parent::__construct( $code );
618 - $this->data['core'] = array(
619 - 'label' => 'MediaWiki Core',
620 - 'var' => 'magicWords',
621 - 'file' => Language::getMessagesFileName( '%CODE%' ),
622 - 'code' => false,
623 - );
624 -
625 - global $wgTranslateExtensionDirectory;
626 - $groups = MessageGroups::singleton()->getGroups();
627 - foreach ( $groups as $g ) {
628 - if ( !$g instanceof ExtensionMessageGroup ) {
629 - continue;
630 - }
631 -
632 - $file = $g->getMagicFile();
633 - if ( $file === null ) {
634 - continue;
635 - }
636 -
637 - $file = "$wgTranslateExtensionDirectory/$file";
638 - if ( file_exists( $file ) ) {
639 - $this->data[$g->getId()] = array(
640 - 'label' => $g->getLabel(),
641 - 'var' => 'magicWords',
642 - 'file' => $file,
643 - 'code' => $code,
644 - );
645 - }
646 - }
647 - }
648 -
649 - public function highlight( $key, $values ) {
650 - if ( count( $values ) && $key === 'redirect' ) {
651 - $values[0] = "<b>$values[0]</b>";
652 - }
653 -
654 - return $values;
655 - }
656 -}
657 -
658 -/**
659 - * Adds support for translating namespace names via Special:AdvancedTranslate.
660 - * @todo Needs documentation.
661 - */
662 -class NamespaceCM extends ComplexMessages {
663 - protected $id = SpecialMagic::MODULE_NAMESPACE;
664 - protected $elementsInArray = false;
665 - protected $databaseMsg = 'sp-translate-data-Namespaces';
666 -
667 - public function __construct( $code ) {
668 - parent::__construct( $code );
669 - $this->data['core'] = array(
670 - 'label' => 'MediaWiki Core',
671 - 'var' => 'namespaceNames',
672 - 'file' => Language::getMessagesFileName( '%CODE%' ),
673 - 'code' => false,
674 - );
675 - }
676 -
677 - protected $constants = array(
678 - -2 => 'NS_MEDIA',
679 - -1 => 'NS_SPECIAL',
680 - 0 => 'NS_MAIN',
681 - 1 => 'NS_TALK',
682 - 2 => 'NS_USER',
683 - 3 => 'NS_USER_TALK',
684 - 4 => 'NS_PROJECT',
685 - 5 => 'NS_PROJECT_TALK',
686 - 6 => 'NS_FILE',
687 - 7 => 'NS_FILE_TALK',
688 - 8 => 'NS_MEDIAWIKI',
689 - 9 => 'NS_MEDIAWIKI_TALK',
690 - 10 => 'NS_TEMPLATE',
691 - 11 => 'NS_TEMPLATE_TALK',
692 - 12 => 'NS_HELP',
693 - 13 => 'NS_HELP_TALK',
694 - 14 => 'NS_CATEGORY',
695 - 15 => 'NS_CATEGORY_TALK',
696 - );
697 -
698 - protected function validateEach( &$errors = array(), $group, &$used ) {
699 - parent::validateEach( $errors, $group, $used );
700 - foreach ( $this->getIterator( $group ) as $key ) {
701 - $values = $this->val( $group, self::LANG_CURRENT, $key );
702 -
703 - if ( count( $values ) > 1 ) {
704 - $link = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$key" ), $key );
705 - $errors[] = "Namespace $link can have only one translation. Replace the translation with a new one, and notify staff about the change.";
706 - }
707 - }
708 - }
709 -}
Index: trunk/extensions/Translate/groups/MediaWikiExtensions.php
@@ -1,230 +0,0 @@
2 -<?php
3 -/**
4 - * Classes for %MediaWiki extension translation.
5 - *
6 - * @file
7 - * @author Niklas Laxström
8 - * @copyright Copyright © 2008-2010, Niklas Laxström
9 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
10 - */
11 -
12 -/**
13 - * Class which handles special definition format for %MediaWiki extensions.
14 - */
15 -class PremadeMediawikiExtensionGroups {
16 - protected $groups;
17 - protected $definitionFile = null;
18 - protected $useConfigure = true;
19 - protected $idPrefix = 'ext-';
20 - protected $namespaces = array( NS_MEDIAWIKI, NS_MEDIAWIKI_TALK );
21 -
22 - public function __construct() {
23 - global $wgTranslateExtensionDirectory;
24 - $dir = dirname( __FILE__ );
25 - $this->definitionFile = $dir . '/mediawiki-defines.txt';
26 - $this->path = $wgTranslateExtensionDirectory;
27 - }
28 -
29 - /// Initialisation function
30 - public function init() {
31 - if ( $this->groups !== null ) return;
32 - $groups = $this->parseFile();
33 - $this->groups = $this->processGroups( $groups );
34 - }
35 -
36 - /// Makes an group id from extension name
37 - static function foldId( $name ) {
38 - return preg_replace( '/\s+/', '', strtolower( $name ) );
39 - }
40 -
41 - /// Registers all extensions
42 - public function addAll() {
43 - global $wgTranslateAC, $wgTranslateEC;
44 - $this->init();
45 -
46 - if ( !count( $this->groups ) ) return;
47 -
48 - foreach ( $this->groups as $id => $g ) {
49 - $wgTranslateAC[$id] = array( $this, 'factory' );
50 - $wgTranslateEC[] = $id;
51 - }
52 - }
53 -
54 - public function factory( $id ) {
55 - $info = $this->groups[$id];
56 - $group = ExtensionMessageGroup::factory( $info['name'], $id );
57 - $group->setMessageFile( $info['file'] );
58 - $group->setPath( $this->path );
59 - $group->namespaces = $this->namespaces;
60 -
61 - if ( isset( $info['prefix'] ) ) {
62 - $mangler = new StringMatcher( $info['prefix'], $info['mangle'] );
63 - $group->setMangler( $mangler );
64 - $info['ignored'] = $mangler->mangle( $info['ignored'] );
65 - $info['optional'] = $mangler->mangle( $info['optional'] );
66 - }
67 -
68 - if ( !empty( $info['var'] ) ) $group->setVariableName( $info['var'] );
69 - if ( !empty( $info['optional'] ) ) $group->setOptional( $info['optional'] );
70 - if ( !empty( $info['ignored'] ) ) $group->setIgnored( $info['ignored'] );
71 - if ( isset( $info['desc'] ) ) {
72 - $group->setDescription( $info['desc'] );
73 - } else {
74 - $group->setDescriptionMsg( $info['descmsg'], $info['url'] );
75 - }
76 -
77 - if ( isset( $info['aliasfile'] ) ) $group->setAliasFile( $info['aliasfile'] );
78 - if ( isset( $info['magicfile'] ) ) $group->setMagicFile( $info['magicfile'] );
79 -
80 - return $group;
81 - }
82 -
83 - protected function parseFile() {
84 - $defines = file_get_contents( $this->definitionFile );
85 - $linefeed = '(\r\n|\n)';
86 - $sections = array_map( 'trim', preg_split( "/$linefeed{2,}/", $defines, - 1, PREG_SPLIT_NO_EMPTY ) );
87 - $groups = array();
88 -
89 - foreach ( $sections as $section ) {
90 - $lines = array_map( 'trim', preg_split( "/$linefeed/", $section ) );
91 - $newgroup = array();
92 -
93 - foreach ( $lines as $line ) {
94 - if ( $line === '' || $line[0] === '#' ) continue;
95 -
96 - if ( strpos( $line, '=' ) === false ) {
97 - if ( empty( $newgroup['name'] ) ) {
98 - $newgroup['name'] = $line;
99 - } else {
100 - throw new MWException( "Trying to define name twice: " . $line );
101 - }
102 - } else {
103 - list( $key, $value ) = array_map( 'trim', explode( '=', $line, 2 ) );
104 - switch ( $key ) {
105 - case 'file':
106 - case 'var':
107 - case 'id':
108 - case 'descmsg':
109 - case 'desc':
110 - case 'magicfile':
111 - case 'aliasfile':
112 - $newgroup[$key] = $value;
113 - break;
114 - case 'optional':
115 - case 'ignored':
116 - $values = array_map( 'trim', explode( ',', $value ) );
117 - if ( !isset( $newgroup[$key] ) ) {
118 - $newgroup[$key] = array();
119 - }
120 - $newgroup[$key] = array_merge( $newgroup[$key], $values );
121 - break;
122 - case 'prefix':
123 - list( $prefix, $messages ) = array_map( 'trim', explode( '|', $value, 2 ) );
124 - if ( isset( $newgroup['prefix'] ) && $newgroup['prefix'] !== $prefix ) {
125 - throw new MWException( "Only one prefix supported: {$newgroup['prefix']} !== $prefix" );
126 - }
127 - $newgroup['prefix'] = $prefix;
128 -
129 - if ( !isset( $newgroup['mangle'] ) ) $newgroup['mangle'] = array();
130 -
131 - $messages = array_map( 'trim', explode( ',', $messages ) );
132 - $newgroup['mangle'] = array_merge( $newgroup['mangle'], $messages );
133 - break;
134 - default:
135 - throw new MWException( "Unknown key:" . $key );
136 - }
137 - }
138 - }
139 -
140 - if ( count( $newgroup ) ) {
141 - if ( empty( $newgroup['name'] ) ) {
142 - throw new MWException( "Name missing\n" . print_r( $newgroup, true ) );
143 - }
144 - $groups[] = $newgroup;
145 - }
146 - }
147 -
148 - return $groups;
149 - }
150 -
151 - protected function processGroups( $groups ) {
152 - $configureData = $this->loadConfigureExtensionData();
153 - $fixedGroups = array();
154 - foreach ( $groups as $g ) {
155 - if ( !is_array( $g ) ) {
156 - $g = array( $g );
157 - }
158 -
159 - $name = $g['name'];
160 -
161 - if ( isset( $g['id'] ) ) {
162 - $id = $g['id'];
163 - } else {
164 - $id = $this->idPrefix . preg_replace( '/\s+/', '', strtolower( $name ) );
165 - }
166 -
167 - if ( isset( $g['file'] ) ) {
168 - $file = $g['file'];
169 - } else {
170 - $file = preg_replace( '/\s+/', '', "$name/$name.i18n.php" );
171 - }
172 -
173 - if ( isset( $g['descmsg'] ) ) {
174 - $descmsg = $g['descmsg'];
175 - } else {
176 - $descmsg = str_replace( $this->idPrefix, '', $id ) . '-desc';
177 - }
178 -
179 - $configureId = self::foldId( $name );
180 - if ( isset( $configureData[$configureId]['url'] ) ) {
181 - $url = $configureData[$configureId]['url'];
182 - } else {
183 - $url = false;
184 - }
185 -
186 - $newgroup = array(
187 - 'name' => $name,
188 - 'file' => $file,
189 - 'descmsg' => $descmsg,
190 - 'url' => $url,
191 - );
192 -
193 - $copyvars = array( 'ignored', 'optional', 'var', 'desc', 'prefix', 'mangle', 'magicfile', 'aliasfile' );
194 - foreach ( $copyvars as $var ) {
195 - if ( isset( $g[$var] ) ) {
196 - $newgroup[$var] = $g[$var];
197 - }
198 - }
199 -
200 - $fixedGroups[$id] = $newgroup;
201 - }
202 - return $fixedGroups;
203 - }
204 -
205 - protected function loadConfigureExtensionData() {
206 - if ( !$this->useConfigure ) {
207 - return array();
208 - }
209 -
210 - global $wgAutoloadClasses, $IP, $wgTranslateExtensionDirectory;
211 -
212 - $postfix = 'Configure/load_txt_def/TxtDef.php';
213 - if ( file_exists( "$IP/extensions/$postfix" ) ) {
214 - $prefix = "$IP/extensions";
215 - } elseif ( file_exists( "$wgTranslateExtensionDirectory/$postfix" ) ) {
216 - $prefix = $wgTranslateExtensionDirectory;
217 - } else {
218 - $prefix = false;
219 - }
220 -
221 - if ( $prefix ) {
222 - $wgAutoloadClasses['TxtDef'] = "$prefix/$postfix";
223 - $tmp = TxtDef::loadFromFile( "$prefix/Configure/settings/Settings-ext.txt" );
224 - return array_combine( array_map( array( __CLASS__, 'foldId' ), array_keys( $tmp ) ), array_values( $tmp ) );
225 - }
226 -
227 - return array();
228 - }
229 -
230 -
231 -}
Index: trunk/extensions/Translate/groups/Voctrain.php
@@ -1,72 +0,0 @@
2 -<?php
3 -/**
4 - * Support for Voctrain vocabulary trainer.
5 - * http://www.omegawiki.org/extensions/Wikidata/util/voctrain/trainer.php
6 - *
7 - * @file
8 - * @copyright Copyright © 2009-2010, Niklas Laxström
9 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
10 - */
11 -
12 -/**
13 - * Old-style message group for Vocabulary trainer.
14 - */
15 -class VoctrainMessageGroup extends ExtensionMessageGroup {
16 -
17 - public function getChecker() {
18 - $checker = new VoctrainMessageChecker( $this );
19 - $checker->setChecks( array(
20 - array( $checker, 'VoctrainVariablesCheck' ),
21 - array( $checker, 'braceBalanceCheck' ),
22 - ) );
23 - return $checker;
24 - }
25 -
26 -}
27 -
28 -/**
29 - * %Message checker for Vocabulary trainer.
30 - */
31 -class VoctrainMessageChecker extends MessageChecker {
32 - /**
33 - * Checks for missing and unknown parameters
34 - * @param $messages Iterable list of TMessage objects.
35 - * @param $code Language code of the translations.
36 - * @param $warnings Array where warnings are appended to.
37 - */
38 - protected function VoctrainVariablesCheck( $messages, $code, &$warnings ) {
39 - foreach ( $messages as $message ) {
40 - $key = $message->key();
41 - $definition = $message->definition();
42 - $translation = $message->translation();
43 -
44 - $varPattern = '%[^% ]+';
45 - preg_match_all( "/$varPattern/U", $definition, $defVars );
46 - preg_match_all( "/$varPattern/U", $translation, $transVars );
47 -
48 - # Check for missing variables in the translation
49 - $subcheck = 'missing';
50 - $params = self::compareArrays( $defVars[0], $transVars[0] );
51 - if ( count( $params ) ) {
52 - $warnings[$key][] = array(
53 - array( 'variable', $subcheck, $key, $code ),
54 - 'translate-checks-parameters',
55 - array( 'PARAMS', $params ),
56 - array( 'COUNT', count( $params ) ),
57 - );
58 - }
59 -
60 - # Check for unknown variables in the translation
61 - $subcheck = 'unknown';
62 - $params = self::compareArrays( $transVars[0], $defVars[0] );
63 - if ( count( $params ) ) {
64 - $warnings[$key][] = array(
65 - array( 'variable', $subcheck, $key, $code ),
66 - 'translate-checks-parameters-unknown',
67 - array( 'PARAMS', $params ),
68 - array( 'COUNT', count( $params ) ),
69 - );
70 - }
71 - }
72 - }
73 -}
Index: trunk/extensions/Translate/groups/MediaWiki/Checker.php
@@ -1,250 +0,0 @@
2 -<?php
3 -/**
4 - * Implements MessageChecker for %MediaWiki.
5 - *
6 - * @file
7 - * @author Niklas Laxström
8 - * @copyright Copyright © 2008-2010, Niklas Laxström
9 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
10 - */
11 -
12 -/**
13 - * %MediaWiki specific message checks.
14 - *
15 - * @ingroup MessageCheckers
16 - */
17 -class MediaWikiMessageChecker extends MessageChecker {
18 - /**
19 - * Checks if the translation uses all variables $[1-9] that the definition
20 - * uses and vice versa.
21 - *
22 - * @param $messages \array Iterable list of TMessage objects.
23 - * @param $code \string Language code of the translations.
24 - * @param $warnings \array Array where warnings are appended to.
25 - */
26 - protected function wikiParameterCheck( $messages, $code, &$warnings ) {
27 - return parent::parameterCheck( $messages, $code, $warnings, '/\$[1-9]/' );
28 - }
29 -
30 - /**
31 - * Checks if the translation uses links that are discouraged. Valid links are
32 - * those that link to Special: or {{ns:special}}: or project pages trough
33 - * MediaWiki messages like {{MediaWiki:helppage-url}}:. Also links in the
34 - * definition are allowed.
35 - *
36 - * @param $messages \array Iterable list of TMessage objects.
37 - * @param $code \string Language code of the translations.
38 - * @param $warnings \array Array where warnings are appended to.
39 - */
40 - protected function wikiLinksCheck( $messages, $code, &$warnings ) {
41 - $tc = Title::legalChars() . '#%{}';
42 -
43 - foreach ( $messages as $message ) {
44 - $key = $message->key();
45 - $definition = $message->definition();
46 - $translation = $message->translation();
47 -
48 - $subcheck = 'extra';
49 - $matches = $links = array();
50 - preg_match_all( "/\[\[([{$tc}]+)(\\|(.+?))?]]/sDu", $translation, $matches );
51 - for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
52 - $backMatch = preg_quote( $matches[1][$i], '/' );
53 -
54 - if ( preg_match( "/\[\[$backMatch/", $definition ) ) {
55 - continue;
56 - }
57 -
58 - $links[] = "[[{$matches[1][$i]}{$matches[2][$i]}]]";
59 - }
60 -
61 - if ( count( $links ) ) {
62 - $warnings[$key][] = array(
63 - array( 'links', $subcheck, $key, $code ),
64 - 'translate-checks-links',
65 - array( 'PARAMS', $links ),
66 - array( 'COUNT', count( $links ) ),
67 - );
68 - }
69 -
70 - $subcheck = 'missing';
71 - $matches = $links = array();
72 - preg_match_all( "/\[\[([{$tc}]+)(\\|(.+?))?]]/sDu", $definition, $matches );
73 - for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
74 - $backMatch = preg_quote( $matches[1][$i], '/' );
75 -
76 - if ( preg_match( "/\[\[$backMatch/", $translation ) ) {
77 - continue;
78 - }
79 -
80 - $links[] = "[[{$matches[1][$i]}{$matches[2][$i]}]]";
81 - }
82 -
83 - if ( count( $links ) ) {
84 - $warnings[$key][] = array(
85 - array( 'links', $subcheck, $key, $code ),
86 - 'translate-checks-links-missing',
87 - array( 'PARAMS', $links ),
88 - array( 'COUNT', count( $links ) ),
89 - );
90 - }
91 - }
92 - }
93 -
94 - /**
95 - * Checks if the \<br /> and \<hr /> tags are using the correct syntax.
96 - *
97 - * @param $messages \array Iterable list of TMessage objects.
98 - * @param $code \string Language code of the translations.
99 - * @param $warnings \array Array where warnings are appended to.
100 - */
101 - protected function XhtmlCheck( $messages, $code, &$warnings ) {
102 - foreach ( $messages as $message ) {
103 - $key = $message->key();
104 - $translation = $message->translation();
105 - if ( strpos( $translation, '<' ) === false ) {
106 - continue;
107 - }
108 -
109 - $subcheck = 'invalid';
110 - $tags = array(
111 - '~<hr *(\\\\)?>~suDi' => '<hr />', // Wrong syntax
112 - '~<br *(\\\\)?>~suDi' => '<br />',
113 - '~<hr/>~suDi' => '<hr />', // Wrong syntax
114 - '~<br/>~suDi' => '<br />',
115 - '~<(HR|Hr|hR) />~su' => '<hr />', // Case
116 - '~<(BR|Br|bR) />~su' => '<br />',
117 - );
118 -
119 - $wrongTags = array();
120 - foreach ( $tags as $wrong => $correct ) {
121 - $matches = array();
122 - preg_match_all( $wrong, $translation, $matches, PREG_PATTERN_ORDER );
123 - foreach ( $matches[0] as $wrongMatch ) {
124 - $wrongTags[$wrongMatch] = "$wrongMatch → $correct";
125 - }
126 - }
127 -
128 - if ( count( $wrongTags ) ) {
129 - $warnings[$key][] = array(
130 - array( 'xhtml', $subcheck, $key, $code ),
131 - 'translate-checks-xhtml',
132 - array( 'PARAMS', $wrongTags ),
133 - array( 'COUNT', count( $wrongTags ) ),
134 - );
135 - }
136 - }
137 - }
138 -
139 - /**
140 - * Checks if the translation doesn't use plural while the definition has one.
141 - *
142 - * @param $messages \array Iterable list of TMessage objects.
143 - * @param $code \string Language code of the translations.
144 - * @param $warnings \array Array where warnings are appended to.
145 - */
146 - protected function pluralCheck( $messages, $code, &$warnings ) {
147 - foreach ( $messages as $message ) {
148 - $key = $message->key();
149 - $definition = $message->definition();
150 - $translation = $message->translation();
151 -
152 - $subcheck = 'missing';
153 - if (
154 - stripos( $definition, '{{plural:' ) !== false &&
155 - stripos( $translation, '{{plural:' ) === false
156 - ) {
157 - $warnings[$key][] = array(
158 - array( 'plural', $subcheck, $key, $code ),
159 - 'translate-checks-plural',
160 - );
161 - }
162 - }
163 - }
164 -
165 - /**
166 - * Checks for page names that they have an untranslated namespace.
167 - *
168 - * @param $messages \array Iterable list of TMessage objects.
169 - * @param $code \string Language code of the translations.
170 - * @param $warnings \array Array where warnings are appended to.
171 - */
172 - protected function pagenameMessagesCheck( $messages, $code, &$warnings ) {
173 - foreach ( $messages as $message ) {
174 - $key = $message->key();
175 - $definition = $message->definition();
176 - $translation = $message->translation();
177 -
178 - $subcheck = 'namespace';
179 - $namespaces = 'help|project|\{\{ns:project}}|mediawiki';
180 - $matches = array();
181 - if ( preg_match( "/^($namespaces):[\w\s]+$/ui", $definition, $matches ) ) {
182 - if ( !preg_match( "/^{$matches[1]}:.+$/u", $translation ) ) {
183 - $warnings[$key][] = array(
184 - array( 'pagename', $subcheck, $key, $code ),
185 - 'translate-checks-pagename',
186 - );
187 - }
188 - }
189 - }
190 - }
191 -
192 - /**
193 - * Checks for some miscellaneous messages with special syntax.
194 - *
195 - * @param $messages \array Iterable list of TMessage objects.
196 - * @param $code \string Language code of the translations.
197 - * @param $warnings \array Array where warnings are appended to.
198 - */
199 - protected function miscMWChecks( $messages, $code, &$warnings ) {
200 - $timeList = array( 'protect-expiry-options', 'ipboptions' );
201 -
202 - foreach ( $messages as $message ) {
203 - $key = $message->key();
204 - $definition = $message->definition();
205 - $translation = $message->translation();
206 -
207 -
208 - if ( in_array( strtolower( $key ), $timeList, true ) ) {
209 - $defArray = explode( ',', $definition );
210 - $traArray = explode( ',', $translation );
211 -
212 - $subcheck = 'timelist-count';
213 - $defCount = count( $defArray );
214 - $traCount = count( $traArray );
215 - if ( $defCount !== $traCount ) {
216 - $warnings[$key][] = array(
217 - array( 'miscmw', $subcheck, $key, $code ),
218 - 'translate-checks-format',
219 - "Parameter count is $traCount; should be $defCount", // @todo Missing i18n.
220 - );
221 - continue;
222 - }
223 -
224 - for ( $i = 0; $i < count( $defArray ); $i++ ) {
225 - $defItems = array_map( 'trim', explode( ':', $defArray[$i] ) );
226 - $traItems = array_map( 'trim', explode( ':', $traArray[$i] ) );
227 -
228 - $subcheck = 'timelist-format';
229 - if ( count( $traItems ) !== 2 ) {
230 - $warnings[$key][] = array(
231 - array( 'miscmw', $subcheck, $key, $code ),
232 - 'translate-checks-format',
233 - "<nowiki>$traArray[$i]</nowiki> is malformed", // @todo Missing i18n.
234 - );
235 - continue;
236 - }
237 -
238 - $subcheck = 'timelist-format-value';
239 - if ( $traItems[1] !== $defItems[1] ) {
240 - $warnings[$key][] = array(
241 - array( 'miscmw', $subcheck, $key, $code ),
242 - 'translate-checks-format',
243 - "<tt><nowiki>$traItems[1] !== $defItems[1]</nowiki></tt>",
244 - );
245 - continue;
246 - }
247 - }
248 - }
249 - }
250 - }
251 -}
Index: trunk/extensions/Translate/groups/Toolserver/ToolserverTextdomains.php
@@ -1,101 +0,0 @@
2 -<?php
3 -/**
4 - * Class for Toolserver Intuition for Translatewiki.net
5 - *
6 - * @file
7 - * @author Niklas Laxström
8 - * @author Krinkle
9 - * @copyright Copyright © 2008-2010, Niklas Laxström
10 - * @copyright Copyright © 2011, Krinkle
11 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
12 - */
13 -
14 -/**
15 - * Support for tools using Toolserver Intuition at the Toolserver.
16 - */
17 -class PremadeToolserverTextdomains extends PremadeMediawikiExtensionGroups {
18 - protected $useConfigure = false;
19 - protected $groups;
20 - protected $idPrefix = 'tsint-';
21 - protected $namespaces = array( NS_TOOLSERVER, NS_TOOLSERVER_TALK );
22 -
23 - public function __construct() {
24 - global $wgTranslateGroupRoot;
25 -
26 - parent::__construct();
27 - $dir = dirname( __FILE__ );
28 - $this->definitionFile = $dir . '/toolserver-textdomains.txt';
29 - $this->path = "$wgTranslateGroupRoot/toolserver/language/messages/";
30 - }
31 -
32 - protected function processGroups( $groups ) {
33 - $configureData = $this->loadConfigureExtensionData();
34 - $fixedGroups = array();
35 - foreach ( $groups as $g ) {
36 - if ( !is_array( $g ) ) {
37 - $g = array( $g );
38 - }
39 -
40 - $name = $g['name'];
41 - $sanitizedName = preg_replace( '/\s+/', '', strtolower( $name ) );
42 -
43 - if ( isset( $g['id'] ) ) {
44 - $id = $g['id'];
45 - } else {
46 - $id = $this->idPrefix . $sanitizedName;
47 - }
48 -
49 - if ( isset( $g['file'] ) ) {
50 - $file = $g['file'];
51 - } else {
52 - // Toolserver Intuition text-domains are case-insensitive and internally
53 - // converts to lowercase names starting with a capital letter.
54 - // eg. "MyTool" -> "Mytool.i18n.php"
55 - // No subdirectories!
56 - $file = ucfirst( $sanitizedName ) . '.i18n.php';
57 - }
58 -
59 - if ( isset( $g['descmsg'] ) ) {
60 - $descmsg = $g['descmsg'];
61 - } else {
62 - $descmsg = "$id-desc";
63 - }
64 -
65 - if ( isset( $g['url'] ) ) {
66 - $url = $g['url'];
67 - } else {
68 - $url = false;
69 - }
70 -
71 - $newgroup = array(
72 - 'name' => 'Toolserver - ' . $name,
73 - 'file' => $file,
74 - 'descmsg' => $descmsg,
75 - 'url' => $url,
76 - );
77 -
78 - // Prefix is required, if not customized use the sanitized name
79 - if ( !isset( $g['prefix'] ) ) {
80 - $g['prefix'] = "$sanitizedName-";
81 - }
82 -
83 - // All messages are prefixed with their groupname
84 - $g['mangle'] = array( '*' );
85 -
86 - // Prevent E_NOTICE undefined index.
87 - // PremadeMediawikiExtensionGroups::factory should probably check this better instead
88 - if ( !isset( $g['ignored'] ) ) $g['ignored'] = array();
89 - if ( !isset( $g['optional'] ) ) $g['optional'] = array();
90 -
91 - $copyvars = array( 'ignored', 'optional', 'var', 'desc', 'prefix', 'mangle', 'magicfile', 'aliasfile' );
92 - foreach ( $copyvars as $var ) {
93 - if ( isset( $g[$var] ) ) {
94 - $newgroup[$var] = $g[$var];
95 - }
96 - }
97 -
98 - $fixedGroups[$id] = $newgroup;
99 - }
100 - return $fixedGroups;
101 - }
102 -}
Index: trunk/extensions/Translate/groups/Wikia/WikiaExtensions.php
@@ -1,28 +0,0 @@
2 -<?php
3 -/**
4 - * Classes for Wikia extension translation.
5 - *
6 - * @file
7 - * @author Niklas Laxström
8 - * @copyright Copyright © 2008-2010, Niklas Laxström
9 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
10 - */
11 -
12 -/**
13 - * Support for %MediaWiki extensions in Wikias repository.
14 - */
15 -class PremadeWikiaExtensionGroups extends PremadeMediawikiExtensionGroups {
16 - protected $useConfigure = false;
17 - protected $idPrefix = 'wikia-';
18 - protected $path = null;
19 - protected $namespaces = array( NS_WIKIA, NS_WIKIA_TALK );
20 -
21 - public function __construct() {
22 - global $wgTranslateGroupRoot;
23 -
24 - parent::__construct();
25 - $dir = dirname( __FILE__ );
26 - $this->definitionFile = $dir . '/extensions.txt';
27 - $this->path = "$wgTranslateGroupRoot/wikia/";
28 - }
29 -}
Index: trunk/extensions/Translate/Translate.php
@@ -15,7 +15,7 @@
1616 /**
1717 * Version number used in extension credits and in other placed where needed.
1818 */
19 -define( 'TRANSLATE_VERSION', '2011-09-22' );
 19+define( 'TRANSLATE_VERSION', '2011-09-28' );
2020
2121 /**
2222 * Extension credits properties.
Index: trunk/extensions/Translate/README
@@ -29,7 +29,11 @@
3030 http://translatewiki.net/docs/Translate/html/
3131
3232 == Change log ==
33 -
 33+* 2011-09-28
 34+- Started taking PHP code out of the groups/ folder anticipating Wikimedia
 35+ review. Later we will move all content of the groups folder elsewhere to
 36+ reduce the number of changes of changes to the Translate extension
 37+ considerably.
3438 * 2011-09-26
3539 - Special:SupportedLanguages can now show site specific messages in
3640 supportedlanguages-localsummary message
Index: trunk/extensions/Translate/_autoload.php
@@ -125,20 +125,20 @@
126126 * @name Classes for predefined old-style message groups
127127 * @{
128128 */
129 -$wgAutoloadClasses['PremadeMediawikiExtensionGroups'] = $dir . 'groups/MediaWikiExtensions.php';
130 -$wgAutoloadClasses['PremadeWikiaExtensionGroups'] = $dir . 'groups/Wikia/WikiaExtensions.php';
131 -$wgAutoloadClasses['PremadeToolserverTextdomains'] = $dir . 'groups/Toolserver/ToolserverTextdomains.php';
132 -$wgAutoloadClasses['MediaWikiMessageChecker'] = $dir . 'groups/MediaWiki/Checker.php';
 129+$wgAutoloadClasses['PremadeMediawikiExtensionGroups'] = $dir . 'ffs/MediaWikiExtensions.php';
 130+$wgAutoloadClasses['PremadeWikiaExtensionGroups'] = $dir . 'ffs//WikiaExtensions.php';
 131+$wgAutoloadClasses['PremadeToolserverTextdomains'] = $dir . 'ffs/ToolserverTextdomains.php';
 132+$wgAutoloadClasses['MediaWikiMessageChecker'] = $dir . 'ffs/MediaWikiChecker.php';
133133 /**@}*/
134134
135135 /**
136136 * @name Non-message translation item support
137137 * @{
138138 */
139 -$wgAutoloadClasses['ComplexMessages'] = $dir . 'groups/ComplexMessages.php';
140 -$wgAutoloadClasses['SpecialPageAliasesCM'] = $dir . 'groups/ComplexMessages.php';
141 -$wgAutoloadClasses['MagicWordsCM'] = $dir . 'groups/ComplexMessages.php';
142 -$wgAutoloadClasses['NamespaceCM'] = $dir . 'groups/ComplexMessages.php';
 139+$wgAutoloadClasses['ComplexMessages'] = $dir . 'ffs/MediaWikiComplexMessages.php';
 140+$wgAutoloadClasses['SpecialPageAliasesCM'] = $dir . 'ffs/MediaWikiComplexMessages.php';
 141+$wgAutoloadClasses['MagicWordsCM'] = $dir . 'ffs/MediaWikiComplexMessages.php';
 142+$wgAutoloadClasses['NamespaceCM'] = $dir . 'ffs/MediaWikiComplexMessages.php';
143143 /**@}*/
144144
145145 /**
Index: trunk/extensions/Translate/ffs/MediaWikiComplexMessages.php
@@ -0,0 +1,708 @@
 2+<?php
 3+/**
 4+ * Classes for complex messages (%MediaWiki special page aliases, namespace names, magic words).
 5+ *
 6+ * @file
 7+ * @author Niklas Laxström
 8+ * @copyright Copyright © 2008-2010, Niklas Laxström
 9+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 10+ */
 11+
 12+/**
 13+ * Base class which implements handling and translation interface of
 14+ * non-message %MediaWiki items.
 15+ * @todo Needs documentation.
 16+ */
 17+abstract class ComplexMessages {
 18+
 19+ const LANG_MASTER = 0;
 20+ const LANG_CHAIN = 1;
 21+ const LANG_CURRENT = 2;
 22+
 23+ protected $language = null;
 24+ protected $id = '__BUG__';
 25+ protected $variable = '__BUG__';
 26+ protected $data = null;
 27+ protected $elementsInArray = true;
 28+ protected $databaseMsg = '__BUG__';
 29+ protected $chainable = false;
 30+ protected $firstMagic = false;
 31+ protected $constants = array();
 32+
 33+ protected $tableAttributes = array(
 34+ 'class' => 'wikitable',
 35+ 'border' => '2',
 36+ 'cellpadding' => '4',
 37+ 'cellspacing' => '0',
 38+ 'style' => 'background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse;',
 39+ );
 40+
 41+ public function __construct( $language ) {
 42+ $this->language = $language;
 43+ }
 44+
 45+ public function getTitle() {
 46+ return wfMsg( 'translate-magic-' . $this->id );
 47+ }
 48+
 49+ #
 50+ # Data retrieval
 51+ #
 52+ protected $init = false;
 53+ public function getGroups() {
 54+ if ( !$this->init ) {
 55+ $saved = $this->getSavedData();
 56+ foreach ( $this->data as &$group ) {
 57+ $this->getData( $group, $saved );
 58+ }
 59+ $this->init = true;
 60+ }
 61+
 62+ return $this->data;
 63+
 64+ }
 65+
 66+ public function cleanData( $defs, $current ) {
 67+ foreach ( $current as $item => $values ) {
 68+ if ( !$this->elementsInArray ) {
 69+ break;
 70+ }
 71+
 72+ if ( !isset( $defs[$item] ) ) {
 73+ unset( $current[$item] );
 74+ continue;
 75+ }
 76+
 77+ foreach ( $values as $index => $value )
 78+ if ( in_array( $value, $defs[$item], true ) ) {
 79+ unset( $current[$item][$index] );
 80+ }
 81+ }
 82+ return $current;
 83+ }
 84+
 85+ public function mergeMagic( $defs, $current ) {
 86+ foreach ( $current as $item => &$values ) {
 87+ $newchain = $defs[$item];
 88+ array_splice( $newchain, 1, 0, $values );
 89+ $values = $newchain;
 90+
 91+ }
 92+ return $current;
 93+ }
 94+
 95+ public function getData( &$group, $savedData ) {
 96+ $defs = $this->readVariable( $group, 'en' );
 97+ $code = $this->language;
 98+
 99+ $current = wfArrayMerge( $this->readVariable( $group, $code ), $savedData );
 100+
 101+ // Clean up duplicates to definitions from saved data
 102+ $current = $this->cleanData( $defs, $current );
 103+
 104+ $chain = $current;
 105+ if ( $this->chainable ) {
 106+ foreach ( Language::getFallbacksFor( $code ) as $code ) {
 107+ $fbdata = $this->readVariable( $group, $code );
 108+ if ( $this->firstMagic ) {
 109+ $fbdata = $this->cleanData( $defs, $fbdata );
 110+ }
 111+
 112+ $chain = array_merge_recursive( $chain, $fbdata );
 113+ }
 114+ }
 115+
 116+ if ( $this->firstMagic ) {
 117+ $chain = $this->mergeMagic( $defs, $chain );
 118+ }
 119+
 120+ $data = $group['data'] = array( $defs, $chain, $current );
 121+
 122+ return $data;
 123+ }
 124+
 125+ /**
 126+ * Gets data from request. Needs to be run before the form is displayed and
 127+ * validation. Not needed for export, which uses request directly.
 128+ */
 129+ public function loadFromRequest( WebRequest $request ) {
 130+ $saved = $this->parse( $this->formatForSave( $request ) );
 131+ foreach ( $this->data as &$group ) {
 132+ $this->getData( $group, $saved );
 133+ }
 134+ }
 135+
 136+ /**
 137+ * Gets saved data from Mediawiki namespace
 138+ * @return Array
 139+ */
 140+ protected function getSavedData() {
 141+ $data = TranslateUtils::getMessageContent( $this->databaseMsg, $this->language );
 142+
 143+ if ( !$data ) {
 144+ return array();
 145+ } else {
 146+ return $this->parse( $data );
 147+ }
 148+ }
 149+
 150+ protected function parse( $data ) {
 151+ $lines = array_map( 'trim', explode( "\n", $data ) );
 152+ $array = array();
 153+ foreach ( $lines as $line ) {
 154+ if ( $line === '' || $line[0] === '#' || $line[0] === '<' ) {
 155+ continue;
 156+ }
 157+
 158+ if ( strpos( $line, '=' ) === false ) {
 159+ continue;
 160+ }
 161+
 162+ list( $name, $values ) = array_map( 'trim', explode( '=', $line, 2 ) );
 163+ if ( $name === '' || $values === '' ) {
 164+ continue;
 165+ }
 166+
 167+ $data = array_map( 'trim', explode( ',', $values ) );
 168+ $array[$name] = $data;
 169+ }
 170+
 171+ return $array;
 172+ }
 173+
 174+ /**
 175+ * Return an array of keys that can be used to iterate over all keys
 176+ * @return Array of keys for data
 177+ */
 178+ protected function getIterator( $group ) {
 179+ $groups = $this->getGroups();
 180+ return array_keys( $groups[$group]['data'][self::LANG_MASTER] );
 181+ }
 182+
 183+ protected function val( $group, $type, $key ) {
 184+ $array = $this->getGroups();
 185+ $subarray = @$array[$group]['data'][$type][$key];
 186+ if ( $this->elementsInArray ) {
 187+ if ( !$subarray || !count( $subarray ) ) {
 188+ return array();
 189+ }
 190+ } else {
 191+ if ( !$subarray ) {
 192+ return array();
 193+ }
 194+ }
 195+
 196+ if ( !is_array( $subarray ) ) {
 197+ $subarray = array( $subarray );
 198+ }
 199+
 200+ return $subarray;
 201+ }
 202+
 203+ /**
 204+ */
 205+ protected function readVariable( $group, $code ) {
 206+ $file = $group['file'];
 207+ if ( !$group['code'] ) {
 208+ $file = str_replace( '%CODE%', str_replace( '-', '_', ucfirst( $code ) ), $file );
 209+ }
 210+
 211+ $ { $group['var'] } = array(); # Initialize
 212+ if ( file_exists( $file ) ) {
 213+ require( $file ); # Include
 214+ }
 215+
 216+ if ( $group['code'] ) {
 217+ $data = (array) @$ { $group['var'] } [$code];
 218+ } else {
 219+ $data = $ { $group['var'] } ;
 220+ }
 221+
 222+ return self::arrayMapRecursive( 'strval', $data );
 223+ }
 224+
 225+ public static function arrayMapRecursive( $callback, $data ) {
 226+ foreach ( $data as $index => $values ) {
 227+ if ( is_array( $values ) ) {
 228+ $data[$index] = self::arrayMapRecursive( $callback, $values );
 229+ } else {
 230+ $data[$index] = call_user_func( $callback, $values );
 231+ }
 232+ }
 233+ return $data;
 234+ }
 235+
 236+ #
 237+ # /Data retrieval
 238+ #
 239+
 240+ #
 241+ # Output
 242+ #
 243+ public function header( $title ) {
 244+ $colspan = array( 'colspan' => 3 );
 245+ $header = Xml::element( 'th', $colspan, $this->getTitle() . ' - ' . $title );
 246+ $subheading[] = '<th>' . wfMsgHtml( 'translate-magic-cm-original' ) . '</th>';
 247+ $subheading[] = '<th>' . wfMsgHtml( 'translate-magic-cm-current' ) . '</th>';
 248+ $subheading[] = '<th>' . wfMsgHtml( 'translate-magic-cm-to-be' ) . '</th>';
 249+ return '<tr>' . $header . '</tr>' .
 250+ '<tr>' . implode( "\n", $subheading ) . '</tr>';
 251+ }
 252+
 253+ public function output() {
 254+ global $wgRequest;
 255+
 256+ $colspan = array( 'colspan' => 3 );
 257+
 258+ $s = Xml::openElement( 'table', $this->tableAttributes );
 259+
 260+ foreach ( array_keys( $this->data ) as $group ) {
 261+ $s .= $this->header( $this->data[$group]['label'] );
 262+
 263+ foreach ( $this->getIterator( $group ) as $key ) {
 264+ $rowContents = '';
 265+
 266+ $value = $this->val( $group, self::LANG_MASTER, $key );
 267+ if ( $this->firstMagic ) {
 268+ array_shift( $value );
 269+ }
 270+
 271+ $value = array_map( 'htmlspecialchars', $value );
 272+ $rowContents .= '<td>' . $this->formatElement( $value ) . '</td>';
 273+
 274+ $value = $this->val( $group, self::LANG_CHAIN, $key );
 275+ if ( $this->firstMagic ) {
 276+ array_shift( $value );
 277+ }
 278+
 279+ $value = array_map( 'htmlspecialchars', $value );
 280+ $value = $this->highlight( $key, $value );
 281+ $rowContents .= '<td>' . $this->formatElement( $value ) . '</td>';
 282+
 283+ $value = $this->val( $group, self::LANG_CURRENT, $key );
 284+ $rowContents .= '<td>' . $this->editElement( $key, $this->formatElement( $value ) ) . '</td>';
 285+
 286+ $s .= Xml::tags( 'tr', array( 'id' => "mw-sp-magic-$key" ), $rowContents );
 287+ }
 288+ }
 289+
 290+ global $wgUser;
 291+ if ( $wgUser->isAllowed( 'translate' ) ) {
 292+ $s .= '<tr>' . Xml::tags( 'td', $colspan, $this->getButtons() ) . '<tr>';
 293+ }
 294+
 295+ $s .= Xml::closeElement( 'table' );
 296+
 297+ return Xml::tags(
 298+ 'form',
 299+ array( 'method' => 'post', 'action' => $wgRequest->getRequestURL() ),
 300+ $s
 301+ );
 302+ }
 303+
 304+ public function getButtons() {
 305+ return
 306+ Xml::inputLabel( wfMsg( 'translate-magic-cm-comment' ), 'comment', 'sp-translate-magic-comment' ) .
 307+ Xml::submitButton( wfMsg( 'translate-magic-cm-save' ), array( 'name' => 'savetodb' ) );
 308+ }
 309+
 310+ public function formatElement( $element ) {
 311+ if ( !count( $element ) ) {
 312+ return '';
 313+ }
 314+
 315+ if ( is_array( $element ) ) {
 316+ $element = array_map( 'trim', $element );
 317+ $element = implode( ', ', $element );
 318+ }
 319+ return trim( $element );
 320+ }
 321+
 322+ function getKeyForEdit( $key ) {
 323+ return Sanitizer::escapeId( 'sp-translate-magic-cm-' . $this->id . $key );
 324+ }
 325+
 326+ public function editElement( $key, $contents ) {
 327+ return Xml::input( $this->getKeyForEdit( $key ), 40, $contents );
 328+ }
 329+
 330+ #
 331+ # /Output
 332+ #
 333+
 334+ #
 335+ # Save to database
 336+ #
 337+
 338+ function getKeyForSave() {
 339+ return $this->databaseMsg . '/' . $this->language;
 340+ }
 341+
 342+ function formatForSave( $request ) {
 343+ $text = '';
 344+ foreach ( array_keys( $this->data ) as $group ) {
 345+ foreach ( $this->getIterator( $group ) as $key ) {
 346+ $data = $request->getText( $this->getKeyForEdit( $key ) );
 347+ // Make a nice array out of the submit with trimmed values.
 348+ $data = array_map( 'trim', explode( ',', $data ) );
 349+ // Normalise: Replace spaces with underscores.
 350+ $data = str_replace( ' ', '_', $data );
 351+ // Create final format.
 352+ $data = implode( ', ', $data );
 353+ if ( $data !== '' ) {
 354+ $text .= "$key = $data\n" ;
 355+ }
 356+ }
 357+ }
 358+ return $text;
 359+ }
 360+
 361+ public function save( $request ) {
 362+ $title = Title::newFromText( 'MediaWiki:' . $this->getKeyForSave() );
 363+ $article = new Article( $title );
 364+
 365+ $data = "# DO NOT EDIT THIS PAGE DIRECTLY! Use [[Special:AdvancedTranslate]].\n<pre>\n" . $this->formatForSave( $request ) . "\n</pre>";
 366+
 367+ $comment = $request->getText( 'comment', wfMsgForContent( 'translate-magic-cm-updatedusing' ) );
 368+ $status = $article->doEdit( $data, $comment, 0 );
 369+
 370+ if ( $status === false || ( is_object( $status ) && !$status->isOK() ) ) {
 371+ throw new MWException( wfMsg( 'translate-magic-cm-savefailed' ) );
 372+ }
 373+
 374+ /* Reset outdated array */
 375+ $this->init = false;
 376+ }
 377+
 378+ #
 379+ # !Save to database
 380+ #
 381+
 382+ #
 383+ # Export
 384+ #
 385+ public function validate( &$errors = array(), $filter = false ) {
 386+ $used = array();
 387+ foreach ( array_keys( $this->data ) as $group ) {
 388+ if ( $filter !== false && !in_array( $group, (array) $filter, true ) ) {
 389+ continue;
 390+ }
 391+
 392+ $this->validateEach( $errors, $group, $used );
 393+ }
 394+ }
 395+
 396+ protected function validateEach( &$errors = array(), $group, &$used ) {
 397+ foreach ( $this->getIterator( $group ) as $key ) {
 398+ $values = $this->val( $group, self::LANG_CURRENT, $key );
 399+ $link = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$key" ), $key );
 400+
 401+ if ( count( $values ) !== count( array_filter( $values ) ) ) {
 402+ $errors[] = "There is empty value in $link.";
 403+ }
 404+
 405+ foreach ( $values as $v ) {
 406+ if ( isset( $used[$v] ) ) {
 407+ $otherkey = $used[$v];
 408+ $first = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$otherkey" ), $otherkey );
 409+ $errors[] = "Translation <b>$v</b> is used more than once for $first and $link.";
 410+ } else {
 411+ $used[$v] = $key;
 412+ }
 413+ }
 414+ }
 415+ }
 416+
 417+ public function export( $filter = false ) {
 418+ $text = '';
 419+ $errors = array();
 420+ $this->validate( $errors, $filter );
 421+ foreach ( $errors as $_ ) $text .= "#!!# $_\n";
 422+
 423+ foreach ( $this->getGroups() as $group => $data ) {
 424+ if ( $filter !== false && !in_array( $group, (array) $filter, true ) ) {
 425+ continue;
 426+ }
 427+
 428+ $text .= $this->exportEach( $group, $data );
 429+ }
 430+
 431+ return $text;
 432+ }
 433+
 434+ protected function exportEach( $group, $data ) {
 435+ $var = $data['var'];
 436+ $items = $data['data'];
 437+
 438+ $extra = $data['code'] ? "['{$this->language}']" : '';
 439+
 440+ $out = '';
 441+
 442+ $indexKeys = array();
 443+ foreach ( array_keys( $items[self::LANG_MASTER] ) as $key ) {
 444+ $indexKeys[$key] = isset( $this->constants[$key] ) ? $this->constants[$key] : "'$key'";
 445+ }
 446+
 447+ $padTo = max( array_map( 'strlen', $indexKeys ) ) + 3;
 448+
 449+ foreach ( $this->getIterator( $group ) as $key ) {
 450+ $temp = "\t{$indexKeys[$key]}";
 451+
 452+ while ( strlen( $temp ) <= $padTo ) {
 453+ $temp .= ' ';
 454+ }
 455+
 456+ $from = self::LANG_CURRENT;
 457+ // Abuse of the firstMagic property, should use something proper
 458+ if ( $this->firstMagic ) {
 459+ $from = self::LANG_CHAIN;
 460+ }
 461+
 462+ // Check for translations
 463+ $val = $this->val( $group, self::LANG_CURRENT, $key );
 464+ if ( !$val || !count( $val ) ) {
 465+ continue;
 466+ }
 467+
 468+ // Then get the data we really want
 469+ $val = $this->val( $group, $from, $key );
 470+
 471+ // Remove duplicated entries, causes problems with magic words
 472+ // Just to be sure, it should not be possible to save invalid data anymore
 473+ $val = array_unique( $val /* @todo SORT_REGULAR */ );
 474+
 475+ // So do empty elements...
 476+ foreach ( $val as $k => $v ) {
 477+ if ( $v === '' ) {
 478+ unset( $val[$k] );
 479+ }
 480+ }
 481+
 482+ // Another check
 483+ if ( !count( $val ) ) {
 484+ continue;
 485+ }
 486+
 487+ $normalized = array_map( array( $this, 'normalize' ), $val );
 488+ if ( $this->elementsInArray ) {
 489+ $temp .= "=> array( " . implode( ', ', $normalized ) . " ),";
 490+ } else {
 491+ $temp .= "=> " . implode( ', ', $normalized ) . ",";
 492+ }
 493+ $out .= $temp . "\n";
 494+ }
 495+
 496+ if ( $out !== '' ) {
 497+ $text = "# {$data['label']} \n";
 498+ $text .= "\$$var$extra = array(\n" . $out . ");\n\n";
 499+ return $text;
 500+ } else {
 501+ return '';
 502+ }
 503+ }
 504+
 505+ /**
 506+ * Returns string with quotes that should be valid php
 507+ */
 508+ protected function normalize( $data ) {
 509+ # Escape quotes
 510+ if ( !is_string( $data ) ) {
 511+ throw new MWException();
 512+ }
 513+ $data = preg_replace( "/(?<!\\\\)'/", "\'", trim( $data ) );
 514+ return "'$data'";
 515+ }
 516+
 517+ #
 518+ # /Export
 519+ #
 520+ public function highlight( $key, $values ) {
 521+ return $values;
 522+ }
 523+}
 524+
 525+/**
 526+ * Adds support for translating special page aliases via Special:AdvancedTranslate.
 527+ * @todo Needs documentation.
 528+ */
 529+class SpecialPageAliasesCM extends ComplexMessages {
 530+ protected $id = SpecialMagic::MODULE_SPECIAL;
 531+ protected $databaseMsg = 'sp-translate-data-SpecialPageAliases';
 532+ protected $chainable = true;
 533+
 534+
 535+ public function __construct( $code ) {
 536+ parent::__construct( $code );
 537+ $this->data['core'] = array(
 538+ 'label' => 'MediaWiki Core',
 539+ 'var' => 'specialPageAliases',
 540+ 'file' => Language::getMessagesFileName( '%CODE%' ),
 541+ 'code' => false,
 542+ );
 543+
 544+ global $wgTranslateExtensionDirectory;
 545+ $groups = MessageGroups::singleton()->getGroups();
 546+ foreach ( $groups as $g ) {
 547+ if ( !$g instanceof ExtensionMessageGroup ) {
 548+ continue;
 549+ }
 550+
 551+ $file = $g->getAliasFile();
 552+ if ( $file === null ) {
 553+ continue;
 554+ }
 555+
 556+ $file = "$wgTranslateExtensionDirectory/$file";
 557+ if ( file_exists( $file ) ) {
 558+ $this->data[$g->getId()] = array(
 559+ 'label' => $g->getLabel(),
 560+ 'var' => $g->getVariableNameAlias(),
 561+ 'file' => $file,
 562+ 'code' => $code,
 563+ );
 564+ }
 565+ }
 566+ }
 567+
 568+ public function highlight( $key, $values ) {
 569+ if ( count( $values ) ) {
 570+ if ( !isset( $values[0] ) ) {
 571+ throw new MWException( "Something missing from values: " . print_r( $values, true ) );
 572+ }
 573+
 574+ $values[0] = "<b>$values[0]</b>";
 575+ }
 576+ return $values;
 577+ }
 578+
 579+ protected function validateEach( &$errors = array(), $group, &$used ) {
 580+ parent::validateEach( $errors, $group, $used );
 581+ foreach ( $this->getIterator( $group ) as $key ) {
 582+ $values = $this->val( $group, self::LANG_CURRENT, $key );
 583+
 584+ foreach ( $values as $_ ) {
 585+ wfSuppressWarnings();
 586+ $title = SpecialPage::getTitleFor( $_ );
 587+ wfRestoreWarnings();
 588+ $link = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$key" ), $key );
 589+ if ( $title === null ) {
 590+ if ( $_ !== '' ) {
 591+ // Empty values checked elsewhere
 592+ $errors[] = "Translation <b>$_</b> is invalid title in $link.";
 593+ }
 594+ } else {
 595+ $text = $title->getText();
 596+ $dbkey = $title->getDBkey();
 597+ if ( $text !== $_ && $dbkey !== $_ ) {
 598+ $errors[] = "Translation <b>$_</b> for $link is not in normalised form, which is <b>$text</b>";
 599+ }
 600+ }
 601+ }
 602+ }
 603+ }
 604+}
 605+
 606+/**
 607+ * Adds support for translating magic words via Special:AdvancedTranslate.
 608+ * @todo Needs documentation.
 609+ */
 610+class MagicWordsCM extends ComplexMessages {
 611+ protected $id = SpecialMagic::MODULE_MAGIC;
 612+ protected $firstMagic = true;
 613+ protected $chainable = true;
 614+ protected $databaseMsg = 'sp-translate-data-MagicWords';
 615+
 616+ public function __construct( $code ) {
 617+ parent::__construct( $code );
 618+ $this->data['core'] = array(
 619+ 'label' => 'MediaWiki Core',
 620+ 'var' => 'magicWords',
 621+ 'file' => Language::getMessagesFileName( '%CODE%' ),
 622+ 'code' => false,
 623+ );
 624+
 625+ global $wgTranslateExtensionDirectory;
 626+ $groups = MessageGroups::singleton()->getGroups();
 627+ foreach ( $groups as $g ) {
 628+ if ( !$g instanceof ExtensionMessageGroup ) {
 629+ continue;
 630+ }
 631+
 632+ $file = $g->getMagicFile();
 633+ if ( $file === null ) {
 634+ continue;
 635+ }
 636+
 637+ $file = "$wgTranslateExtensionDirectory/$file";
 638+ if ( file_exists( $file ) ) {
 639+ $this->data[$g->getId()] = array(
 640+ 'label' => $g->getLabel(),
 641+ 'var' => 'magicWords',
 642+ 'file' => $file,
 643+ 'code' => $code,
 644+ );
 645+ }
 646+ }
 647+ }
 648+
 649+ public function highlight( $key, $values ) {
 650+ if ( count( $values ) && $key === 'redirect' ) {
 651+ $values[0] = "<b>$values[0]</b>";
 652+ }
 653+
 654+ return $values;
 655+ }
 656+}
 657+
 658+/**
 659+ * Adds support for translating namespace names via Special:AdvancedTranslate.
 660+ * @todo Needs documentation.
 661+ */
 662+class NamespaceCM extends ComplexMessages {
 663+ protected $id = SpecialMagic::MODULE_NAMESPACE;
 664+ protected $elementsInArray = false;
 665+ protected $databaseMsg = 'sp-translate-data-Namespaces';
 666+
 667+ public function __construct( $code ) {
 668+ parent::__construct( $code );
 669+ $this->data['core'] = array(
 670+ 'label' => 'MediaWiki Core',
 671+ 'var' => 'namespaceNames',
 672+ 'file' => Language::getMessagesFileName( '%CODE%' ),
 673+ 'code' => false,
 674+ );
 675+ }
 676+
 677+ protected $constants = array(
 678+ -2 => 'NS_MEDIA',
 679+ -1 => 'NS_SPECIAL',
 680+ 0 => 'NS_MAIN',
 681+ 1 => 'NS_TALK',
 682+ 2 => 'NS_USER',
 683+ 3 => 'NS_USER_TALK',
 684+ 4 => 'NS_PROJECT',
 685+ 5 => 'NS_PROJECT_TALK',
 686+ 6 => 'NS_FILE',
 687+ 7 => 'NS_FILE_TALK',
 688+ 8 => 'NS_MEDIAWIKI',
 689+ 9 => 'NS_MEDIAWIKI_TALK',
 690+ 10 => 'NS_TEMPLATE',
 691+ 11 => 'NS_TEMPLATE_TALK',
 692+ 12 => 'NS_HELP',
 693+ 13 => 'NS_HELP_TALK',
 694+ 14 => 'NS_CATEGORY',
 695+ 15 => 'NS_CATEGORY_TALK',
 696+ );
 697+
 698+ protected function validateEach( &$errors = array(), $group, &$used ) {
 699+ parent::validateEach( $errors, $group, $used );
 700+ foreach ( $this->getIterator( $group ) as $key ) {
 701+ $values = $this->val( $group, self::LANG_CURRENT, $key );
 702+
 703+ if ( count( $values ) > 1 ) {
 704+ $link = Xml::element( 'a', array( 'href' => "#mw-sp-magic-$key" ), $key );
 705+ $errors[] = "Namespace $link can have only one translation. Replace the translation with a new one, and notify staff about the change.";
 706+ }
 707+ }
 708+ }
 709+}
Property changes on: trunk/extensions/Translate/ffs/MediaWikiComplexMessages.php
___________________________________________________________________
Added: svn:eol-style
1710 + native
Index: trunk/extensions/Translate/ffs/MediaWikiExtensions.php
@@ -0,0 +1,230 @@
 2+<?php
 3+/**
 4+ * Classes for %MediaWiki extension translation.
 5+ *
 6+ * @file
 7+ * @author Niklas Laxström
 8+ * @copyright Copyright © 2008-2010, Niklas Laxström
 9+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 10+ */
 11+
 12+/**
 13+ * Class which handles special definition format for %MediaWiki extensions.
 14+ */
 15+class PremadeMediawikiExtensionGroups {
 16+ protected $groups;
 17+ protected $definitionFile = null;
 18+ protected $useConfigure = true;
 19+ protected $idPrefix = 'ext-';
 20+ protected $namespaces = array( NS_MEDIAWIKI, NS_MEDIAWIKI_TALK );
 21+
 22+ public function __construct() {
 23+ global $wgTranslateExtensionDirectory;
 24+ $dir = dirname( __FILE__ );
 25+ $this->definitionFile = $dir . '/mediawiki-defines.txt';
 26+ $this->path = $wgTranslateExtensionDirectory;
 27+ }
 28+
 29+ /// Initialisation function
 30+ public function init() {
 31+ if ( $this->groups !== null ) return;
 32+ $groups = $this->parseFile();
 33+ $this->groups = $this->processGroups( $groups );
 34+ }
 35+
 36+ /// Makes an group id from extension name
 37+ static function foldId( $name ) {
 38+ return preg_replace( '/\s+/', '', strtolower( $name ) );
 39+ }
 40+
 41+ /// Registers all extensions
 42+ public function addAll() {
 43+ global $wgTranslateAC, $wgTranslateEC;
 44+ $this->init();
 45+
 46+ if ( !count( $this->groups ) ) return;
 47+
 48+ foreach ( $this->groups as $id => $g ) {
 49+ $wgTranslateAC[$id] = array( $this, 'factory' );
 50+ $wgTranslateEC[] = $id;
 51+ }
 52+ }
 53+
 54+ public function factory( $id ) {
 55+ $info = $this->groups[$id];
 56+ $group = ExtensionMessageGroup::factory( $info['name'], $id );
 57+ $group->setMessageFile( $info['file'] );
 58+ $group->setPath( $this->path );
 59+ $group->namespaces = $this->namespaces;
 60+
 61+ if ( isset( $info['prefix'] ) ) {
 62+ $mangler = new StringMatcher( $info['prefix'], $info['mangle'] );
 63+ $group->setMangler( $mangler );
 64+ $info['ignored'] = $mangler->mangle( $info['ignored'] );
 65+ $info['optional'] = $mangler->mangle( $info['optional'] );
 66+ }
 67+
 68+ if ( !empty( $info['var'] ) ) $group->setVariableName( $info['var'] );
 69+ if ( !empty( $info['optional'] ) ) $group->setOptional( $info['optional'] );
 70+ if ( !empty( $info['ignored'] ) ) $group->setIgnored( $info['ignored'] );
 71+ if ( isset( $info['desc'] ) ) {
 72+ $group->setDescription( $info['desc'] );
 73+ } else {
 74+ $group->setDescriptionMsg( $info['descmsg'], $info['url'] );
 75+ }
 76+
 77+ if ( isset( $info['aliasfile'] ) ) $group->setAliasFile( $info['aliasfile'] );
 78+ if ( isset( $info['magicfile'] ) ) $group->setMagicFile( $info['magicfile'] );
 79+
 80+ return $group;
 81+ }
 82+
 83+ protected function parseFile() {
 84+ $defines = file_get_contents( $this->definitionFile );
 85+ $linefeed = '(\r\n|\n)';
 86+ $sections = array_map( 'trim', preg_split( "/$linefeed{2,}/", $defines, - 1, PREG_SPLIT_NO_EMPTY ) );
 87+ $groups = array();
 88+
 89+ foreach ( $sections as $section ) {
 90+ $lines = array_map( 'trim', preg_split( "/$linefeed/", $section ) );
 91+ $newgroup = array();
 92+
 93+ foreach ( $lines as $line ) {
 94+ if ( $line === '' || $line[0] === '#' ) continue;
 95+
 96+ if ( strpos( $line, '=' ) === false ) {
 97+ if ( empty( $newgroup['name'] ) ) {
 98+ $newgroup['name'] = $line;
 99+ } else {
 100+ throw new MWException( "Trying to define name twice: " . $line );
 101+ }
 102+ } else {
 103+ list( $key, $value ) = array_map( 'trim', explode( '=', $line, 2 ) );
 104+ switch ( $key ) {
 105+ case 'file':
 106+ case 'var':
 107+ case 'id':
 108+ case 'descmsg':
 109+ case 'desc':
 110+ case 'magicfile':
 111+ case 'aliasfile':
 112+ $newgroup[$key] = $value;
 113+ break;
 114+ case 'optional':
 115+ case 'ignored':
 116+ $values = array_map( 'trim', explode( ',', $value ) );
 117+ if ( !isset( $newgroup[$key] ) ) {
 118+ $newgroup[$key] = array();
 119+ }
 120+ $newgroup[$key] = array_merge( $newgroup[$key], $values );
 121+ break;
 122+ case 'prefix':
 123+ list( $prefix, $messages ) = array_map( 'trim', explode( '|', $value, 2 ) );
 124+ if ( isset( $newgroup['prefix'] ) && $newgroup['prefix'] !== $prefix ) {
 125+ throw new MWException( "Only one prefix supported: {$newgroup['prefix']} !== $prefix" );
 126+ }
 127+ $newgroup['prefix'] = $prefix;
 128+
 129+ if ( !isset( $newgroup['mangle'] ) ) $newgroup['mangle'] = array();
 130+
 131+ $messages = array_map( 'trim', explode( ',', $messages ) );
 132+ $newgroup['mangle'] = array_merge( $newgroup['mangle'], $messages );
 133+ break;
 134+ default:
 135+ throw new MWException( "Unknown key:" . $key );
 136+ }
 137+ }
 138+ }
 139+
 140+ if ( count( $newgroup ) ) {
 141+ if ( empty( $newgroup['name'] ) ) {
 142+ throw new MWException( "Name missing\n" . print_r( $newgroup, true ) );
 143+ }
 144+ $groups[] = $newgroup;
 145+ }
 146+ }
 147+
 148+ return $groups;
 149+ }
 150+
 151+ protected function processGroups( $groups ) {
 152+ $configureData = $this->loadConfigureExtensionData();
 153+ $fixedGroups = array();
 154+ foreach ( $groups as $g ) {
 155+ if ( !is_array( $g ) ) {
 156+ $g = array( $g );
 157+ }
 158+
 159+ $name = $g['name'];
 160+
 161+ if ( isset( $g['id'] ) ) {
 162+ $id = $g['id'];
 163+ } else {
 164+ $id = $this->idPrefix . preg_replace( '/\s+/', '', strtolower( $name ) );
 165+ }
 166+
 167+ if ( isset( $g['file'] ) ) {
 168+ $file = $g['file'];
 169+ } else {
 170+ $file = preg_replace( '/\s+/', '', "$name/$name.i18n.php" );
 171+ }
 172+
 173+ if ( isset( $g['descmsg'] ) ) {
 174+ $descmsg = $g['descmsg'];
 175+ } else {
 176+ $descmsg = str_replace( $this->idPrefix, '', $id ) . '-desc';
 177+ }
 178+
 179+ $configureId = self::foldId( $name );
 180+ if ( isset( $configureData[$configureId]['url'] ) ) {
 181+ $url = $configureData[$configureId]['url'];
 182+ } else {
 183+ $url = false;
 184+ }
 185+
 186+ $newgroup = array(
 187+ 'name' => $name,
 188+ 'file' => $file,
 189+ 'descmsg' => $descmsg,
 190+ 'url' => $url,
 191+ );
 192+
 193+ $copyvars = array( 'ignored', 'optional', 'var', 'desc', 'prefix', 'mangle', 'magicfile', 'aliasfile' );
 194+ foreach ( $copyvars as $var ) {
 195+ if ( isset( $g[$var] ) ) {
 196+ $newgroup[$var] = $g[$var];
 197+ }
 198+ }
 199+
 200+ $fixedGroups[$id] = $newgroup;
 201+ }
 202+ return $fixedGroups;
 203+ }
 204+
 205+ protected function loadConfigureExtensionData() {
 206+ if ( !$this->useConfigure ) {
 207+ return array();
 208+ }
 209+
 210+ global $wgAutoloadClasses, $IP, $wgTranslateExtensionDirectory;
 211+
 212+ $postfix = 'Configure/load_txt_def/TxtDef.php';
 213+ if ( file_exists( "$IP/extensions/$postfix" ) ) {
 214+ $prefix = "$IP/extensions";
 215+ } elseif ( file_exists( "$wgTranslateExtensionDirectory/$postfix" ) ) {
 216+ $prefix = $wgTranslateExtensionDirectory;
 217+ } else {
 218+ $prefix = false;
 219+ }
 220+
 221+ if ( $prefix ) {
 222+ $wgAutoloadClasses['TxtDef'] = "$prefix/$postfix";
 223+ $tmp = TxtDef::loadFromFile( "$prefix/Configure/settings/Settings-ext.txt" );
 224+ return array_combine( array_map( array( __CLASS__, 'foldId' ), array_keys( $tmp ) ), array_values( $tmp ) );
 225+ }
 226+
 227+ return array();
 228+ }
 229+
 230+
 231+}
Property changes on: trunk/extensions/Translate/ffs/MediaWikiExtensions.php
___________________________________________________________________
Added: svn:eol-style
1232 + native
Index: trunk/extensions/Translate/ffs/ToolserverTextdomains.php
@@ -0,0 +1,101 @@
 2+<?php
 3+/**
 4+ * Class for Toolserver Intuition for Translatewiki.net
 5+ *
 6+ * @file
 7+ * @author Niklas Laxström
 8+ * @author Krinkle
 9+ * @copyright Copyright © 2008-2010, Niklas Laxström
 10+ * @copyright Copyright © 2011, Krinkle
 11+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 12+ */
 13+
 14+/**
 15+ * Support for tools using Toolserver Intuition at the Toolserver.
 16+ */
 17+class PremadeToolserverTextdomains extends PremadeMediawikiExtensionGroups {
 18+ protected $useConfigure = false;
 19+ protected $groups;
 20+ protected $idPrefix = 'tsint-';
 21+ protected $namespaces = array( NS_TOOLSERVER, NS_TOOLSERVER_TALK );
 22+
 23+ public function __construct() {
 24+ global $wgTranslateGroupRoot;
 25+
 26+ parent::__construct();
 27+ $dir = dirname( __FILE__ );
 28+ $this->definitionFile = $dir . '/toolserver-textdomains.txt';
 29+ $this->path = "$wgTranslateGroupRoot/toolserver/language/messages/";
 30+ }
 31+
 32+ protected function processGroups( $groups ) {
 33+ $configureData = $this->loadConfigureExtensionData();
 34+ $fixedGroups = array();
 35+ foreach ( $groups as $g ) {
 36+ if ( !is_array( $g ) ) {
 37+ $g = array( $g );
 38+ }
 39+
 40+ $name = $g['name'];
 41+ $sanitizedName = preg_replace( '/\s+/', '', strtolower( $name ) );
 42+
 43+ if ( isset( $g['id'] ) ) {
 44+ $id = $g['id'];
 45+ } else {
 46+ $id = $this->idPrefix . $sanitizedName;
 47+ }
 48+
 49+ if ( isset( $g['file'] ) ) {
 50+ $file = $g['file'];
 51+ } else {
 52+ // Toolserver Intuition text-domains are case-insensitive and internally
 53+ // converts to lowercase names starting with a capital letter.
 54+ // eg. "MyTool" -> "Mytool.i18n.php"
 55+ // No subdirectories!
 56+ $file = ucfirst( $sanitizedName ) . '.i18n.php';
 57+ }
 58+
 59+ if ( isset( $g['descmsg'] ) ) {
 60+ $descmsg = $g['descmsg'];
 61+ } else {
 62+ $descmsg = "$id-desc";
 63+ }
 64+
 65+ if ( isset( $g['url'] ) ) {
 66+ $url = $g['url'];
 67+ } else {
 68+ $url = false;
 69+ }
 70+
 71+ $newgroup = array(
 72+ 'name' => 'Toolserver - ' . $name,
 73+ 'file' => $file,
 74+ 'descmsg' => $descmsg,
 75+ 'url' => $url,
 76+ );
 77+
 78+ // Prefix is required, if not customized use the sanitized name
 79+ if ( !isset( $g['prefix'] ) ) {
 80+ $g['prefix'] = "$sanitizedName-";
 81+ }
 82+
 83+ // All messages are prefixed with their groupname
 84+ $g['mangle'] = array( '*' );
 85+
 86+ // Prevent E_NOTICE undefined index.
 87+ // PremadeMediawikiExtensionGroups::factory should probably check this better instead
 88+ if ( !isset( $g['ignored'] ) ) $g['ignored'] = array();
 89+ if ( !isset( $g['optional'] ) ) $g['optional'] = array();
 90+
 91+ $copyvars = array( 'ignored', 'optional', 'var', 'desc', 'prefix', 'mangle', 'magicfile', 'aliasfile' );
 92+ foreach ( $copyvars as $var ) {
 93+ if ( isset( $g[$var] ) ) {
 94+ $newgroup[$var] = $g[$var];
 95+ }
 96+ }
 97+
 98+ $fixedGroups[$id] = $newgroup;
 99+ }
 100+ return $fixedGroups;
 101+ }
 102+}
Property changes on: trunk/extensions/Translate/ffs/ToolserverTextdomains.php
___________________________________________________________________
Added: svn:eol-style
1103 + native
Index: trunk/extensions/Translate/ffs/WikiaExtensions.php
@@ -0,0 +1,28 @@
 2+<?php
 3+/**
 4+ * Classes for Wikia extension translation.
 5+ *
 6+ * @file
 7+ * @author Niklas Laxström
 8+ * @copyright Copyright © 2008-2010, Niklas Laxström
 9+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 10+ */
 11+
 12+/**
 13+ * Support for %MediaWiki extensions in Wikias repository.
 14+ */
 15+class PremadeWikiaExtensionGroups extends PremadeMediawikiExtensionGroups {
 16+ protected $useConfigure = false;
 17+ protected $idPrefix = 'wikia-';
 18+ protected $path = null;
 19+ protected $namespaces = array( NS_WIKIA, NS_WIKIA_TALK );
 20+
 21+ public function __construct() {
 22+ global $wgTranslateGroupRoot;
 23+
 24+ parent::__construct();
 25+ $dir = dirname( __FILE__ );
 26+ $this->definitionFile = $dir . '/extensions.txt';
 27+ $this->path = "$wgTranslateGroupRoot/wikia/";
 28+ }
 29+}
Property changes on: trunk/extensions/Translate/ffs/WikiaExtensions.php
___________________________________________________________________
Added: svn:eol-style
130 + native
Index: trunk/extensions/Translate/ffs/Voctrain.php
@@ -0,0 +1,72 @@
 2+<?php
 3+/**
 4+ * Support for Voctrain vocabulary trainer.
 5+ * http://www.omegawiki.org/extensions/Wikidata/util/voctrain/trainer.php
 6+ *
 7+ * @file
 8+ * @copyright Copyright © 2009-2010, Niklas Laxström
 9+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 10+ */
 11+
 12+/**
 13+ * Old-style message group for Vocabulary trainer.
 14+ */
 15+class VoctrainMessageGroup extends ExtensionMessageGroup {
 16+
 17+ public function getChecker() {
 18+ $checker = new VoctrainMessageChecker( $this );
 19+ $checker->setChecks( array(
 20+ array( $checker, 'VoctrainVariablesCheck' ),
 21+ array( $checker, 'braceBalanceCheck' ),
 22+ ) );
 23+ return $checker;
 24+ }
 25+
 26+}
 27+
 28+/**
 29+ * %Message checker for Vocabulary trainer.
 30+ */
 31+class VoctrainMessageChecker extends MessageChecker {
 32+ /**
 33+ * Checks for missing and unknown parameters
 34+ * @param $messages Iterable list of TMessage objects.
 35+ * @param $code Language code of the translations.
 36+ * @param $warnings Array where warnings are appended to.
 37+ */
 38+ protected function VoctrainVariablesCheck( $messages, $code, &$warnings ) {
 39+ foreach ( $messages as $message ) {
 40+ $key = $message->key();
 41+ $definition = $message->definition();
 42+ $translation = $message->translation();
 43+
 44+ $varPattern = '%[^% ]+';
 45+ preg_match_all( "/$varPattern/U", $definition, $defVars );
 46+ preg_match_all( "/$varPattern/U", $translation, $transVars );
 47+
 48+ # Check for missing variables in the translation
 49+ $subcheck = 'missing';
 50+ $params = self::compareArrays( $defVars[0], $transVars[0] );
 51+ if ( count( $params ) ) {
 52+ $warnings[$key][] = array(
 53+ array( 'variable', $subcheck, $key, $code ),
 54+ 'translate-checks-parameters',
 55+ array( 'PARAMS', $params ),
 56+ array( 'COUNT', count( $params ) ),
 57+ );
 58+ }
 59+
 60+ # Check for unknown variables in the translation
 61+ $subcheck = 'unknown';
 62+ $params = self::compareArrays( $transVars[0], $defVars[0] );
 63+ if ( count( $params ) ) {
 64+ $warnings[$key][] = array(
 65+ array( 'variable', $subcheck, $key, $code ),
 66+ 'translate-checks-parameters-unknown',
 67+ array( 'PARAMS', $params ),
 68+ array( 'COUNT', count( $params ) ),
 69+ );
 70+ }
 71+ }
 72+ }
 73+}
Property changes on: trunk/extensions/Translate/ffs/Voctrain.php
___________________________________________________________________
Added: svn:eol-style
174 + native
Index: trunk/extensions/Translate/ffs/MediaWikiChecker.php
@@ -0,0 +1,250 @@
 2+<?php
 3+/**
 4+ * Implements MessageChecker for %MediaWiki.
 5+ *
 6+ * @file
 7+ * @author Niklas Laxström
 8+ * @copyright Copyright © 2008-2010, Niklas Laxström
 9+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 10+ */
 11+
 12+/**
 13+ * %MediaWiki specific message checks.
 14+ *
 15+ * @ingroup MessageCheckers
 16+ */
 17+class MediaWikiMessageChecker extends MessageChecker {
 18+ /**
 19+ * Checks if the translation uses all variables $[1-9] that the definition
 20+ * uses and vice versa.
 21+ *
 22+ * @param $messages \array Iterable list of TMessage objects.
 23+ * @param $code \string Language code of the translations.
 24+ * @param $warnings \array Array where warnings are appended to.
 25+ */
 26+ protected function wikiParameterCheck( $messages, $code, &$warnings ) {
 27+ return parent::parameterCheck( $messages, $code, $warnings, '/\$[1-9]/' );
 28+ }
 29+
 30+ /**
 31+ * Checks if the translation uses links that are discouraged. Valid links are
 32+ * those that link to Special: or {{ns:special}}: or project pages trough
 33+ * MediaWiki messages like {{MediaWiki:helppage-url}}:. Also links in the
 34+ * definition are allowed.
 35+ *
 36+ * @param $messages \array Iterable list of TMessage objects.
 37+ * @param $code \string Language code of the translations.
 38+ * @param $warnings \array Array where warnings are appended to.
 39+ */
 40+ protected function wikiLinksCheck( $messages, $code, &$warnings ) {
 41+ $tc = Title::legalChars() . '#%{}';
 42+
 43+ foreach ( $messages as $message ) {
 44+ $key = $message->key();
 45+ $definition = $message->definition();
 46+ $translation = $message->translation();
 47+
 48+ $subcheck = 'extra';
 49+ $matches = $links = array();
 50+ preg_match_all( "/\[\[([{$tc}]+)(\\|(.+?))?]]/sDu", $translation, $matches );
 51+ for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
 52+ $backMatch = preg_quote( $matches[1][$i], '/' );
 53+
 54+ if ( preg_match( "/\[\[$backMatch/", $definition ) ) {
 55+ continue;
 56+ }
 57+
 58+ $links[] = "[[{$matches[1][$i]}{$matches[2][$i]}]]";
 59+ }
 60+
 61+ if ( count( $links ) ) {
 62+ $warnings[$key][] = array(
 63+ array( 'links', $subcheck, $key, $code ),
 64+ 'translate-checks-links',
 65+ array( 'PARAMS', $links ),
 66+ array( 'COUNT', count( $links ) ),
 67+ );
 68+ }
 69+
 70+ $subcheck = 'missing';
 71+ $matches = $links = array();
 72+ preg_match_all( "/\[\[([{$tc}]+)(\\|(.+?))?]]/sDu", $definition, $matches );
 73+ for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
 74+ $backMatch = preg_quote( $matches[1][$i], '/' );
 75+
 76+ if ( preg_match( "/\[\[$backMatch/", $translation ) ) {
 77+ continue;
 78+ }
 79+
 80+ $links[] = "[[{$matches[1][$i]}{$matches[2][$i]}]]";
 81+ }
 82+
 83+ if ( count( $links ) ) {
 84+ $warnings[$key][] = array(
 85+ array( 'links', $subcheck, $key, $code ),
 86+ 'translate-checks-links-missing',
 87+ array( 'PARAMS', $links ),
 88+ array( 'COUNT', count( $links ) ),
 89+ );
 90+ }
 91+ }
 92+ }
 93+
 94+ /**
 95+ * Checks if the \<br /> and \<hr /> tags are using the correct syntax.
 96+ *
 97+ * @param $messages \array Iterable list of TMessage objects.
 98+ * @param $code \string Language code of the translations.
 99+ * @param $warnings \array Array where warnings are appended to.
 100+ */
 101+ protected function XhtmlCheck( $messages, $code, &$warnings ) {
 102+ foreach ( $messages as $message ) {
 103+ $key = $message->key();
 104+ $translation = $message->translation();
 105+ if ( strpos( $translation, '<' ) === false ) {
 106+ continue;
 107+ }
 108+
 109+ $subcheck = 'invalid';
 110+ $tags = array(
 111+ '~<hr *(\\\\)?>~suDi' => '<hr />', // Wrong syntax
 112+ '~<br *(\\\\)?>~suDi' => '<br />',
 113+ '~<hr/>~suDi' => '<hr />', // Wrong syntax
 114+ '~<br/>~suDi' => '<br />',
 115+ '~<(HR|Hr|hR) />~su' => '<hr />', // Case
 116+ '~<(BR|Br|bR) />~su' => '<br />',
 117+ );
 118+
 119+ $wrongTags = array();
 120+ foreach ( $tags as $wrong => $correct ) {
 121+ $matches = array();
 122+ preg_match_all( $wrong, $translation, $matches, PREG_PATTERN_ORDER );
 123+ foreach ( $matches[0] as $wrongMatch ) {
 124+ $wrongTags[$wrongMatch] = "$wrongMatch → $correct";
 125+ }
 126+ }
 127+
 128+ if ( count( $wrongTags ) ) {
 129+ $warnings[$key][] = array(
 130+ array( 'xhtml', $subcheck, $key, $code ),
 131+ 'translate-checks-xhtml',
 132+ array( 'PARAMS', $wrongTags ),
 133+ array( 'COUNT', count( $wrongTags ) ),
 134+ );
 135+ }
 136+ }
 137+ }
 138+
 139+ /**
 140+ * Checks if the translation doesn't use plural while the definition has one.
 141+ *
 142+ * @param $messages \array Iterable list of TMessage objects.
 143+ * @param $code \string Language code of the translations.
 144+ * @param $warnings \array Array where warnings are appended to.
 145+ */
 146+ protected function pluralCheck( $messages, $code, &$warnings ) {
 147+ foreach ( $messages as $message ) {
 148+ $key = $message->key();
 149+ $definition = $message->definition();
 150+ $translation = $message->translation();
 151+
 152+ $subcheck = 'missing';
 153+ if (
 154+ stripos( $definition, '{{plural:' ) !== false &&
 155+ stripos( $translation, '{{plural:' ) === false
 156+ ) {
 157+ $warnings[$key][] = array(
 158+ array( 'plural', $subcheck, $key, $code ),
 159+ 'translate-checks-plural',
 160+ );
 161+ }
 162+ }
 163+ }
 164+
 165+ /**
 166+ * Checks for page names that they have an untranslated namespace.
 167+ *
 168+ * @param $messages \array Iterable list of TMessage objects.
 169+ * @param $code \string Language code of the translations.
 170+ * @param $warnings \array Array where warnings are appended to.
 171+ */
 172+ protected function pagenameMessagesCheck( $messages, $code, &$warnings ) {
 173+ foreach ( $messages as $message ) {
 174+ $key = $message->key();
 175+ $definition = $message->definition();
 176+ $translation = $message->translation();
 177+
 178+ $subcheck = 'namespace';
 179+ $namespaces = 'help|project|\{\{ns:project}}|mediawiki';
 180+ $matches = array();
 181+ if ( preg_match( "/^($namespaces):[\w\s]+$/ui", $definition, $matches ) ) {
 182+ if ( !preg_match( "/^{$matches[1]}:.+$/u", $translation ) ) {
 183+ $warnings[$key][] = array(
 184+ array( 'pagename', $subcheck, $key, $code ),
 185+ 'translate-checks-pagename',
 186+ );
 187+ }
 188+ }
 189+ }
 190+ }
 191+
 192+ /**
 193+ * Checks for some miscellaneous messages with special syntax.
 194+ *
 195+ * @param $messages \array Iterable list of TMessage objects.
 196+ * @param $code \string Language code of the translations.
 197+ * @param $warnings \array Array where warnings are appended to.
 198+ */
 199+ protected function miscMWChecks( $messages, $code, &$warnings ) {
 200+ $timeList = array( 'protect-expiry-options', 'ipboptions' );
 201+
 202+ foreach ( $messages as $message ) {
 203+ $key = $message->key();
 204+ $definition = $message->definition();
 205+ $translation = $message->translation();
 206+
 207+
 208+ if ( in_array( strtolower( $key ), $timeList, true ) ) {
 209+ $defArray = explode( ',', $definition );
 210+ $traArray = explode( ',', $translation );
 211+
 212+ $subcheck = 'timelist-count';
 213+ $defCount = count( $defArray );
 214+ $traCount = count( $traArray );
 215+ if ( $defCount !== $traCount ) {
 216+ $warnings[$key][] = array(
 217+ array( 'miscmw', $subcheck, $key, $code ),
 218+ 'translate-checks-format',
 219+ "Parameter count is $traCount; should be $defCount", // @todo Missing i18n.
 220+ );
 221+ continue;
 222+ }
 223+
 224+ for ( $i = 0; $i < count( $defArray ); $i++ ) {
 225+ $defItems = array_map( 'trim', explode( ':', $defArray[$i] ) );
 226+ $traItems = array_map( 'trim', explode( ':', $traArray[$i] ) );
 227+
 228+ $subcheck = 'timelist-format';
 229+ if ( count( $traItems ) !== 2 ) {
 230+ $warnings[$key][] = array(
 231+ array( 'miscmw', $subcheck, $key, $code ),
 232+ 'translate-checks-format',
 233+ "<nowiki>$traArray[$i]</nowiki> is malformed", // @todo Missing i18n.
 234+ );
 235+ continue;
 236+ }
 237+
 238+ $subcheck = 'timelist-format-value';
 239+ if ( $traItems[1] !== $defItems[1] ) {
 240+ $warnings[$key][] = array(
 241+ array( 'miscmw', $subcheck, $key, $code ),
 242+ 'translate-checks-format',
 243+ "<tt><nowiki>$traItems[1] !== $defItems[1]</nowiki></tt>",
 244+ );
 245+ continue;
 246+ }
 247+ }
 248+ }
 249+ }
 250+ }
 251+}
Property changes on: trunk/extensions/Translate/ffs/MediaWikiChecker.php
___________________________________________________________________
Added: svn:eol-style
1252 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r98324Partial revert of r98323: no need to move Checker.php for MediaWiki.siebrand16:23, 28 September 2011
r98326Follow-up r98323: fix path to configuration files.siebrand16:29, 28 September 2011
r98937Follow-up r98323: remove $wgAutoloadClasses['MediaWikiMessageChecker'] which ...siebrand22:11, 4 October 2011

Comments

#Comment by Reedy (talk | contribs)   20:59, 4 October 2011
$wgAutoloadClasses['MediaWikiMessageChecker'] = $dir . 'ffs/MediaWikiChecker.php';

Not sure how specifically to go about this...

I noticed the MediaWikiMessageChecker was apparently missing, which is in /trunk/translate

I see it's only used in WikiPageMessageGroup, so it's standalone

Just find it a slight concern with it being unconditionally loaded, I'm guessing that'll complain on other wikis where it's not there...

Status & tagging log