46 if ( isset( $options[
'help'] ) ) {
51 if ( isset( $options[
'lang'] ) ) {
52 $this->code = $options[
'lang'];
58 if ( isset( $options[
'level'] ) ) {
59 $this->level = $options[
'level'];
62 $this->doLinks = isset( $options[
'links'] );
63 $this->includeExif = !isset( $options[
'noexif'] );
64 $this->checkAll = isset( $options[
'all'] );
66 if ( isset( $options[
'prefix'] ) ) {
67 $this->linksPrefix = $options[
'prefix'];
70 if ( isset( $options[
'wikilang'] ) ) {
71 $this->wikiCode = $options[
'wikilang'];
74 if ( isset( $options[
'whitelist'] ) ) {
75 $this->checks = explode(
',', $options[
'whitelist'] );
76 } elseif ( isset( $options[
'blacklist'] ) ) {
77 $this->checks = array_diff(
79 explode(
',', $options[
'blacklist'] )
81 } elseif ( isset( $options[
'easy'] ) ) {
87 if ( isset( $options[
'output'] ) ) {
88 $this->output = $options[
'output'];
91 $this->L =
new Languages( $this->includeExif );
100 'untranslated',
'duplicate',
'obsolete',
'variables',
'empty',
'plural',
101 'whitespace',
'xhtml',
'chars',
'links',
'unbalanced',
'namespace',
102 'projecttalk',
'magic',
'magic-old',
'magic-over',
'magic-case',
103 'special',
'special-old',
113 'namespace',
'projecttalk',
'magic',
'magic-old',
'magic-over',
114 'magic-case',
'special',
'special-old',
124 'duplicate',
'obsolete',
'empty',
'whitespace',
'xhtml',
'chars',
'magic-old',
125 'magic-over',
'magic-case',
'special-old',
135 'untranslated' =>
'getUntranslatedMessages',
136 'duplicate' =>
'getDuplicateMessages',
137 'obsolete' =>
'getObsoleteMessages',
138 'variables' =>
'getMessagesWithMismatchVariables',
139 'plural' =>
'getMessagesWithoutPlural',
140 'empty' =>
'getEmptyMessages',
141 'whitespace' =>
'getMessagesWithWhitespace',
142 'xhtml' =>
'getNonXHTMLMessages',
143 'chars' =>
'getMessagesWithWrongChars',
144 'links' =>
'getMessagesWithDubiousLinks',
145 'unbalanced' =>
'getMessagesWithUnbalanced',
146 'namespace' =>
'getUntranslatedNamespaces',
147 'projecttalk' =>
'getProblematicProjectTalks',
148 'magic' =>
'getUntranslatedMagicWords',
149 'magic-old' =>
'getObsoleteMagicWords',
150 'magic-over' =>
'getOverridingMagicWords',
151 'magic-case' =>
'getCaseMismatchMagicWords',
152 'special' =>
'getUntraslatedSpecialPages',
153 'special-old' =>
'getObsoleteSpecialPages',
165 'namespace' => [
'getNamespaceNames',
'en' ],
166 'projecttalk' =>
null,
167 'magic' => [
'getMagicWords',
'en' ],
168 'magic-old' => [
'getMagicWords', null ],
169 'magic-over' => [
'getMagicWords', null ],
170 'magic-case' => [
'getMagicWords', null ],
171 'special' => [
'getSpecialPageAliases',
'en' ],
172 'special-old' => [
'getSpecialPageAliases', null ],
182 'untranslated' =>
'$1 message(s) of $2 are not translated to $3, but exist in en:',
183 'duplicate' =>
'$1 message(s) of $2 are translated the same in en and $3:',
185 '$1 message(s) of $2 do not exist in en or are in the ignore list, but exist in $3:',
186 'variables' =>
'$1 message(s) of $2 in $3 don\'t match the variables used in en:',
187 'plural' =>
'$1 message(s) of $2 in $3 don\'t use {{plural}} while en uses:',
188 'empty' =>
'$1 message(s) of $2 in $3 are empty or -:',
189 'whitespace' =>
'$1 message(s) of $2 in $3 have trailing whitespace:',
190 'xhtml' =>
'$1 message(s) of $2 in $3 contain illegal XHTML:',
192 '$1 message(s) of $2 in $3 include hidden chars which should not be used in the messages:',
193 'links' =>
'$1 message(s) of $2 in $3 have problematic link(s):',
194 'unbalanced' =>
'$1 message(s) of $2 in $3 have unbalanced {[]}:',
195 'namespace' =>
'$1 namespace name(s) of $2 are not translated to $3, but exist in en:',
197 '$1 namespace name(s) and alias(es) in $3 are project talk namespaces without the parameter:',
198 'magic' =>
'$1 magic word(s) of $2 are not translated to $3, but exist in en:',
199 'magic-old' =>
'$1 magic word(s) of $2 do not exist in en, but exist in $3:',
200 'magic-over' =>
'$1 magic word(s) of $2 in $3 do not contain the original en word(s):',
202 '$1 magic word(s) of $2 in $3 change the case-sensitivity of the original en word:',
203 'special' =>
'$1 special page alias(es) of $2 are not translated to $3, but exist in en:',
204 'special-old' =>
'$1 special page alias(es) of $2 do not exist in en, but exist in $3:',
214 Run
this script to check a specific language file, or all of them.
215 Command line settings are in form --parameter[=value].
218 --lang:
Language code (
default: the installation
default language).
219 --all: Check all customized languages.
220 --level: Show the following display level (
default: 2):
221 * 0: Skip the checks (useful
for checking syntax).
222 * 1: Show only the stub headers and number of wrong messages, without
224 * 2: Show only the headers and the message keys, without the message
226 * 3: Show both the headers and the complete messages, with both keys and
228 --links: Link the message values (
default off).
229 --prefix: prefix to add to links.
230 --wikilang: For the links, what is the content language of the wiki to
231 display the output in (
default en).
232 --noexif: Do not check
for Exif messages (a bit hard and boring to
233 translate),
if you know what they are currently not translated and want
234 to focus on other problems (
default off).
235 --whitelist: Do only the following checks (form: code,code).
236 --blacklist: Do not
do the following checks (form: code,code).
237 --easy: Do only the easy checks, which can be treated by non-speakers of
240 Check codes (ideally, all of them should result 0; all the checks are executed
241 by
default (except language-specific check blacklists in
checkLanguage.inc):
242 * untranslated: Messages which are required to translate, but are not
244 * duplicate: Messages which translation equal to fallback.
245 * obsolete: Messages which are untranslatable or
do not exist, but are
247 * variables: Messages without variables which should be used, or with
248 variables which should not be used.
249 * empty: Empty messages and messages that contain only -.
250 * whitespace: Messages which have trailing whitespace.
251 * xhtml: Messages which are not well-formed XHTML (checks only few common
253 * chars: Messages with hidden characters.
254 * links: Messages which contains broken links to pages (does not find all).
255 * unbalanced: Messages which contains unequal numbers of opening {[ and
257 *
namespace: Namespace names that were not translated.
258 * projecttalk: Namespace names and aliases where the project talk does not
260 * magic: Magic words that were not translated.
261 * magic-old: Magic words which
do not exist.
262 * magic-over: Magic words that
override the original English word.
263 * magic-
case: Magic words whose translation changes the
case-sensitivity of
264 the original English word.
265 * special: Special page names that were not translated.
266 * special-old: Special page names which
do not exist.
276 if ( $this->level > 0 ) {
277 switch ( $this->output ) {
285 throw new MWException(
"Invalid output type $this->output" );
294 $ignoredCodes = [
'en',
'enRTL' ];
298 if ( $this->checkAll ) {
299 foreach ( $this->L->getLanguages() as $language ) {
300 if ( !in_array( $language, $ignoredCodes ) ) {
301 $this->results[$language] = $this->
checkLanguage( $language );
305 if ( in_array( $this->code, $ignoredCodes ) ) {
306 throw new MWException(
"Cannot check code $this->code." );
314 foreach (
$checks as $check => $messages ) {
315 foreach ( $messages as $key => $details ) {
317 unset( $this->results[
$code][$check][$key] );
329 static $blacklist =
null;
331 if ( $blacklist !==
null ) {
340 Hooks::run(
'LocalisationChecksBlacklist', [ &$blacklist ] );
356 foreach ( $blacklist as $item ) {
357 if ( isset( $item[
'check'] ) && $check !== $item[
'check'] ) {
361 if ( isset( $item[
'code'] ) && !in_array(
$code, $item[
'code'] ) ) {
365 if ( isset( $item[
'message'] ) &&
366 ( $message ===
false || !in_array( $message, $item[
'message'] ) )
386 if ( $this->level === 0 ) {
387 $this->L->getMessages(
$code );
393 foreach ( $this->checks as $check ) {
399 $callback = [
$this->L, $checkFunctions[$check] ];
400 if ( !is_callable( $callback ) ) {
416 if ( $this->doLinks ) {
417 $displayKey = ucfirst( $key );
418 if (
$code == $this->wikiCode ) {
419 return "[[{$this->linksPrefix}MediaWiki:$displayKey|$key]]";
421 return "[[{$this->linksPrefix}MediaWiki:$displayKey/$code|$key]]";
433 $translated = $this->L->getMessages(
$code );
434 $translated = count( $translated[
'translated'] );
435 foreach (
$results as $check => $messages ) {
436 $count = count( $messages );
438 if ( $check ==
'untranslated' ) {
439 $translatable = $this->L->getGeneralMessages();
440 $total = count( $translatable[
'translatable'] );
443 $totalCount = $totalCount[$check];
444 $callback = [
$this->L, $totalCount[0] ];
445 $callCode = $totalCount[1] ? $totalCount[1] :
$code;
446 $total = count( call_user_func( $callback, $callCode ) );
448 $total = $translated;
450 $search = [
'$1',
'$2',
'$3' ];
451 $replace = [ $count, $total,
$code ];
453 echo
"\n" . str_replace( $search, $replace, $descriptions[$check] ) .
"\n";
454 if ( $this->level == 1 ) {
455 echo
"[messages are hidden]\n";
457 foreach ( $messages as $key => $value ) {
461 if ( $this->level == 2 || empty( $value ) ) {
464 echo
"* $key: '$value'\n";
478 $rows[] =
'! Language !! Code !! Total !! ' .
481 $detailTextForLang =
"==$code==\n";
484 $detailTextForLangChecks = [];
485 foreach (
$results as $check => $messages ) {
489 $count = count( $messages );
492 $messageDetails = [];
493 foreach ( $messages as $key => $details ) {
495 $messageDetails[] = $displayKey;
497 $detailTextForLangChecks[] =
"=== $code-$check ===\n* " . implode(
', ', $messageDetails );
498 $numbers[] =
"'''[[#$code-$check|$count]]'''";
504 if ( count( $detailTextForLangChecks ) ) {
505 $detailText .= $detailTextForLang . implode(
"\n", $detailTextForLangChecks ) .
"\n";
509 # Don't list languages without problems
513 $rows[] =
"| $language || $code || $problems || " . implode(
' || ', $numbers );
516 $tableRows = implode(
"\n|-\n", $rows );
521 '''Check results are
for:
''' <code>$version</code>
524 {|
class=
"sortable wikitable" border=
"2" cellpadding=
"4" cellspacing=
"0" style=
"background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse; clear: both;"
539 foreach ( $this->results as
$results ) {
541 if ( !empty( $messages ) ) {
562 if ( isset( $options[
'help'] ) ) {
567 if ( isset( $options[
'lang'] ) ) {
568 $this->code = $options[
'lang'];
574 if ( isset( $options[
'level'] ) ) {
575 $this->level = $options[
'level'];
578 $this->doLinks = isset( $options[
'links'] );
580 if ( isset( $options[
'wikilang'] ) ) {
581 $this->wikiCode = $options[
'wikilang'];
584 if ( isset( $options[
'whitelist'] ) ) {
585 $this->checks = explode(
',', $options[
'whitelist'] );
586 } elseif ( isset( $options[
'blacklist'] ) ) {
587 $this->checks = array_diff(
589 explode(
',', $options[
'blacklist'] )
591 } elseif ( isset( $options[
'easy'] ) ) {
597 if ( isset( $options[
'output'] ) ) {
598 $this->output = $options[
'output'];
601 # Some additional checks not enabled by default
602 if ( isset( $options[
'duplicate'] ) ) {
603 $this->checks[] =
'duplicate';
606 $this->extensions = [];
607 $extensions =
new PremadeMediawikiExtensionGroups();
609 if ( $extension ==
'all' ) {
610 foreach ( MessageGroups::singleton()->getGroups() as $group ) {
611 if ( strpos( $group->getId(),
'ext-' ) === 0 && !$group->isMeta() ) {
615 } elseif ( $extension ==
'wikimedia' ) {
616 $wikimedia = MessageGroups::getGroup(
'ext-0-wikimedia' );
617 foreach ( $wikimedia->wmfextensions() as $extension ) {
618 $group = MessageGroups::getGroup( $extension );
621 } elseif ( $extension ==
'flaggedrevs' ) {
622 foreach ( MessageGroups::singleton()->getGroups() as $group ) {
623 if ( strpos( $group->getId(),
'ext-flaggedrevs-' ) === 0 && !$group->isMeta() ) {
630 $group = MessageGroups::getGroup(
'ext-' . $extension );
633 $this->extensions[] = $extension;
635 print "No such extension $extension.\n";
647 'untranslated',
'duplicate',
'obsolete',
'variables',
'empty',
'plural',
648 'whitespace',
'xhtml',
'chars',
'links',
'unbalanced',
666 'duplicate',
'obsolete',
'empty',
'whitespace',
'xhtml',
'chars',
676 Run
this script to check the status of a specific language in extensions, or
677 all of them. Command line settings are in form --parameter[=value], except
for
680 * First parameter (mandatory): Extension name, multiple extension names
681 (separated by commas),
"all" for all the extensions,
"wikimedia" for
682 extensions used by
Wikimedia or
"flaggedrevs" for all FLaggedRevs
684 * lang:
Language code (
default: the installation
default language).
686 * level: Show the following display level (
default: 2).
687 * links: Link the message values (
default off).
688 * wikilang: For the links, what is the content language of the wiki to
689 display the output in (
default en).
690 * whitelist: Do only the following checks (form: code,code).
691 * blacklist: Do not perform the following checks (form: code,code).
692 * easy: Do only the easy checks, which can be treated by non-speakers of
695 Check codes (ideally, all of them should result 0; all the checks are executed
696 by
default (except language-specific check blacklists in
checkLanguage.inc):
697 * untranslated: Messages which are required to translate, but are not
699 * duplicate: Messages which translation equal to fallback.
700 * obsolete: Messages which are untranslatable, but translated.
701 * variables: Messages without variables which should be used, or with
702 variables which should not be used.
703 * empty: Empty messages.
704 * whitespace: Messages which have trailing whitespace.
705 * xhtml: Messages which are not well-formed XHTML (checks only few common
707 * chars: Messages with hidden characters.
708 * links: Messages which contains broken links to pages (does not find all).
709 * unbalanced: Messages which contains unequal numbers of opening {[ and
712 Display levels (
default: 2):
713 * 0: Skip the checks (useful
for checking syntax).
714 * 1: Show only the stub headers and number of wrong messages, without list
716 * 2: Show only the headers and the message keys, without the message
718 * 3: Show both the headers and the complete messages, with both keys and
737 foreach ( $this->extensions as $extension ) {
738 $this->L = $extension;
740 $this->results[
$code] = parent::checkLanguage(
$code );
743 echo $extension->name() .
":\n";
745 if ( $this->level > 0 ) {
746 switch ( $this->output ) {
754 throw new MWException(
"Invalid output type $this->output" );
769 'code' => [
'az',
'bo',
'cdo',
'dz',
'id',
'fa',
'gan',
'gan-hans',
770 'gan-hant',
'gn',
'hak',
'hu',
'ja',
'jv',
'ka',
'kk-arab',
771 'kk-cyrl',
'kk-latn',
'km',
'kn',
'ko',
'lzh',
'mn',
'ms',
772 'my',
'sah',
'sq',
'tet',
'th',
'to',
'tr',
'vi',
'wuu',
'xmf',
773 'yo',
'yue',
'zh',
'zh-classical',
'zh-cn',
'zh-hans',
774 'zh-hant',
'zh-hk',
'zh-sg',
'zh-tw',
'zh-yue'