summaryrefslogtreecommitdiff
path: root/maintenance/language
diff options
context:
space:
mode:
Diffstat (limited to 'maintenance/language')
-rw-r--r--maintenance/language/alltrans.php16
-rw-r--r--maintenance/language/checkLanguage.php177
-rw-r--r--maintenance/language/checktrans.php44
-rw-r--r--maintenance/language/date-formats.php45
-rw-r--r--maintenance/language/diffLanguage.php159
-rw-r--r--maintenance/language/dumpMessages.php20
-rw-r--r--maintenance/language/duplicatetrans.php43
-rw-r--r--maintenance/language/function-list.php44
-rw-r--r--maintenance/language/lang2po.php154
-rw-r--r--maintenance/language/langmemusage.php30
-rw-r--r--maintenance/language/languages.inc686
-rw-r--r--maintenance/language/splitLanguageFiles.inc1168
-rw-r--r--maintenance/language/splitLanguageFiles.php13
-rw-r--r--maintenance/language/transstat.php211
-rw-r--r--maintenance/language/unusedMessages.php42
-rw-r--r--maintenance/language/validate.php40
16 files changed, 2892 insertions, 0 deletions
diff --git a/maintenance/language/alltrans.php b/maintenance/language/alltrans.php
new file mode 100644
index 00000000..f8db9c0d
--- /dev/null
+++ b/maintenance/language/alltrans.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * @package MediaWiki
+ * @subpackage Maintenance
+ *
+ * Get all the translations messages, as defined in the English language file.
+ */
+
+require_once( dirname(__FILE__).'/../commandLine.inc' );
+
+$wgEnglishMessages = array_keys( Language::getMessagesFor( 'en' ) );
+foreach( $wgEnglishMessages as $key ) {
+ echo "$key\n";
+}
+
+?>
diff --git a/maintenance/language/checkLanguage.php b/maintenance/language/checkLanguage.php
new file mode 100644
index 00000000..11c8ec92
--- /dev/null
+++ b/maintenance/language/checkLanguage.php
@@ -0,0 +1,177 @@
+<?php
+/**
+ * Check a language file.
+ *
+ * @package MediaWiki
+ * @subpackage Maintenance
+ */
+
+require_once( dirname(__FILE__).'/../commandLine.inc' );
+require_once( 'languages.inc' );
+
+/**
+ * Check a language.
+ *
+ * @param $code The language code.
+ */
+function checkLanguage( $code ) {
+ global $wgLanguages, $wgGeneralMessages, $wgRequiredMessagesNumber, $wgDisplayLevel, $wgLinks, $wgWikiLanguage, $wgChecks;
+
+ # Get messages
+ $messages = $wgLanguages->getMessages( $code );
+ $messagesNumber = count( $messages['translated'] );
+
+ # Skip the checks if specified
+ if ( $wgDisplayLevel == 0 ) {
+ return;
+ }
+
+ # Untranslated messages
+ if ( in_array( 'untranslated', $wgChecks ) ) {
+ $untranslatedMessages = $wgLanguages->getUntranslatedMessages( $code );
+ $untranslatedMessagesNumber = count( $untranslatedMessages );
+ $wgLanguages->outputMessagesList( $untranslatedMessages, $code, "\n$untranslatedMessagesNumber messages of $wgRequiredMessagesNumber are not translated to $code, but exist in en:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Duplicate messages
+ if ( in_array( 'duplicate', $wgChecks ) ) {
+ $duplicateMessages = $wgLanguages->getDuplicateMessages( $code );
+ $duplicateMessagesNumber = count( $duplicateMessages );
+ $wgLanguages->outputMessagesList( $duplicateMessages, $code, "\n$duplicateMessagesNumber messages of $messagesNumber are translated the same in en and $code:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Obsolete messages
+ if ( in_array( 'obsolete', $wgChecks ) ) {
+ $obsoleteMessages = $messages['obsolete'];
+ $obsoleteMessagesNumber = count( $obsoleteMessages );
+ $wgLanguages->outputMessagesList( $obsoleteMessages, $code, "\n$obsoleteMessagesNumber messages of $messagesNumber are not exist in en (or are in the ignored list), but still exist in $code:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Messages without variables
+ if ( in_array( 'variables', $wgChecks ) ) {
+ $messagesWithoutVariables = $wgLanguages->getMessagesWithoutVariables( $code );
+ $messagesWithoutVariablesNumber = count( $messagesWithoutVariables );
+ $wgLanguages->outputMessagesList( $messagesWithoutVariables, $code, "\n$messagesWithoutVariablesNumber messages of $messagesNumber in $code don't use some variables while en uses them:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Empty messages
+ if ( in_array( 'empty', $wgChecks ) ) {
+ $emptyMessages = $wgLanguages->getEmptyMessages( $code );
+ $emptyMessagesNumber = count( $emptyMessages );
+ $wgLanguages->outputMessagesList( $emptyMessages, $code, "\n$emptyMessagesNumber messages of $messagesNumber in $code are empty or -:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Messages with whitespace
+ if ( in_array( 'whitespace', $wgChecks ) ) {
+ $messagesWithWhitespace = $wgLanguages->getMessagesWithWhitespace( $code );
+ $messagesWithWhitespaceNumber = count( $messagesWithWhitespace );
+ $wgLanguages->outputMessagesList( $messagesWithWhitespace, $code, "\n$messagesWithWhitespaceNumber messages of $messagesNumber in $code have a trailing whitespace:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Non-XHTML messages
+ if ( in_array( 'xhtml', $wgChecks ) ) {
+ $nonXHTMLMessages = $wgLanguages->getNonXHTMLMessages( $code );
+ $nonXHTMLMessagesNumber = count( $nonXHTMLMessages );
+ $wgLanguages->outputMessagesList( $nonXHTMLMessages, $code, "\n$nonXHTMLMessagesNumber messages of $messagesNumber in $code are not well-formed XHTML:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+
+ # Messages with wrong characters
+ if ( in_array( 'chars', $wgChecks ) ) {
+ $messagesWithWrongChars = $wgLanguages->getMessagesWithWrongChars( $code );
+ $messagesWithWrongCharsNumber = count( $messagesWithWrongChars );
+ $wgLanguages->outputMessagesList( $messagesWithWrongChars, $code, "\n$messagesWithWrongCharsNumber messages of $messagesNumber in $code include hidden chars which should not be used in the messages:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+ }
+}
+
+# Show help
+if ( isset( $options['help'] ) ) {
+ echo <<<END
+Run this script to check a specific language file, or all of them.
+Parameters:
+ * lang: Language code (default: the installation default language). You can also specify "all" to check all the languages.
+ * help: Show this help.
+ * level: Show the following level (default: 2).
+ * links: Link the message values (default off).
+ * wikilang: For the links, what is the content language of the wiki to display the output in (default en).
+ * whitelist: Make only the following checks (form: code,code).
+ * blacklist: Don't make the following checks (form: code,code).
+ * duplicate: Additionally check for messages which are translated the same to English (default off).
+ * noexif: Don't check for EXIF messages (a bit hard and boring to translate), if you know that they are currently not translated and want to focus on other problems (default off).
+Check codes (ideally, all of them should result 0; all the checks are executed by default):
+ * untranslated: Messages which are required to translate, but are not translated.
+ * obsolete: Messages which are untranslatable, but translated.
+ * variables: Messages without variables which should be used.
+ * empty: Empty messages.
+ * whitespace: Messages which have trailing whitespace.
+ * xhtml: Messages which are not well-formed XHTML.
+ * chars: Messages with hidden characters.
+Display levels (default: 2):
+ * 0: Skip the checks (useful for checking syntax).
+ * 1: Show only the stub headers and number of wrong messages, without list of messages.
+ * 2: Show only the headers and the message keys, without the message values.
+ * 3: Show both the headers and the complete messages, with both keys and values.
+
+END;
+ exit();
+}
+
+# Get the language code
+if ( isset( $options['lang'] ) ) {
+ $wgCode = $options['lang'];
+} else {
+ $wgCode = $wgContLang->getCode();
+}
+
+# Get the display level
+if ( isset( $options['level'] ) ) {
+ $wgDisplayLevel = $options['level'];
+} else {
+ $wgDisplayLevel = 2;
+}
+
+# Get the links options
+$wgLinks = isset( $options['links'] );
+$wgWikiLanguage = isset( $options['wikilang'] ) ? $options['wikilang'] : 'en';
+
+# Get the checks to do
+$wgChecks = array( 'untranslated', 'obsolete', 'variables', 'empty', 'whitespace', 'xhtml', 'chars' );
+if ( isset( $options['whitelist'] ) ) {
+ $wgChecks = explode( ',', $options['whitelist'] );
+} elseif ( isset( $options['blacklist'] ) ) {
+ $wgChecks = array_diff( $wgChecks, explode( ',', $options['blacklist'] ) );
+}
+
+# Add duplicate option if specified
+if ( isset( $options['duplicate'] ) ) {
+ $wgChecks[] = 'duplicate';
+}
+
+# Should check for EXIF?
+$wgCheckEXIF = !isset( $options['noexif'] );
+
+# Get language objects
+$wgLanguages = new languages( $wgCheckEXIF );
+
+# Get the general messages
+$wgGeneralMessages = $wgLanguages->getGeneralMessages();
+$wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] );
+
+# Check the language
+if ( $wgCode == 'all' ) {
+ foreach ( $wgLanguages->getLanguages() as $language ) {
+ if ( $language != 'en' && $language != 'enRTL' ) {
+ checkLanguage( $language );
+ }
+ }
+} else {
+ # Can't check English
+ if ( $wgCode == 'en' ) {
+ echo "Current selected language is English, which cannot be checked.\n";
+ } else if ( $wgCode == 'enRTL' ) {
+ echo "Current selected language is RTL English, which cannot be checked.\n";
+ } else {
+ checkLanguage( $wgCode );
+ }
+}
+
+?>
diff --git a/maintenance/language/checktrans.php b/maintenance/language/checktrans.php
new file mode 100644
index 00000000..a5772d47
--- /dev/null
+++ b/maintenance/language/checktrans.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @package MediaWiki
+ * @subpackage Maintenance
+ * Check to see if all messages have been translated into the selected language.
+ * To run this script, you must have a working installation, and you can specify
+ * a language, or the script will check the installation language.
+ */
+
+/** */
+require_once(dirname(__FILE__).'/../commandLine.inc');
+
+if ( isset( $args[0] ) ) {
+ $code = $args[0];
+} else {
+ $code = $wgLang->getCode();
+}
+
+if ( $code == 'en' ) {
+ print "Current selected language is English. Cannot check translations.\n";
+ exit();
+}
+
+$filename = Language::getMessagesFileName( $code );
+if ( file_exists( $filename ) ) {
+ require( $filename );
+} else {
+ $messages = array();
+}
+
+$count = $total = 0;
+$wgEnglishMessages = Language::getMessagesFor( 'en' );
+$wgLocalMessages = $messages;
+
+foreach ( $wgEnglishMessages as $key => $msg ) {
+ ++$total;
+ if ( !isset( $wgLocalMessages[$key] ) ) {
+ print "'{$key}' => \"$msg\",\n";
+ ++$count;
+ }
+}
+
+print "{$count} messages of {$total} are not translated in the language {$code}.\n";
+?>
diff --git a/maintenance/language/date-formats.php b/maintenance/language/date-formats.php
new file mode 100644
index 00000000..962c2f8c
--- /dev/null
+++ b/maintenance/language/date-formats.php
@@ -0,0 +1,45 @@
+<?php
+
+$ts = '20010115123456';
+
+
+$IP = dirname( __FILE__ ) . '/../..';
+require_once( "$IP/maintenance/commandLine.inc" );
+
+foreach ( glob( "$IP/languages/messages/Messages*.php" ) as $filename ) {
+ $base = basename( $filename );
+ if ( !preg_match( '/Messages(.*)\.php$/', $base, $m ) ) {
+ continue;
+ }
+ $code = str_replace( '_', '-', strtolower( $m[1] ) );
+ print "$code ";
+ $lang = Language::factory( $code );
+ $prefs = $lang->getDatePreferences();
+ if ( !$prefs ) {
+ $prefs = array( 'default' );
+ }
+ print "date: ";
+ foreach ( $prefs as $index => $pref ) {
+ if ( $index > 0 ) {
+ print ' | ';
+ }
+ print $lang->date( $ts, false, $pref );
+ }
+ print "\n$code time: ";
+ foreach ( $prefs as $index => $pref ) {
+ if ( $index > 0 ) {
+ print ' | ';
+ }
+ print $lang->time( $ts, false, $pref );
+ }
+ print "\n$code both: ";
+ foreach ( $prefs as $index => $pref ) {
+ if ( $index > 0 ) {
+ print ' | ';
+ }
+ print $lang->timeanddate( $ts, false, $pref );
+ }
+ print "\n\n";
+}
+
+?>
diff --git a/maintenance/language/diffLanguage.php b/maintenance/language/diffLanguage.php
new file mode 100644
index 00000000..2aaa5902
--- /dev/null
+++ b/maintenance/language/diffLanguage.php
@@ -0,0 +1,159 @@
+<?php
+# MediaWiki web-based config/installation
+# Copyright (C) 2004 Ashar Voultoiz <thoane@altern.org> and others
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+/**
+ * Usage: php DiffLanguage.php [lang [file]]
+ *
+ * lang: Enter the language code following "Language" of the LanguageXX.php you
+ * want to check. If using linux you might need to follow case aka Zh and not
+ * zh.
+ *
+ * file: A php language file you want to include to compare mediawiki
+ * Language{Lang}.php against (for example Special:Allmessages PHP output).
+ *
+ * The goal is to get a list of messages not yet localised in a languageXX.php
+ * file using the language.php file as reference.
+ *
+ * The script then print a list of wgAllMessagesXX keys that aren't localised, a
+ * percentage of messages correctly localised and the number of messages to be
+ * translated.
+ *
+ * @package MediaWiki
+ * @subpackage Maintenance
+ */
+
+/** This script run from the commandline */
+require_once( dirname(__FILE__).'/../parserTests.inc' );
+require_once( dirname(__FILE__).'/../commandLine.inc' );
+
+if( isset($options['help']) ) { usage(); wfDie(); }
+
+$wgLanguageCode = ucfirstlcrest($wgLanguageCode);
+/** Language messages we will use as reference. By default 'en' */
+$referenceMessages = $wgAllMessagesEn;
+$referenceLanguage = 'En';
+$referenceFilename = 'Language'.$referenceLanguage.'.php';
+/** Language messages we will test. */
+$testMessages = array();
+$testLanguage = '';
+/** whereas we use an external language file */
+$externalRef = false;
+
+# FUNCTIONS
+/** @todo more informations !! */
+function usage() {
+echo 'php DiffLanguage.php [lang [file]] [--color=(yes|no|light)]'."\n";
+}
+
+/** Return a given string with first letter upper case, the rest lowercase */
+function ucfirstlcrest($string) {
+ return strtoupper(substr($string,0,1)).strtolower(substr($string,1));
+}
+
+/**
+ * Return a $wgAllmessages array shipped in MediaWiki
+ * @param string $languageCode Formated language code
+ * @return array The MediaWiki default $wgAllMessages array requested
+ */
+function getMediawikiMessages($languageCode = 'En') {
+
+ $foo = "wgAllMessages$languageCode";
+ global $$foo, $wgSkinNamesEn;
+
+ // it might already be loaded in LocalSettings.php
+ if(!isset($$foo)) {
+ global $IP;
+ $langFile = $IP.'/languages/classes/Language'.$languageCode.'.php';
+ if (file_exists( $langFile ) ) {
+ print "Including $langFile\n";
+ global $wgNamespaceNamesEn;
+ include($langFile);
+ } else wfDie("ERROR: The file $langFile does not exist !\n");
+ }
+ return $$foo;
+}
+
+/**
+ * Return a $wgAllmessages array in a given file. Language of the array
+ * need to be given cause we can not detect which language it provides
+ * @param string $filename Filename of the file containing a message array
+ * @param string $languageCode Language of the external array
+ * @return array A $wgAllMessages array from an external file.
+ */
+function getExternalMessages($filename, $languageCode) {
+ print "Including external file $filename.\n";
+ include($filename);
+ $foo = "wgAllMessages$languageCode";
+ return $$foo;
+}
+
+# MAIN ENTRY
+if ( isset($args[0]) ) {
+ $lang = ucfirstlcrest($args[0],1);
+
+ // eventually against another language file we will use as reference instead
+ // of the default english language.
+ if( isset($args[1])) {
+ // we assume the external file contain an array of messages for the
+ // lang we are testing
+ $referenceMessages = getExternalMessages( $args[1], $lang );
+ $referenceLanguage = $lang;
+ $referenceFilename = $args[1];
+ $externalRef = true;
+ }
+
+ // Load datas from MediaWiki
+ $testMessages = getMediawikiMessages($lang);
+ $testLanguage = $lang;
+} else {
+ usage();
+ wfDie();
+}
+
+/** parsertest is used to do differences */
+$myParserTest = new ParserTest();
+
+# Get all references messages and check if they exist in the tested language
+$i = 0;
+
+$msg = "MW Language{$testLanguage}.php against ";
+if($externalRef) { $msg .= 'external file '; }
+else { $msg .= 'internal file '; }
+$msg .= $referenceFilename.' ('.$referenceLanguage."):\n----\n";
+echo $msg;
+
+// process messages
+foreach($referenceMessages as $index => $ref)
+{
+ // message is not localized
+ if(!(isset($testMessages[$index]))) {
+ $i++;
+ print "'$index' => \"$ref\",\n";
+ // Messages in the same language differs
+ } elseif( ($lang == $referenceLanguage) AND ($testMessages[$index] != $ref)) {
+ print "\n$index differs:\n";
+ print $myParserTest->quickDiff($testMessages[$index],$ref,'tested','reference');
+ }
+}
+
+echo "\n----\n".$msg;
+echo "$referenceLanguage language is complete at ".number_format((100 - $i/count($wgAllMessagesEn) * 100),2)."%\n";
+echo "$i unlocalised messages of the ".count($wgAllMessagesEn)." messages available.\n";
+?>
diff --git a/maintenance/language/dumpMessages.php b/maintenance/language/dumpMessages.php
new file mode 100644
index 00000000..bd7e2aed
--- /dev/null
+++ b/maintenance/language/dumpMessages.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * @todo document
+ * @package MediaWiki
+ * @subpackage Maintenance
+ */
+
+/** */
+require_once( dirname(__FILE__).'/../commandLine.inc' );
+$wgMessageCache->disableTransform();
+$messages = array();
+$wgEnglishMessages = array_keys( Language::getMessagesFor( 'en' ) );
+foreach ( $wgEnglishMessages as $key )
+{
+ $messages[$key] = wfMsg( $key );
+}
+print "MediaWiki $wgVersion language file\n";
+print serialize( $messages );
+
+?>
diff --git a/maintenance/language/duplicatetrans.php b/maintenance/language/duplicatetrans.php
new file mode 100644
index 00000000..9273ee6e
--- /dev/null
+++ b/maintenance/language/duplicatetrans.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Prints out messages that are the same as the message with the corrisponding
+ * key in the English file
+ *
+ * @package MediaWiki
+ * @subpackage Maintenance
+ */
+
+require_once(dirname(__FILE__).'/../commandLine.inc');
+
+if ( isset( $args[0] ) ) {
+ $code = $args[0];
+} else {
+ $code = $wgLang->getCode();
+}
+
+if ( $code == 'en' ) {
+ print "Current selected language is English. Cannot check translations.\n";
+ exit();
+}
+
+$filename = Language::getMessagesFileName( $code );
+if ( file_exists( $filename ) ) {
+ require( $filename );
+} else {
+ $messages = array();
+}
+
+$count = $total = 0;
+$wgEnglishMessages = Language::getMessagesFor( 'en' );
+$wgLocalMessages = $messages;
+
+foreach ( $wgLocalMessages as $key => $msg ) {
+ ++$total;
+ if ( @$wgEnglishMessages[$key] == $msg ) {
+ echo "* $key\n";
+ ++$count;
+ }
+}
+
+echo "{$count} messages of {$total} are duplicates in the language {$code}\n";
+?>
diff --git a/maintenance/language/function-list.php b/maintenance/language/function-list.php
new file mode 100644
index 00000000..84efb29d
--- /dev/null
+++ b/maintenance/language/function-list.php
@@ -0,0 +1,44 @@
+<?php
+
+define( 'MEDIAWIKI', 1 );
+define( 'NOT_REALLY_MEDIAWIKI', 1 );
+
+class Language {}
+foreach ( glob( 'Language*.php' ) as $file ) {
+ if ( $file != 'Language.php' ) {
+ require_once( $file );
+ }
+}
+
+$removedFunctions = array( 'date', 'time', 'timeanddate', 'formatMonth', 'formatDay',
+ 'getMonthName', 'getMonthNameGen', 'getMonthAbbreviation', 'getWeekdayName',
+ 'userAdjust', 'dateFormat', 'timeSeparator', 'timeDateSeparator', 'timeBeforeDate',
+ 'monthByLatinNumber', 'getSpecialMonthName',
+
+ 'commafy'
+);
+
+$numRemoved = 0;
+$total = 0;
+$classes = get_declared_classes();
+ksort( $classes );
+foreach ( $classes as $class ) {
+ if ( !preg_match( '/^Language/', $class ) || $class == 'Language' || $class == 'LanguageConverter' ) {
+ continue;
+ }
+
+ print "$class\n";
+ $methods = get_class_methods( $class );
+ print_r( $methods );
+
+ if ( !count( array_diff( $methods, $removedFunctions ) ) ) {
+ print "removed\n";
+ $numRemoved++;
+ }
+ $total++;
+ print "\n";
+}
+
+print "$numRemoved will be removed out of $total\n";
+
+?>
diff --git a/maintenance/language/lang2po.php b/maintenance/language/lang2po.php
new file mode 100644
index 00000000..520d8d6e
--- /dev/null
+++ b/maintenance/language/lang2po.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * Convert Language files to .po files !
+ *
+ * Todo:
+ * - generate .po header
+ * - fix escaping of \
+ */
+
+/** This is a command line script */
+require_once(dirname(__FILE__).'/../commandLine.inc');
+require_once(dirname(__FILE__).'/languages.inc');
+
+define('ALL_LANGUAGES', true);
+define('XGETTEXT_BIN', 'xgettext');
+define('MSGMERGE_BIN', 'msgmerge');
+
+// used to generate the .pot
+define('XGETTEXT_OPTIONS', '-n --keyword=wfMsg --keyword=wfMsgForContent --keyword=wfMsgHtml --keyword=wfMsgWikiHtml ');
+define('MSGMERGE_OPTIONS', ' -v ');
+
+define('LOCALE_OUTPUT_DIR', $IP.'/locale');
+
+
+if( isset($options['help']) ) { usage(); wfDie(); }
+// default output is WikiText
+if( !isset($options['lang']) ) { $options['lang'] = ALL_LANGUAGES; }
+
+function usage() {
+print <<<END
+Usage: php lang2po.php [--help] [--lang=<langcode>] [--stdout]
+ --help: this message.
+ --lang: a lang code you want to generate a .po for (default: all languages).
+
+END;
+}
+
+
+/**
+ * Return a dummy header for later edition.
+ * @return string A dummy header
+ */
+function poHeader() {
+return
+'# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2005 MediaWiki
+# This file is distributed under the same license as the MediaWiki package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: bugzilllaaaaa\n"
+"POT-Creation-Date: 2005-08-16 20:13+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: VARIOUS <nobody>\n"
+"Language-Team: LANGUAGE <nobody>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+';
+}
+
+/**
+ * generate and write a file in .po format.
+ *
+ * @param string $langcode Code of a language it will process.
+ * @param array &$messages Array containing the various messages.
+ * @return string Filename where stuff got saved or false.
+ */
+function generatePo($langcode, &$messages) {
+ $data = poHeader();
+
+ // Generate .po entries
+ foreach($messages as $identifier => $content) {
+ $data .= "msgid \"$identifier\"\n";
+
+ // Escape backslashes
+ $tmp = str_replace('\\', '\\\\', $content);
+ // Escape doublelquotes
+ $tmp = preg_replace( "/(?<!\\\\)\"/", '\"', $tmp);
+ // Rewrite multilines to gettext format
+ $tmp = str_replace("\n", "\"\n\"", $tmp);
+
+ $data .= 'msgstr "'. $tmp . "\"\n\n";
+ }
+
+ // Write the content to a file in locale/XX/messages.po
+ $dir = LOCALE_OUTPUT_DIR.'/'.$langcode;
+ if( !is_dir($dir) ) { mkdir( $dir, 0770 ); }
+ $filename = $dir.'/fromlanguagefile.po';
+
+ $file = fopen( $filename , 'wb' );
+ if( fwrite( $file, $data ) ) {
+ fclose( $file );
+ return $filename;
+ } else {
+ fclose( $file );
+ return false;
+ }
+}
+
+function generatePot() {
+ global $IP;
+ $curdir = getcwd();
+ chdir($IP);
+ exec( XGETTEXT_BIN
+ .' '.XGETTEXT_OPTIONS
+ .' -o '.LOCALE_OUTPUT_DIR.'/wfMsg.pot'
+ .' includes/*php'
+ );
+ chdir($curdir);
+}
+
+function applyPot($langcode) {
+ $langdir = LOCALE_OUTPUT_DIR.'/'.$langcode;
+
+ $from = $langdir.'/fromlanguagefile.po';
+ $pot = LOCALE_OUTPUT_DIR.'/wfMsg.pot';
+ $dest = $langdir.'/messages.po';
+
+ // Merge template and generate file to get final .po
+ exec(MSGMERGE_BIN.MSGMERGE_OPTIONS." $from $pot -o $dest ");
+ // delete no more needed file
+// unlink($from);
+}
+
+// Generate a template .pot based on source tree
+echo "Getting 'gettext' default messages from sources:";
+generatePot();
+echo "done.\n";
+
+
+$langTool = new languages();
+
+// Do all languages
+foreach ( $langTool->getMessages() as $langcode) {
+ echo "Loading messages for $langcode:\t";
+ require_once( Language::getClassFileName( $langcode ) );
+ $arr = 'wgAllMessages'.$langcode;
+ if(!@is_array($$arr)) {
+ echo "NONE FOUND\n";
+ } else {
+ echo "ok\n";
+ if( ! generatePo($langcode, $$arr) ) {
+ echo "ERROR: Failed to wrote file.\n";
+ } else {
+ echo "Applying template:";
+ applyPot($langcode);
+ }
+ }
+}
+?>
diff --git a/maintenance/language/langmemusage.php b/maintenance/language/langmemusage.php
new file mode 100644
index 00000000..974bb0d8
--- /dev/null
+++ b/maintenance/language/langmemusage.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Dumb program that tries to get the memory usage
+ * for each language file.
+ */
+
+/** This is a command line script */
+require_once(dirname(__FILE__).'/../commandLine.inc');
+require_once(dirname(__FILE__).'/languages.inc');
+
+$langtool = new languages();
+
+if ( ! function_exists( 'memory_get_usage' ) )
+ wfDie( "You must compile PHP with --enable-memory-limit\n" );
+
+$memlast = $memstart = memory_get_usage();
+
+print 'Base memory usage: '.$memstart."\n";
+
+foreach ( $langtool->getLanguages() as $langcode ) {
+ require_once( Language::getClassFileName( $langcode ) );
+ $memstep = memory_get_usage();
+ printf( "%12s: %d\n", $langcode, ($memstep- $memlast) );
+ $memlast = $memstep;
+}
+
+$memend = memory_get_usage();
+
+echo ' Total Usage: '.($memend - $memstart)."\n";
+?>
diff --git a/maintenance/language/languages.inc b/maintenance/language/languages.inc
new file mode 100644
index 00000000..86cd0869
--- /dev/null
+++ b/maintenance/language/languages.inc
@@ -0,0 +1,686 @@
+<?php
+/**
+ * Handle messages in the language files.
+ *
+ * @package MediaWiki
+ * @subpackage Maintenance
+ */
+
+class languages {
+ private $mLanguages; # List of languages
+ private $mRawMessages; # Raw list of the messages in each language
+ private $mMessages; # Messages in each language (except for English), divided to groups
+ private $mGeneralMessages; # General messages in English, divided to groups
+ private $mIgnoredMessages = array(
+ 'sidebar',
+ 'addsection',
+ 'anonnotice',
+ 'catseparator',
+ 'googlesearch',
+ 'exif-make-value',
+ 'exif-model-value',
+ 'exif-software-value',
+ 'history_copyright',
+ 'licenses',
+ 'loginend',
+ 'loginlanguagelinks',
+ 'markaspatrolledlink',
+ 'newarticletextanon',
+ 'noarticletextanon',
+ 'number_of_watching_users_RCview',
+ 'pubmedurl',
+ 'randompage-url',
+ 'recentchanges-url',
+ 'rfcurl',
+ 'shareddescriptionfollows',
+ 'signupend',
+ 'sitenotice',
+ 'sitesubtitle',
+ 'sitetitle',
+ 'talkpagetext',
+ 'trackback',
+ 'trackbackexcerpt',
+ 'widthheight',
+ ); # All the messages which should be exist only in the English file
+ private $mOptionalMessages = array(
+ 'imgmultigotopost',
+ 'linkprefix',
+ 'allpages-summary',
+ 'booksources-summary',
+ 'ipblocklist-summary',
+ 'listusers-summary',
+ 'longpages-summary',
+ 'preferences-summary',
+ 'specialpages-summary',
+ 'whatlinkshere-summary',
+ 'whatlinkshere-barrow',
+ 'imagelist-summary',
+ 'mimesearch-summary',
+ 'listredirects-summary',
+ 'uncategorizedpages-summary',
+ 'uncategorizedcategories-summary',
+ 'uncategorizedimages-summary',
+ 'popularpages-summary',
+ 'wantedcategories-summary',
+ 'wantedpages-summary',
+ 'mostlinked-summary',
+ 'mostlinkedcategories-summary',
+ 'mostcategories-summary',
+ 'mostimages-summary',
+ 'mostrevisions-summary',
+ 'prefixindex-summary',
+ 'shortpages-summary',
+ 'newpages-summary',
+ 'ancientpages-summary',
+ 'newimages-summary',
+ 'unwatchedpages-summary',
+ 'userrights-summary',
+ 'variantname-zh-cn',
+ 'variantname-zh-tw',
+ 'variantname-zh-hk',
+ 'variantname-zh-sg',
+ 'variantname-zh',
+ 'variantname-sr-ec',
+ 'variantname-sr-el',
+ 'variantname-sr-jc',
+ 'variantname-sr-jl',
+ 'variantname-sr',
+ 'variantname-kk-tr',
+ 'variantname-kk-kz',
+ 'variantname-kk-cn',
+ 'variantname-kk',
+ ); # All the messages which may be translated or not, depending on the language
+ private $mEXIFMessages = array(
+ 'exif-imagewidth',
+ 'exif-imagelength',
+ 'exif-bitspersample',
+ 'exif-compression',
+ 'exif-photometricinterpretation',
+ 'exif-orientation',
+ 'exif-samplesperpixel',
+ 'exif-planarconfiguration',
+ 'exif-ycbcrsubsampling',
+ 'exif-ycbcrpositioning',
+ 'exif-xresolution',
+ 'exif-yresolution',
+ 'exif-resolutionunit',
+ 'exif-stripoffsets',
+ 'exif-rowsperstrip',
+ 'exif-stripbytecounts',
+ 'exif-jpeginterchangeformat',
+ 'exif-jpeginterchangeformatlength',
+ 'exif-transferfunction',
+ 'exif-whitepoint',
+ 'exif-primarychromaticities',
+ 'exif-ycbcrcoefficients',
+ 'exif-referenceblackwhite',
+ 'exif-datetime',
+ 'exif-imagedescription',
+ 'exif-make',
+ 'exif-model',
+ 'exif-software',
+ 'exif-artist',
+ 'exif-copyright',
+ 'exif-exifversion',
+ 'exif-flashpixversion',
+ 'exif-colorspace',
+ 'exif-componentsconfiguration',
+ 'exif-compressedbitsperpixel',
+ 'exif-pixelydimension',
+ 'exif-pixelxdimension',
+ 'exif-makernote',
+ 'exif-usercomment',
+ 'exif-relatedsoundfile',
+ 'exif-datetimeoriginal',
+ 'exif-datetimedigitized',
+ 'exif-subsectime',
+ 'exif-subsectimeoriginal',
+ 'exif-subsectimedigitized',
+ 'exif-exposuretime',
+ 'exif-exposuretime-format',
+ 'exif-fnumber',
+ 'exif-fnumber-format',
+ 'exif-exposureprogram',
+ 'exif-spectralsensitivity',
+ 'exif-isospeedratings',
+ 'exif-oecf',
+ 'exif-shutterspeedvalue',
+ 'exif-aperturevalue',
+ 'exif-brightnessvalue',
+ 'exif-exposurebiasvalue',
+ 'exif-maxaperturevalue',
+ 'exif-subjectdistance',
+ 'exif-meteringmode',
+ 'exif-lightsource',
+ 'exif-flash',
+ 'exif-focallength',
+ 'exif-focallength-format',
+ 'exif-subjectarea',
+ 'exif-flashenergy',
+ 'exif-spatialfrequencyresponse',
+ 'exif-focalplanexresolution',
+ 'exif-focalplaneyresolution',
+ 'exif-focalplaneresolutionunit',
+ 'exif-subjectlocation',
+ 'exif-exposureindex',
+ 'exif-sensingmethod',
+ 'exif-filesource',
+ 'exif-scenetype',
+ 'exif-cfapattern',
+ 'exif-customrendered',
+ 'exif-exposuremode',
+ 'exif-whitebalance',
+ 'exif-digitalzoomratio',
+ 'exif-focallengthin35mmfilm',
+ 'exif-scenecapturetype',
+ 'exif-gaincontrol',
+ 'exif-contrast',
+ 'exif-saturation',
+ 'exif-sharpness',
+ 'exif-devicesettingdescription',
+ 'exif-subjectdistancerange',
+ 'exif-imageuniqueid',
+ 'exif-gpsversionid',
+ 'exif-gpslatituderef',
+ 'exif-gpslatitude',
+ 'exif-gpslongituderef',
+ 'exif-gpslongitude',
+ 'exif-gpsaltituderef',
+ 'exif-gpsaltitude',
+ 'exif-gpstimestamp',
+ 'exif-gpssatellites',
+ 'exif-gpsstatus',
+ 'exif-gpsmeasuremode',
+ 'exif-gpsdop',
+ 'exif-gpsspeedref',
+ 'exif-gpsspeed',
+ 'exif-gpstrackref',
+ 'exif-gpstrack',
+ 'exif-gpsimgdirectionref',
+ 'exif-gpsimgdirection',
+ 'exif-gpsmapdatum',
+ 'exif-gpsdestlatituderef',
+ 'exif-gpsdestlatitude',
+ 'exif-gpsdestlongituderef',
+ 'exif-gpsdestlongitude',
+ 'exif-gpsdestbearingref',
+ 'exif-gpsdestbearing',
+ 'exif-gpsdestdistanceref',
+ 'exif-gpsdestdistance',
+ 'exif-gpsprocessingmethod',
+ 'exif-gpsareainformation',
+ 'exif-gpsdatestamp',
+ 'exif-gpsdifferential',
+ 'exif-compression-1',
+ 'exif-compression-6',
+ 'exif-photometricinterpretation-2',
+ 'exif-photometricinterpretation-6',
+ 'exif-orientation-1',
+ 'exif-orientation-2',
+ 'exif-orientation-3',
+ 'exif-orientation-4',
+ 'exif-orientation-5',
+ 'exif-orientation-6',
+ 'exif-orientation-7',
+ 'exif-orientation-8',
+ 'exif-planarconfiguration-1',
+ 'exif-planarconfiguration-2',
+ 'exif-xyresolution-i',
+ 'exif-xyresolution-c',
+ 'exif-colorspace-1',
+ 'exif-colorspace-ffff.h',
+ 'exif-componentsconfiguration-0',
+ 'exif-componentsconfiguration-1',
+ 'exif-componentsconfiguration-2',
+ 'exif-componentsconfiguration-3',
+ 'exif-componentsconfiguration-4',
+ 'exif-componentsconfiguration-5',
+ 'exif-componentsconfiguration-6',
+ 'exif-exposureprogram-0',
+ 'exif-exposureprogram-1',
+ 'exif-exposureprogram-2',
+ 'exif-exposureprogram-3',
+ 'exif-exposureprogram-4',
+ 'exif-exposureprogram-5',
+ 'exif-exposureprogram-6',
+ 'exif-exposureprogram-7',
+ 'exif-exposureprogram-8',
+ 'exif-subjectdistance-value',
+ 'exif-meteringmode-0',
+ 'exif-meteringmode-1',
+ 'exif-meteringmode-2',
+ 'exif-meteringmode-3',
+ 'exif-meteringmode-4',
+ 'exif-meteringmode-5',
+ 'exif-meteringmode-6',
+ 'exif-meteringmode-255',
+ 'exif-lightsource-0',
+ 'exif-lightsource-1',
+ 'exif-lightsource-2',
+ 'exif-lightsource-3',
+ 'exif-lightsource-4',
+ 'exif-lightsource-9',
+ 'exif-lightsource-10',
+ 'exif-lightsource-11',
+ 'exif-lightsource-12',
+ 'exif-lightsource-13',
+ 'exif-lightsource-14',
+ 'exif-lightsource-15',
+ 'exif-lightsource-17',
+ 'exif-lightsource-18',
+ 'exif-lightsource-19',
+ 'exif-lightsource-20',
+ 'exif-lightsource-21',
+ 'exif-lightsource-22',
+ 'exif-lightsource-23',
+ 'exif-lightsource-24',
+ 'exif-lightsource-255',
+ 'exif-focalplaneresolutionunit-2',
+ 'exif-sensingmethod-1',
+ 'exif-sensingmethod-2',
+ 'exif-sensingmethod-3',
+ 'exif-sensingmethod-4',
+ 'exif-sensingmethod-5',
+ 'exif-sensingmethod-7',
+ 'exif-sensingmethod-8',
+ 'exif-filesource-3',
+ 'exif-scenetype-1',
+ 'exif-customrendered-0',
+ 'exif-customrendered-1',
+ 'exif-exposuremode-0',
+ 'exif-exposuremode-1',
+ 'exif-exposuremode-2',
+ 'exif-whitebalance-0',
+ 'exif-whitebalance-1',
+ 'exif-scenecapturetype-0',
+ 'exif-scenecapturetype-1',
+ 'exif-scenecapturetype-2',
+ 'exif-scenecapturetype-3',
+ 'exif-gaincontrol-0',
+ 'exif-gaincontrol-1',
+ 'exif-gaincontrol-2',
+ 'exif-gaincontrol-3',
+ 'exif-gaincontrol-4',
+ 'exif-contrast-0',
+ 'exif-contrast-1',
+ 'exif-contrast-2',
+ 'exif-saturation-0',
+ 'exif-saturation-1',
+ 'exif-saturation-2',
+ 'exif-sharpness-0',
+ 'exif-sharpness-1',
+ 'exif-sharpness-2',
+ 'exif-subjectdistancerange-0',
+ 'exif-subjectdistancerange-1',
+ 'exif-subjectdistancerange-2',
+ 'exif-subjectdistancerange-3',
+ 'exif-gpslatitude-n',
+ 'exif-gpslatitude-s',
+ 'exif-gpslongitude-e',
+ 'exif-gpslongitude-w',
+ 'exif-gpsstatus-a',
+ 'exif-gpsstatus-v',
+ 'exif-gpsmeasuremode-2',
+ 'exif-gpsmeasuremode-3',
+ 'exif-gpsspeed-k',
+ 'exif-gpsspeed-m',
+ 'exif-gpsspeed-n',
+ 'exif-gpsdirection-t',
+ 'exif-gpsdirection-m',
+ ); # All the EXIF messages, may be set as optional if defined as such
+
+ /**
+ * Load the list of languages: all the Messages*.php
+ * files in the languages directory.
+ *
+ * @param $exif Treat the EXIF messages?
+ */
+ function __construct( $exif = true ) {
+ $this->mLanguages = array_keys( Language::getLanguageNames( true ) );
+ sort( $this->mLanguages );
+ if ( !$exif ) {
+ $this->mOptionalMessages = array_merge( $this->mOptionalMessages, $this->mEXIFMessages );
+ }
+ }
+
+ /**
+ * Get the language list.
+ *
+ * @return The language list.
+ */
+ public function getLanguages() {
+ return $this->mLanguages;
+ }
+
+ /**
+ * Load the raw messages for a specific langauge from the messages file.
+ *
+ * @param $code The langauge code.
+ */
+ private function loadRawMessages( $code ) {
+ if ( isset( $this->mRawMessages[$code] ) ) {
+ return;
+ }
+ $filename = Language::getMessagesFileName( $code );
+ if ( file_exists( $filename ) ) {
+ require( $filename );
+ if ( isset( $messages ) ) {
+ $this->mRawMessages[$code] = $messages;
+ } else {
+ $this->mRawMessages[$code] = array();
+ }
+ } else {
+ $this->mRawMessages[$code] = array();
+ }
+ }
+
+ /**
+ * Load the messages for a specific language (which is not English) and divide them to groups:
+ * all - all the messages.
+ * required - messages which should be translated in order to get a complete translation.
+ * optional - messages which can be translated, the fallback translation is used if not translated.
+ * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
+ * translated - messages which are either required or optional, but translated from English and needed.
+ *
+ * @param $code The language code.
+ */
+ private function loadMessages( $code ) {
+ if ( isset( $this->mMessages[$code] ) ) {
+ return;
+ }
+ $this->loadRawMessages( $code );
+ $this->loadGeneralMessages();
+ $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
+ $this->mMessages[$code]['required'] = array();
+ $this->mMessages[$code]['optional'] = array();
+ $this->mMessages[$code]['obsolete'] = array();
+ $this->mMessages[$code]['translated'] = array();
+ foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
+ if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
+ $this->mMessages[$code]['required'][$key] = $value;
+ $this->mMessages[$code]['translated'][$key] = $value;
+ } else if ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
+ $this->mMessages[$code]['optional'][$key] = $value;
+ $this->mMessages[$code]['translated'][$key] = $value;
+ } else {
+ $this->mMessages[$code]['obsolete'][$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * Load the messages for English and divide them to groups:
+ * all - all the messages.
+ * required - messages which should be translated to other languages in order to get a complete translation.
+ * optional - messages which can be translated to other languages, but it's not required for a complete translation.
+ * ignored - messages which should not be translated to other languages.
+ * translatable - messages which are either required or optional, but can be translated from English.
+ */
+ private function loadGeneralMessages() {
+ if ( isset( $this->mGeneralMessages ) ) {
+ return;
+ }
+ $this->loadRawMessages( 'en' );
+ $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
+ $this->mGeneralMessages['required'] = array();
+ $this->mGeneralMessages['optional'] = array();
+ $this->mGeneralMessages['ignored'] = array();
+ $this->mGeneralMessages['translatable'] = array();
+ foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
+ if ( in_array( $key, $this->mIgnoredMessages ) ) {
+ $this->mGeneralMessages['ignored'][$key] = $value;
+ } else if ( in_array( $key, $this->mOptionalMessages ) ) {
+ $this->mGeneralMessages['optional'][$key] = $value;
+ $this->mGeneralMessages['translatable'][$key] = $value;
+ } else {
+ $this->mGeneralMessages['required'][$key] = $value;
+ $this->mGeneralMessages['translatable'][$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * Get all the messages for a specific langauge (not English), without the
+ * fallback language messages, divided to groups:
+ * all - all the messages.
+ * required - messages which should be translated in order to get a complete translation.
+ * optional - messages which can be translated, the fallback translation is used if not translated.
+ * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
+ * translated - messages which are either required or optional, but translated from English and needed.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The messages in this language.
+ */
+ public function getMessages( $code ) {
+ $this->loadMessages( $code );
+ return $this->mMessages[$code];
+ }
+
+ /**
+ * Get all the general English messages, divided to groups:
+ * all - all the messages.
+ * required - messages which should be translated to other languages in order to get a complete translation.
+ * optional - messages which can be translated to other languages, but it's not required for a complete translation.
+ * ignored - messages which should not be translated to other languages.
+ * translatable - messages which are either required or optional, but can be translated from English.
+ *
+ * @return The general English messages.
+ */
+ public function getGeneralMessages() {
+ $this->loadGeneralMessages();
+ return $this->mGeneralMessages;
+ }
+
+ /**
+ * Get the untranslated messages for a specific language.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The untranslated messages for this language.
+ */
+ public function getUntranslatedMessages( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $requiredGeneralMessages = array_keys( $this->mGeneralMessages['required'] );
+ $requiredMessages = array_keys( $this->mMessages[$code]['required'] );
+ $untranslatedMessages = array();
+ foreach ( array_diff( $requiredGeneralMessages, $requiredMessages ) as $key ) {
+ $untranslatedMessages[$key] = $this->mGeneralMessages['required'][$key];
+ }
+ return $untranslatedMessages;
+ }
+
+ /**
+ * Get the duplicate messages for a specific language.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The duplicate messages for this language.
+ */
+ public function getDuplicateMessages( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $duplicateMessages = array();
+ foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+ if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
+ $duplicateMessages[$key] = $value;
+ }
+ }
+ return $duplicateMessages;
+ }
+
+ /**
+ * Get the messages which do not use some variables.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The messages which do not use some variables in this language.
+ */
+ public function getMessagesWithoutVariables( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
+ $messagesWithoutVariables = array();
+ foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+ $missing = false;
+ foreach ( $variables as $var ) {
+ if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
+ !preg_match( "/$var/sU", $value ) ) {
+ $missing = true;
+ }
+ }
+ if ( $missing ) {
+ $messagesWithoutVariables[$key] = $value;
+ }
+ }
+ return $messagesWithoutVariables;
+ }
+
+ /**
+ * Get the empty messages.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The empty messages for this language.
+ */
+ public function getEmptyMessages( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $emptyMessages = array();
+ foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+ if ( $value === '' || $value === '-' ) {
+ $emptyMessages[$key] = $value;
+ }
+ }
+ return $emptyMessages;
+ }
+
+ /**
+ * Get the messages with trailing whitespace.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The messages with trailing whitespace in this language.
+ */
+ public function getMessagesWithWhitespace( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $messagesWithWhitespace = array();
+ foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+ if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
+ $messagesWithWhitespace[$key] = $value;
+ }
+ }
+ return $messagesWithWhitespace;
+ }
+
+ /**
+ * Get the non-XHTML messages.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The non-XHTML messages for this language.
+ */
+ public function getNonXHTMLMessages( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $wrongPhrases = array(
+ '<hr *\\?>',
+ '<br *\\?>',
+ '<hr/>',
+ '<br/>',
+ );
+ $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
+ $nonXHTMLMessages = array();
+ foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+ if ( preg_match( $wrongPhrases, $value ) ) {
+ $nonXHTMLMessages[$key] = $value;
+ }
+ }
+ return $nonXHTMLMessages;
+ }
+
+ /**
+ * Get the messages which include wrong characters.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The messages which include wrong characters in this language.
+ */
+ public function getMessagesWithWrongChars( $code ) {
+ $this->loadGeneralMessages();
+ $this->loadMessages( $code );
+ $wrongChars = array(
+ '[LRM]' => "\xE2\x80\x8E",
+ '[RLM]' => "\xE2\x80\x8F",
+ '[LRE]' => "\xE2\x80\xAA",
+ '[RLE]' => "\xE2\x80\xAB",
+ '[POP]' => "\xE2\x80\xAC",
+ '[LRO]' => "\xE2\x80\xAD",
+ '[RLO]' => "\xE2\x80\xAB",
+ '[ZWSP]'=> "\xE2\x80\x8B",
+ '[NBSP]'=> "\xC2\xA0",
+ '[WJ]' => "\xE2\x81\xA0",
+ '[BOM]' => "\xEF\xBB\xBF",
+ '[FFFD]'=> "\xEF\xBF\xBD",
+ );
+ $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
+ $wrongCharsMessages = array();
+ foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+ if ( preg_match( $wrongRegExp, $value ) ) {
+ foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
+ $value = str_replace( $hiddenChar, $viewableChar, $value );
+ }
+ $wrongCharsMessages[$key] = $value;
+ }
+ }
+ return $wrongCharsMessages;
+ }
+
+ /**
+ * Output a messages list
+ *
+ * @param $messages The messages list
+ * @param $code The language code
+ * @param $text The text to show before the list (optional)
+ * @param $level The display level (optional)
+ * @param $links Show links (optional)
+ * @param $wikilang The langauge of the wiki to display the list in, for the links (optional)
+ */
+ public function outputMessagesList( $messages, $code, $text = '', $level = 2, $links = false, $wikilang = null ) {
+ if ( count( $messages ) == 0 ) {
+ return;
+ }
+ if ( $text ) {
+ echo "$text\n";
+ }
+ if ( $level == 1 ) {
+ echo "[messages are hidden]\n";
+ } else {
+ foreach ( $messages as $key => $value ) {
+ if ( $links ) {
+ $displayKey = ucfirst( $key );
+ if ( !isset( $wikilang ) ) {
+ global $wgContLang;
+ $wikilang = $wgContLang->getCode();
+ }
+ if ( $code == $wikilang ) {
+ $displayKey = "[[MediaWiki:$displayKey|$key]]";
+ } else {
+ $displayKey = "[[MediaWiki:$displayKey/$code|$key]]";
+ }
+ } else {
+ $displayKey = $key;
+ }
+ if ( $level == 2 ) {
+ echo "* $displayKey\n";
+ } else {
+ echo "* $displayKey: '$value'\n";
+ }
+ }
+ }
+ }
+}
+
+?>
diff --git a/maintenance/language/splitLanguageFiles.inc b/maintenance/language/splitLanguageFiles.inc
new file mode 100644
index 00000000..c2500778
--- /dev/null
+++ b/maintenance/language/splitLanguageFiles.inc
@@ -0,0 +1,1168 @@
+<?php
+/**
+ * This is an experimental list. It will later be used with a script to split
+ * the languages files in several parts then the message system will only load
+ * in memory the parts which are actually needed.
+ *
+ * Generated using: grep -r foobar *
+ *
+ * $commonMsg is the default array. Other arrays will only be loaded if needed.
+ */
+$installerMsg = array (
+'mainpagetext',
+'mainpagedocfooter',
+);
+
+$ActionMsg = array (
+'delete' => array(
+ 'delete',
+ 'deletethispage',
+ 'undelete_short1',
+ 'undelete_short',
+ 'undelete',
+ 'undeletepage',
+ 'undeletepagetext',
+ 'undeletearticle',
+ 'undeleterevisions',
+ 'undeletehistory',
+ 'undeleterevision',
+ 'undeletebtn',
+ 'undeletedarticle',
+ 'undeletedrevisions',
+ 'undeletedtext',
+ ),
+'move' => array(
+ 'move',
+ 'movethispage',
+),
+'revert' => array(
+
+),
+'protect' => array(
+ 'confirmprotect',
+ 'confirmprotecttext',
+ 'confirmunprotect',
+ 'confirmunprotecttext',
+ 'protect',
+ 'protectcomment',
+ 'protectmoveonly',
+ 'protectpage',
+ 'protectsub',
+ 'protectthispage',
+ 'unprotect',
+ 'unprotectthispage',
+ 'unprotectsub',
+ 'unprotectcomment',
+),
+);
+
+$CreditsMsg = array(
+'anonymous',
+'siteuser',
+'lastmodifiedby',
+'and',
+'othercontribs',
+'others',
+'siteusers',
+'creditspage',
+'nocredits',
+);
+
+// When showing differences
+$DifferenceMsg = array(
+'previousdiff',
+'nextdiff',
+);
+
+// used on page edition
+$EditMsg = array(
+'bold_sample',
+'bold_tip',
+'italic_sample',
+'italic_tip',
+'link_sample',
+'link_tip',
+'extlink_sample',
+'extlink_tip',
+'headline_sample',
+'headline_tip',
+'math_sample',
+'math_tip',
+'nowiki_sample',
+'nowiki_tip',
+'image_sample',
+'image_tip',
+'media_sample',
+'media_tip',
+'sig_tip',
+'hr_tip',
+
+'accesskey-search',
+'accesskey-minoredit',
+'accesskey-save',
+'accesskey-preview',
+'accesskey-diff',
+'accesskey-compareselectedversions',
+'tooltip-search',
+'tooltip-minoredit',
+'tooltip-save',
+'tooltip-preview',
+'tooltip-diff',
+'tooltip-compareselectedversions',
+'tooltip-watch',
+
+'copyrightwarning',
+'copyrightwarning2',
+'editconflict',
+'editing',
+'editingcomment',
+'editingold',
+'editingsection',
+'explainconflict',
+'infobox',
+'infobox_alert',
+'longpagewarning',
+'nonunicodebrowser',
+'previewconflict',
+'previewnote',
+'protectedpagewarning',
+'readonlywarning',
+'spamprotectiontitle',
+'spamprotectiontext',
+'spamprotectionmatch',
+'templatesused',
+'yourdiff',
+'yourtext',
+);
+
+// Per namespace
+$NamespaceCategory = array (
+'category_header',
+'categoryarticlecount',
+'categoryarticlecount1',
+'listingcontinuesabbrev',
+'subcategories',
+'subcategorycount',
+'subcategorycount1',
+'usenewcategorypage',
+);
+
+$NamespaceImage = array (
+'deletedrevision',
+'edit-externally',
+'edit-externally-help',
+'showbigimage',
+);
+
+$NamespaceSpecialMsg = array(
+'nosuchspecialpage',
+'nospecialpagetext',
+);
+
+
+
+// per special pages
+$SpecialAllMessages = array(
+'allmessages',
+'allmessagesname',
+'allmessagesdefault',
+'allmessagescurrent',
+'allmessagestext',
+'allmessagesnotsupportedUI',
+'allmessagesnotsupportedDB',
+);
+
+
+$SpecialAllPages = array(
+'articlenamespace',
+'allpagesformtext1',
+'allpagesformtext2',
+'allarticles',
+'allpagesprev',
+'allpagesnext',
+'allpagesnamespace',
+'allpagessubmit',
+);
+
+
+$SpecialAskSQLMsg = array(
+'asksql',
+'asksqltext',
+'sqlislogged',
+'sqlquery',
+'querybtn',
+'selectonly',
+'querysuccessful',
+);
+
+$SpecialBlockip = array(
+'blockip',
+'blockiptext',
+'range_block_disabled',
+'ipb_expiry_invalid',
+'ip_range_invalid',
+'ipbexpiry',
+'ipbsubmit',
+);
+
+$SpecialContributions = array(
+'contribsub',
+'contributionsall',
+'newbies',
+'nocontribs',
+'ucnote',
+'uclinks',
+'uctop',
+);
+
+$SpecialExportMsg = array (
+'export',
+'exporttext',
+'exportcuronly',
+);
+
+$SpecialImagelist = array(
+'imagelistall',
+);
+
+$SpecialImportMsg = array (
+'import',
+'importtext',
+'importfailed',
+'importnotext',
+'importsuccess',
+'importhistoryconflict',
+);
+
+$SpecialLockdbMsg = array(
+'lockdb',
+'unlockdb',
+'lockdbtext',
+'unlockdbtext',
+'lockconfirm',
+'unlockconfirm',
+'lockbtn',
+'unlockbtn',
+'locknoconfirm',
+'lockdbsuccesssub',
+'unlockdbsuccesssub',
+'lockdbsuccesstext',
+'unlockdbsuccesstext',
+);
+
+$SpecialLogMsg = array(
+'specialloguserlabel',
+'speciallogtitlelabel',
+);
+
+$SpecialMaintenance = array(
+'maintenance',
+'maintnancepagetext',
+'maintenancebacklink',
+'disambiguations',
+'disambiguationspage',
+'disambiguationstext',
+'doubleredirects',
+'doubleredirectstext',
+'brokenredirects',
+'brokenredirectstext',
+'selflinks',
+'selflinkstext',
+'mispeelings',
+'mispeelingstext',
+'mispeelingspage',
+'missinglanguagelinks',
+'missinglanguagelinksbutton',
+'missinglanguagelinkstext',
+);
+
+$SpecialMakeSysopMsg = array (
+'already_bureaucrat',
+'already_sysop',
+'makesysop',
+'makesysoptitle',
+'makesysoptext',
+'makesysopname',
+'makesysopsubmit',
+'makesysopok',
+'makesysopfail',
+'rights',
+'set_rights_fail',
+'set_user_rights',
+'user_rights_set',
+);
+
+$SpecialMovepageMsg = array(
+'newtitle',
+'movearticle',
+'movenologin',
+'movenologintext',
+'movepage',
+'movepagebtn',
+'movepagetalktext',
+'movepagetext',
+'movetalk',
+'pagemovedsub',
+'pagemovedtext',
+'talkexists',
+'talkpagemoved',
+'talkpagenotmoved',
+
+);
+
+$SpecialPreferencesMsg = array(
+'tog-underline',
+'tog-highlightbroken',
+'tog-justify',
+'tog-hideminor',
+'tog-usenewrc',
+'tog-numberheadings',
+'tog-showtoolbar',
+'tog-editondblclick',
+'tog-editsection',
+'tog-editsectiononrightclick',
+'tog-showtoc',
+'tog-rememberpassword',
+'tog-editwidth',
+'tog-watchdefault',
+'tog-minordefault',
+'tog-previewontop',
+'tog-previewonfirst',
+'tog-nocache',
+'tog-enotifwatchlistpages',
+'tog-enotifusertalkpages',
+'tog-enotifminoredits',
+'tog-enotifrevealaddr',
+'tog-shownumberswatching',
+'tog-rcusemodstyle',
+'tog-showupdated',
+'tog-fancysig',
+'tog-externaleditor',
+
+'imagemaxsize',
+'prefs-help-email',
+'prefs-help-email-enotif',
+'prefs-help-realname',
+'prefs-help-userdata',
+'prefs-misc',
+'prefs-personal',
+'prefs-rc',
+'resetprefs',
+'saveprefs',
+'oldpassword',
+'newpassword',
+'retypenew',
+'textboxsize',
+'rows',
+'columns',
+'searchresultshead',
+'resultsperpage',
+'contextlines',
+'contextchars',
+'stubthreshold',
+'recentchangescount',
+'savedprefs',
+'timezonelegend',
+'timezonetext',
+'localtime',
+'timezoneoffset',
+'servertime',
+'guesstimezone',
+'emailflag',
+'defaultns',
+'default',
+);
+
+$SpecialRecentchangesMsg = array(
+'changes',
+'recentchanges',
+'recentchanges-url',
+'recentchangestext',
+'rcloaderr',
+'rcnote',
+'rcnotefrom',
+'rclistfrom',
+'showhideminor',
+'rclinks',
+'rchide',
+'rcliu',
+'diff',
+'hist',
+'hide',
+'show',
+'tableform',
+'listform',
+'nchanges',
+'minoreditletter',
+'newpageletter',
+'sectionlink',
+'number_of_watching_users_RCview',
+'number_of_watching_users_pageview',
+'recentchangesall',
+);
+
+$SpecialRecentchangeslinkedMsg = array(
+'rclsub',
+);
+
+$SpecialSearchMsg = array(
+'searchresults',
+'searchresulttext',
+'searchquery',
+'badquery',
+'badquerytext',
+'matchtotals',
+'nogomatch',
+'titlematches',
+'notitlematches',
+'textmatches',
+'notextmatches',
+);
+
+$SpecialSitesettingsMsg = array(
+'sitesettings',
+'sitesettings-features',
+'sitesettings-permissions',
+'sitesettings-memcached',
+'sitesettings-debugging',
+'sitesettings-caching',
+'sitesettings-wgShowIPinHeader',
+'sitesettings-wgUseDatabaseMessages',
+'sitesettings-wgUseCategoryMagic',
+'sitesettings-wgUseCategoryBrowser',
+'sitesettings-wgHitcounterUpdateFreq',
+'sitesettings-wgAllowExternalImages',
+'sitesettings-permissions-readonly',
+'sitesettings-permissions-whitelist',
+'sitesettings-permissions-banning',
+'sitesettings-permissions-miser',
+'sitesettings-wgReadOnly',
+'sitesettings-wgReadOnlyFile',
+'sitesettings-wgWhitelistEdit',
+'sitesettings-wgWhitelistRead',
+'sitesettings-wgWhitelistAccount-user',
+'sitesettings-wgWhitelistAccount-sysop',
+'sitesettings-wgWhitelistAccount-developer',
+'sitesettings-wgSysopUserBans',
+'sitesettings-wgSysopRangeBans',
+'sitesettings-wgDefaultBlockExpiry',
+'sitesettings-wgMiserMode',
+'sitesettings-wgDisableQueryPages',
+'sitesettings-wgUseWatchlistCache',
+'sitesettings-wgWLCacheTimeout',
+'sitesettings-cookies',
+'sitesettings-performance',
+'sitesettings-images',
+);
+
+$SpecialStatisticsMsg = array(
+'statistics',
+'sitestats',
+'userstats',
+'sitestatstext',
+'userstatstext',
+);
+
+$SpecialUndelte = array(
+'deletepage',
+);
+
+$SpecialUploadMsg = array(
+'affirmation',
+'badfilename',
+'badfiletype',
+'emptyfile',
+'fileexists',
+'filedesc',
+'filename',
+'filesource',
+'filestatus',
+'fileuploaded',
+'ignorewarning',
+'illegalfilename',
+'largefile',
+'minlength',
+'noaffirmation',
+'reupload',
+'reuploaddesc',
+'savefile',
+'successfulupload',
+'upload',
+'uploadbtn',
+'uploadcorrupt',
+'uploaddisabled',
+'uploadfile',
+'uploadedimage',
+'uploaderror',
+'uploadlink',
+'uploadlog',
+'uploadlogpage',
+'uploadlogpagetext',
+'uploadnologin',
+'uploadnologintext',
+'uploadtext',
+'uploadwarning',
+);
+
+$SpecialUserlevelsMsg = array(
+'saveusergroups',
+'userlevels-editusergroup',
+'userlevels-groupsavailable',
+'userlevels-groupshelp',
+'userlevels-groupsmember',
+);
+
+$SpecialUserloginMsg = array(
+'acct_creation_throttle_hit',
+'loginend',
+'loginsuccesstitle',
+'loginsuccess',
+'nocookiesnew',
+'nocookieslogin',
+'noemail',
+'noname',
+'nosuchuser',
+'mailmypassword',
+'mailmypasswordauthent',
+'passwordremindermailsubject',
+'passwordremindermailbody',
+'passwordsent',
+'passwordsentforemailauthentication',
+'userexists',
+'wrongpassword',
+);
+
+$SpecialValidateMsg = array(
+'val_yes',
+'val_no',
+'val_revision',
+'val_time',
+'val_list_header',
+'val_add',
+'val_del',
+'val_warning',
+'val_rev_for',
+'val_rev_stats_link',
+'val_iamsure',
+'val_clear_old',
+'val_merge_old',
+'val_form_note',
+'val_noop',
+'val_percent',
+'val_percent_single',
+'val_total',
+'val_version',
+'val_tab',
+'val_this_is_current_version',
+'val_version_of',
+'val_table_header',
+'val_stat_link_text',
+'val_view_version',
+'val_validate_version',
+'val_user_validations',
+'val_no_anon_validation',
+'val_validate_article_namespace_only',
+'val_validated',
+'val_article_lists',
+'val_page_validation_statistics',
+);
+
+$SpecialVersionMsg = array(
+'special_version_prefix',
+'special_version_postfix'
+);
+
+$SpecialWatchlistMsg = array(
+'watchlistall1',
+'watchlistall2',
+'wlnote',
+'wlshowlast',
+'wlsaved',
+'wlhideshowown',
+'wlshow',
+'wlhide',
+);
+
+$SpecialWhatlinkshereMsg = array(
+'linklistsub',
+'nolinkshere',
+'isredirect',
+);
+
+
+$commonMsg = array (
+'sunday',
+'monday',
+'tuesday',
+'wednesday',
+'thursday',
+'friday',
+'saturday',
+'january',
+'february',
+'march',
+'april',
+'may_long',
+'june',
+'july',
+'august',
+'september',
+'october',
+'november',
+'december',
+'jan',
+'feb',
+'mar',
+'apr',
+'may',
+'jun',
+'jul',
+'aug',
+'sep',
+'oct',
+'nov',
+'dec',
+'categories',
+'category',
+'linktrail',
+'mainpage',
+'portal',
+'portal-url',
+'about',
+'aboutsite',
+'aboutpage',
+'article',
+'help',
+'helppage',
+'wikititlesuffix',
+'bugreports',
+'bugreportspage',
+'sitesupport',
+'sitesupport-url',
+'faq',
+'faqpage',
+'edithelp',
+'newwindow',
+'edithelppage',
+'cancel',
+'qbfind',
+'qbbrowse',
+'qbedit',
+'qbpageoptions',
+'qbpageinfo',
+'qbmyoptions',
+'qbspecialpages',
+'moredotdotdot',
+'mypage',
+'mytalk',
+'anontalk',
+'navigation',
+'metadata',
+'metadata_page',
+'currentevents',
+'currentevents-url',
+'disclaimers',
+'disclaimerpage',
+'errorpagetitle',
+'returnto',
+'tagline',
+'whatlinkshere',
+'search',
+'go',
+'history',
+'history_short',
+'info_short',
+'printableversion',
+'edit',
+'editthispage',
+'newpage',
+'talkpage',
+'specialpage',
+'personaltools',
+'postcomment',
+'addsection',
+'articlepage',
+'subjectpage',
+'talk',
+'toolbox',
+'userpage',
+'wikipediapage',
+'imagepage',
+'viewtalkpage',
+'otherlanguages',
+'redirectedfrom',
+'lastmodified',
+'viewcount',
+'copyright',
+'poweredby',
+'printsubtitle',
+'protectedpage',
+'administrators',
+'sysoptitle',
+'sysoptext',
+'developertitle',
+'developertext',
+'bureaucrattitle',
+'bureaucrattext',
+'nbytes',
+'ok',
+'sitetitle',
+'pagetitle',
+'sitesubtitle',
+'retrievedfrom',
+'newmessages',
+'newmessageslink',
+'editsection',
+'toc',
+'showtoc',
+'hidetoc',
+'thisisdeleted',
+'restorelink',
+'feedlinks',
+'sitenotice',
+'nstab-main',
+'nstab-user',
+'nstab-media',
+'nstab-special',
+'nstab-wp',
+'nstab-image',
+'nstab-mediawiki',
+'nstab-template',
+'nstab-help',
+'nstab-category',
+'nosuchaction',
+'nosuchactiontext',
+
+
+'error',
+'databaseerror',
+'dberrortext',
+'dberrortextcl',
+'noconnect',
+'nodb',
+'cachederror',
+'laggedslavemode',
+'readonly',
+'enterlockreason',
+'readonlytext',
+'missingarticle',
+'internalerror',
+'filecopyerror',
+'filerenameerror',
+'filedeleteerror',
+'filenotfound',
+'unexpected',
+'formerror',
+'badarticleerror',
+'cannotdelete',
+'badtitle',
+'badtitletext',
+'perfdisabled',
+'perfdisabledsub',
+'perfcached',
+'wrong_wfQuery_params',
+'viewsource',
+'protectedtext',
+'seriousxhtmlerrors',
+'logouttitle',
+'logouttext',
+'welcomecreation',
+
+'loginpagetitle',
+'yourname',
+'yourpassword',
+'yourpasswordagain',
+'newusersonly',
+'remembermypassword',
+'loginproblem',
+'alreadyloggedin',
+'login',
+'loginprompt',
+'userlogin',
+'logout',
+'userlogout',
+'notloggedin',
+'createaccount',
+'createaccountmail',
+'badretype',
+
+'youremail',
+'yourrealname',
+'yourlanguage',
+'yourvariant',
+'yournick',
+'emailforlost',
+'loginerror',
+'nosuchusershort',
+
+'mailerror',
+'emailauthenticated',
+'emailnotauthenticated',
+'invalidemailaddress',
+'disableduntilauthent',
+'disablednoemail',
+
+'summary',
+'subject',
+'minoredit',
+'watchthis',
+'savearticle',
+'preview',
+'showpreview',
+'showdiff',
+'blockedtitle',
+'blockedtext',
+'whitelistedittitle',
+'whitelistedittext',
+'whitelistreadtitle',
+'whitelistreadtext',
+'whitelistacctitle',
+'whitelistacctext',
+'loginreqtitle',
+'loginreqtext',
+'accmailtitle',
+'accmailtext',
+'newarticle',
+'newarticletext',
+'talkpagetext',
+'anontalkpagetext',
+'noarticletext',
+'clearyourcache',
+'usercssjsyoucanpreview',
+'usercsspreview',
+'userjspreview',
+'updated',
+'note',
+'storedversion', // not used ? Editpage ?
+'revhistory',
+'nohistory',
+'revnotfound',
+'revnotfoundtext',
+'loadhist',
+'currentrev',
+'revisionasof',
+'revisionasofwithlink',
+'previousrevision',
+'nextrevision',
+'currentrevisionlink',
+'cur',
+'next',
+'last',
+'orig',
+'histlegend',
+'history_copyright',
+'difference',
+'loadingrev',
+'lineno',
+'editcurrent',
+'selectnewerversionfordiff',
+'selectolderversionfordiff',
+'compareselectedversions',
+
+'prevn',
+'nextn',
+'viewprevnext',
+'showingresults',
+'showingresultsnum',
+'nonefound',
+'powersearch',
+'powersearchtext',
+'searchdisabled',
+'googlesearch',
+'blanknamespace',
+'preferences',
+'prefsnologin',
+'prefsnologintext',
+'prefslogintext',
+'prefsreset',
+'qbsettings',
+'qbsettingsnote',
+'changepassword',
+'skin',
+'math',
+'dateformat',
+
+'math_failure',
+'math_unknown_error',
+'math_unknown_function',
+'math_lexing_error',
+'math_syntax_error',
+'math_image_error',
+'math_bad_tmpdir',
+'math_bad_output',
+'math_notexvc',
+
+
+
+
+
+
+'grouplevels-lookup-group',
+'grouplevels-group-edit',
+'editgroup',
+'addgroup',
+'userlevels-lookup-user',
+'userlevels-user-editname',
+'editusergroup',
+'grouplevels-editgroup',
+'grouplevels-addgroup',
+'grouplevels-editgroup-name',
+'grouplevels-editgroup-description',
+'savegroup',
+
+// common to several pages
+'copyrightpage',
+'copyrightpagename',
+'imagelist',
+'imagelisttext',
+'ilshowmatch',
+'ilsubmit',
+'showlast',
+'byname',
+'bydate',
+'bysize',
+
+
+
+'imgdelete',
+'imgdesc',
+'imglegend',
+'imghistory',
+'revertimg',
+'deleteimg',
+'deleteimgcompletely',
+'imghistlegend',
+'imagelinks',
+'linkstoimage',
+'nolinkstoimage',
+
+// unused ??
+'uploadedfiles',
+'getimagelist',
+
+
+'sharedupload',
+'shareduploadwiki',
+
+// Special pages names
+'orphans',
+'geo',
+'validate',
+'lonelypages',
+'uncategorizedpages',
+'uncategorizedcategories',
+'unusedimages',
+'popularpages',
+'nviews',
+'wantedpages',
+'nlinks',
+'allpages',
+'randompage',
+'randompage-url',
+'shortpages',
+'longpages',
+'deadendpages',
+'listusers',
+'specialpages',
+'spheading',
+'restrictedpheading',
+'recentchangeslinked',
+
+
+'debug',
+'newpages',
+'ancientpages',
+'intl',
+'unusedimagestext',
+'booksources',
+'categoriespagetext',
+'data',
+'userlevels',
+'grouplevels',
+'booksourcetext',
+'isbn',
+'rfcurl',
+'pubmedurl',
+'alphaindexline',
+'version',
+'log',
+'alllogstext',
+'nextpage',
+'mailnologin',
+'mailnologintext',
+'emailuser',
+'emailpage',
+'emailpagetext',
+'usermailererror',
+'defemailsubject',
+'noemailtitle',
+'noemailtext',
+'emailfrom',
+'emailto',
+'emailsubject',
+'emailmessage',
+'emailsend',
+'emailsent',
+'emailsenttext',
+'watchlist',
+'watchlistsub',
+'nowatchlist',
+'watchnologin',
+'watchnologintext',
+'addedwatch',
+'addedwatchtext',
+'removedwatch',
+'removedwatchtext',
+'watch',
+'watchthispage',
+'unwatch',
+'unwatchthispage',
+'notanarticle',
+'watchnochange',
+'watchdetails',
+'watchmethod-recent',
+'watchmethod-list',
+'removechecked',
+'watchlistcontains',
+'watcheditlist',
+'removingchecked',
+'couldntremove',
+'iteminvalidname',
+
+'updatedmarker',
+'email_notification_mailer',
+'email_notification_infotext',
+'email_notification_reset',
+'email_notification_newpagetext',
+'email_notification_to',
+'email_notification_subject',
+'email_notification_lastvisitedrevisiontext',
+'email_notification_body',
+
+'confirm',
+'excontent',
+'exbeforeblank',
+'exblank',
+'confirmdelete',
+'deletesub',
+'historywarning',
+'confirmdeletetext',
+'actioncomplete',
+'deletedtext',
+'deletedarticle',
+'dellogpage',
+'dellogpagetext',
+'deletionlog',
+'reverted',
+'deletecomment',
+'imagereverted',
+'rollback',
+'rollback_short',
+'rollbacklink',
+'rollbackfailed',
+'cantrollback',
+'alreadyrolled',
+'revertpage',
+'editcomment',
+'sessionfailure',
+
+'protectlogpage',
+'protectlogtext',
+
+'protectedarticle',
+'unprotectedarticle',
+
+'contributions',
+'mycontris',
+'notargettitle', // not used ?
+'notargettext', // not used ?
+
+'linkshere',
+
+'ipaddress',
+'ipadressorusername', // not used ?
+'ipbreason',
+
+'badipaddress',
+'noblockreason',
+'blockipsuccesssub',
+'blockipsuccesstext',
+'unblockip',
+'unblockiptext',
+'ipusubmit',
+'ipusuccess',
+'ipblocklist',
+'blocklistline',
+'blocklink',
+'unblocklink',
+'contribslink',
+'autoblocker',
+'blocklogpage',
+'blocklogentry',
+'blocklogtext',
+'unblocklogentry', // not used ?
+
+'proxyblocker',
+'proxyblockreason',
+'proxyblocksuccess',
+'sorbs',
+'sorbsreason',
+
+'setbureaucratflag',
+'bureaucratlog',
+'rightslogtext',
+'bureaucratlogentry',
+
+'articleexists', // not used ?
+
+'movedto',
+'1movedto2',
+'1movedto2_redir',
+'movelogpage',
+'movelogpagetext',
+
+'thumbnail-more',
+'missingimage',
+'filemissing',
+'Monobook.css',
+'nodublincore',
+'nocreativecommons',
+'notacceptable',
+
+// used in Article::
+'infosubtitle',
+'numedits',
+'numtalkedits',
+'numwatchers',
+'numauthors',
+'numtalkauthors',
+
+// not used ?
+'mw_math_png',
+'mw_math_simple',
+'mw_math_html',
+'mw_math_source',
+'mw_math_modern',
+'mw_math_mathml',
+
+// Patrolling
+'markaspatrolleddiff',
+'markaspatrolledlink',
+'markaspatrolledtext',
+'markedaspatrolled',
+'markedaspatrolledtext',
+'rcpatroldisabled', // not used ?
+'rcpatroldisabledtext', // not used ?
+
+'Monobook.js',
+'newimages',
+'noimages',
+'variantname-zh-cn',
+'variantname-zh-tw',
+'variantname-zh-hk',
+'variantname-zh-sg',
+'variantname-zh',
+'zhconversiontable',
+'passwordtooshort', // sp preferences / userlogin
+);
+?>
diff --git a/maintenance/language/splitLanguageFiles.php b/maintenance/language/splitLanguageFiles.php
new file mode 100644
index 00000000..2263e611
--- /dev/null
+++ b/maintenance/language/splitLanguageFiles.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * splitLanguageFiles
+ * Should read each of the languages files then split them in several subpart
+ * under ./languages/XX/ according to the arrays in splitLanguageFiles.inc .
+ *
+ * Also need to rewrite the wfMsg system / message-cache.
+ */
+
+include(dirname(__FILE__).'/../commandLine.inc');
+
+
+?>
diff --git a/maintenance/language/transstat.php b/maintenance/language/transstat.php
new file mode 100644
index 00000000..590da121
--- /dev/null
+++ b/maintenance/language/transstat.php
@@ -0,0 +1,211 @@
+<?php
+/**
+ * Statistics about the localisation.
+ *
+ * @package MediaWiki
+ * @subpackage Maintenance
+ *
+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ * @author Ashar Voultoiz <thoane@altern.org>
+ *
+ * Output is posted from time to time on:
+ * http://meta.wikimedia.org/wiki/Localization_statistics
+ */
+
+require_once( 'maintenance/commandLine.inc' );
+require_once( 'languages.inc' );
+
+if ( isset( $options['help'] ) ) {
+ showUsage();
+}
+
+# Default output is WikiText
+if ( !isset( $options['output'] ) ) {
+ $options['output'] = 'wiki';
+}
+
+/** Print a usage message*/
+function showUsage() {
+ print <<<END
+Usage: php transstat.php [--help] [--output=csv|text|wiki]
+ --help : this helpful message
+ --output : select an output engine one of:
+ * 'csv' : Comma Separated Values.
+ * 'wiki' : MediaWiki syntax (default).
+ * 'metawiki' : MediaWiki syntax used for Meta-Wiki.
+ * 'text' : Text with tabs.
+Example: php maintenance/transstat.php --output=text
+
+END;
+ exit();
+}
+
+/** A general output object. Need to be overriden */
+class statsOutput {
+ function formatPercent( $subset, $total, $revert = false, $accuracy = 2 ) {
+ return @sprintf( '%.' . $accuracy . 'f%%', 100 * $subset / $total );
+ }
+
+ # Override the following methods
+ function heading() {
+ }
+ function footer() {
+ }
+ function blockstart() {
+ }
+ function blockend() {
+ }
+ function element( $in, $heading = false ) {
+ }
+}
+
+/** Outputs WikiText */
+class wikiStatsOutput extends statsOutput {
+ function heading() {
+ global $IP;
+ $version = SpecialVersion::getVersion( $IP );
+ echo "'''Statistics are based on:''' <code>" . $version . "</code>\n\n";
+ echo "'''Note:''' These statistics can be generated by running <code>php maintenance/language/transstat.php</code>.\n\n";
+ echo "For additional information on specific languages (the message names, the actual problems, etc.), run <code>php maintenance/language/checkLanguage.php --lang=foo</code>.\n\n";
+ echo '{| border="2" cellpadding="4" cellspacing="0" style="background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse;" width="100%"'."\n";
+ }
+ function footer() {
+ echo "|}\n";
+ }
+ function blockstart() {
+ echo "|-\n";
+ }
+ function blockend() {
+ echo '';
+ }
+ function element( $in, $heading = false ) {
+ echo ($heading ? '!' : '|') . " $in\n";
+ }
+ function formatPercent( $subset, $total, $revert = false, $accuracy = 2 ) {
+ $v = @round(255 * $subset / $total);
+ if ( $revert ) {
+ $v = 255 - $v;
+ }
+ if ( $v < 128 ) {
+ # Red to Yellow
+ $red = 'FF';
+ $green = sprintf( '%02X', 2 * $v );
+ } else {
+ # Yellow to Green
+ $red = sprintf('%02X', 2 * ( 255 - $v ) );
+ $green = 'FF';
+ }
+ $blue = '00';
+ $color = $red . $green . $blue;
+
+ $percent = statsOutput::formatPercent( $subset, $total, $revert, $accuracy );
+ return 'bgcolor="#'. $color .'" | '. $percent;
+ }
+}
+
+/** Outputs WikiText and appends category and text only used for Meta-Wiki */
+class metawikiStatsOutput extends wikiStatsOutput {
+ function heading() {
+ echo "See [[MediaWiki localisation]] to learn how you can help translating MediaWiki.\n\n";
+ parent::heading();
+ }
+ function footer() {
+ parent::footer();
+ echo "\n[[Category:Localisation|Statistics]]\n";
+ }
+}
+
+/** Output text. To be used on a terminal for example. */
+class textStatsOutput extends statsOutput {
+ function element( $in, $heading = false ) {
+ echo $in."\t";
+ }
+ function blockend() {
+ echo "\n";
+ }
+}
+
+/** csv output. Some people love excel */
+class csvStatsOutput extends statsOutput {
+ function element( $in, $heading = false ) {
+ echo $in . ";";
+ }
+ function blockend() {
+ echo "\n";
+ }
+}
+
+# Select an output engine
+switch ( $options['output'] ) {
+ case 'wiki':
+ $wgOut = new wikiStatsOutput();
+ break;
+ case 'metawiki':
+ $wgOut = new metawikiStatsOutput();
+ break;
+ case 'text':
+ $wgOut = new textStatsOutput();
+ break;
+ case 'csv':
+ $wgOut = new csvStatsOutput();
+ break;
+ default:
+ showUsage();
+}
+
+# Languages
+$wgLanguages = new languages();
+
+# Header
+$wgOut->heading();
+$wgOut->blockstart();
+$wgOut->element( 'Language', true );
+$wgOut->element( 'Translated', true );
+$wgOut->element( '%', true );
+$wgOut->element( 'Obsolete', true );
+$wgOut->element( '%', true );
+$wgOut->element( 'Problematic', true );
+$wgOut->element( '%', true );
+$wgOut->blockend();
+
+$wgGeneralMessages = $wgLanguages->getGeneralMessages();
+$wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] );
+
+foreach ( $wgLanguages->getLanguages() as $code ) {
+ # Don't check English or RTL English
+ if ( $code == 'en' || $code == 'enRTL' ) {
+ continue;
+ }
+
+ # Calculate the numbers
+ $language = $wgContLang->getLanguageName( $code );
+ $messages = $wgLanguages->getMessages( $code );
+ $messagesNumber = count( $messages['translated'] );
+ $requiredMessagesNumber = count( $messages['required'] );
+ $requiredMessagesPercent = $wgOut->formatPercent( $requiredMessagesNumber, $wgRequiredMessagesNumber );
+ $obsoleteMessagesNumber = count( $messages['obsolete'] );
+ $obsoleteMessagesPercent = $wgOut->formatPercent( $obsoleteMessagesNumber, $messagesNumber, true );
+ $messagesWithoutVariables = $wgLanguages->getMessagesWithoutVariables( $code );
+ $emptyMessages = $wgLanguages->getEmptyMessages( $code );
+ $messagesWithWhitespace = $wgLanguages->getMessagesWithWhitespace( $code );
+ $nonXHTMLMessages = $wgLanguages->getNonXHTMLMessages( $code );
+ $messagesWithWrongChars = $wgLanguages->getMessagesWithWrongChars( $code );
+ $problematicMessagesNumber = count( array_unique( array_merge( $messagesWithoutVariables, $emptyMessages, $messagesWithWhitespace, $nonXHTMLMessages, $messagesWithWrongChars ) ) );
+ $problematicMessagesPercent = $wgOut->formatPercent( $problematicMessagesNumber, $messagesNumber, true );
+
+ # Output them
+ $wgOut->blockstart();
+ $wgOut->element( "$language ($code)" );
+ $wgOut->element( "$requiredMessagesNumber/$wgRequiredMessagesNumber" );
+ $wgOut->element( $requiredMessagesPercent );
+ $wgOut->element( "$obsoleteMessagesNumber/$messagesNumber" );
+ $wgOut->element( $obsoleteMessagesPercent );
+ $wgOut->element( "$problematicMessagesNumber/$messagesNumber" );
+ $wgOut->element( $problematicMessagesPercent );
+ $wgOut->blockend();
+}
+
+# Footer
+$wgOut->footer();
+
+?>
diff --git a/maintenance/language/unusedMessages.php b/maintenance/language/unusedMessages.php
new file mode 100644
index 00000000..8b117eca
--- /dev/null
+++ b/maintenance/language/unusedMessages.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Prints out messages in localisation files that are no longer used.
+ *
+ * @package MediaWiki
+ * @subpackage Maintenance
+ */
+
+require_once(dirname(__FILE__).'/../commandLine.inc');
+
+if ( isset( $args[0] ) ) {
+ $code = $args[0];
+} else {
+ $code = $wgLang->getCode();
+}
+
+if ( $code == 'en' ) {
+ print "Current selected language is English. Cannot check translations.\n";
+ exit();
+}
+
+$filename = Language::getMessagesFileName( $code );
+if ( file_exists( $filename ) ) {
+ require( $filename );
+} else {
+ $messages = array();
+}
+
+$count = $total = 0;
+$wgEnglishMessages = Language::getMessagesFor( 'en' );
+$wgLocalMessages = $messages;
+
+foreach ( $wgLocalMessages as $key => $msg ) {
+ ++$total;
+ if ( !isset( $wgEnglishMessages[$key] ) ) {
+ print "* $key\n";
+ ++$count;
+ }
+}
+
+print "{$count} messages of {$total} are unused in the language {$code}\n";
+?>
diff --git a/maintenance/language/validate.php b/maintenance/language/validate.php
new file mode 100644
index 00000000..10d98d37
--- /dev/null
+++ b/maintenance/language/validate.php
@@ -0,0 +1,40 @@
+<?php
+
+if ( !isset( $argv[1] ) ) {
+ print "Usage: php {$argv[0]} <filename>\n";
+ exit( 1 );
+}
+array_shift( $argv );
+
+define( 'MEDIAWIKI', 1 );
+define( 'NOT_REALLY_MEDIAWIKI', 1 );
+
+$IP = dirname( __FILE__ ) . '/../..';
+
+require_once( "$IP/includes/Defines.php" );
+require_once( "$IP/languages/Language.php" );
+
+$files = array();
+foreach ( $argv as $arg ) {
+ $files = array_merge( $files, glob( $arg ) );
+}
+
+foreach ( $files as $filename ) {
+ print "$filename...";
+ $vars = getVars( $filename );
+ $keys = array_keys( $vars );
+ $diff = array_diff( $keys, Language::$mLocalisationKeys );
+ if ( $diff ) {
+ print "\nWarning: unrecognised variable(s): " . implode( ', ', $diff ) ."\n";
+ } else {
+ print " ok\n";
+ }
+}
+
+function getVars( $filename ) {
+ require( $filename );
+ $vars = get_defined_vars();
+ unset( $vars['filename'] );
+ return $vars;
+}
+?>