summaryrefslogtreecommitdiff
path: root/extensions/LocalisationUpdate
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-08-12 09:28:15 +0200
committerPierre Schmitz <pierre@archlinux.de>2013-08-12 09:28:15 +0200
commit08aa4418c30cfc18ccc69a0f0f9cb9e17be6c196 (patch)
tree577a29fb579188d16003a209ce2a2e9c5b0aa2bd /extensions/LocalisationUpdate
parentcacc939b34e315b85e2d72997811eb6677996cc1 (diff)
Update to MediaWiki 1.21.1
Diffstat (limited to 'extensions/LocalisationUpdate')
-rw-r--r--extensions/LocalisationUpdate/KNOWN_ISSUES.txt11
-rw-r--r--extensions/LocalisationUpdate/LocalisationUpdate.class.php588
-rw-r--r--extensions/LocalisationUpdate/LocalisationUpdate.i18n.php528
-rw-r--r--extensions/LocalisationUpdate/LocalisationUpdate.php42
-rw-r--r--extensions/LocalisationUpdate/QuickArrayReader.php187
-rw-r--r--extensions/LocalisationUpdate/README_FIRST.txt8
-rw-r--r--extensions/LocalisationUpdate/update.php38
7 files changed, 1402 insertions, 0 deletions
diff --git a/extensions/LocalisationUpdate/KNOWN_ISSUES.txt b/extensions/LocalisationUpdate/KNOWN_ISSUES.txt
new file mode 100644
index 00000000..7ce14cd0
--- /dev/null
+++ b/extensions/LocalisationUpdate/KNOWN_ISSUES.txt
@@ -0,0 +1,11 @@
+- Only works with SVN revision 50605 or later of the
+ MediaWiki core
+
+
+
+Key issues at the moment:
+* Seems to want to store a copy of the localization updates in each local database.
+We've got hundreds of wikis run from the same installation set; we don't want to multiply our effort by 1000.
+
+* It doesn't seem to be using available memcached stuff; unsure yet whether this is taken care of
+by the general message caching or if we're going to end up making extra hits we don't need.
diff --git a/extensions/LocalisationUpdate/LocalisationUpdate.class.php b/extensions/LocalisationUpdate/LocalisationUpdate.class.php
new file mode 100644
index 00000000..39368b7c
--- /dev/null
+++ b/extensions/LocalisationUpdate/LocalisationUpdate.class.php
@@ -0,0 +1,588 @@
+<?php
+
+/**
+ * Class for localization updates.
+ *
+ * TODO: refactor code to remove duplication
+ */
+class LocalisationUpdate {
+
+ private static $newHashes = null;
+ private static $filecache = array();
+
+ /**
+ * LocalisationCacheRecache hook handler.
+ *
+ * @param $lc LocalisationCache
+ * @param $langcode String
+ * @param $cache Array
+ *
+ * @return true
+ */
+ public static function onRecache( LocalisationCache $lc, $langcode, array &$cache ) {
+ // Handle fallback sequence and load all fallback messages from the cache
+ $codeSequence = array_merge( array( $langcode ), $cache['fallbackSequence'] );
+ // Iterate over the fallback sequence in reverse, otherwise the fallback
+ // language will override the requested language
+ foreach ( array_reverse( $codeSequence ) as $code ) {
+ if ( $code == 'en' ) {
+ // Skip English, otherwise we end up trying to read
+ // the nonexistent cache file for en a couple hundred times
+ continue;
+ }
+
+ $cache['messages'] = array_merge(
+ $cache['messages'],
+ self::readFile( $code )
+ );
+
+ $cache['deps'][] = new FileDependency(
+ self::filename( $code )
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Called from the cronjob to fetch new messages from SVN.
+ *
+ * @param $options Array
+ *
+ * @return true
+ */
+ public static function updateMessages( array $options ) {
+ global $wgLocalisationUpdateDirectory, $wgLocalisationUpdateCoreURL,
+ $wgLocalisationUpdateExtensionURL, $wgLocalisationUpdateSVNURL;
+
+ $verbose = !isset( $options['quiet'] );
+ $all = isset( $options['all'] );
+ $skipCore = isset( $options['skip-core'] );
+ $skipExtensions = isset( $options['skip-extensions'] );
+
+ if( isset( $options['outdir'] ) ) {
+ $wgLocalisationUpdateDirectory = $options['outdir'];
+ }
+
+ $coreUrl = $wgLocalisationUpdateCoreURL;
+ $extUrl = $wgLocalisationUpdateExtensionURL;
+
+ // Some ugly BC
+ if ( $wgLocalisationUpdateSVNURL ) {
+ $coreUrl = $wgLocalisationUpdateSVNURL . '/phase3/$2';
+ $extUrl = $wgLocalisationUpdateSVNURL . '/extensions/$1/$2';
+ }
+
+ // Some more ugly BC
+ if ( isset( $options['svnurl'] ) ) {
+ $coreUrl = $options['svnurl'] . '/phase3/$2';
+ $extUrl = $options['svnurl'] . '/extensions/$1/$2';
+ }
+
+ $result = 0;
+
+ // Update all MW core messages.
+ if( !$skipCore ) {
+ $result = self::updateMediawikiMessages( $verbose, $coreUrl );
+ }
+
+ // Update all Extension messages.
+ if( !$skipExtensions ) {
+ if( $all ) {
+ global $IP;
+ $extFiles = array();
+
+ // Look in extensions/ for all available items...
+ // TODO: add support for $wgExtensionAssetsPath
+ $dirs = new RecursiveDirectoryIterator( "$IP/extensions/" );
+
+ // I ain't kidding... RecursiveIteratorIterator.
+ foreach( new RecursiveIteratorIterator( $dirs ) as $pathname => $item ) {
+ $filename = basename( $pathname );
+ $matches = array();
+ if( preg_match( '/^(.*)\.i18n\.php$/', $filename, $matches ) ) {
+ $group = $matches[1];
+ $extFiles[$group] = $pathname;
+ }
+ }
+ } else {
+ global $wgExtensionMessagesFiles;
+ $extFiles = $wgExtensionMessagesFiles;
+ }
+ foreach ( $extFiles as $extension => $locFile ) {
+ $result += self::updateExtensionMessages( $locFile, $extension, $verbose, $extUrl );
+ }
+ }
+
+ self::writeHashes();
+
+ // And output the result!
+ self::myLog( "Updated {$result} messages in total" );
+ self::myLog( "Done" );
+
+ return true;
+ }
+
+ /**
+ * Update Extension Messages.
+ *
+ * @param $file String
+ * @param $extension String
+ * @param $verbose Boolean
+ *
+ * @return Integer: the amount of updated messages
+ */
+ public static function updateExtensionMessages( $file, $extension, $verbose, $extUrl ) {
+ $match = array();
+ $ok = preg_match( '~^.*/extensions/([^/]+)/(.*)$~U', $file, $match );
+ if ( !$ok ) {
+ return null;
+ }
+
+ $ext = $match[1];
+ $extFile = $match[2];
+
+ // Create a full path.
+ $svnfile = str_replace(
+ array( '$1', '$2' ),
+ array( $ext, $extFile ),
+ $extUrl
+ );
+
+ // Compare the 2 files.
+ $result = self::compareExtensionFiles( $extension, $svnfile, $file, $verbose );
+
+ return $result;
+ }
+
+ /**
+ * Update the MediaWiki Core Messages.
+ *
+ * @param $verbose Boolean
+ *
+ * @return Integer: the amount of updated messages
+ */
+ public static function updateMediawikiMessages( $verbose, $coreUrl ) {
+ // Find the changed English strings (as these messages won't be updated in ANY language).
+ $localUrl = Language::getMessagesFileName( 'en' );
+ $repoUrl = str_replace( '$2', 'languages/messages/MessagesEn.php', $coreUrl );
+ $changedEnglishStrings = self::compareFiles( $repoUrl, $localUrl, $verbose );
+
+ // Count the changes.
+ $changedCount = 0;
+
+ $languages = Language::fetchLanguageNames( null, 'mwfile' );
+ foreach ( array_keys( $languages ) as $code ) {
+ $localUrl = Language::getMessagesFileName( $code );
+ // Not prefixed with $IP
+ $filename = Language::getFilename( 'languages/messages/Messages', $code );
+ $repoUrl = str_replace( '$2', $filename, $coreUrl );
+
+ // Compare the files.
+ $changedCount += self::compareFiles( $repoUrl, $localUrl, $verbose, $changedEnglishStrings, false, true );
+ }
+
+ // Log some nice info.
+ self::myLog( "{$changedCount} MediaWiki messages are updated" );
+
+ return $changedCount;
+ }
+
+ /**
+ * Removes all unneeded content from a file and returns it.
+ *
+ * @param $contents String
+ *
+ * @return String
+ */
+ public static function cleanupFile( $contents ) {
+ // We don't need any PHP tags.
+ $contents = strtr( $contents,
+ array(
+ '<?php' => '',
+ '?' . '>' => ''
+ )
+ );
+
+ $results = array();
+
+ // And we only want message arrays.
+ preg_match_all( '/\$messages(.*\s)*?\);/', $contents, $results );
+
+ // But we want them all in one string.
+ if( !empty( $results[0] ) && is_array( $results[0] ) ) {
+ $contents = implode( "\n\n", $results[0] );
+ } else {
+ $contents = '';
+ }
+
+ // And we hate the windows vs linux linebreaks.
+ $contents = preg_replace( '/\r\n?/', "\n", $contents );
+
+ return $contents;
+ }
+
+ /**
+ * Returns the contents of a file or false on failiure.
+ *
+ * @param $file String
+ *
+ * @return string or false
+ */
+ public static function getFileContents( $file ) {
+ global $wgLocalisationUpdateRetryAttempts;
+
+ $attempts = 0;
+ $filecontents = '';
+
+ // Use cURL to get the SVN contents.
+ if ( preg_match( "/^http/", $file ) ) {
+ while( !$filecontents && $attempts <= $wgLocalisationUpdateRetryAttempts ) {
+ if( $attempts > 0 ) {
+ $delay = 1;
+ self::myLog( 'Failed to download ' . $file . "; retrying in ${delay}s..." );
+ sleep( $delay );
+ }
+
+ $filecontents = Http::get( $file );
+ $attempts++;
+ }
+ if ( !$filecontents ) {
+ self::myLog( 'Cannot get the contents of ' . $file . ' (curl)' );
+ return false;
+ }
+ } else {// otherwise try file_get_contents
+ if ( !( $filecontents = file_get_contents( $file ) ) ) {
+ self::myLog( 'Cannot get the contents of ' . $file );
+ return false;
+ }
+ }
+
+ return $filecontents;
+ }
+
+ /**
+ * Returns a pair of arrays containing the messages from two files, or
+ * a pair of nulls if the files don't need to be checked.
+ *
+ * @param $tag String
+ * @param $file1 String
+ * @param $file2 String
+ * @param $verbose Boolean
+ * @param $alwaysGetResult Boolean
+ *
+ * @return array
+ */
+ public static function loadFilesToCompare( $tag, $file1, $file2, $verbose, $alwaysGetResult = true ) {
+ $file1contents = self::getFileContents( $file1 );
+ if ( $file1contents === false || $file1contents === '' ) {
+ self::myLog( "Failed to read $file1" );
+ return array( null, null );
+ }
+
+ $file2contents = self::getFileContents( $file2 );
+ if ( $file2contents === false || $file2contents === '' ) {
+ self::myLog( "Failed to read $file2" );
+ return array( null, null );
+ }
+
+ // Only get the part we need.
+ $file1contents = self::cleanupFile( $file1contents );
+ $file1hash = md5( $file1contents );
+
+ $file2contents = self::cleanupFile( $file2contents );
+ $file2hash = md5( $file2contents );
+
+ // Check if the file has changed since our last update.
+ if ( !$alwaysGetResult ) {
+ if ( !self::checkHash( $file1, $file1hash ) && !self::checkHash( $file2, $file2hash ) ) {
+ self::myLog( "Skipping {$tag} since the files haven't changed since our last update", $verbose );
+ return array( null, null );
+ }
+ }
+
+ // Get the array with messages.
+ $messages1 = self::parsePHP( $file1contents, 'messages' );
+ if ( !is_array( $messages1 ) ) {
+ if ( strpos( $file1contents, '$messages' ) === false ) {
+ // No $messages array. This happens for some languages that only have a fallback
+ $messages1 = array();
+ } else {
+ // Broken file? Report and bail
+ self::myLog( "Failed to parse $file1" );
+ return array( null, null );
+ }
+ }
+
+ $messages2 = self::parsePHP( $file2contents, 'messages' );
+ if ( !is_array( $messages2 ) ) {
+ // Broken file? Report and bail
+ if ( strpos( $file2contents, '$messages' ) === false ) {
+ // No $messages array. This happens for some languages that only have a fallback
+ $messages2 = array();
+ } else {
+ self::myLog( "Failed to parse $file2" );
+ return array( null, null );
+ }
+ }
+
+ self::saveHash( $file1, $file1hash );
+ self::saveHash( $file2, $file2hash );
+
+ return array( $messages1, $messages2 );
+ }
+
+ /**
+ * Compare new and old messages lists, and optionally save the new
+ * messages if they've changed.
+ *
+ * @param $langcode String
+ * @param $old_messages Array
+ * @param $new_messages Array
+ * @param $verbose Boolean
+ * @param $forbiddenKeys Array
+ * @param $saveResults Boolean
+ *
+ * @return array|int
+ */
+ private static function compareLanguageArrays( $langcode, $old_messages, $new_messages, $verbose, $forbiddenKeys, $saveResults ) {
+ // Get the currently-cached messages, if any
+ $cur_messages = self::readFile( $langcode );
+
+ // Update the messages lists with the cached messages
+ $old_messages = array_merge( $old_messages, $cur_messages );
+ $new_messages = array_merge( $cur_messages, $new_messages );
+
+ // Use the old/cached version for any forbidden keys
+ if ( count( $forbiddenKeys ) ) {
+ $new_messages = array_merge(
+ array_diff_key( $new_messages, $forbiddenKeys ),
+ array_intersect_key( $old_messages, $forbiddenKeys )
+ );
+ }
+
+
+ if ( $saveResults ) {
+ // If anything has changed from the saved version, save the new version
+ if ( $new_messages != $cur_messages ) {
+ // Count added, updated, and deleted messages:
+ // diff( new, cur ) gives added + updated, and diff( cur, new )
+ // gives deleted + updated.
+ $changed = array_diff_assoc( $new_messages, $cur_messages ) +
+ array_diff_assoc( $cur_messages, $new_messages );
+ $updates = count( $changed );
+ self::myLog( "{$updates} messages updated for {$langcode}.", $verbose );
+ self::writeFile( $langcode, $new_messages );
+ } else {
+ $updates = 0;
+ }
+ return $updates;
+ } else {
+ // Find all deleted or changed messages
+ $changedStrings = array_diff_assoc( $old_messages, $new_messages );
+ return $changedStrings;
+ }
+ }
+
+ /**
+ * Returns an array containing the differences between the files.
+ *
+ * @param $newfile String
+ * @param $oldfile String
+ * @param $verbose Boolean
+ * @param $forbiddenKeys Array
+ * @param $alwaysGetResult Boolean
+ * @param $saveResults Boolean
+ *
+ * @return array|int
+ */
+ public static function compareFiles( $newfile, $oldfile, $verbose, array $forbiddenKeys = array(), $alwaysGetResult = true, $saveResults = false ) {
+ // Get the languagecode.
+ $langcode = Language::getCodeFromFileName( $newfile, 'Messages' );
+
+ list( $new_messages, $old_messages ) = self::loadFilesToCompare(
+ $langcode, $newfile, $oldfile, $verbose, $alwaysGetResult
+ );
+ if ( $new_messages === null || $old_messages === null ) {
+ return $saveResults ? 0 : array();
+ }
+
+ return self::compareLanguageArrays( $langcode, $old_messages, $new_messages, $verbose, $forbiddenKeys, $saveResults );
+ }
+
+ /**
+ *
+ * @param $extension String
+ * @param $newfile String
+ * @param $oldfile String
+ * @param $verbose Boolean
+ * @param $alwaysGetResult Boolean
+ * @param $saveResults Boolean
+ *
+ * @return Integer: the amount of updated messages
+ */
+ public static function compareExtensionFiles( $extension, $newfile, $oldfile, $verbose ) {
+ list( $new_messages, $old_messages ) = self::loadFilesToCompare(
+ $extension, $newfile, $oldfile, $verbose, false
+ );
+ if ( $new_messages === null || $old_messages === null ) {
+ return 0;
+ }
+
+ // Update counter.
+ $updates = 0;
+
+ if ( empty( $new_messages['en'] ) ) {
+ $new_messages['en'] = array();
+ }
+
+ if ( empty( $old_messages['en'] ) ) {
+ $old_messages['en'] = array();
+ }
+
+ // Find the changed english strings.
+ $forbiddenKeys = self::compareLanguageArrays( 'en', $old_messages['en'], $new_messages['en'], $verbose, array(), false );
+
+ // Do an update for each language.
+ foreach ( $new_messages as $language => $messages ) {
+ if ( $language == 'en' ) { // Skip english.
+ continue;
+ }
+
+ if ( !isset( $old_messages[$language] ) ) {
+ $old_messages[$language] = array();
+ }
+
+ $updates += self::compareLanguageArrays( $language, $old_messages[$language], $messages, $verbose, $forbiddenKeys, true );
+ }
+
+ // And log some stuff.
+ self::myLog( "Updated " . $updates . " messages for the '{$extension}' extension", $verbose );
+
+ return $updates;
+ }
+
+ /**
+ * Checks whether a messages file has a certain hash.
+ *
+ * TODO: Swap return values, this is insane
+ *
+ * @param $file string Filename
+ * @param $hash string Hash
+ *
+ * @return bool True if $file does NOT have hash $hash, false if it does
+ */
+ public static function checkHash( $file, $hash ) {
+ $hashes = self::readFile( 'hashes' );
+ return @$hashes[$file] !== $hash;
+ }
+
+ /**
+ * @param $file
+ * @param $hash
+ */
+ public static function saveHash( $file, $hash ) {
+ if ( is_null( self::$newHashes ) ) {
+ self::$newHashes = self::readFile( 'hashes' );
+ }
+
+ self::$newHashes[$file] = $hash;
+ }
+
+ public static function writeHashes() {
+ self::writeFile( 'hashes', self::$newHashes );
+ }
+
+ /**
+ * Logs a message.
+ *
+ * @param $log String
+ * @param bool $verbose
+ */
+ public static function myLog( $log, $verbose = true ) {
+ if ( !$verbose ) {
+ return;
+ }
+ if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
+ wfDebug( $log . "\n" );
+ } else {
+ print( $log . "\n" );
+ }
+ }
+
+ /**
+ * @param $php
+ * @param $varname
+ * @return bool|array
+ */
+ public static function parsePHP( $php, $varname ) {
+ try {
+ $reader = new QuickArrayReader("<?php $php");
+ return $reader->getVar( $varname );
+ } catch( Exception $e ) {
+ self::myLog( "Failed to read file: " . $e );
+ return false;
+ }
+ }
+
+ /**
+ * @param $lang
+ * @return string
+ * @throws MWException
+ */
+ public static function filename( $lang ) {
+ global $wgLocalisationUpdateDirectory, $wgCacheDirectory;
+
+ $dir = $wgLocalisationUpdateDirectory ?
+ $wgLocalisationUpdateDirectory :
+ $wgCacheDirectory;
+
+ if ( !$dir ) {
+ throw new MWException( 'No cache directory configured' );
+ }
+
+ return "$dir/l10nupdate-$lang.cache";
+ }
+
+ /**
+ * @param $lang
+ * @return mixed
+ */
+ public static function readFile( $lang ) {
+ if ( !isset( self::$filecache[$lang] ) ) {
+ $file = self::filename( $lang );
+ $contents = @file_get_contents( $file );
+
+ if ( $contents === false ) {
+ wfDebug( "Failed to read file '$file'\n" );
+ $retval = array();
+ } else {
+ $retval = unserialize( $contents );
+
+ if ( $retval === false ) {
+ wfDebug( "Corrupted data in file '$file'\n" );
+ $retval = array();
+ }
+ }
+ self::$filecache[$lang] = $retval;
+ }
+
+ return self::$filecache[$lang];
+ }
+
+ /**
+ * @param $lang
+ * @param $var
+ * @throws MWException
+ */
+ public static function writeFile( $lang, $var ) {
+ $file = self::filename( $lang );
+
+ if ( !@file_put_contents( $file, serialize( $var ) ) ) {
+ throw new MWException( "Failed to write to file '$file'" );
+ }
+
+ self::$filecache[$lang] = $var;
+ }
+
+}
diff --git a/extensions/LocalisationUpdate/LocalisationUpdate.i18n.php b/extensions/LocalisationUpdate/LocalisationUpdate.i18n.php
new file mode 100644
index 00000000..5d52609f
--- /dev/null
+++ b/extensions/LocalisationUpdate/LocalisationUpdate.i18n.php
@@ -0,0 +1,528 @@
+<?php
+/**
+ * Internationalisation file for LocalisationUpdate extension.
+ *
+ * @file
+ * @ingroup Extensions
+ */
+
+$messages = array();
+
+/** English
+ * @author Tom Maaswinkel
+ */
+$messages['en'] = array(
+ 'localisationupdate-desc' => 'Keeps the localised messages as up to date as possible',
+);
+
+/** Message documentation (Message documentation)
+ * @author Fryed-peach
+ * @author Purodha
+ * @author Shirayuki
+ */
+$messages['qqq'] = array(
+ 'localisationupdate-desc' => '{{desc|name=Localisation Update|url=http://www.mediawiki.org/wiki/Extension:LocalisationUpdate}}',
+);
+
+/** Afrikaans (Afrikaans)
+ * @author Naudefj
+ */
+$messages['af'] = array(
+ 'localisationupdate-desc' => 'Hou die gelokaliseerde boodskappe so op datum as moontlik',
+);
+
+/** Arabic (العربية)
+ * @author Meno25
+ */
+$messages['ar'] = array(
+ 'localisationupdate-desc' => 'يبقي الرسائل المترجمة محدثة كأفضل ما يكون',
+);
+
+/** Asturian (asturianu)
+ * @author Xuacu
+ */
+$messages['ast'] = array(
+ 'localisationupdate-desc' => 'Caltién los mensaxes llocalizaos tan anovaos como se pueda',
+);
+
+/** Bashkir (башҡортса)
+ * @author Assele
+ */
+$messages['ba'] = array(
+ 'localisationupdate-desc' => 'Локалләштерелгән хәбәрҙәрҙең мөмкин тиклем яңы булыуын тәьмин итә',
+);
+
+/** Bavarian (Boarisch)
+ * @author Man77
+ */
+$messages['bar'] = array(
+ 'localisationupdate-desc' => "Lokalisiade Texte und Nåchrichtn so aktuell håidn wia's gråd gehd",
+);
+
+/** Belarusian (Taraškievica orthography) (беларуская (тарашкевіца)‎)
+ * @author EugeneZelenko
+ * @author Wizardist
+ */
+$messages['be-tarask'] = array(
+ 'localisationupdate-desc' => 'Сочыць за актуальнасьцю лякалізаваных паведамленьняў, наколькі гэта магчыма',
+);
+
+/** Bulgarian (български)
+ * @author DCLXVI
+ */
+$messages['bg'] = array(
+ 'localisationupdate-desc' => 'Поддържа локализираните съобщения възможно най-актуални',
+);
+
+/** Bengali (বাংলা)
+ * @author Bellayet
+ */
+$messages['bn'] = array(
+ 'localisationupdate-desc' => 'স্থানীয়করণকৃত বার্তাসমূহ যথাসম্ভব হালনাগাদ রাখে',
+);
+
+/** Breton (brezhoneg)
+ * @author Fulup
+ */
+$messages['br'] = array(
+ 'localisationupdate-desc' => "Derc'hel da hizivaat ar c'hemennoù troet ken fonnus ha ma'z eus tu",
+);
+
+/** Bosnian (bosanski)
+ * @author CERminator
+ */
+$messages['bs'] = array(
+ 'localisationupdate-desc' => 'Zadržavanje lokaliziranih poruka ažurnim koliko je god moguće',
+);
+
+/** Catalan (català)
+ * @author Paucabot
+ */
+$messages['ca'] = array(
+ 'localisationupdate-desc' => 'Manté els missatges localitzats tan actualitzats com sigui possible',
+);
+
+/** Czech (česky)
+ * @author Mormegil
+ */
+$messages['cs'] = array(
+ 'localisationupdate-desc' => 'Udržuje lokalizovaná hlášení co možná nejaktuálnější',
+);
+
+/** Welsh (Cymraeg)
+ * @author Lloffiwr
+ */
+$messages['cy'] = array(
+ 'localisationupdate-desc' => "Yn diweddaru'r cyfieithiadau o negeseuon mor aml â phosib",
+);
+
+/** Danish (dansk)
+ * @author Peter Alberti
+ */
+$messages['da'] = array(
+ 'localisationupdate-desc' => 'Holder de lokaliserede meddelelser så opdaterede som muligt',
+);
+
+/** German (Deutsch)
+ * @author Kghbln
+ * @author Purodha
+ */
+$messages['de'] = array(
+ 'localisationupdate-desc' => 'Ermöglicht es lokalisierte Texte und Nachrichten so aktuell wie möglich zu halten',
+);
+
+/** Lower Sorbian (dolnoserbski)
+ * @author Michawiki
+ */
+$messages['dsb'] = array(
+ 'localisationupdate-desc' => 'Źaržy lokalizěrowane powěźeńki tak aktualne ako móžno',
+);
+
+/** Greek (Ελληνικά)
+ * @author Omnipaedista
+ */
+$messages['el'] = array(
+ 'localisationupdate-desc' => 'Διατηρεί τις μεταφράσεις μηνυμάτων όσο πιο ενημερωμένες γίνεται',
+);
+
+/** Esperanto (Esperanto)
+ * @author Yekrats
+ */
+$messages['eo'] = array(
+ 'localisationupdate-desc' => 'Ĝisdatigas la asimilitajn mesaĝojn tiom eble',
+);
+
+/** Spanish (español)
+ * @author Crazymadlover
+ */
+$messages['es'] = array(
+ 'localisationupdate-desc' => 'Mantiene los mensajes localizados tan actualizados como sea posible',
+);
+
+/** Estonian (eesti)
+ * @author Pikne
+ */
+$messages['et'] = array(
+ 'localisationupdate-desc' => 'Hoiab lokaliseeritud sõnumid nii ajakohased kui võimalik.',
+);
+
+/** Basque (euskara)
+ * @author Kobazulo
+ */
+$messages['eu'] = array(
+ 'localisationupdate-desc' => 'Itzulitako mezuak ahalik eta eguneratuen mantentzen ditu',
+);
+
+/** Persian (فارسی)
+ * @author ZxxZxxZ
+ */
+$messages['fa'] = array(
+ 'localisationupdate-desc' => 'پیغام‌های محلی‌سازی‌شده را تا جای ممکن به‌روز نگه می‌دارد',
+);
+
+/** Finnish (suomi)
+ * @author Crt
+ * @author Nike
+ */
+$messages['fi'] = array(
+ 'localisationupdate-desc' => 'Pitää ohjelmiston käännöksen ajantasaisena.',
+);
+
+/** French (français)
+ * @author Crochet.david
+ */
+$messages['fr'] = array(
+ 'localisationupdate-desc' => 'Maintenir la traduction des messages à jour autant que possible',
+);
+
+/** Galician (galego)
+ * @author Toliño
+ */
+$messages['gl'] = array(
+ 'localisationupdate-desc' => 'Mantén as mensaxes localizadas tan actualizadas como é posible',
+);
+
+/** Swiss German (Alemannisch)
+ * @author Als-Holder
+ */
+$messages['gsw'] = array(
+ 'localisationupdate-desc' => 'Halt d Syschtemnochrichte so aktuälle wie megli',
+);
+
+/** Hebrew (עברית)
+ * @author YaronSh
+ */
+$messages['he'] = array(
+ 'localisationupdate-desc' => 'שמירת ההודעות המתורגמות מעודכנות ככל הניתן',
+);
+
+/** Hiligaynon (Ilonggo)
+ * @author Tagimata
+ */
+$messages['hil'] = array(
+ 'localisationupdate-desc' => 'Gatugo sang mga mensahe nga lokal para mapahibalo sang madali',
+);
+
+/** Croatian (hrvatski)
+ * @author SpeedyGonsales
+ */
+$messages['hr'] = array(
+ 'localisationupdate-desc' => 'Dogradnja za osvježavanje lokalizacije poruka MediaWikija',
+);
+
+/** Upper Sorbian (hornjoserbsce)
+ * @author Michawiki
+ */
+$messages['hsb'] = array(
+ 'localisationupdate-desc' => 'Dźerži lokalizowane zdźělenki tak aktualne kaž móžno',
+);
+
+/** Hungarian (magyar)
+ * @author Glanthor Reviol
+ */
+$messages['hu'] = array(
+ 'localisationupdate-desc' => 'Frissíti a lefordított üzeneteket',
+);
+
+/** Interlingua (interlingua)
+ * @author McDutchie
+ */
+$messages['ia'] = array(
+ 'localisationupdate-desc' => 'Mantene le messages localisate tanto actual como possibile',
+);
+
+/** Indonesian (Bahasa Indonesia)
+ * @author Bennylin
+ */
+$messages['id'] = array(
+ 'localisationupdate-desc' => 'Mengusahakan agar pesan-pesan yang telah diterjemahkan tetap semutakhir mungkin',
+);
+
+/** Iloko (Ilokano)
+ * @author Lam-ang
+ */
+$messages['ilo'] = array(
+ 'localisationupdate-desc' => 'Taginayonenna a mapabaro dagiti naipatarus a mensahe',
+);
+
+/** Italian (italiano)
+ * @author Darth Kule
+ */
+$messages['it'] = array(
+ 'localisationupdate-desc' => 'Mantiene i messaggi localizzati quanto più aggiornati è possibile',
+);
+
+/** Japanese (日本語)
+ * @author Fryed-peach
+ * @author Shirayuki
+ */
+$messages['ja'] = array(
+ 'localisationupdate-desc' => 'メッセージの翻訳をできるだけ最新に保つ',
+);
+
+/** Khmer (ភាសាខ្មែរ)
+ * @author វ័ណថារិទ្ធ
+ */
+$messages['km'] = array(
+ 'localisationupdate-desc' => 'រក្សា​សារ​ដែលបាន​ប្រែសម្រួល​ទាំងឡាយ អោយនៅ​ទាន់សម័យ​តាមដែលអាចធ្វើទៅបាន​',
+);
+
+/** Korean (한국어)
+ * @author Kwj2772
+ */
+$messages['ko'] = array(
+ 'localisationupdate-desc' => '번역된 시스템 메시지를 가능한 한 최신으로 유지',
+);
+
+/** Colognian (Ripoarisch)
+ * @author Purodha
+ */
+$messages['ksh'] = array(
+ 'localisationupdate-desc' => 'Texte un Nohreeschte vum Wiki esu joot wi müjjelich om neueste Shtand halde',
+);
+
+/** Luxembourgish (Lëtzebuergesch)
+ * @author Robby
+ */
+$messages['lb'] = array(
+ 'localisationupdate-desc' => 'hält déi lokaliséiert Messagen esou aktuell wéi méiglech.',
+);
+
+/** Macedonian (македонски)
+ * @author Bjankuloski06
+ */
+$messages['mk'] = array(
+ 'localisationupdate-desc' => 'Ги одржува локализираните пораки колку што е можно пообновени и повеќе во тек со настаните',
+);
+
+/** Malayalam (മലയാളം)
+ * @author Praveenp
+ */
+$messages['ml'] = array(
+ 'localisationupdate-desc' => 'പ്രാദേശികഭാഷയിലാക്കിയ സന്ദേശങ്ങൾ കഴിയുന്നത്ര വേഗം ചേർക്കാൻ ഉപയോഗിക്കുന്നു',
+);
+
+/** Malay (Bahasa Melayu)
+ * @author Anakmalaysia
+ */
+$messages['ms'] = array(
+ 'localisationupdate-desc' => 'Memastikan kekemaskinian mesej-mesej yang disetempatkan',
+);
+
+/** Norwegian Bokmål (norsk (bokmål)‎)
+ * @author Nghtwlkr
+ */
+$messages['nb'] = array(
+ 'localisationupdate-desc' => 'Holder de lokaliserte meldingene så oppdaterte som mulig',
+);
+
+/** Dutch (Nederlands)
+ * @author Siebrand
+ */
+$messages['nl'] = array(
+ 'localisationupdate-desc' => 'Houdt de gelokaliseerde berichten zo actueel mogelijk',
+);
+
+/** Norwegian Nynorsk (norsk (nynorsk)‎)
+ * @author Gunnernett
+ */
+$messages['nn'] = array(
+ 'localisationupdate-desc' => 'Held dei lokaliserte meldingane så oppdaterte som mogleg',
+);
+
+/** Occitan (occitan)
+ * @author Cedric31
+ */
+$messages['oc'] = array(
+ 'localisationupdate-desc' => 'Manténer la traduccion dels messatges a jorn autant que possible',
+);
+
+/** Polish (polski)
+ * @author Sp5uhe
+ */
+$messages['pl'] = array(
+ 'localisationupdate-desc' => 'Uaktualnia lokalne komunikaty w miarę możliwości na bieżąco',
+);
+
+/** Piedmontese (Piemontèis)
+ * @author Dragonòt
+ */
+$messages['pms'] = array(
+ 'localisationupdate-desc' => 'A manten i messagi localisà ël pì agiornà possìbil',
+);
+
+/** Portuguese (português)
+ * @author Hamilton Abreu
+ * @author Malafaya
+ */
+$messages['pt'] = array(
+ 'localisationupdate-desc' => 'Mantém as mensagens localizadas tão actualizadas quanto possível',
+);
+
+/** Brazilian Portuguese (português do Brasil)
+ * @author Eduardo.mps
+ */
+$messages['pt-br'] = array(
+ 'localisationupdate-desc' => 'Mantém as mensagens localizadas tão atualizadas quanto possível',
+);
+
+/** Romanian (română)
+ * @author KlaudiuMihaila
+ */
+$messages['ro'] = array(
+ 'localisationupdate-desc' => 'Menține mesajele localizate cât mai actualizate',
+);
+
+/** tarandíne (tarandíne)
+ * @author Joetaras
+ */
+$messages['roa-tara'] = array(
+ 'localisationupdate-desc' => "Mandine le messagge localizzate 'u cchiù aggiornate possibbile",
+);
+
+/** Russian (русский)
+ * @author Александр Сигачёв
+ */
+$messages['ru'] = array(
+ 'localisationupdate-desc' => 'Поддерживает актуальность локализованных сообщений, насколько это возможно',
+);
+
+/** Slovak (slovenčina)
+ * @author Helix84
+ */
+$messages['sk'] = array(
+ 'localisationupdate-desc' => 'Udržiava lokalizované správy čo najaktuálnejšie',
+);
+
+/** Serbian (Cyrillic script) (српски (ћирилица)‎)
+ * @author Михајло Анђелковић
+ */
+$messages['sr-ec'] = array(
+ 'localisationupdate-desc' => 'Ажурира локализоване поруке колико је то могуће',
+);
+
+/** Serbian (Latin script) (srpski (latinica)‎)
+ * @author Liangent
+ */
+$messages['sr-el'] = array(
+ 'localisationupdate-desc' => 'Ažurira lokalizovane poruke koliko je to moguće',
+);
+
+/** Sundanese (Basa Sunda)
+ * @author Kandar
+ */
+$messages['su'] = array(
+ 'localisationupdate-desc' => 'Ngajaga sangkan talatah-talatah nu geus dialihbasakeun salawasnya mutahir',
+);
+
+/** Swedish (svenska)
+ * @author Boivie
+ */
+$messages['sv'] = array(
+ 'localisationupdate-desc' => 'Håller de lokaliserade meddelandena så uppdaterade som möjligt',
+);
+
+/** Tamil (தமிழ்)
+ * @author செல்வா
+ */
+$messages['ta'] = array(
+ 'localisationupdate-desc' => 'உட்சூழலுக்கான செய்திகளை கூடியமட்டிலும் இன்றையநிலையில் வைக்கப்பட்டுள்ளன',
+);
+
+/** Telugu (తెలుగు)
+ * @author Veeven
+ */
+$messages['te'] = array(
+ 'localisationupdate-desc' => 'స్ధానికీకరించిన సందేశాలను సాధ్యమైనంత తాజాగా ఉంచుతుంది',
+);
+
+/** Tagalog (Tagalog)
+ * @author AnakngAraw
+ */
+$messages['tl'] = array(
+ 'localisationupdate-desc' => 'Pinananatili ang mga mensaheng lokalisado bilang pinaka nasasapanahon',
+);
+
+/** Turkish (Türkçe)
+ * @author Joseph
+ */
+$messages['tr'] = array(
+ 'localisationupdate-desc' => 'Yerelleştirilen mesajları mümkün olabildiğince güncel tutar',
+);
+
+/** Ukrainian (українська)
+ * @author Prima klasy4na
+ */
+$messages['uk'] = array(
+ 'localisationupdate-desc' => 'Забезпечує оновлення локалізованих повідомлень у міру можливості',
+);
+
+/** Veps (vepsän kel’)
+ * @author Игорь Бродский
+ */
+$messages['vep'] = array(
+ 'localisationupdate-desc' => 'Pidab lokaliziruidud tedotused veresin, ku voib',
+);
+
+/** Vietnamese (Tiếng Việt)
+ * @author Vinhtantran
+ */
+$messages['vi'] = array(
+ 'localisationupdate-desc' => 'Giữ các thông điệp bản địa hóa được cập nhật nhất có thể',
+);
+
+/** Walloon (walon)
+ * @author Srtxg
+ */
+$messages['wa'] = array(
+ 'localisationupdate-desc' => "Po wårder les ratournaedjes di l' eterface li pus a djoû possibe",
+);
+
+/** Yiddish (ייִדיש)
+ * @author פוילישער
+ */
+$messages['yi'] = array(
+ 'localisationupdate-desc' => 'האלטן די לאקאליזירטע מעלדונגען אקטועל ווי נאר מעגלעך',
+);
+
+/** Cantonese (粵語)
+ * @author Tom Maaswinkel
+ */
+$messages['yue'] = array(
+ 'localisationupdate-desc' => '將本地化嘅信息保持最新',
+);
+
+/** Simplified Chinese (中文(简体)‎)
+ * @author Tom Maaswinkel
+ */
+$messages['zh-hans'] = array(
+ 'localisationupdate-desc' => '将本地化的信息保持最新',
+);
+
+/** Traditional Chinese (中文(繁體)‎)
+ * @author Mark85296341
+ * @author Tom Maaswinkel
+ */
+$messages['zh-hant'] = array(
+ 'localisationupdate-desc' => '將本地化的資訊盡可能保持最新',
+);
diff --git a/extensions/LocalisationUpdate/LocalisationUpdate.php b/extensions/LocalisationUpdate/LocalisationUpdate.php
new file mode 100644
index 00000000..89f0659d
--- /dev/null
+++ b/extensions/LocalisationUpdate/LocalisationUpdate.php
@@ -0,0 +1,42 @@
+<?php
+
+
+/**
+ * Directory to store serialized cache files in. Defaults to $wgCacheDirectory.
+ * It's OK to share this directory among wikis as long as the wiki you run
+ * update.php on has all extensions the other wikis using the same directory
+ * have.
+ * NOTE: If this variable and $wgCacheDirectory are both false, this extension
+ * WILL NOT WORK.
+ */
+$wgLocalisationUpdateDirectory = false;
+
+
+/**
+ * These should point to either an HTTP-accessible file or local file system.
+ * $1 is the name of the repo (for extensions) and $2 is the name of file in the repo.
+ */
+$wgLocalisationUpdateCoreURL = "https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=blob_plain;f=$2;hb=HEAD";
+$wgLocalisationUpdateExtensionURL = "https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/extensions/$1.git;a=blob_plain;f=$2;hb=HEAD";
+
+/// Deprecated
+$wgLocalisationUpdateSVNURL = false;
+
+$wgLocalisationUpdateRetryAttempts = 5;
+
+// Info about me!
+$wgExtensionCredits['other'][] = array(
+ 'path' => __FILE__,
+ 'name' => 'LocalisationUpdate',
+ 'author' => array( 'Tom Maaswinkel', 'Niklas Laxström', 'Roan Kattouw' ),
+ 'version' => '1.0',
+ 'url' => 'https://www.mediawiki.org/wiki/Extension:LocalisationUpdate',
+ 'descriptionmsg' => 'localisationupdate-desc',
+);
+
+$wgHooks['LocalisationCacheRecache'][] = 'LocalisationUpdate::onRecache';
+
+$dir = __DIR__ . '/';
+$wgExtensionMessagesFiles['LocalisationUpdate'] = $dir . 'LocalisationUpdate.i18n.php';
+$wgAutoloadClasses['LocalisationUpdate'] = $dir . 'LocalisationUpdate.class.php';
+$wgAutoloadClasses['QuickArrayReader'] = $dir . 'QuickArrayReader.php';
diff --git a/extensions/LocalisationUpdate/QuickArrayReader.php b/extensions/LocalisationUpdate/QuickArrayReader.php
new file mode 100644
index 00000000..214d5a61
--- /dev/null
+++ b/extensions/LocalisationUpdate/QuickArrayReader.php
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * Quickie parser class that can happily read the subset of PHP we need
+ * for our localization arrays safely.
+ *
+ * About an order of magnitude faster than ConfEditor(), but still an
+ * order of magnitude slower than eval().
+ */
+class QuickArrayReader {
+ var $vars = array();
+
+ /**
+ * @param $string string
+ */
+ function __construct( $string ) {
+ $scalarTypes = array(
+ T_LNUMBER => true,
+ T_DNUMBER => true,
+ T_STRING => true,
+ T_CONSTANT_ENCAPSED_STRING => true,
+ );
+ $skipTypes = array(
+ T_WHITESPACE => true,
+ T_COMMENT => true,
+ T_DOC_COMMENT => true,
+ );
+ $tokens = token_get_all( $string );
+ $count = count( $tokens );
+ for( $i = 0; $i < $count; ) {
+ while( isset($skipTypes[$tokens[$i][0]] ) ) {
+ $i++;
+ }
+ switch( $tokens[$i][0] ) {
+ case T_OPEN_TAG:
+ $i++;
+ continue;
+ case T_VARIABLE:
+ // '$messages' -> 'messages'
+ $varname = trim( substr( $tokens[$i][1], 1 ) );
+ $varindex = null;
+
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( $tokens[$i] === '[' ) {
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( isset($scalarTypes[$tokens[$i][0]] ) ) {
+ $varindex = $this->parseScalar( $tokens[$i] );
+ } else {
+ throw $this->except( $tokens[$i], 'scalar index' );
+ }
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( $tokens[$i] !== ']' ) {
+ throw $this->except( $tokens[$i], ']' );
+ }
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+ }
+
+ if( $tokens[$i] !== '=' ) {
+ throw $this->except( $tokens[$i], '=' );
+ }
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( isset($scalarTypes[$tokens[$i][0]] ) ) {
+ $buildval = $this->parseScalar( $tokens[$i] );
+ } elseif( $tokens[$i][0] === T_ARRAY ) {
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+ if( $tokens[$i] !== '(' ) {
+ throw $this->except( $tokens[$i], '(' );
+ }
+ $buildval = array();
+ do {
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( $tokens[$i] === ')' ) {
+ break;
+ }
+ if( isset($scalarTypes[$tokens[$i][0]] ) ) {
+ $key = $this->parseScalar( $tokens[$i] );
+ }
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( $tokens[$i][0] !== T_DOUBLE_ARROW ) {
+ throw $this->except( $tokens[$i], '=>' );
+ }
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( isset($scalarTypes[$tokens[$i][0]] ) ) {
+ $val = $this->parseScalar( $tokens[$i] );
+ }
+ @$buildval[$key] = $val;
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+
+ if( $tokens[$i] === ',' ) {
+ continue;
+ } elseif( $tokens[$i] === ')' ) {
+ break;
+ } else {
+ throw $this->except( $tokens[$i], ', or )' );
+ }
+ } while(true);
+ } else {
+ throw $this->except( $tokens[$i], 'scalar or array' );
+ }
+ if( is_null( $varindex ) ) {
+ $this->vars[$varname] = $buildval;
+ } else {
+ @$this->vars[$varname][$varindex] = $buildval;
+ }
+ while( isset($skipTypes[$tokens[++$i][0]] ) );
+ if( $tokens[$i] !== ';' ) {
+ throw $this->except($tokens[$i], ';');
+ }
+ $i++;
+ break;
+ default:
+ throw $this->except($tokens[$i], 'open tag, whitespace, or variable.');
+ }
+ }
+ }
+
+ /**
+ * @param $got string
+ * @param $expected string
+ * @return Exception
+ */
+ private function except( $got, $expected ) {
+ if( is_array( $got ) ) {
+ $got = token_name( $got[0] ) . " ('" . $got[1] . "')";
+ } else {
+ $got = "'" . $got . "'";
+ }
+ return new Exception( "Expected $expected, got $got" );
+ }
+
+ /**
+ * Parse a scalar value in PHP
+ *
+ * @param $token string
+ *
+ * @return mixed Parsed value
+ */
+ function parseScalar( $token ) {
+ if( is_array( $token ) ) {
+ $str = $token[1];
+ } else {
+ $str = $token;
+ }
+ if ( $str !== '' && $str[0] == '\'' )
+ // Single-quoted string
+ // @fixme trim() call is due to mystery bug where whitespace gets
+ // appended to the token; without it we ended up reading in the
+ // extra quote on the end!
+ return strtr( substr( trim( $str ), 1, -1 ),
+ array( '\\\'' => '\'', '\\\\' => '\\' ) );
+ if ( $str !== '' && @$str[0] == '"' )
+ // Double-quoted string
+ // @fixme trim() call is due to mystery bug where whitespace gets
+ // appended to the token; without it we ended up reading in the
+ // extra quote on the end!
+ return stripcslashes( substr( trim( $str ), 1, -1 ) );
+ if ( substr( $str, 0, 4 ) === 'true' )
+ return true;
+ if ( substr( $str, 0, 5 ) === 'false' )
+ return false;
+ if ( substr( $str, 0, 4 ) === 'null' )
+ return null;
+ // Must be some kind of numeric value, so let PHP's weak typing
+ // be useful for a change
+ return $str;
+ }
+
+ /**
+ * @param $varname string
+ * @return null|string
+ */
+ function getVar( $varname ) {
+ if( isset( $this->vars[$varname] ) ) {
+ return $this->vars[$varname];
+ } else {
+ return null;
+ }
+ }
+}
+
diff --git a/extensions/LocalisationUpdate/README_FIRST.txt b/extensions/LocalisationUpdate/README_FIRST.txt
new file mode 100644
index 00000000..3973c435
--- /dev/null
+++ b/extensions/LocalisationUpdate/README_FIRST.txt
@@ -0,0 +1,8 @@
+To install this extension first include
+LocalisationUpdate/LocalisationUpdate.php in your LocalSettings.php
+
+Then add the required new tables to your database by running
+php maintenance/update.php on the command line.
+
+Whenever you want to run an update, run
+php extensions/LocalisationUpdate/update.php on the command line.
diff --git a/extensions/LocalisationUpdate/update.php b/extensions/LocalisationUpdate/update.php
new file mode 100644
index 00000000..750fc4f2
--- /dev/null
+++ b/extensions/LocalisationUpdate/update.php
@@ -0,0 +1,38 @@
+<?php
+
+$IP = strval( getenv( 'MW_INSTALL_PATH' ) ) !== ''
+ ? getenv( 'MW_INSTALL_PATH' )
+ : realpath( dirname( __FILE__ ) . "/../../" );
+
+// TODO: migrate to maintenance class
+require_once( "$IP/maintenance/commandLine.inc" );
+
+if( isset( $options['help'] ) ) {
+ print "Fetches updated localisation files from MediaWiki development SVN\n";
+ print "and saves into local database to merge with release defaults.\n";
+ print "\n";
+ print "Usage: php extensions/LocalisationUpdate/update.php\n";
+ print "Options:\n";
+ print " --quiet Suppress progress output\n";
+ print " --skip-core Don't fetch MediaWiki core files\n";
+ print " --skip-extensions Don't fetch any extension files\n";
+ print " --all Fetch all present extensions, not just those enabled\n";
+ print " --outdir=<dir> Override output directory for serialized update files\n";
+ print " --svnurl=<url> URL to SVN repository, or path to local SVN checkout. Deprecated.\n";
+ print "\n";
+ exit( 0 );
+}
+
+
+$starttime = microtime( true );
+
+// Prevent the script from timing out
+set_time_limit( 0 );
+ini_set( "max_execution_time", 0 );
+ini_set( 'memory_limit', -1 );
+
+LocalisationUpdate::updateMessages( $options );
+
+$endtime = microtime( true );
+$totaltime = ( $endtime - $starttime );
+print "All done in " . $totaltime . " seconds\n";