Merge "Add convenience maintenance script wrapper"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 8 Nov 2013 13:09:42 +0000 (13:09 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 8 Nov 2013 13:09:42 +0000 (13:09 +0000)
159 files changed:
CREDITS
RELEASE-NOTES-1.22
RELEASE-NOTES-1.23
docs/hooks.txt
docs/memcached.txt
includes/Article.php
includes/AutoLoader.php
includes/DefaultSettings.php
includes/EditPage.php
includes/Exception.php
includes/FormOptions.php
includes/GlobalFunctions.php
includes/ImagePage.php
includes/OutputPage.php
includes/Timestamp.php
includes/Title.php
includes/WebRequest.php
includes/Wiki.php
includes/WikiPage.php
includes/api/ApiQueryImageInfo.php [changed mode: 0755->0644]
includes/api/ApiQueryRecentChanges.php
includes/changes/ChangesList.php
includes/changes/RecentChange.php
includes/clientpool/RedisConnectionPool.php
includes/content/ContentHandler.php
includes/db/Database.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseOracle.php
includes/db/DatabaseSqlite.php
includes/filebackend/FileBackendStore.php
includes/filebackend/FileOp.php
includes/filebackend/SwiftFileBackend.php
includes/filerepo/ForeignAPIRepo.php [changed mode: 0755->0644]
includes/filerepo/file/ForeignAPIFile.php [changed mode: 0755->0644]
includes/installer/DatabaseInstaller.php
includes/installer/DatabaseUpdater.php
includes/installer/Installer.php
includes/installer/MysqlUpdater.php
includes/installer/WebInstaller.php
includes/installer/WebInstallerOutput.php
includes/installer/WebInstallerPage.php
includes/job/JobQueueFederated.php
includes/job/JobQueueRedis.php
includes/logging/LogEntry.php
includes/media/FormatMetadata.php [changed mode: 0755->0644]
includes/media/MediaHandler.php [changed mode: 0755->0644]
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/specials/SpecialConfirmemail.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialSearch.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialWatchlist.php
includes/utils/ArrayUtils.php
includes/utils/Cdb.php
includes/utils/CdbPHP.php [new file with mode: 0644]
includes/utils/Cdb_PHP.php [deleted file]
includes/utils/ConfEditor.php
includes/utils/HashRing.php
includes/utils/IP.php
includes/utils/MWCryptRand.php
includes/utils/MWFunction.php
includes/utils/MappedIterator.php
includes/utils/StringUtils.php
includes/utils/UIDGenerator.php
includes/utils/ZipDirectoryReader.php
languages/Language.php
languages/LanguageConverter.php
languages/Names.php
languages/messages/MessagesAce.php
languages/messages/MessagesBcl.php
languages/messages/MessagesBg.php
languages/messages/MessagesBn.php
languages/messages/MessagesBs.php
languages/messages/MessagesCa.php
languages/messages/MessagesCdo.php
languages/messages/MessagesCe.php
languages/messages/MessagesCs.php
languages/messages/MessagesCy.php
languages/messages/MessagesDe.php
languages/messages/MessagesEl.php
languages/messages/MessagesEn.php
languages/messages/MessagesEt.php
languages/messages/MessagesEu.php
languages/messages/MessagesFa.php
languages/messages/MessagesFi.php
languages/messages/MessagesFo.php
languages/messages/MessagesFr.php
languages/messages/MessagesGan_hans.php
languages/messages/MessagesGd.php
languages/messages/MessagesGom_latn.php [new file with mode: 0644]
languages/messages/MessagesHak.php
languages/messages/MessagesHe.php
languages/messages/MessagesIs.php
languages/messages/MessagesIt.php
languages/messages/MessagesJa.php
languages/messages/MessagesKo.php
languages/messages/MessagesLa.php
languages/messages/MessagesMhr.php
languages/messages/MessagesMk.php
languages/messages/MessagesMn.php
languages/messages/MessagesPms.php
languages/messages/MessagesPt.php
languages/messages/MessagesQqq.php
languages/messages/MessagesRu.php
languages/messages/MessagesSl.php
languages/messages/MessagesSr_ec.php
languages/messages/MessagesSr_el.php
languages/messages/MessagesSv.php
languages/messages/MessagesTe.php
languages/messages/MessagesTr.php
languages/messages/MessagesUk.php
languages/messages/MessagesVi.php
languages/messages/MessagesWuu.php
languages/messages/MessagesYi.php
languages/messages/MessagesZh_hans.php
languages/messages/MessagesZh_hant.php
maintenance/cleanupUploadStash.php
maintenance/jsduck/config.json
maintenance/jsduck/external.js
maintenance/update.php
resources/Resources.php
resources/jquery/jquery.spinner.js
resources/mediawiki.page/mediawiki.page.watch.ajax.js
resources/mediawiki/mediawiki.Title.js
resources/mediawiki/mediawiki.jqueryMsg.js
resources/mediawiki/mediawiki.js
resources/mediawiki/mediawiki.util.js
skins/common/protect.js
skins/common/upload.js
skins/common/wikibits.js
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/CdbTest.php [deleted file]
tests/phpunit/includes/ExceptionTest.php [new file with mode: 0644]
tests/phpunit/includes/FormOptionsTest.php
tests/phpunit/includes/GlobalFunctions/wfExpandUrlTest.php
tests/phpunit/includes/HashRingTest.php [deleted file]
tests/phpunit/includes/IPTest.php [deleted file]
tests/phpunit/includes/StringUtilsTest.php [deleted file]
tests/phpunit/includes/TemplateCategoriesTest.php
tests/phpunit/includes/UIDGeneratorTest.php [deleted file]
tests/phpunit/includes/ZipDirectoryReaderTest.php [deleted file]
tests/phpunit/includes/api/format/ApiFormatWddxTest.php
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/logging/LogFormatterTest.php [changed mode: 0755->0644]
tests/phpunit/includes/logging/LogTests.i18n.php [changed mode: 0755->0644]
tests/phpunit/includes/utils/CdbTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/HashRingTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/IPTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/StringUtilsTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/UIDGeneratorTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/ZipDirectoryReaderTest.php [new file with mode: 0644]
tests/phpunit/structure/AutoLoaderTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

diff --git a/CREDITS b/CREDITS
index 23636ae..01505b0 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -22,6 +22,7 @@ following names for their contribution to the product.
 * church of emacs
 * Daniel Friesen
 * Daniel Kinzler
+* Daniel Renfro
 * Danny B.
 * David McCabe
 * Derk-Jan Hartman
index 25d5c42..1c364ff 100644 (file)
@@ -260,6 +260,9 @@ production.
   Previously in the "SimpleAntiSpam" extension by Ryan Schmidt.
 * populateRevisionLength.php maintenance script updated to also populate
   archive.ar_len field.
+* (bug 43571) DatabaseMySQLBase learned to list views, optionally filtered by a
+  prefix. Also fixed PHPUnit test suite when using a MySQL backend containing
+  views.
 
 === Bug fixes in 1.22 ===
 * (bug 47271) $wgContentHandlerUseDB should be set to false during the upgrade
@@ -533,6 +536,7 @@ changes to languages because of Bugzilla reports.
   The file never contained any re-usable components. To use it in a skin, load
   'mediawiki.legacy.wikibits' (which IEFixes depends on) and that will import
   IEFixes automatically if user agent conditions are met.
+* Code specific to the Math extension was marked as deprecated.
 
 == Compatibility ==
 
index 710f26f..04be2a2 100644 (file)
@@ -9,6 +9,15 @@ MediaWiki 1.23 is an alpha-quality branch and is not recommended for use in
 production.
 
 === Configuration changes in 1.23 ===
+* $wgDebugLogGroups values may be set to an associative array with a
+  'destination' key specifying the log destination. The array may also contain
+  a 'sample' key with a positive integer value N indicating that the log group
+  should be sampled by dispatching one in every N messages on average. The
+  sampling is random.
+* In addition to the current exception log format, MediaWiki now serializes
+  exception metadata to JSON and logs it to the 'exception-json' log group.
+  This makes MediaWiki easier to integrate with log aggregation and analysis
+  tools.
 
 === New features in 1.23 ===
 * ResourceLoader can utilize the Web Storage API to cache modules client-side.
@@ -20,12 +29,18 @@ production.
   enabled with care.
 * (bug 6092) Add expensive parser functions {{REVISIONID:}}, {{REVISIONUSER:}}
   and {{REVISIONTIMESTAMP:}} (with friends).
+* Add "wgRelevantUserName" to mw.config containing the current
+  Skin::getRelevantUser value
 
 === Bug fixes in 1.23 ===
 * (bug 41759) The "updated since last visit" markers (on history pages, recent
   changes and watchlist) and the talk page message indicator are now correctly
   updated when the user is viewing old revisions of pages, instead of always
   acting as if the latest revision was being viewed.
+* (bug 56443) Special:ConfirmEmail no longer shows a "Mail a confirmation code"
+  when the email address is already confirmed. Also, consistently use
+  "confirmed", rather than "authenticated", when messaging whether or not the
+  user has confirmed an email address.
 
 === API changes in 1.23 ===
 * (bug 54884) action=parse&prop=categories now indicates hidden and missing
@@ -39,6 +54,8 @@ changes to languages because of Bugzilla reports.
 
 === Other changes in 1.23 ===
 * The global variable $wgArticle has been removed after a lengthy deprecation
+* mediawiki.util: mw.util.wikiGetlink has been renamed to getUrl. (The old name still
+  works, but is deprecated.)
 
 == Compatibility ==
 
index 96a72df..7bd725a 100644 (file)
@@ -1847,7 +1847,7 @@ needs formatting. If nothing handles this hook, the default is to use "$key" to
 get the label, and "$key-value" or "$key-value-text"/"$key-value-html" to
 format the value.
 $key: Key for the limit report item (string)
-$value: Value of the limit report item
+&$value: Value of the limit report item
 &$report: String onto which to append the data
 $isHTML: If true, $report is an HTML table with two columns; if false, it's
        text intended for display in a monospaced font.
@@ -1855,7 +1855,8 @@ $localize: If false, $report should be output in English.
 
 'ParserLimitReportPrepare': Called at the end of Parser:parse() when the parser will
 include comments about size of the text parsed. Hooks should use
-$output->setLimitReportData() to populate data.
+$output->setLimitReportData() to populate data. Functions for this hook should
+not use $wgLang; do that in ParserLimitReportFormat instead.
 $parser: Parser object
 $output: ParserOutput object
 
index f54a4e7..16c5760 100644 (file)
@@ -78,7 +78,7 @@ usage evenly), make its entry a subarray:
 == PHP client for memcached ==
 
 MediaWiki uses a fork of Ryan T. Dean's pure-PHP memcached client.
-The newer PECL module is not yet supported.
+It also supports the PECL PHP extension for memcached.
 
 MediaWiki uses three object for object caching:
 * $wgMemc, controlled by $wgMainCacheType
@@ -91,7 +91,7 @@ database. If the cache daemon can't be contacted, it should also
 disable itself fairly smoothly.
 
 By default, $wgMemc is used but when it is $parserMemc or $messageMemc
-this is mentionned below.
+this is mentioned below.
 
 == Keys used ==
 
index 928fda0..a6afd8e 100644 (file)
@@ -1238,7 +1238,8 @@ class Article implements Page {
                } elseif ( $this->getTitle()->quickUserCan( 'create', $this->getContext()->getUser() )
                        && $this->getTitle()->quickUserCan( 'edit', $this->getContext()->getUser() )
                ) {
-                       $text = wfMessage( 'noarticletext' )->plain();
+                       $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
+                       $text = wfMessage( $message )->plain();
                } else {
                        $text = wfMessage( 'noarticletext-nopermission' )->plain();
                }
index 1417c77..29d2209 100644 (file)
@@ -843,12 +843,14 @@ $wgAutoloadLocalClasses = array(
        'ResourceLoaderNoscriptModule' => 'includes/resourceloader/ResourceLoaderNoscriptModule.php',
        'ResourceLoaderSiteModule' => 'includes/resourceloader/ResourceLoaderSiteModule.php',
        'ResourceLoaderStartUpModule' => 'includes/resourceloader/ResourceLoaderStartUpModule.php',
-       'ResourceLoaderUserCSSPrefsModule' => 'includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php',
+       'ResourceLoaderUserCSSPrefsModule' =>
+               'includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php',
        'ResourceLoaderUserGroupsModule' => 'includes/resourceloader/ResourceLoaderUserGroupsModule.php',
        'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php',
        'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php',
        'ResourceLoaderUserTokensModule' => 'includes/resourceloader/ResourceLoaderUserTokensModule.php',
-       'ResourceLoaderLanguageDataModule' => 'includes/resourceloader/ResourceLoaderLanguageDataModule.php',
+       'ResourceLoaderLanguageDataModule' =>
+               'includes/resourceloader/ResourceLoaderLanguageDataModule.php',
        'ResourceLoaderWikiModule' => 'includes/resourceloader/ResourceLoaderWikiModule.php',
 
        # includes/revisiondelete
@@ -1044,13 +1046,13 @@ $wgAutoloadLocalClasses = array(
 
        # includes/utils
        'ArrayUtils' => 'includes/utils/ArrayUtils.php',
-       'CdbFunctions' => 'includes/utils/Cdb_PHP.php',
+       'CdbFunctions' => 'includes/utils/CdbPHP.php',
        'CdbReader' => 'includes/utils/Cdb.php',
-       'CdbReader_DBA' => 'includes/utils/Cdb.php',
-       'CdbReader_PHP' => 'includes/utils/Cdb_PHP.php',
+       'CdbReaderDBA' => 'includes/utils/Cdb.php',
+       'CdbReaderPHP' => 'includes/utils/CdbPHP.php',
        'CdbWriter' => 'includes/utils/Cdb.php',
-       'CdbWriter_DBA' => 'includes/utils/Cdb.php',
-       'CdbWriter_PHP' => 'includes/utils/Cdb_PHP.php',
+       'CdbWriterDBA' => 'includes/utils/Cdb.php',
+       'CdbWriterPHP' => 'includes/utils/CdbPHP.php',
        'ConfEditor' => 'includes/utils/ConfEditor.php',
        'ConfEditorParseError' => 'includes/utils/ConfEditor.php',
        'ConfEditorToken' => 'includes/utils/ConfEditor.php',
@@ -1140,7 +1142,7 @@ $wgAutoloadLocalClasses = array(
 );
 
 class AutoLoader {
-       static $autoloadLocalClassesLower = null;
+       static protected  $autoloadLocalClassesLower = null;
 
        /**
         * autoload - take a class name and attempt to load it
@@ -1222,6 +1224,14 @@ class AutoLoader {
        static function loadClass( $class ) {
                return class_exists( $class );
        }
+
+       /**
+        * Method to clear the protected class property $autoloadLocalClassesLower.
+        * Used in tests.
+        */
+       static function resetAutoloadLocalClassesLower() {
+               self::$autoloadLocalClassesLower = null;
+       }
 }
 
 spl_autoload_register( array( 'AutoLoader', 'autoload' ) );
index ebae110..92bb05e 100644 (file)
@@ -4895,10 +4895,29 @@ $wgDebugDBTransactions = false;
 $wgDebugDumpSql = false;
 
 /**
- * Set to an array of log group keys to filenames.
+ * Map of string log group names to log destinations.
+ *
  * If set, wfDebugLog() output for that group will go to that file instead
  * of the regular $wgDebugLogFile. Useful for enabling selective logging
  * in production.
+ *
+ * Log destinations may be string values specifying a filename or URI, or they
+ * may be filename or an associative array mapping 'destination' to the desired
+ * filename. The associative array may also contain a 'sample' key with an
+ * integer value, specifying a sampling factor.
+ *
+ * @par Example:
+ * @code
+ * $wgDebugLogGroups['redis'] = '/var/log/mediawiki/redis.log';
+ * @endcode
+ *
+ * @par Advanced example:
+ * @code
+ * $wgDebugLogGroups['memcached'] = (
+ *     'destination' => '/var/log/mediawiki/memcached.log',
+ *     'sample' => 1000,  // log 1 message out of every 1,000.
+ * );
+ * @endcode
  */
 $wgDebugLogGroups = array();
 
index 530e267..68691c5 100644 (file)
@@ -2937,7 +2937,7 @@ HTML
 
                foreach ( $output->getLimitReportData() as $key => $value ) {
                        if ( wfRunHooks( 'ParserLimitReportFormat',
-                               array( $key, $value, &$limitReport, true, true )
+                               array( $key, &$value, &$limitReport, true, true )
                        ) ) {
                                $keyMsg = wfMessage( $key );
                                $valueMsg = wfMessage( array( "$key-value-html", "$key-value" ) );
index ac98564..008be15 100644 (file)
@@ -779,6 +779,21 @@ class MWExceptionHandler {
                return $e->_mwLogId;
        }
 
+       /**
+        * If the exception occurred in the course of responding to a request,
+        * returns the requested URL. Otherwise, returns false.
+        *
+        * @since 1.23
+        * @return string|bool
+        */
+       public static function getURL() {
+               global $wgRequest;
+               if ( !isset( $wgRequest ) || $wgRequest instanceof FauxRequest ) {
+                       return false;
+               }
+               return $wgRequest->getRequestURL();
+       }
+
        /**
         * Return the requested URL and point to file and line number from which the
         * exception occurred.
@@ -788,23 +803,88 @@ class MWExceptionHandler {
         * @return string
         */
        public static function getLogMessage( Exception $e ) {
-               global $wgRequest;
-
                $id = self::getLogId( $e );
                $file = $e->getFile();
                $line = $e->getLine();
                $message = $e->getMessage();
+               $url = self::getURL() ?: '[no req]';
 
-               if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
-                       $url = $wgRequest->getRequestURL();
-                       if ( !$url ) {
-                               $url = '[no URL]';
-                       }
-               } else {
-                       $url = '[no req]';
+               return "[$id] $url   Exception from line $line of $file: $message";
+       }
+
+       /**
+        * Serialize an Exception object to JSON.
+        *
+        * The JSON object will have keys 'id', 'file', 'line', 'message', and
+        * 'url'. These keys map to string values, with the exception of 'line',
+        * which is a number, and 'url', which may be either a string URL or or
+        * null if the exception did not occur in the context of serving a web
+        * request.
+        *
+        * If $wgLogExceptionBacktrace is true, it will also have a 'backtrace'
+        * key, mapped to the array return value of Exception::getTrace, but with
+        * each element in each frame's "args" array (if set) replaced with the
+        * argument's class name (if the argument is an object) or type name (if
+        * the argument is a PHP primitive).
+        *
+        * @par Sample JSON record ($wgLogExceptionBacktrace = false):
+        * @code
+        *  {
+        *    "id": "c41fb419",
+        *    "file": "/var/www/mediawiki/includes/cache/MessageCache.php",
+        *    "line": 704,
+        *    "message": "Non-string key given",
+        *    "url": "/wiki/Main_Page"
+        *  }
+        * @endcode
+        *
+        * @par Sample JSON record ($wgLogExceptionBacktrace = true):
+        * @code
+        *  {
+        *    "id": "dc457938",
+        *    "file": "/vagrant/mediawiki/includes/cache/MessageCache.php",
+        *    "line": 704,
+        *    "message": "Non-string key given",
+        *    "url": "/wiki/Main_Page",
+        *    "backtrace": [{
+        *      "file": "/vagrant/mediawiki/extensions/VisualEditor/VisualEditor.hooks.php",
+        *      "line": 80,
+        *      "function": "get",
+        *      "class": "MessageCache",
+        *      "type": "->",
+        *      "args": ["array"]
+        *    }]
+        *  }
+        * @endcode
+        *
+        * @since 1.23
+        * @param Exception $e
+        * @param bool $pretty Add non-significant whitespace to improve readability (default: false).
+        * @param int $escaping Bitfield consisting of FormatJson::.*_OK class constants.
+        * @return string|bool: JSON string if successful; false upon failure
+        */
+       public static function jsonSerializeException( Exception $e, $pretty = false, $escaping = 0 ) {
+               global $wgLogExceptionBacktrace;
+
+               $exceptionData = array(
+                       'id' => self::getLogId( $e ),
+                       'file' => $e->getFile(),
+                       'line' => $e->getLine(),
+                       'message' => $e->getMessage(),
+               );
+
+               // Because MediaWiki is first and foremost a web application, we set a
+               // 'url' key unconditionally, but set it to null if the exception does
+               // not occur in the context of a web request, as a way of making that
+               // fact visible and explicit.
+               $exceptionData['url'] = self::getURL() ?: null;
+
+               if ( $wgLogExceptionBacktrace ) {
+                       // Argument values may not be serializable, so redact them.
+                       $exceptionData['backtrace'] = self::getRedactedTrace( $e );
                }
 
-               return "[$id] $url   Exception from line $line of $file: $message";
+               return FormatJson::encode( $exceptionData, $pretty, $escaping );
        }
 
        /**
@@ -826,7 +906,13 @@ class MWExceptionHandler {
                        } else {
                                wfDebugLog( 'exception', $log );
                        }
+
+                       $json = self::jsonSerializeException( $e, false, FormatJson::ALL_OK );
+                       if ( $json !== false ) {
+                               wfDebugLog( 'exception-json', $json, false );
+                       }
                }
+
        }
 
 }
index 54822e3..cd6e207 100644 (file)
@@ -4,6 +4,7 @@
  *
  * Copyright © 2008, Niklas Laxström
  * Copyright © 2011, Antoine Musso
+ * Copyright © 2013, Bartosz Dziewoński
  *
  * 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
@@ -42,6 +43,9 @@ class FormOptions implements ArrayAccess {
        const STRING = 0;
        /** Integer type, maps guessType() to WebRequest::getInt() */
        const INT = 1;
+       /** Float type, maps guessType() to WebRequest::getFloat()
+         * @since 1.23 */
+       const FLOAT = 4;
        /** Boolean type, maps guessType() to WebRequest::getBool() */
        const BOOL = 2;
        /** Integer type or null, maps to WebRequest::getIntOrNull()
@@ -112,6 +116,8 @@ class FormOptions implements ArrayAccess {
                        return self::BOOL;
                } elseif ( is_int( $data ) ) {
                        return self::INT;
+               } elseif ( is_float( $data ) ) {
+                       return self::FLOAT;
                } elseif ( is_string( $data ) ) {
                        return self::STRING;
                } else {
@@ -234,19 +240,29 @@ class FormOptions implements ArrayAccess {
        }
 
        /**
-        * Validate and set an option integer value
-        * The value will be altered to fit in the range.
+        * @see validateBounds()
+        */
+       public function validateIntBounds( $name, $min, $max ) {
+               $this->validateBounds( $name, $min, $max );
+       }
+
+       /**
+        * Constrain a numeric value for a given option to a given range. The value will be altered to fit
+        * in the range.
         *
-        * @param string $name option name
-        * @param int $min minimum value
-        * @param int $max maximum value
+        * @since 1.23
+        *
+        * @param string $name Option name
+        * @param int|float $min Minimum value
+        * @param int|float $max Maximum value
         * @throws MWException If option is not of type INT
         */
-       public function validateIntBounds( $name, $min, $max ) {
+       public function validateBounds( $name, $min, $max ) {
                $this->validateName( $name, true );
+               $type = $this->options[$name]['type'];
 
-               if ( $this->options[$name]['type'] !== self::INT ) {
-                       throw new MWException( "Option $name is not of type int" );
+               if ( $type !== self::INT && $type !== self::FLOAT ) {
+                       throw new MWException( "Option $name is not of type INT or FLOAT" );
                }
 
                $value = $this->getValueReal( $this->options[$name] );
@@ -333,6 +349,9 @@ class FormOptions implements ArrayAccess {
                                case self::INT:
                                        $value = $r->getInt( $name, $default );
                                        break;
+                               case self::FLOAT:
+                                       $value = $r->getFloat( $name, $default );
+                                       break;
                                case self::STRING:
                                        $value = $r->getText( $name, $default );
                                        break;
index ff91ba0..1eb5c3e 100644 (file)
@@ -495,7 +495,7 @@ function wfAppendQuery( $url, $query ) {
  *    no valid URL can be constructed
  */
 function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
-       global $wgServer, $wgCanonicalServer, $wgInternalServer;
+       global $wgServer, $wgCanonicalServer, $wgInternalServer, $wgRequest;
        $serverUrl = $wgServer;
        if ( $defaultProto === PROTO_CANONICAL ) {
                $serverUrl = $wgCanonicalServer;
@@ -505,7 +505,7 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
                $serverUrl = $wgInternalServer;
        }
        if ( $defaultProto === PROTO_CURRENT ) {
-               $defaultProto = WebRequest::detectProtocol() . '://';
+               $defaultProto = $wgRequest->getProtocol() . '://';
        }
 
        // Analyze $serverUrl to obtain its protocol
@@ -1008,7 +1008,12 @@ function wfDebugMem( $exact = false ) {
 
 /**
  * Send a line to a supplementary debug log file, if configured, or main debug log if not.
- * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log.
+ * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to a string
+ * filename or an associative array mapping 'destination' to the desired filename. The
+ * associative array may also contain a 'sample' key with an integer value, specifying
+ * a sampling factor.
+ *
+ * @since 1.23 support for sampling log messages via $wgDebugLogGroups.
  *
  * @param $logGroup String
  * @param $text String
@@ -1018,14 +1023,28 @@ function wfDebugMem( $exact = false ) {
 function wfDebugLog( $logGroup, $text, $public = true ) {
        global $wgDebugLogGroups;
        $text = trim( $text ) . "\n";
-       if ( isset( $wgDebugLogGroups[$logGroup] ) ) {
-               $time = wfTimestamp( TS_DB );
-               $wiki = wfWikiID();
-               $host = wfHostname();
-               wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
-       } elseif ( $public === true ) {
-               wfDebug( "[$logGroup] $text", false );
+
+       if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
+               if ( $public === true ) {
+                       wfDebug( "[$logGroup] $text", false );
+               }
+               return;
+       }
+
+       $logConfig = $wgDebugLogGroups[$logGroup];
+       if ( is_array( $logConfig ) ) {
+               if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
+                       return;
+               }
+               $destination = $logConfig['destination'];
+       } else {
+               $destination = strval( $logConfig );
        }
+
+       $time = wfTimestamp( TS_DB );
+       $wiki = wfWikiID();
+       $host = wfHostname();
+       wfErrorLog( "$time $host $wiki: $text", $destination );
 }
 
 /**
@@ -2836,6 +2855,15 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
        if ( $useLogPipe ) {
                $desc[3] = array( 'pipe', 'w' );
        }
+
+       # TODO/FIXME: This is a bad hack to workaround an HHVM bug that prevents
+       # proc_open() from opening stdin/stdout, so use /dev/null *for now*
+       # See bug 56597 / https://github.com/facebook/hhvm/issues/1247 for more info
+       if ( wfIsHHVM() ) {
+               $desc[0] = array( 'file', '/dev/null', 'r' );
+               $desc[2] = array( 'file', '/dev/null', 'w' );
+       }
+
        $pipes = null;
        $proc = proc_open( $cmd, $desc, $pipes );
        if ( !$proc ) {
@@ -3305,18 +3333,6 @@ function wfRelativePath( $path, $from ) {
        return implode( DIRECTORY_SEPARATOR, $pieces );
 }
 
-/**
- * Do any deferred updates and clear the list
- *
- * @deprecated since 1.19
- * @see DeferredUpdates::doUpdate()
- * @param $commit string
- */
-function wfDoUpdates( $commit = '' ) {
-       wfDeprecated( __METHOD__, '1.19' );
-       DeferredUpdates::doUpdates( $commit );
-}
-
 /**
  * Convert an arbitrarily-long digit string from one numeric base
  * to another, optionally zero-padding to a minimum column width.
@@ -3423,19 +3439,6 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
        return str_pad( $result, $pad, '0', STR_PAD_LEFT );
 }
 
-/**
- * Create an object with a given name and an array of construct parameters
- *
- * @param $name String
- * @param array $p parameters
- * @return object
- * @deprecated since 1.18, warnings in 1.18, removal in 1.20
- */
-function wfCreateObject( $name, $p ) {
-       wfDeprecated( __FUNCTION__, '1.18' );
-       return MWFunction::newObj( $name, $p );
-}
-
 /**
  * @return bool
  */
@@ -3825,21 +3828,6 @@ function wfWaitForSlaves( $maxLag = false, $wiki = false, $cluster = false ) {
        }
 }
 
-/**
- * Used to be used for outputting text in the installer/updater
- * @deprecated since 1.18, warnings in 1.18, remove in 1.20
- */
-function wfOut( $s ) {
-       wfDeprecated( __FUNCTION__, '1.18' );
-       global $wgCommandLineMode;
-       if ( $wgCommandLineMode ) {
-               echo $s;
-       } else {
-               echo htmlspecialchars( $s );
-       }
-       flush();
-}
-
 /**
  * Count down from $n to zero on the terminal, with a one-second pause
  * between showing each number. For use in command-line scripts.
index cf05ee2..d6b8a03 100644 (file)
@@ -350,15 +350,33 @@ class ImagePage extends Article {
                                        $linktext = wfMessage( 'show-big-image' )->escaped();
                                        if ( $this->displayImg->getRepo()->canTransformVia404() ) {
                                                $thumbSizes = $wgImageLimits;
+                                               // Also include the full sized resolution in the list, so
+                                               // that users know they can get it. This will link to the
+                                               // original file asset if mustRender() === false. In the case
+                                               // that we mustRender, some users have indicated that they would
+                                               // find it useful to have the full size image in the rendered
+                                               // image format.
+                                               $thumbSizes[] = array( $width_orig, $height_orig );
                                        } else {
                                                # Creating thumb links triggers thumbnail generation.
                                                # Just generate the thumb for the current users prefs.
                                                $thumbSizes = array( $this->getImageLimitsFromOption( $user, 'thumbsize' ) );
+                                               if ( !$this->displayImg->mustRender() ) {
+                                                       // We can safely include a link to the "full-size" preview,
+                                                       // without actually rendering.
+                                                       $thumbSizes[] = array( $width_orig, $height_orig );
+                                               }
                                        }
                                        # Generate thumbnails or thumbnail links as needed...
                                        $otherSizes = array();
                                        foreach ( $thumbSizes as $size ) {
-                                               if ( $size[0] < $width_orig && $size[1] < $height_orig
+                                               // We include a thumbnail size in the list, if it is
+                                               // less than or equal to the original size of the image
+                                               // asset ($width_orig/$height_orig). We also exclude
+                                               // the current thumbnail's size ($width/$height)
+                                               // since that is added to the message separately, so
+                                               // it can be denoted as the current size being shown.
+                                               if ( $size[0] <= $width_orig && $size[1] <= $height_orig
                                                        && $size[0] != $width && $size[1] != $height )
                                                {
                                                        $sizeLink = $this->makeSizeLink( $params, $size[0], $size[1] );
@@ -367,6 +385,7 @@ class ImagePage extends Article {
                                                        }
                                                }
                                        }
+                                       $otherSizes = array_unique( $otherSizes );
                                        $msgsmall = '';
                                        $sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height );
                                        if ( $sizeLinkBigImagePreview ) {
index 7f0454f..b4fda13 100644 (file)
@@ -2980,9 +2980,11 @@ $templates
                $ns = $title->getNamespace();
                $canonicalNamespace = MWNamespace::exists( $ns ) ? MWNamespace::getCanonicalName( $ns ) : $title->getNsText();
 
+               $sk = $this->getSkin();
                // Get the relevant title so that AJAX features can use the correct page name
                // when making API requests from certain special pages (bug 34972).
-               $relevantTitle = $this->getSkin()->getRelevantTitle();
+               $relevantTitle = $sk->getRelevantTitle();
+               $relevantUser = $sk->getRelevantUser();
 
                if ( $ns == NS_SPECIAL ) {
                        list( $canonicalSpecialPageName, /*...*/ ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
@@ -3059,6 +3061,9 @@ $templates
                if ( $this->mRedirectedFrom ) {
                        $vars['wgRedirectedFrom'] = $this->mRedirectedFrom->getPrefixedDBkey();
                }
+               if ( $relevantUser ) {
+                       $vars['wgRelevantUserName'] = $relevantUser->getName();
+               }
 
                // Allow extensions to add their custom variables to the mw.config map.
                // Use the 'ResourceLoaderGetConfigVars' hook if the variable is not
index edcd6a8..c1c6455 100644 (file)
@@ -93,9 +93,9 @@ class MWTimestamp {
                        # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6
                        $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
                                        str_replace( '+00:00', 'UTC', $ts ) );
-               } elseif ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
+               } elseif ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z?$/', $ts, $da ) ) {
                        # TS_ISO_8601
-               } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
+               } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z?$/', $ts, $da ) ) {
                        #TS_ISO_8601_BASIC
                } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/', $ts, $da ) ) {
                        # TS_POSTGRES
index 21872e4..09c5a3a 100644 (file)
@@ -86,6 +86,7 @@ class Title {
        var $mRedirect = null;            // /< Is the article at this title a redirect?
        var $mNotificationTimestamp = array(); // /< Associative array of user ID -> timestamp/false
        var $mHasSubpage;                 // /< Whether a page has any subpages
+       private $mPageLanguage = false;   // /< The (string) language code of the page's language and content code.
        // @}
 
        /**
@@ -3110,6 +3111,7 @@ class Title {
                $this->mLatestID = false;
                $this->mContentModel = false;
                $this->mEstimateRevisions = null;
+               $this->mPageLanguage = false;
        }
 
        /**
@@ -4812,18 +4814,26 @@ class Title {
         * @return Language
         */
        public function getPageLanguage() {
-               global $wgLang;
+               global $wgLang, $wgLanguageCode;
+               wfProfileIn( __METHOD__ );
                if ( $this->isSpecialPage() ) {
                        // special pages are in the user language
+                       wfProfileOut( __METHOD__ );
                        return $wgLang;
                }
 
-               //TODO: use the LinkCache to cache this! Note that this may depend on user settings, so the cache should be only per-request.
-               //NOTE: ContentHandler::getPageLanguage() may need to load the content to determine the page language!
-               $contentHandler = ContentHandler::getForTitle( $this );
-               $pageLang = $contentHandler->getPageLanguage( $this );
-
-               return wfGetLangObj( $pageLang );
+               if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
+                       // Note that this may depend on user settings, so the cache should be only per-request.
+                       // NOTE: ContentHandler::getPageLanguage() may need to load the content to determine the page language!
+                       // Checking $wgLanguageCode hasn't changed for the benefit of unit tests.
+                       $contentHandler = ContentHandler::getForTitle( $this );
+                       $langObj = wfGetLangObj( $contentHandler->getPageLanguage( $this ) );
+                       $this->mPageLanguage = array( $langObj->getCode(), $wgLanguageCode );
+               } else {
+                       $langObj =  wfGetLangObj( $this->mPageLanguage[0] );
+               }
+               wfProfileOut( __METHOD__ );
+               return $langObj;
        }
 
        /**
index b17cb9e..4ad7344 100644 (file)
@@ -50,6 +50,12 @@ class WebRequest {
         */
        private $ip;
 
+       /**
+        * Cached URL protocol
+        * @var string
+        */
+       private $protocol;
+
        public function __construct() {
                /// @todo FIXME: This preemptive de-quoting can interfere with other web libraries
                ///        and increases our memory footprint. It would be cleaner to do on
@@ -160,7 +166,8 @@ class WebRequest {
         * @return string
         */
        public static function detectServer() {
-               list( $proto, $stdPort ) = self::detectProtocolAndStdPort();
+               $proto = self::detectProtocol();
+               $stdPort = $proto === 'https' ? 443 : 80;
 
                $varNames = array( 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' );
                $host = 'localhost';
@@ -189,25 +196,32 @@ class WebRequest {
        }
 
        /**
+        * Detect the protocol from $_SERVER.
+        * This is for use prior to Setup.php, when no WebRequest object is available.
+        * At other times, use the non-static function getProtocol().
+        *
         * @return array
         */
-       public static function detectProtocolAndStdPort() {
+       public static function detectProtocol() {
                if ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) ||
                        ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
                        $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) ) {
-                       $arr = array( 'https', 443 );
+                       return 'https';
                } else {
-                       $arr = array( 'http', 80 );
+                       return 'http';
                }
                return $arr;
        }
 
        /**
+        * Get the current URL protocol (http or https)
         * @return string
         */
-       public static function detectProtocol() {
-               list( $proto, ) = self::detectProtocolAndStdPort();
-               return $proto;
+       public function getProtocol() {
+               if ( $this->protocol === null ) {
+                       $this->protocol = self::detectProtocol();
+               }
+               return $this->protocol;
        }
 
        /**
@@ -466,6 +480,20 @@ class WebRequest {
                        : null;
        }
 
+       /**
+        * Fetch a floating point value from the input or return $default if not set.
+        * Guaranteed to return a float; non-numeric input will typically
+        * return 0.
+        *
+        * @since 1.23
+        * @param $name String
+        * @param $default Float
+        * @return Float
+        */
+       public function getFloat( $name, $default = 0 ) {
+               return floatval( $this->getVal( $name, $default ) );
+       }
+
        /**
         * Fetch a boolean value from the input or return $default if not set.
         * Guaranteed to return true or false, with normal PHP semantics for
@@ -1312,9 +1340,10 @@ class FauxRequest extends WebRequest {
         *   fake GET/POST values
         * @param bool $wasPosted whether to treat the data as POST
         * @param $session Mixed: session array or null
+        * @param string $protocol 'http' or 'https'
         * @throws MWException
         */
-       public function __construct( $data = array(), $wasPosted = false, $session = null ) {
+       public function __construct( $data = array(), $wasPosted = false, $session = null, $protocol = 'http' ) {
                if ( is_array( $data ) ) {
                        $this->data = $data;
                } else {
@@ -1324,6 +1353,7 @@ class FauxRequest extends WebRequest {
                if ( $session ) {
                        $this->session = $session;
                }
+               $this->protocol = $protocol;
        }
 
        /**
@@ -1385,6 +1415,10 @@ class FauxRequest extends WebRequest {
                $this->notImplemented( __METHOD__ );
        }
 
+       public function getProtocol() {
+               return $this->protocol;
+       }
+
        /**
         * @param string $name The name of the header to get (case insensitive).
         * @return bool|string
@@ -1524,4 +1558,8 @@ class DerivativeRequest extends FauxRequest {
        public function getIP() {
                return $this->base->getIP();
        }
+
+       public function getProtocol() {
+               return $this->base->getProtocol();
+       }
 }
index a690176..50bba7b 100644 (file)
@@ -540,7 +540,7 @@ class MediaWiki {
                                        && $this->context->getUser()->requiresHTTPS()
                                )
                        ) &&
-                       $request->detectProtocol() == 'http'
+                       $request->getProtocol() == 'http'
                ) {
                        $oldUrl = $request->getFullRequestURL();
                        $redirUrl = str_replace( 'http://', 'https://', $oldUrl );
index 6d2d80c..576979c 100644 (file)
@@ -2747,6 +2747,9 @@ class WikiPage implements Page, IDBAccessObject {
                $updates = $this->getDeletionUpdates( $content );
                DataUpdate::runUpdates( $updates );
 
+               // Reparse any pages transcluding this page
+               LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
+
                // Clear caches
                WikiPage::onArticleDelete( $this->mTitle );
 
old mode 100755 (executable)
new mode 100644 (file)
index 6b10bdc..8d969fc 100644 (file)
@@ -456,6 +456,10 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        $vals['patrolled'] = '';
                }
 
+               if ( $this->fld_patrolled && ChangesList::isUnpatrolled( $row, $this->getUser() ) ) {
+                       $vals['unpatrolled'] = '';
+               }
+
                if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
                        $vals['logid'] = intval( $row->rc_logid );
                        $vals['logtype'] = $row->rc_log_type;
@@ -655,7 +659,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                                ' ids            - Adds the page ID, recent changes ID and the new and old revision ID',
                                ' sizes          - Adds the new and old page length in bytes',
                                ' redirect       - Tags edit if page is a redirect',
-                               ' patrolled      - Tags edits that have been patrolled',
+                               ' patrolled      - Tags patrollable edits as being patrolled or unpatrolled',
                                ' loginfo        - Adds log information (logid, logtype, etc) to log entries',
                                ' tags           - Lists tags for the entry',
                                ' sha1           - Adds the content checksum for entries associated with a revision',
@@ -741,7 +745,8 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                                'redirect' => 'boolean'
                        ),
                        'patrolled' => array(
-                               'patrolled' => 'boolean'
+                               'patrolled' => 'boolean',
+                               'unpatrolled' => 'boolean'
                        ),
                        'loginfo' => array(
                                'logid' => array(
index bf800c4..fe5bf78 100644 (file)
@@ -49,19 +49,6 @@ class ChangesList extends ContextSource {
                $this->preCacheMessages();
        }
 
-       /**
-        * Fetch an appropriate changes list class for the main context
-        * This first argument used to be an User object.
-        *
-        * @deprecated in 1.18; use newFromContext() instead
-        * @param string|User $unused Unused
-        * @return ChangesList|EnhancedChangesList|OldChangesList derivative
-        */
-       public static function newFromUser( $unused ) {
-               wfDeprecated( __METHOD__, '1.18' );
-               return self::newFromContext( RequestContext::getMain() );
-       }
-
        /**
         * Fetch an appropriate changes list class for the specified context
         * Some users might want to use an enhanced list format, for instance
@@ -539,14 +526,32 @@ class ChangesList extends ContextSource {
        }
 
        protected function showAsUnpatrolled( RecentChange $rc ) {
-               $unpatrolled = false;
-               if ( !$rc->mAttribs['rc_patrolled'] ) {
-                       if ( $this->getUser()->useRCPatrol() ) {
-                               $unpatrolled = true;
-                       } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
-                               $unpatrolled = true;
+               return self::isUnpatrolled( $rc, $this->getUser() );
+       }
+
+       /**
+        * @param object|RecentChange $rc Database row from recentchanges or a RecentChange object
+        * @param User $user
+        * @return bool
+        */
+       public static function isUnpatrolled( $rc, User $user ) {
+               if ( $rc instanceof RecentChange ) {
+                       $isPatrolled = $rc->mAttribs['rc_patrolled'];
+                       $rcType = $rc->mAttribs['rc_type'];
+               } else {
+                       $isPatrolled = $rc->rc_patrolled;
+                       $rcType = $rc->rc_type;
+               }
+
+               if ( !$isPatrolled ) {
+                       if ( $user->useRCPatrol() ) {
+                               return true;
+                       }
+                       if ( $user->useNPPatrol() && $rcType == RC_NEW ) {
+                               return true;
                        }
                }
-               return $unpatrolled;
+
+               return false;
        }
 }
index 282890f..8d4c9c1 100644 (file)
@@ -714,7 +714,7 @@ class RecentChange {
        /**
         * Makes a pseudo-RC entry from a cur row
         *
-        * @deprected in 1.22
+        * @deprecated in 1.22
         * @param $row
         */
        public function loadFromCurRow( $row ) {
index f7653eb..c8e98a7 100644 (file)
@@ -283,6 +283,25 @@ class RedisConnectionPool {
                        }
                }
        }
+
+       /**
+        * Resend an AUTH request to the redis server (useful after disconnects)
+        *
+        * This method is for internal use only
+        *
+        * @param string $server
+        * @param Redis $conn
+        * @return bool Success
+        */
+       public function reauthenticateConnection( $server, Redis $conn ) {
+               if ( $this->password !== null ) {
+                       if ( !$conn->auth( $this->password ) ) {
+                               wfDebugLog( 'redis', "Authentication error connecting to $server" );
+                               return false;
+                       }
+               }
+               return true;
+       }
 }
 
 /**
@@ -324,10 +343,21 @@ class RedisConnRef {
        public function luaEval( $script, array $params, $numKeys ) {
                $sha1 = sha1( $script ); // 40 char hex
                $conn = $this->conn; // convenience
+               $server = $this->server; // convenience
 
                // Try to run the server-side cached copy of the script
                $conn->clearLastError();
                $res = $conn->evalSha( $sha1, $params, $numKeys );
+               // If we got a permission error reply that means that (a) we are not in
+               // multi()/pipeline() and (b) some connection problem likely occured. If
+               // the password the client gave was just wrong, an exception should have
+               // been thrown back in getConnection() previously.
+               if ( preg_match( '/^ERR operation not permitted\b/', $conn->getLastError() ) ) {
+                       $this->pool->reauthenticateConnection( $server, $conn );
+                       $conn->clearLastError();
+                       $res = $conn->eval( $script, $params, $numKeys );
+                       wfDebugLog( 'redis', "Used automatic re-authentication for Lua script $sha1." );
+               }
                // If the script is not in cache, use eval() to retry and cache it
                if ( preg_match( '/^NOSCRIPT/', $conn->getLastError() ) ) {
                        $conn->clearLastError();
@@ -336,7 +366,6 @@ class RedisConnRef {
                }
 
                if ( $conn->getLastError() ) { // script bug?
-                       $server = $this->server;
                        wfDebugLog( 'redis', "Lua script error on server $server: " . $conn->getLastError() );
                }
 
index 2a92e23..8991290 100644 (file)
@@ -907,6 +907,10 @@ abstract class ContentHandler {
                $undo_content = $undo->getContent();
                $undoafter_content = $undoafter->getContent();
 
+               if ( !$undo_content || !$undoafter_content ) {
+                       return false; // no content to undo
+               }
+
                $this->checkModelID( $cur_content->getModel() );
                $this->checkModelID( $undo_content->getModel() );
                $this->checkModelID( $undoafter_content->getModel() );
index cd907e9..30e92e5 100644 (file)
@@ -301,6 +301,12 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         */
        protected $fileHandle = null;
 
+       /**
+        * @since 1.22
+        * @var Process cache of VIEWs names in the database
+        */
+       protected $allViews = null;
+
 # ------------------------------------------------------------------------------
 # Accessors
 # ------------------------------------------------------------------------------
@@ -681,29 +687,37 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         * connection object, by specifying no parameters to __construct(). This
         * feature is deprecated and should be removed.
         *
-        * FIXME: The long list of formal parameters here is not really appropriate
-        * for MySQL, and not at all appropriate for any other DBMS. It should be
-        * replaced by named parameters as in DatabaseBase::factory().
-        *
         * DatabaseBase subclasses should not be constructed directly in external
         * code. DatabaseBase::factory() should be used instead.
         *
-        * @param string $server database server host
-        * @param string $user database user name
-        * @param string $password database user password
-        * @param string $dbName database name
-        * @param $flags
-        * @param string $tablePrefix database table prefixes. By default use the prefix gave in LocalSettings.php
-        * @param bool $foreign disable some operations specific to local databases
+        * @param array Parameters passed from DatabaseBase::factory()
         */
-       function __construct( $server = false, $user = false, $password = false, $dbName = false,
-               $flags = 0, $tablePrefix = 'get from global', $foreign = false
-       ) {
+       function __construct( $params = null ) {
                global $wgDBprefix, $wgCommandLineMode, $wgDebugDBTransactions;
 
                $this->mTrxAtomicLevels = new SplStack;
-               $this->mFlags = $flags;
 
+               if ( is_array( $params ) ) { // MW 1.22
+                       $server = $params['host'];
+                       $user = $params['user'];
+                       $password = $params['password'];
+                       $dbName = $params['dbname'];
+                       $flags = $params['flags'];
+                       $tablePrefix = $params['tablePrefix'];
+                       $foreign = $params['foreign'];
+               } else { // legacy calling pattern
+                       wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" );
+                       $args = func_get_args();
+                       $server = isset( $args[0] ) ? $args[0] : false;
+                       $user = isset( $args[1] ) ? $args[1] : false;
+                       $password = isset( $args[2] ) ? $args[2] : false;
+                       $dbName = isset( $args[3] ) ? $args[3] : false;
+                       $flags = isset( $args[4] ) ? $args[4] : 0;
+                       $tablePrefix = isset( $args[5] ) ? $args[5] : 'get from global';
+                       $foreign = isset( $args[6] ) ? $args[6] : false;
+               }
+
+               $this->mFlags = $flags;
                if ( $this->mFlags & DBO_DEFAULT ) {
                        if ( $wgCommandLineMode ) {
                                $this->mFlags &= ~DBO_TRX;
@@ -800,15 +814,16 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
 
                $class = 'Database' . ucfirst( $driver );
                if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
-                       return new $class(
-                               isset( $p['host'] ) ? $p['host'] : false,
-                               isset( $p['user'] ) ? $p['user'] : false,
-                               isset( $p['password'] ) ? $p['password'] : false,
-                               isset( $p['dbname'] ) ? $p['dbname'] : false,
-                               isset( $p['flags'] ) ? $p['flags'] : 0,
-                               isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global',
-                               isset( $p['foreign'] ) ? $p['foreign'] : false
+                       $params = array(
+                               'host' => isset( $p['host'] ) ? $p['host'] : false,
+                               'user' => isset( $p['user'] ) ? $p['user'] : false,
+                               'password' => isset( $p['password'] ) ? $p['password'] : false,
+                               'dbname' => isset( $p['dbname'] ) ? $p['dbname'] : false,
+                               'flags' => isset( $p['flags'] ) ? $p['flags'] : 0,
+                               'tablePrefix' => isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global',
+                               'foreign' => isset( $p['foreign'] ) ? $p['foreign'] : false
                        );
+                       return new $class( $params );
                } else {
                        return null;
                }
@@ -3444,6 +3459,40 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                throw new MWException( 'DatabaseBase::listTables is not implemented in descendant class' );
        }
 
+       /**
+        * Reset the views process cache set by listViews()
+        * @since 1.22
+        */
+       final public function clearViewsCache() {
+               $this->allViews = null;
+       }
+
+       /**
+        * Lists all the VIEWs in the database
+        *
+        * For caching purposes the list of all views should be stored in
+        * $this->allViews. The process cache can be cleared with clearViewsCache()
+        *
+        * @param string $prefix   Only show VIEWs with this prefix, eg. unit_test_
+        * @param string $fname    Name of calling function
+        * @throws MWException
+        * @since 1.22
+        */
+       public function listViews( $prefix = null, $fname = __METHOD__ ) {
+               throw new MWException( 'DatabaseBase::listViews is not implemented in descendant class' );
+       }
+
+       /**
+        * Differentiates between a TABLE and a VIEW
+        *
+        * @param $name string: Name of the database-structure to test.
+        * @throws MWException
+        * @since 1.22
+        */
+       public function isView( $name ) {
+               throw new MWException( 'DatabaseBase::isView is not implemented in descendant class' );
+       }
+
        /**
         * Convert a timestamp in one of the formats accepted by wfTimestamp()
         * to the format used for inserting into timestamp fields in this DBMS.
index 49579b6..26c9d24 100644 (file)
@@ -996,6 +996,55 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
                return $status;
        }
 
+       /**
+        * Lists VIEWs in the database
+        *
+        * @param string $prefix   Only show VIEWs with this prefix, eg.
+        * unit_test_, or $wgDBprefix. Default: null, would return all views.
+        * @param string $fname    Name of calling function
+        * @return array
+        * @since 1.22
+        */
+       public function listViews( $prefix = null, $fname = __METHOD__ ) {
+
+               if ( !isset( $this->allViews ) ) {
+
+                       // The name of the column containing the name of the VIEW
+                       $propertyName = 'Tables_in_' . $this->mDBname;
+
+                       // Query for the VIEWS
+                       $result = $this->query( 'SHOW FULL TABLES WHERE TABLE_TYPE = "VIEW"' );
+                       $this->allViews = array();
+                       while ( ($row = $this->fetchRow($result)) !== false ) {
+                               array_push( $this->allViews, $row[$propertyName] );
+                       }
+               }
+
+               if ( is_null($prefix) || $prefix === '' ) {
+                       return $this->allViews;
+               }
+
+               $filteredViews = array();
+               foreach ( $this->allViews as $viewName ) {
+                       // Does the name of this VIEW start with the table-prefix?
+                       if ( strpos( $viewName, $prefix ) === 0 ) {
+                               array_push( $filteredViews, $viewName );
+                       }
+               }
+               return $filteredViews;
+       }
+
+       /**
+        * Differentiates between a TABLE and a VIEW.
+        *
+        * @param $name string: Name of the TABLE/VIEW to test
+        * @return bool
+        * @since 1.22
+        */
+       public function isView( $name, $prefix = null ) {
+               return in_array( $name, $this->listViews( $prefix ) );
+       }
+
 }
 
 
index fbaa4da..97070fb 100644 (file)
@@ -196,12 +196,27 @@ class DatabaseOracle extends DatabaseBase {
 
        var $mFieldInfoCache = array();
 
-       function __construct( $server = false, $user = false, $password = false, $dbName = false,
-               $flags = 0, $tablePrefix = 'get from global' )
-       {
+       function __construct( $p = null ) {
                global $wgDBprefix;
-               $tablePrefix = $tablePrefix == 'get from global' ? strtoupper( $wgDBprefix ) : strtoupper( $tablePrefix );
-               parent::__construct( $server, $user, $password, $dbName, $flags, $tablePrefix );
+
+               if ( !is_array( $p ) ) { // legacy calling pattern
+                       wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" );
+                       $args = func_get_args();
+                       $p = array(
+                               'host' => isset( $args[0] ) ? $args[0] : false,
+                               'user' => isset( $args[1] ) ? $args[1] : false,
+                               'password' => isset( $args[2] ) ? $args[2] : false,
+                               'dbname' => isset( $args[3] ) ? $args[3] : false,
+                               'flags' => isset( $args[4] ) ? $args[4] : 0,
+                               'tablePrefix' => isset( $args[5] ) ? $args[5] : 'get from global',
+                               'foreign' => isset( $args[6] ) ? $args[6] : false
+                       );
+               }
+               if ( $p['tablePrefix'] == 'get from global' ) {
+                       $p['tablePrefix'] = $wgDBprefix;
+               }
+               $p['tablePrefix'] = strtoupper( $p['tablePrefix'] );
+               parent::__construct( $p );
                wfRunHooks( 'DatabaseOraclePostInit', array( $this ) );
        }
 
index 4a51226..79a3b1e 100644 (file)
@@ -39,23 +39,30 @@ class DatabaseSqlite extends DatabaseBase {
         */
        protected $mConn;
 
-       /**
-        * Constructor.
-        * Parameters $server, $user and $password are not used.
-        * @param $server string
-        * @param $user string
-        * @param $password string
-        * @param $dbName string
-        * @param $flags int
-        */
-       function __construct( $server = false, $user = false, $password = false, $dbName = false, $flags = 0 ) {
-               $this->mName = $dbName;
-               parent::__construct( $server, $user, $password, $dbName, $flags );
+       function __construct( $p = null ) {
+               global $wgSharedDB;
+
+               if ( !is_array( $p ) ) { // legacy calling pattern
+                       wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" );
+                       $args = func_get_args();
+                       $p = array(
+                               'host' => isset( $args[0] ) ? $args[0] : false,
+                               'user' => isset( $args[1] ) ? $args[1] : false,
+                               'password' => isset( $args[2] ) ? $args[2] : false,
+                               'dbname' => isset( $args[3] ) ? $args[3] : false,
+                               'flags' => isset( $args[4] ) ? $args[4] : 0,
+                               'tablePrefix' => isset( $args[5] ) ? $args[5] : 'get from global',
+                               'foreign' => isset( $args[6] ) ? $args[6] : false
+                       );
+               }
+               $this->mName = $p['dbname'];
+               parent::__construct( $p );
                // parent doesn't open when $user is false, but we can work with $dbName
-               if ( $dbName && !$this->isOpen() ) {
-                       global $wgSharedDB;
-                       if ( $this->open( $server, $user, $password, $dbName ) && $wgSharedDB ) {
-                               $this->attachDatabase( $wgSharedDB );
+               if ( $p['dbname'] && !$this->isOpen() ) {
+                       if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
+                               if ( $wgSharedDB ) {
+                                       $this->attachDatabase( $wgSharedDB );
+                               }
                        }
                }
        }
@@ -803,6 +810,9 @@ class DatabaseSqlite extends DatabaseBase {
                        $s = preg_replace( '/\(\d+\)/', '', $s );
                        // No FULLTEXT
                        $s = preg_replace( '/\bfulltext\b/i', '', $s );
+               } elseif ( preg_match( '/^\s*DROP INDEX/i', $s ) ) {
+                       // DROP INDEX is database-wide, not table-specific, so no ON <table> clause.
+                       $s = preg_replace( '/\sON\s+[^\s]*/i', '', $s );
                }
                return $s;
        }
index 29089c9..10c8dc3 100644 (file)
@@ -52,7 +52,7 @@ abstract class FileBackendStore extends FileBackend {
        protected $maxFileSize = 4294967296; // integer bytes (4GiB)
 
        const CACHE_TTL = 10; // integer; TTL in seconds for process cache entries
-       const CACHE_CHEAP_SIZE = 300; // integer; max entries in "cheap cache"
+       const CACHE_CHEAP_SIZE = 500; // integer; max entries in "cheap cache"
        const CACHE_EXPENSIVE_SIZE = 5; // integer; max entries in "expensive cache"
 
        /**
index fe83308..3c5b7b2 100644 (file)
@@ -63,45 +63,28 @@ abstract class FileOp {
         */
        final public function __construct( FileBackendStore $backend, array $params ) {
                $this->backend = $backend;
-               list( $required, $optional ) = $this->allowedParams();
-               // @todo normalizeAnyStoragePaths() calls are overzealous, use a parameter list
+               list( $required, $optional, $paths ) = $this->allowedParams();
                foreach ( $required as $name ) {
                        if ( isset( $params[$name] ) ) {
-                               // Normalize paths so the paths to the same file have the same string
-                               $this->params[$name] = self::normalizeAnyStoragePaths( $params[$name] );
+                               $this->params[$name] = $params[$name];
                        } else {
                                throw new MWException( "File operation missing parameter '$name'." );
                        }
                }
                foreach ( $optional as $name ) {
                        if ( isset( $params[$name] ) ) {
-                               // Normalize paths so the paths to the same file have the same string
-                               $this->params[$name] = self::normalizeAnyStoragePaths( $params[$name] );
+                               $this->params[$name] = $params[$name];
                        }
                }
-               $this->params = $params;
-       }
-
-       /**
-        * Normalize $item or anything in $item that is a valid storage path
-        *
-        * @param string $item|array
-        * @return string|Array
-        */
-       protected function normalizeAnyStoragePaths( $item ) {
-               if ( is_array( $item ) ) {
-                       $res = array();
-                       foreach ( $item as $k => $v ) {
-                               $k = self::normalizeIfValidStoragePath( $k );
-                               $v = self::normalizeIfValidStoragePath( $v );
-                               $res[$k] = $v;
+               foreach ( $paths as $name ) {
+                       if ( isset( $this->params[$name] ) ) {
+                               // Normalize paths so the paths to the same file have the same string
+                               $this->params[$name] = self::normalizeIfValidStoragePath( $this->params[$name] );
                        }
-                       return $res;
-               } else {
-                       return self::normalizeIfValidStoragePath( $item );
                }
        }
 
+
        /**
         * Normalize a string if it is a valid storage path
         *
@@ -308,10 +291,10 @@ abstract class FileOp {
        /**
         * Get the file operation parameters
         *
-        * @return Array (required params list, optional params list)
+        * @return Array (required params list, optional params list, list of params that are paths)
         */
        protected function allowedParams() {
-               return array( array(), array() );
+               return array( array(), array(), array() );
        }
 
        /**
@@ -459,8 +442,11 @@ abstract class FileOp {
  */
 class CreateFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'content', 'dst' ),
-                       array( 'overwrite', 'overwriteSame', 'headers' ) );
+               return array(
+                       array( 'content', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'headers' ),
+                       array( 'dst' )
+               );
        }
 
        protected function doPrecheck( array &$predicates ) {
@@ -511,8 +497,11 @@ class CreateFileOp extends FileOp {
  */
 class StoreFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'src', 'dst' ),
-                       array( 'overwrite', 'overwriteSame', 'headers' ) );
+               return array(
+                       array( 'src', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'headers' ),
+                       array( 'src', 'dst' )
+               );
        }
 
        protected function doPrecheck( array &$predicates ) {
@@ -573,8 +562,11 @@ class StoreFileOp extends FileOp {
  */
 class CopyFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'src', 'dst' ),
-                       array( 'overwrite', 'overwriteSame', 'ignoreMissingSource', 'headers' ) );
+               return array(
+                       array( 'src', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'ignoreMissingSource', 'headers' ),
+                       array( 'src', 'dst' )
+               );
        }
 
        protected function doPrecheck( array &$predicates ) {
@@ -639,8 +631,11 @@ class CopyFileOp extends FileOp {
  */
 class MoveFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'src', 'dst' ),
-                       array( 'overwrite', 'overwriteSame', 'ignoreMissingSource', 'headers' ) );
+               return array(
+                       array( 'src', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'ignoreMissingSource', 'headers' ),
+                       array( 'src', 'dst' )
+               );
        }
 
        protected function doPrecheck( array &$predicates ) {
@@ -715,7 +710,7 @@ class MoveFileOp extends FileOp {
  */
 class DeleteFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'src' ), array( 'ignoreMissingSource' ) );
+               return array( array( 'src' ), array( 'ignoreMissingSource' ), array( 'src' ) );
        }
 
        protected function doPrecheck( array &$predicates ) {
@@ -760,7 +755,7 @@ class DeleteFileOp extends FileOp {
  */
 class DescribeFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'src' ), array( 'headers' ) );
+               return array( array( 'src' ), array( 'headers' ), array( 'src' ) );
        }
 
        protected function doPrecheck( array &$predicates ) {
index db090a9..ec2e2c0 100644 (file)
@@ -759,8 +759,8 @@ class SwiftFileBackend extends FileBackendStore {
                        $srcObj = $contObj->get_object( $srcRel, $this->headersFromParams( $params ) );
                        $this->addMissingMetadata( $srcObj, $params['src'] );
                        $stat = array(
-                               // Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
-                               'mtime' => wfTimestamp( TS_MW, $srcObj->last_modified ),
+                               // Convert various random Swift dates to TS_MW
+                               'mtime' => $this->convertSwiftDate( $srcObj->last_modified, TS_MW ),
                                'size' => (int)$srcObj->content_length,
                                'sha1' => $srcObj->getMetadataValue( 'Sha1base36' )
                        );
@@ -774,6 +774,21 @@ class SwiftFileBackend extends FileBackendStore {
                return $stat;
        }
 
+       /**
+        * Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT"/"2013-05-11T07:37:27.678360Z".
+        * Dates might also come in like "2013-05-11T07:37:27.678360" from Swift listings,
+        * missing the timezone suffix (though Ceph RGW does not appear to have this bug).
+        *
+        * @param string $ts
+        * @param int $format Output format (TS_* constant)
+        * @return string
+        * @throws MWException
+        */
+       protected function convertSwiftDate( $ts, $format = TS_MW ) {
+               $timestamp = new MWTimestamp( $ts );
+               return $timestamp->getTimestamp( $format );
+       }
+
        /**
         * Fill in any missing object metadata and save it to Swift
         *
@@ -916,10 +931,10 @@ class SwiftFileBackend extends FileBackendStore {
         *
         * @param string $fullCont Resolved container name
         * @param string $dir Resolved storage directory with no trailing slash
-        * @param string|null $after Storage path of file to list items after
+        * @param string|null $after Resolved container relative path to list items after
         * @param integer $limit Max number of items to list
         * @param array $params Parameters for getDirectoryList()
-        * @return Array List of resolved paths of directories directly under $dir
+        * @return Array List of container relative resolved paths of directories directly under $dir
         * @throws FileBackendError
         */
        public function getDirListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
@@ -991,14 +1006,14 @@ class SwiftFileBackend extends FileBackendStore {
         *
         * @param string $fullCont Resolved container name
         * @param string $dir Resolved storage directory with no trailing slash
-        * @param string|null $after Storage path of file to list items after
+        * @param string|null $after Resolved container relative path of file to list items after
         * @param integer $limit Max number of items to list
         * @param array $params Parameters for getDirectoryList()
-        * @return Array List of resolved paths of files under $dir
+        * @return Array List of resolved container relative paths of files under $dir
         * @throws FileBackendError
         */
        public function getFileListPageInternal( $fullCont, $dir, &$after, $limit, array $params ) {
-               $files = array();
+               $files = array(); // list of (path, stat array or null) entries
                if ( $after === INF ) {
                        return $files; // nothing more
                }
@@ -1007,38 +1022,33 @@ class SwiftFileBackend extends FileBackendStore {
                try {
                        $container = $this->getContainer( $fullCont );
                        $prefix = ( $dir == '' ) ? null : "{$dir}/";
+                       $objects = array(); // list of unfiltered names or CF_Object items
                        // Non-recursive: only list files right under $dir
-                       if ( !empty( $params['topOnly'] ) ) { // files and dirs
+                       if ( !empty( $params['topOnly'] ) ) {
                                if ( !empty( $params['adviseStat'] ) ) {
-                                       $limit = min( $limit, self::CACHE_CHEAP_SIZE );
                                        // Note: get_objects() does not include directories
-                                       $objects = $this->loadObjectListing( $params, $dir,
-                                               $container->get_objects( $limit, $after, $prefix, null, '/' ) );
-                                       $files = $objects;
+                                       $objects = $container->get_objects( $limit, $after, $prefix, null, '/' );
                                } else {
+                                       // Note: list_objects() includes directories here
                                        $objects = $container->list_objects( $limit, $after, $prefix, null, '/' );
-                                       foreach ( $objects as $object ) { // files and directories
-                                               if ( substr( $object, -1 ) !== '/' ) {
-                                                       $files[] = $object; // directories end in '/'
-                                               }
-                                       }
                                }
+                               $files = $this->buildFileObjectListing( $params, $dir, $objects );
                        // Recursive: list all files under $dir and its subdirs
-                       } else { // files
+                       } else {
+                               // Note: get_objects()/list_objects() here only return file objects
                                if ( !empty( $params['adviseStat'] ) ) {
-                                       $limit = min( $limit, self::CACHE_CHEAP_SIZE );
-                                       $objects = $this->loadObjectListing( $params, $dir,
-                                               $container->get_objects( $limit, $after, $prefix ) );
+                                       $objects = $container->get_objects( $limit, $after, $prefix );
                                } else {
                                        $objects = $container->list_objects( $limit, $after, $prefix );
                                }
-                               $files = $objects;
+                               $files = $this->buildFileObjectListing( $params, $dir, $objects );
                        }
                        // Page on the unfiltered object listing (what is returned may be filtered)
                        if ( count( $objects ) < $limit ) {
                                $after = INF; // avoid a second RTT
                        } else {
                                $after = end( $objects ); // update last item
+                               $after = is_object( $after ) ? $after->name : $after;
                        }
                } catch ( NoSuchContainerException $e ) {
                } catch ( CloudFilesException $e ) { // some other exception?
@@ -1051,34 +1061,42 @@ class SwiftFileBackend extends FileBackendStore {
        }
 
        /**
-        * Load a list of objects that belong under $dir into stat cache
-        * and return a list of the names of the objects in the same order.
+        * Build a list of file objects, filtering out any directories
+        * and extracting any stat info if provided in $objects (for CF_Objects)
         *
         * @param array $params Parameters for getDirectoryList()
         * @param string $dir Resolved container directory path
-        * @param array $cfObjects List of CF_Object items
-        * @return array List of object names
+        * @param array $objects List of CF_Object items or object names
+        * @return array List of (names,stat array or null) entries
         */
-       private function loadObjectListing( array $params, $dir, array $cfObjects ) {
+       private function buildFileObjectListing( array $params, $dir, array $objects ) {
                $names = array();
-               $storageDir = rtrim( $params['dir'], '/' );
-               $suffixStart = ( $dir === '' ) ? 0 : strlen( $dir ) + 1; // size of "path/to/dir/"
-               // Iterate over the list *backwards* as this primes the stat cache, which is LRU.
-               // If this fills the cache and the caller stats an uncached file before stating
-               // the ones on the listing, there would be zero cache hits if this went forwards.
-               for ( end( $cfObjects ); key( $cfObjects ) !== null; prev( $cfObjects ) ) {
-                       $object = current( $cfObjects );
-                       $path = "{$storageDir}/" . substr( $object->name, $suffixStart );
-                       $val = array(
-                               // Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
-                               'mtime'  => wfTimestamp( TS_MW, $object->last_modified ),
-                               'size'   => (int)$object->content_length,
-                               'latest' => false // eventually consistent
-                       );
-                       $this->cheapCache->set( $path, 'stat', $val );
-                       $names[] = $object->name;
+               foreach ( $objects as $object ) {
+                       if ( is_object( $object ) ) {
+                               $stat = array(
+                                       // Convert various random Swift dates to TS_MW
+                                       'mtime'  => $this->convertSwiftDate( $object->last_modified, TS_MW ),
+                                       'size'   => (int)$object->content_length,
+                                       'latest' => false // eventually consistent
+                               );
+                               $names[] = array( $object->name, $stat );
+                       } elseif ( substr( $object, -1 ) !== '/' ) {
+                               // Omit directories, which end in '/' in listings
+                               $names[] = array( $object, null );
+                       }
                }
-               return array_reverse( $names ); // keep the paths in original order
+               return $names;
+       }
+
+       /**
+        * Do not call this function outside of SwiftFileBackendFileList
+        *
+        * @param string $path Storage path
+        * @param array $val Stat value
+        * @return void
+        */
+       public function loadListingStatInternal( $path, array $val ) {
+               $this->cheapCache->set( $path, 'stat', $val );
        }
 
        protected function doGetFileSha1base36( array $params ) {
@@ -1556,7 +1574,7 @@ class SwiftFileOpHandle extends FileBackendStoreOpHandle {
  * @ingroup FileBackend
  */
 abstract class SwiftFileBackendList implements Iterator {
-       /** @var Array */
+       /** @var Array List of path or (path,stat array) entries */
        protected $bufferIter = array();
        protected $bufferAfter = null; // string; list items *after* this path
        protected $pos = 0; // integer
@@ -1684,7 +1702,13 @@ class SwiftFileBackendFileList extends SwiftFileBackendList {
         * @return string|bool String (relative path) or false
         */
        public function current() {
-               return substr( current( $this->bufferIter ), $this->suffixStart );
+               list( $path, $stat ) = current( $this->bufferIter );
+               $relPath = substr( $path, $this->suffixStart );
+               if ( is_array( $stat ) ) {
+                       $storageDir = rtrim( $this->params['dir'], '/' );
+                       $this->backend->loadListingStatInternal( "$storageDir/$path", $stat );
+               }
+               return $relPath;
        }
 
        /**
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index 0110ac5..43d90e5 100644 (file)
@@ -438,6 +438,10 @@ abstract class DatabaseInstaller {
        /**
         * Get a labelled checkbox to configure a local boolean variable.
         *
+        * @param string $var
+        * @param string $label
+        * @param array $attribs Optional.
+        * @param string $helpData Optional.
         * @return string
         */
        public function getCheckBox( $var, $label, $attribs = array(), $helpData = "" ) {
@@ -544,8 +548,8 @@ abstract class DatabaseInstaller {
 
        /**
         * Get a standard web-user fieldset
-        * @param string $noCreateMsg Message to display instead of the creation checkbox.
-        *   Set this to false to show a creation checkbox.
+        * @param string|bool $noCreateMsg Message to display instead of the creation checkbox.
+        *   Set this to false to show a creation checkbox (default).
         *
         * @return String
         */
index e56c7a9..41cbf50 100644 (file)
@@ -77,7 +77,7 @@ abstract class DatabaseUpdater {
        /**
         * File handle for SQL output.
         *
-        * @var Filehandle
+        * @var resource
         */
        protected $fileHandle = null;
 
@@ -96,9 +96,9 @@ abstract class DatabaseUpdater {
        /**
         * Constructor
         *
-        * @param $db DatabaseBase object to perform updates on
+        * @param DatabaseBase $db To perform updates on
         * @param bool $shared Whether to perform updates on shared tables
-        * @param $maintenance Maintenance Maintenance object which created us
+        * @param Maintenance $maintenance Maintenance object which created us
         */
        protected function __construct( DatabaseBase &$db, $shared, Maintenance $maintenance = null ) {
                $this->db = $db;
@@ -204,7 +204,7 @@ abstract class DatabaseUpdater {
         *
         * @since 1.17
         *
-        * @param array $update the update to run. Format is the following:
+        * @param array $update The update to run. Format is the following:
         *                first item is the callback function, it also can be a
         *                simple string with the name of a function in this class,
         *                following elements are parameters to the function.
@@ -388,7 +388,7 @@ abstract class DatabaseUpdater {
        /**
         * Do all the updates
         *
-        * @param array $what what updates to perform
+        * @param array $what What updates to perform
         */
        public function doUpdates( $what = array( 'core', 'extensions', 'stats' ) ) {
                global $wgVersion;
@@ -423,7 +423,7 @@ abstract class DatabaseUpdater {
         * Helper function for doUpdates()
         *
         * @param array $updates of updates to run
-        * @param $passSelf Boolean: whether to pass this object we calling external
+        * @param bool $passSelf Whether to pass this object we calling external
         *                  functions
         */
        private function runUpdates( array $updates, $passSelf ) {
@@ -450,8 +450,8 @@ abstract class DatabaseUpdater {
        }
 
        /**
-        * @param $version
-        * @param $updates array
+        * @param string $version
+        * @param array $updates
         */
        protected function setAppliedUpdates( $version, $updates = array() ) {
                $this->db->clearFlag( DBO_DDLMODE );
@@ -470,7 +470,6 @@ abstract class DatabaseUpdater {
         * Obviously, only use this for updates that occur after the updatelog table was
         * created!
         * @param string $key Name of the key to check for
-        *
         * @return bool
         */
        public function updateRowExists( $key ) {
@@ -489,7 +488,7 @@ abstract class DatabaseUpdater {
         * Obviously, only use this for updates that occur after the updatelog table was
         * created!
         * @param string $key Name of key to insert
-        * @param string $val [optional] value to insert along with the key
+        * @param string $val [optional] Value to insert along with the key
         */
        public function insertUpdateRow( $key, $val = null ) {
                $this->db->clearFlag( DBO_DDLMODE );
@@ -519,7 +518,7 @@ abstract class DatabaseUpdater {
         * Updates will be prevented if the table is a shared table and it is not
         * specified to run updates on shared tables.
         *
-        * @param string $name table name
+        * @param string $name Table name
         * @return bool
         */
        protected function doTable( $name ) {
@@ -584,7 +583,7 @@ abstract class DatabaseUpdater {
         * 1.13...) with the values being arrays of updates, identical to how
         * updaters.inc did it (for now)
         *
-        * @return Array
+        * @return array
         */
        abstract protected function getCoreUpdateList();
 
@@ -605,8 +604,8 @@ abstract class DatabaseUpdater {
         *
         * This is used as a callback for for sourceLine().
         *
-        * @param string $line text to append to the file
-        * @return Boolean false to skip actually executing the file
+        * @param string $line Text to append to the file
+        * @return bool False to skip actually executing the file
         * @throws MWException
         */
        public function appendLine( $line ) {
@@ -624,7 +623,7 @@ abstract class DatabaseUpdater {
         * @param string $path Path to the patch file
         * @param $isFullPath Boolean Whether to treat $path as a relative or not
         * @param string $msg Description of the patch
-        * @return boolean false if patch is skipped.
+        * @return bool False if patch is skipped.
         */
        protected function applyPatch( $path, $isFullPath = false, $msg = null ) {
                if ( $msg === null ) {
@@ -656,8 +655,8 @@ abstract class DatabaseUpdater {
         *
         * @param string $name Name of the new table
         * @param string $patch Path to the patch file
-        * @param $fullpath Boolean Whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        protected function addTable( $name, $patch, $fullpath = false ) {
                if ( !$this->doTable( $name ) ) {
@@ -679,8 +678,8 @@ abstract class DatabaseUpdater {
         * @param string $table Name of the table to modify
         * @param string $field Name of the new field
         * @param string $patch Path to the patch file
-        * @param $fullpath Boolean Whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        protected function addField( $table, $field, $patch, $fullpath = false ) {
                if ( !$this->doTable( $table ) ) {
@@ -704,8 +703,8 @@ abstract class DatabaseUpdater {
         * @param string $table Name of the table to modify
         * @param string $index Name of the new index
         * @param string $patch Path to the patch file
-        * @param $fullpath Boolean Whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        protected function addIndex( $table, $index, $patch, $fullpath = false ) {
                if ( !$this->doTable( $table ) ) {
@@ -729,8 +728,8 @@ abstract class DatabaseUpdater {
         * @param string $table Name of the table to modify
         * @param string $field Name of the old field
         * @param string $patch Path to the patch file
-        * @param $fullpath Boolean Whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        protected function dropField( $table, $field, $patch, $fullpath = false ) {
                if ( !$this->doTable( $table ) ) {
@@ -752,8 +751,8 @@ abstract class DatabaseUpdater {
         * @param string $table Name of the table to modify
         * @param string $index Name of the index
         * @param string $patch Path to the patch file
-        * @param $fullpath Boolean: Whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        protected function dropIndex( $table, $index, $patch, $fullpath = false ) {
                if ( !$this->doTable( $table ) ) {
@@ -778,8 +777,8 @@ abstract class DatabaseUpdater {
         * @param $skipBothIndexExistWarning Boolean: Whether to warn if both the
         * old and the new indexes exist.
         * @param string $patch Path to the patch file
-        * @param $fullpath Boolean: Whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        protected function renameIndex( $table, $oldIndex, $newIndex,
                $skipBothIndexExistWarning, $patch, $fullpath = false
@@ -830,10 +829,10 @@ abstract class DatabaseUpdater {
         *
         * Public @since 1.20
         *
-        * @param $table string
-        * @param $patch string|false
-        * @param $fullpath bool
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param string $table Table to drop.
+        * @param string|bool $patch String of patch file that will drop the table. Default: false.
+        * @param bool $fullpath Whether $patch is a full path. Default: false.
+        * @return bool False if this was skipped because schema changes are skipped
         */
        public function dropTable( $table, $patch = false, $fullpath = false ) {
                if ( !$this->doTable( $table ) ) {
@@ -860,11 +859,11 @@ abstract class DatabaseUpdater {
        /**
         * Modify an existing field
         *
-        * @param string $table name of the table to which the field belongs
-        * @param string $field name of the field to modify
-        * @param string $patch path to the patch file
-        * @param $fullpath Boolean: whether to treat $patch path as a relative or not
-        * @return Boolean false if this was skipped because schema changes are skipped
+        * @param string $table Name of the table to which the field belongs
+        * @param string $field Name of the field to modify
+        * @param string $patch Path to the patch file
+        * @param bool $fullpath Whether to treat $patch path as a relative or not
+        * @return bool False if this was skipped because schema changes are skipped
         */
        public function modifyField( $table, $field, $patch, $fullpath = false ) {
                if ( !$this->doTable( $table ) ) {
index 2f45005..e6b0fd3 100644 (file)
@@ -1301,8 +1301,13 @@ abstract class Installer {
        /**
         * Same as locateExecutable(), but checks in getPossibleBinPaths() by default
         * @see locateExecutable()
-        * @param $names
-        * @param $versionInfo bool
+        * @param array $names Array of possible names.
+        * @param array|bool $versionInfo Default: false or array with two members:
+        *         0 => Command to run for version check, with $1 for the full executable name
+        *         1 => String to compare the output with
+        *
+        * If $versionInfo is not false, only executables with a version
+        * matching $versionInfo[1] will be returned.
         * @return bool|string
         */
        public static function locateExecutableInDefaultPaths( $names, $versionInfo = false ) {
index fb675d7..3674353 100644 (file)
@@ -250,9 +250,10 @@ class MysqlUpdater extends DatabaseUpdater {
         * 1.4 betas were missing the 'binary' marker from logging.log_title,
         * which causes a collation mismatch error on joins in MySQL 4.1.
         *
-        * @param string $table table name
-        * @param string $field field name to check
-        * @param string $patchFile path to the patch to correct the field
+        * @param string $table Table name
+        * @param string $field Field name to check
+        * @param string $patchFile Path to the patch to correct the field
+        * @return bool
         */
        protected function checkBin( $table, $field, $patchFile ) {
                if ( !$this->doTable( $table ) ) {
@@ -270,10 +271,10 @@ class MysqlUpdater extends DatabaseUpdater {
        /**
         * Check whether an index contain a field
         *
-        * @param string $table table name
-        * @param string $index index name to check
-        * @param string $field field that should be in the index
-        * @return Boolean
+        * @param string $table Table name
+        * @param string $index Index name to check
+        * @param string $field Field that should be in the index
+        * @return bool
         */
        protected function indexHasField( $table, $index, $field ) {
                if ( !$this->doTable( $table ) ) {
index e23edf5..53cb7dc 100644 (file)
@@ -124,7 +124,7 @@ class WebInstaller extends Installer {
        /**
         * Constructor.
         *
-        * @param $request WebRequest
+        * @param WebRequest $request
         */
        public function __construct( WebRequest $request ) {
                parent::__construct();
@@ -142,7 +142,7 @@ class WebInstaller extends Installer {
         *
         * @param array $session initial session array
         *
-        * @return Array: new session array
+        * @return array New session array
         */
        public function execute( array $session ) {
                $this->session = $session;
@@ -391,7 +391,7 @@ class WebInstaller extends Installer {
        /**
         * Temporary error handler for session start debugging.
         * @param $errno
-        * @param $errstr string
+        * @param string $errstr
         */
        public function errorHandler( $errno, $errstr ) {
                $this->phpErrors[] = $errstr;
@@ -424,7 +424,7 @@ class WebInstaller extends Installer {
        /**
         * Get a URL for submission back to the same script.
         *
-        * @param $query array
+        * @param array $query
         * @return string
         */
        public function getUrl( $query = array() ) {
@@ -442,7 +442,7 @@ class WebInstaller extends Installer {
        /**
         * Get a WebInstallerPage by name.
         *
-        * @param $pageName String
+        * @param string $pageName
         * @return WebInstallerPage
         */
        public function getPageByName( $pageName ) {
@@ -454,7 +454,7 @@ class WebInstaller extends Installer {
        /**
         * Get a session variable.
         *
-        * @param $name String
+        * @param string $name
         * @param $default
         * @return null
         */
@@ -468,8 +468,8 @@ class WebInstaller extends Installer {
 
        /**
         * Set a session variable.
-        * @param string $name key for the variable
-        * @param $value Mixed
+        * @param string $name Key for the variable
+        * @param mixed $value
         */
        public function setSession( $name, $value ) {
                $this->session[$name] = $value;
@@ -523,7 +523,7 @@ class WebInstaller extends Installer {
        /**
         * Called by execute() before page output starts, to show a page list.
         *
-        * @param $currentPageName string
+        * @param string $currentPageName
         */
        private function startPageWrapper( $currentPageName ) {
                $s = "<div class=\"config-page-wrapper\">\n";
@@ -563,9 +563,9 @@ class WebInstaller extends Installer {
        /**
         * Get a list item for the page list.
         *
-        * @param $pageName string
-        * @param $enabled boolean
-        * @param $currentPageName string
+        * @param string $pageName
+        * @param bool $enabled
+        * @param string $currentPageName
         *
         * @return string
         */
@@ -630,7 +630,7 @@ class WebInstaller extends Installer {
        /**
         * Get HTML for an error box with an icon.
         *
-        * @param string $text wikitext, get this with wfMessage()->plain()
+        * @param string $text Wikitext, get this with wfMessage()->plain()
         *
         * @return string
         */
@@ -641,7 +641,7 @@ class WebInstaller extends Installer {
        /**
         * Get HTML for a warning box with an icon.
         *
-        * @param string $text wikitext, get this with wfMessage()->plain()
+        * @param string $text Wikitext, get this with wfMessage()->plain()
         *
         * @return string
         */
@@ -652,9 +652,9 @@ class WebInstaller extends Installer {
        /**
         * Get HTML for an info box with an icon.
         *
-        * @param string $text wikitext, get this with wfMessage()->plain()
-        * @param string $icon icon name, file in skins/common/images
-        * @param string $class additional class name to add to the wrapper div
+        * @param string $text Wikitext, get this with wfMessage()->plain()
+        * @param string|bool $icon Icon name, file in skins/common/images. Default: false
+        * @param string|bool $class Additional class name to add to the wrapper div. Default: false.
         *
         * @return string
         */
@@ -691,7 +691,7 @@ class WebInstaller extends Installer {
 
        /**
         * Output a help box.
-        * @param string $msg key for wfMessage()
+        * @param string $msg Key for wfMessage()
         */
        public function showHelpBox( $msg /*, ... */ ) {
                $args = func_get_args();
@@ -703,7 +703,7 @@ class WebInstaller extends Installer {
         * Show a short informational message.
         * Output looks like a list.
         *
-        * @param $msg string
+        * @param string $msg
         */
        public function showMessage( $msg /*, ... */ ) {
                $args = func_get_args();
@@ -715,7 +715,7 @@ class WebInstaller extends Installer {
        }
 
        /**
-        * @param $status Status
+        * @param Status $status
         */
        public function showStatusMessage( Status $status ) {
                $errors = array_merge( $status->getErrorsArray(), $status->getWarningsArray() );
@@ -731,7 +731,7 @@ class WebInstaller extends Installer {
         * @param $msg
         * @param $forId
         * @param $contents
-        * @param $helpData string
+        * @param string $helpData
         * @return string
         */
        public function label( $msg, $forId, $contents, $helpData = "" ) {
@@ -764,7 +764,7 @@ class WebInstaller extends Installer {
        /**
         * Get a labelled text box to configure a variable.
         *
-        * @param $params Array
+        * @param array $params
         *    Parameters are:
         *      var:         The variable to be configured (required)
         *      label:       The message name for the label (required)
@@ -811,7 +811,7 @@ class WebInstaller extends Installer {
        /**
         * Get a labelled textarea to configure a variable
         *
-        * @param $params Array
+        * @param array $params
         *    Parameters are:
         *      var:         The variable to be configured (required)
         *      label:       The message name for the label (required)
@@ -860,7 +860,7 @@ class WebInstaller extends Installer {
         * Get a labelled password box to configure a variable.
         *
         * Implements password hiding
-        * @param $params Array
+        * @param array $params
         *    Parameters are:
         *      var:         The variable to be configured (required)
         *      label:       The message name for the label (required)
@@ -889,7 +889,7 @@ class WebInstaller extends Installer {
        /**
         * Get a labelled checkbox to configure a boolean variable.
         *
-        * @param $params Array
+        * @param array $params
         *    Parameters are:
         *      var:         The variable to be configured (required)
         *      label:       The message name for the label (required)
@@ -940,7 +940,7 @@ class WebInstaller extends Installer {
        /**
         * Get a set of labelled radio buttons.
         *
-        * @param $params Array
+        * @param array $params
         *    Parameters are:
         *      var:             The variable to be configured (required)
         *      label:           The message name for the label (required)
@@ -1006,7 +1006,7 @@ class WebInstaller extends Installer {
        /**
         * Output an error or warning box using a Status object.
         *
-        * @param $status Status
+        * @param Status $status
         */
        public function showStatusBox( $status ) {
                if ( !$status->isGood() ) {
@@ -1027,8 +1027,8 @@ class WebInstaller extends Installer {
         * Assumes that variables containing "password" in the name are (potentially
         * fake) passwords.
         *
-        * @param $varNames Array
-        * @param string $prefix the prefix added to variables to obtain form names
+        * @param array $varNames
+        * @param string $prefix The prefix added to variables to obtain form names
         *
         * @return array
         */
@@ -1092,7 +1092,7 @@ class WebInstaller extends Installer {
         * @param $text
         * @param $attribs
         * @param $parser
-        * @return String Html for download link
+        * @return string Html for download link
         */
        public function downloadLinkHook( $text, $attribs, $parser ) {
                $img = Html::element( 'img', array(
index f2dc37f..6178e5b 100644 (file)
@@ -294,9 +294,8 @@ class WebInstallerOutput {
 
        public function outputFooter() {
                if ( $this->useShortHeader ) {
-?>
-</body></html>
-<?php
+                       echo Html::closeElement( 'body' ) . Html::closeElement( 'html' );
+
                        return;
                }
 ?>
@@ -317,9 +316,8 @@ class WebInstallerOutput {
        </div></div>
 </div>
 
-</body>
-</html>
 <?php
+               echo Html::closeElement( 'body' ) . Html::closeElement( 'html' );
        }
 
        public function outputShortHeader() {
index e30373e..1c95d9a 100644 (file)
@@ -185,7 +185,6 @@ abstract class WebInstallerPage {
 }
 
 class WebInstaller_Language extends WebInstallerPage {
-
        public function execute() {
                global $wgLang;
                $r = $this->parent->request;
index d3ce164..36f4959 100644 (file)
@@ -56,6 +56,8 @@ class JobQueueFederated extends JobQueue {
        /** @var BagOStuff */
        protected $cache;
 
+       protected $maxPartitionsTry;  // integer; maximum number of partitions to try
+
        const CACHE_TTL_SHORT = 30; // integer; seconds to cache info without re-validating
        const CACHE_TTL_LONG = 300; // integer; seconds to cache info that is kept up to date
 
@@ -72,6 +74,10 @@ class JobQueueFederated extends JobQueue {
         *                          the federated queue itself (e.g. 'order' and 'claimTTL').
         *  - partitionsNoPush    : List of partition names that can handle pop() but not push().
         *                          This can be used to migrate away from a certain partition.
+        *  - maxPartitionsTry    : Maximum number of times to attempt job insertion using
+        *                          different partition queues. This improves availability
+        *                          during failure, at the cost of added latency and somewhat
+        *                          less reliable job de-duplication mechanisms.
         * @param array $params
         */
        protected function __construct( array $params ) {
@@ -82,6 +88,9 @@ class JobQueueFederated extends JobQueue {
                if ( !isset( $params['partitionsBySection'][$section] ) ) {
                        throw new MWException( "No configuration for section '$section'." );
                }
+               $this->maxPartitionsTry = isset( $params['maxPartitionsTry'] )
+                       ? $params['maxPartitionsTry']
+                       : 2;
                // Get the full partition map
                $this->partitionMap = $params['partitionsBySection'][$section];
                arsort( $this->partitionMap, SORT_NUMERIC );
@@ -94,10 +103,10 @@ class JobQueueFederated extends JobQueue {
                }
                // Get the config to pass to merge into each partition queue config
                $baseConfig = $params;
-               foreach ( array( 'class', 'sectionsByWiki',
+               foreach ( array( 'class', 'sectionsByWiki', 'maxPartitionsTry',
                        'partitionsBySection', 'configByPartition', 'partitionsNoPush' ) as $o )
                {
-                       unset( $baseConfig[$o] );
+                       unset( $baseConfig[$o] ); // partition queue doesn't care about this
                }
                // Get the partition queue objects
                foreach ( $this->partitionMap as $partition => $w ) {
@@ -194,16 +203,17 @@ class JobQueueFederated extends JobQueue {
        }
 
        protected function doBatchPush( array $jobs, $flags ) {
-               if ( !count( $jobs ) ) {
-                       return true; // nothing to do
-               }
                // Local ring variable that may be changed to point to a new ring on failure
                $partitionRing = $this->partitionPushRing;
-               // Try to insert the jobs and update $partitionsTry on any failures
-               $jobsLeft = $this->tryJobInsertions( $jobs, $partitionRing, $flags );
-               if ( count( $jobsLeft ) ) { // some jobs failed to insert?
-                       // Try to insert the remaning jobs once more, ignoring the bad partitions
-                       return !count( $this->tryJobInsertions( $jobsLeft, $partitionRing, $flags ) );
+               // Try to insert the jobs and update $partitionsTry on any failures.
+               // Retry to insert any remaning jobs again, ignoring the bad partitions.
+               $jobsLeft = $jobs;
+               for ( $i = $this->maxPartitionsTry; $i > 0 && count( $jobsLeft ); --$i ) {
+                       $jobsLeft = $this->tryJobInsertions( $jobsLeft, $partitionRing, $flags );
+               }
+               if ( count( $jobsLeft ) ) {
+                       throw new JobQueueError(
+                               "Could not insert job(s), {$this->maxPartitionsTry} partitions tried." );
                }
                return true;
        }
index 378e175..67bb5a4 100644 (file)
@@ -70,7 +70,7 @@ class JobQueueRedis extends JobQueue {
        /**
         * @params include:
         *   - redisConfig : An array of parameters to RedisConnectionPool::__construct().
-        *                   Note that the serializer option is ignored "none" is always used.
+        *                   Note that the serializer option is ignored as "none" is always used.
         *   - redisServer : A hostname/port combination or the absolute path of a UNIX socket.
         *                   If a hostname is specified but no port, the standard port number
         *                   6379 will be used. Required.
index b2a8e50..a1cb3a7 100644 (file)
@@ -498,16 +498,12 @@ class ManualLogEntry extends LogEntryBase {
        }
 
        /**
-        * Publishes the log entry.
-        * @param int $newId id of the log entry.
-        * @param string $to rcandudp (default), rc, udp
+        * Get a RecentChanges object for the log entry
+        * @param int $newId
+        * @return RecentChange
+        * @since 1.23
         */
-       public function publish( $newId, $to = 'rcandudp' ) {
-               $log = new LogPage( $this->getType() );
-               if ( $log->isRestricted() ) {
-                       return;
-               }
-
+       public function getRecentChange( $newId = 0 ) {
                $formatter = LogFormatter::newFromEntry( $this );
                $context = RequestContext::newExtraneousContext( $this->getTarget() );
                $formatter->setContext( $context );
@@ -524,7 +520,7 @@ class ManualLogEntry extends LogEntryBase {
                                $ip = $user->getName();
                        }
                }
-               $rc = RecentChange::newLogEntry(
+               return RecentChange::newLogEntry(
                        $this->getTimestamp(),
                        $logpage,
                        $user,
@@ -539,6 +535,21 @@ class ManualLogEntry extends LogEntryBase {
                        $formatter->getIRCActionComment() // Used for IRC feeds
                );
 
+       }
+
+       /**
+        * Publishes the log entry.
+        * @param int $newId id of the log entry.
+        * @param string $to rcandudp (default), rc, udp
+        */
+       public function publish( $newId, $to = 'rcandudp' ) {
+               $log = new LogPage( $this->getType() );
+               if ( $log->isRestricted() ) {
+                       return;
+               }
+
+               $rc = $this->getRecentChange( $newId );
+
                if ( $to === 'rc' || $to === 'rcandudp' ) {
                        $rc->save( 'pleasedontudp' );
                }
old mode 100755 (executable)
new mode 100644 (file)
index b34ad65..91c4c9a
@@ -1563,11 +1563,13 @@ class FormatMetadata extends ContextSource {
 
                $common = $file->getCommonMetaArray();
 
-               foreach ( $common as $key => $value ) {
-                       $fileMetadata[$key] = array(
-                               'value' => $value,
-                               'source' => 'file-metadata',
-                       );
+               if ( $common !== false ) {
+                       foreach ( $common as $key => $value ) {
+                               $fileMetadata[$key] = array(
+                                       'value' => $value,
+                                       'source' => 'file-metadata',
+                               );
+                       }
                }
 
                wfProfileOut( __METHOD__ );
old mode 100755 (executable)
new mode 100644 (file)
index 848a1a0..2df3160 100644 (file)
@@ -541,7 +541,7 @@ class Parser {
                        }
                        foreach ( $this->mOutput->getLimitReportData() as $key => $value ) {
                                if ( wfRunHooks( 'ParserLimitReportFormat',
-                                       array( $key, $value, &$limitReport, false, false )
+                                       array( $key, &$value, &$limitReport, false, false )
                                ) ) {
                                        $keyMsg = wfMessage( $key )->inLanguage( 'en' )->useDatabase( false );
                                        $valueMsg = wfMessage( array( "$key-value-text", "$key-value" ) )
index bde508a..e12f32d 100644 (file)
@@ -240,6 +240,7 @@ class ParserOptions {
        function getExternalLinkTarget()            { return $this->mExternalLinkTarget; }
        function getDisableContentConversion()      { return $this->mDisableContentConversion; }
        function getDisableTitleConversion()        { return $this->mDisableTitleConversion; }
+       /** @deprecated since 1.22 use User::getOption('math') instead */
        function getMath()                          { $this->optionUsed( 'math' );
                                                                                                  return $this->mMath; }
        function getThumbSize()                     { $this->optionUsed( 'thumbsize' );
@@ -338,6 +339,7 @@ class ParserOptions {
        function setExternalLinkTarget( $x )        { return wfSetVar( $this->mExternalLinkTarget, $x ); }
        function disableContentConversion( $x = true ) { return wfSetVar( $this->mDisableContentConversion, $x ); }
        function disableTitleConversion( $x = true ) { return wfSetVar( $this->mDisableTitleConversion, $x ); }
+       /** @deprecated since 1.22 */
        function setMath( $x )                      { return wfSetVar( $this->mMath, $x ); }
        function setUserLang( $x )                  {
                if ( is_string( $x ) ) {
index 3828b1c..6757990 100644 (file)
@@ -90,19 +90,17 @@ class EmailConfirmation extends UnlistedSpecialPage {
                        } else {
                                $out->addWikiText( $status->getWikiText( 'confirmemail_sendfailed' ) );
                        }
+               } elseif ( $user->isEmailConfirmed() ) {
+                       // date and time are separate parameters to facilitate localisation.
+                       // $time is kept for backward compat reasons.
+                       // 'emailauthenticated' is also used in SpecialPreferences.php
+                       $lang = $this->getLanguage();
+                       $emailAuthenticated = $user->getEmailAuthenticationTimestamp();
+                       $time = $lang->userTimeAndDate( $emailAuthenticated, $user );
+                       $d = $lang->userDate( $emailAuthenticated, $user );
+                       $t = $lang->userTime( $emailAuthenticated, $user );
+                       $out->addWikiMsg( 'emailauthenticated', $time, $d, $t );
                } else {
-                       if ( $user->isEmailConfirmed() ) {
-                               // date and time are separate parameters to facilitate localisation.
-                               // $time is kept for backward compat reasons.
-                               // 'emailauthenticated' is also used in SpecialPreferences.php
-                               $lang = $this->getLanguage();
-                               $emailAuthenticated = $user->getEmailAuthenticationTimestamp();
-                               $time = $lang->userTimeAndDate( $emailAuthenticated, $user );
-                               $d = $lang->userDate( $emailAuthenticated, $user );
-                               $t = $lang->userTime( $emailAuthenticated, $user );
-                               $out->addWikiMsg( 'emailauthenticated', $time, $d, $t );
-                       }
-
                        if ( $user->isEmailConfirmationPending() ) {
                                $out->wrapWikiMsg(
                                        "<div class=\"error mw-confirmemail-pending\">\n$1\n</div>",
index 501552e..ca2daaf 100644 (file)
@@ -36,7 +36,8 @@
  */
 class SpecialEditWatchlist extends UnlistedSpecialPage {
        /**
-        * Editing modes
+        * Editing modes. EDIT_CLEAR is no longer used; the "Clear" link scared people
+        * too much. Now it's passed on to the raw editor, from which it's very easy to clear.
         */
        const EDIT_CLEAR = 1;
        const EDIT_RAW = 2;
@@ -95,10 +96,6 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                $mode = self::getMode( $this->getRequest(), $mode );
 
                switch ( $mode ) {
-                       case self::EDIT_CLEAR:
-                               // The "Clear" link scared people too much.
-                               // Pass on to the raw editor, from which it's very easy to clear.
-
                        case self::EDIT_RAW:
                                $out->setPageTitle( $this->msg( 'watchlistedit-raw-title' ) );
                                $form = $this->getRawForm();
@@ -638,7 +635,6 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                switch ( $mode ) {
                        case 'clear':
                        case self::EDIT_CLEAR:
-                               return self::EDIT_CLEAR;
                        case 'raw':
                        case self::EDIT_RAW:
                                return self::EDIT_RAW;
index d5a6b29..8c8de8e 100644 (file)
@@ -318,13 +318,12 @@ class SpecialSearch extends SpecialPage {
                        )
                );
                $out->addHtml(
-                       Xml::openElement( 'table', array( 'id' => 'mw-search-top-table', 'cellpadding' => 0, 'cellspacing' => 0 ) ) .
-                       Xml::openElement( 'tr' ) .
-                       Xml::openElement( 'td' ) . "\n" .
+                       # This is an awful awful ID name. It's not a table, but we
+                       # named it poorly from when this was a table so now we're
+                       # stuck with it
+                       Xml::openElement( 'div', array( 'id' => 'mw-search-top-table' ) ) .
                        $this->shortDialog( $term ) .
-                       Xml::closeElement( 'td' ) .
-                       Xml::closeElement( 'tr' ) .
-                       Xml::closeElement( 'table' )
+                       Xml::closeElement( 'div' )
                );
 
                // Sometimes the search engine knows there are too many hits
index 5ac3e65..f40966c 100644 (file)
@@ -106,7 +106,7 @@ class LoginForm extends SpecialPage {
                $this->mAction = $request->getVal( 'action' );
                $this->mRemember = $request->getCheck( 'wpRemember' );
                $this->mFromHTTP = $request->getBool( 'fromhttp', false );
-               $this->mStickHTTPS = ( !$this->mFromHTTP && $request->detectProtocol() === 'https' ) || $request->getBool( 'wpForceHttps', false );
+               $this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' ) || $request->getBool( 'wpForceHttps', false );
                $this->mLanguage = $request->getText( 'uselang' );
                $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
                $this->mToken = ( $this->mType == 'signup' ) ? $request->getVal( 'wpCreateaccountToken' ) : $request->getVal( 'wpLoginToken' );
@@ -168,7 +168,7 @@ class LoginForm extends SpecialPage {
 
                // If logging in and not on HTTPS, either redirect to it or offer a link.
                global $wgSecureLogin;
-               if ( WebRequest::detectProtocol() !== 'https' ) {
+               if ( $this->mRequest->getProtocol() !== 'https' ) {
                        $title = $this->getFullTitle();
                        $query = array(
                                'returnto' => $this->mReturnTo,
@@ -1196,7 +1196,7 @@ class LoginForm extends SpecialPage {
                $template->set( 'secureLoginUrl', $this->mSecureLoginUrl );
                // Use loginend-https for HTTPS requests if it's not blank, loginend otherwise
                // Ditto for signupend.  New forms use neither.
-               $usingHTTPS = WebRequest::detectProtocol() == 'https';
+               $usingHTTPS = $this->mRequest->getProtocol() == 'https';
                $loginendHTTPS = $this->msg( 'loginend-https' );
                $signupendHTTPS = $this->msg( 'signupend-https' );
                if ( $usingHTTPS && !$loginendHTTPS->isBlank() ) {
index 4afa279..336b05b 100644 (file)
@@ -74,18 +74,12 @@ class SpecialWatchlist extends SpecialPage {
 
                $mode = SpecialEditWatchlist::getMode( $request, $par );
                if ( $mode !== false ) {
-                       # TODO: localise?
-                       switch ( $mode ) {
-                               case SpecialEditWatchlist::EDIT_CLEAR:
-                                       $mode = 'clear';
-                                       break;
-                               case SpecialEditWatchlist::EDIT_RAW:
-                                       $mode = 'raw';
-                                       break;
-                               default:
-                                       $mode = null;
+                       if ( $mode === SpecialEditWatchlist::EDIT_RAW ) {
+                               $title = SpecialPage::getTitleFor( 'EditWatchlist', 'raw' );
+                       } else {
+                               $title = SpecialPage::getTitleFor( 'EditWatchlist' );
                        }
-                       $title = SpecialPage::getTitleFor( 'EditWatchlist', $mode );
+
                        $output->redirect( $title->getLocalURL() );
                        return;
                }
index 97a56e1..a222f81 100644 (file)
@@ -63,6 +63,7 @@ class ArrayUtils {
                                break;
                        }
                }
+
                return $i;
        }
 }
index 81c0afe..c6de088 100644 (file)
@@ -35,10 +35,11 @@ abstract class CdbReader {
         */
        public static function open( $fileName ) {
                if ( self::haveExtension() ) {
-                       return new CdbReader_DBA( $fileName );
+                       return new CdbReaderDBA( $fileName );
                } else {
                        wfDebug( "Warning: no dba extension found, using emulation.\n" );
-                       return new CdbReader_PHP( $fileName );
+
+                       return new CdbReaderPHP( $fileName );
                }
        }
 
@@ -55,6 +56,7 @@ abstract class CdbReader {
                if ( !in_array( 'cdb', $handlers ) || !in_array( 'cdb_make', $handlers ) ) {
                        return false;
                }
+
                return true;
        }
 
@@ -87,14 +89,15 @@ abstract class CdbWriter {
         *
         * @param $fileName string
         *
-        * @return CdbWriter_DBA|CdbWriter_PHP
+        * @return CdbWriterDBA|CdbWriterPHP
         */
        public static function open( $fileName ) {
                if ( CdbReader::haveExtension() ) {
-                       return new CdbWriter_DBA( $fileName );
+                       return new CdbWriterDBA( $fileName );
                } else {
                        wfDebug( "Warning: no dba extension found, using emulation.\n" );
-                       return new CdbWriter_PHP( $fileName );
+
+                       return new CdbWriterPHP( $fileName );
                }
        }
 
@@ -122,7 +125,7 @@ abstract class CdbWriter {
 /**
  * Reader class which uses the DBA extension
  */
-class CdbReader_DBA {
+class CdbReaderDBA {
        var $handle;
 
        function __construct( $fileName ) {
@@ -147,7 +150,7 @@ class CdbReader_DBA {
 /**
  * Writer class which uses the DBA extension
  */
-class CdbWriter_DBA {
+class CdbWriterDBA {
        var $handle, $realFileName, $tmpFileName;
 
        function __construct( $fileName ) {
diff --git a/includes/utils/CdbPHP.php b/includes/utils/CdbPHP.php
new file mode 100644 (file)
index 0000000..e7bb4bc
--- /dev/null
@@ -0,0 +1,503 @@
+<?php
+/**
+ * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
+ * appears in PHP 5.3. Changes are:
+ *    * Error returns replaced with exceptions
+ *    * Exception thrown if sizes or offsets are between 2GB and 4GB
+ *    * Some variables renamed
+ *
+ * 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
+ *
+ * @file
+ */
+
+/**
+ * Common functions for readers and writers
+ */
+class CdbFunctions {
+       /**
+        * Take a modulo of a signed integer as if it were an unsigned integer.
+        * $b must be less than 0x40000000 and greater than 0
+        *
+        * @param $a
+        * @param $b
+        *
+        * @return int
+        */
+       public static function unsignedMod( $a, $b ) {
+               if ( $a & 0x80000000 ) {
+                       $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
+
+                       return $m % $b;
+               } else {
+                       return $a % $b;
+               }
+       }
+
+       /**
+        * Shift a signed integer right as if it were unsigned
+        * @param $a
+        * @param $b
+        * @return int
+        */
+       public static function unsignedShiftRight( $a, $b ) {
+               if ( $b == 0 ) {
+                       return $a;
+               }
+               if ( $a & 0x80000000 ) {
+                       return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
+               } else {
+                       return $a >> $b;
+               }
+       }
+
+       /**
+        * The CDB hash function.
+        *
+        * @param $s string
+        *
+        * @return
+        */
+       public static function hash( $s ) {
+               $h = 5381;
+               $len = strlen( $s );
+               for ( $i = 0; $i < $len; $i++ ) {
+                       $h5 = ( $h << 5 ) & 0xffffffff;
+                       // Do a 32-bit sum
+                       // Inlined here for speed
+                       $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
+                       $h =
+                               (
+                                       ( $sum & 0x40000000 ? 1 : 0 )
+                                       + ( $h & 0x80000000 ? 2 : 0 )
+                                       + ( $h & 0x40000000 ? 1 : 0 )
+                                       + ( $h5 & 0x80000000 ? 2 : 0 )
+                                       + ( $h5 & 0x40000000 ? 1 : 0 )
+                               ) << 30
+                               | ( $sum & 0x3fffffff );
+                       $h ^= ord( $s[$i] );
+                       $h &= 0xffffffff;
+               }
+
+               return $h;
+       }
+}
+
+/**
+ * CDB reader class
+ */
+class CdbReaderPHP extends CdbReader {
+       /** The filename */
+       var $fileName;
+
+       /** The file handle */
+       var $handle;
+
+       /* number of hash slots searched under this key */
+       var $loop;
+
+       /* initialized if loop is nonzero */
+       var $khash;
+
+       /* initialized if loop is nonzero */
+       var $kpos;
+
+       /* initialized if loop is nonzero */
+       var $hpos;
+
+       /* initialized if loop is nonzero */
+       var $hslots;
+
+       /* initialized if findNext() returns true */
+       var $dpos;
+
+       /* initialized if cdb_findnext() returns 1 */
+       var $dlen;
+
+       /**
+        * @param $fileName string
+        * @throws MWException
+        */
+       function __construct( $fileName ) {
+               $this->fileName = $fileName;
+               $this->handle = fopen( $fileName, 'rb' );
+               if ( !$this->handle ) {
+                       throw new MWException( 'Unable to open CDB file "' . $this->fileName . '".' );
+               }
+               $this->findStart();
+       }
+
+       function close() {
+               if ( isset( $this->handle ) ) {
+                       fclose( $this->handle );
+               }
+               unset( $this->handle );
+       }
+
+       /**
+        * @param $key
+        * @return bool|string
+        */
+       public function get( $key ) {
+               // strval is required
+               if ( $this->find( strval( $key ) ) ) {
+                       return $this->read( $this->dlen, $this->dpos );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * @param $key
+        * @param $pos
+        * @return bool
+        */
+       protected function match( $key, $pos ) {
+               $buf = $this->read( strlen( $key ), $pos );
+
+               return $buf === $key;
+       }
+
+       protected function findStart() {
+               $this->loop = 0;
+       }
+
+       /**
+        * @throws MWException
+        * @param $length
+        * @param $pos
+        * @return string
+        */
+       protected function read( $length, $pos ) {
+               if ( fseek( $this->handle, $pos ) == -1 ) {
+                       // This can easily happen if the internal pointers are incorrect
+                       throw new MWException(
+                               'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
+               }
+
+               if ( $length == 0 ) {
+                       return '';
+               }
+
+               $buf = fread( $this->handle, $length );
+               if ( $buf === false || strlen( $buf ) !== $length ) {
+                       throw new MWException(
+                               'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
+               }
+
+               return $buf;
+       }
+
+       /**
+        * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
+        * @param $s
+        * @throws MWException
+        * @return mixed
+        */
+       protected function unpack31( $s ) {
+               $data = unpack( 'V', $s );
+               if ( $data[1] > 0x7fffffff ) {
+                       throw new MWException(
+                               'Error in CDB file "' . $this->fileName . '", integer too big.' );
+               }
+
+               return $data[1];
+       }
+
+       /**
+        * Unpack a 32-bit signed integer
+        * @param $s
+        * @return int
+        */
+       protected function unpackSigned( $s ) {
+               $data = unpack( 'va/vb', $s );
+
+               return $data['a'] | ( $data['b'] << 16 );
+       }
+
+       /**
+        * @param $key
+        * @return bool
+        */
+       protected function findNext( $key ) {
+               if ( !$this->loop ) {
+                       $u = CdbFunctions::hash( $key );
+                       $buf = $this->read( 8, ( $u << 3 ) & 2047 );
+                       $this->hslots = $this->unpack31( substr( $buf, 4 ) );
+                       if ( !$this->hslots ) {
+                               return false;
+                       }
+                       $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
+                       $this->khash = $u;
+                       $u = CdbFunctions::unsignedShiftRight( $u, 8 );
+                       $u = CdbFunctions::unsignedMod( $u, $this->hslots );
+                       $u <<= 3;
+                       $this->kpos = $this->hpos + $u;
+               }
+
+               while ( $this->loop < $this->hslots ) {
+                       $buf = $this->read( 8, $this->kpos );
+                       $pos = $this->unpack31( substr( $buf, 4 ) );
+                       if ( !$pos ) {
+                               return false;
+                       }
+                       $this->loop += 1;
+                       $this->kpos += 8;
+                       if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
+                               $this->kpos = $this->hpos;
+                       }
+                       $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
+                       if ( $u === $this->khash ) {
+                               $buf = $this->read( 8, $pos );
+                               $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
+                               if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
+                                       // Found
+                                       $this->dlen = $this->unpack31( substr( $buf, 4 ) );
+                                       $this->dpos = $pos + 8 + $keyLen;
+
+                                       return true;
+                               }
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * @param $key
+        * @return bool
+        */
+       protected function find( $key ) {
+               $this->findStart();
+
+               return $this->findNext( $key );
+       }
+}
+
+/**
+ * CDB writer class
+ */
+class CdbWriterPHP extends CdbWriter {
+       var $handle, $realFileName, $tmpFileName;
+
+       var $hplist;
+       var $numentries, $pos;
+
+       /**
+        * @param $fileName string
+        */
+       function __construct( $fileName ) {
+               $this->realFileName = $fileName;
+               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
+               $this->handle = fopen( $this->tmpFileName, 'wb' );
+               if ( !$this->handle ) {
+                       $this->throwException(
+                               'Unable to open CDB file "' . $this->tmpFileName . '" for write.' );
+               }
+               $this->hplist = array();
+               $this->numentries = 0;
+               $this->pos = 2048; // leaving space for the pointer array, 256 * 8
+               if ( fseek( $this->handle, $this->pos ) == -1 ) {
+                       $this->throwException( 'fseek failed in file "' . $this->tmpFileName . '".' );
+               }
+       }
+
+       function __destruct() {
+               if ( isset( $this->handle ) ) {
+                       $this->close();
+               }
+       }
+
+       /**
+        * @param $key
+        * @param $value
+        * @return
+        */
+       public function set( $key, $value ) {
+               if ( strval( $key ) === '' ) {
+                       // DBA cross-check hack
+                       return;
+               }
+               $this->addbegin( strlen( $key ), strlen( $value ) );
+               $this->write( $key );
+               $this->write( $value );
+               $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
+       }
+
+       /**
+        * @throws MWException
+        */
+       public function close() {
+               $this->finish();
+               if ( isset( $this->handle ) ) {
+                       fclose( $this->handle );
+               }
+               if ( wfIsWindows() && file_exists( $this->realFileName ) ) {
+                       unlink( $this->realFileName );
+               }
+               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
+                       $this->throwException( 'Unable to move the new CDB file into place.' );
+               }
+               unset( $this->handle );
+       }
+
+       /**
+        * @throws MWException
+        * @param $buf
+        */
+       protected function write( $buf ) {
+               $len = fwrite( $this->handle, $buf );
+               if ( $len !== strlen( $buf ) ) {
+                       $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' );
+               }
+       }
+
+       /**
+        * @throws MWException
+        * @param $len
+        */
+       protected function posplus( $len ) {
+               $newpos = $this->pos + $len;
+               if ( $newpos > 0x7fffffff ) {
+                       $this->throwException(
+                               'A value in the CDB file "' . $this->tmpFileName . '" is too large.' );
+               }
+               $this->pos = $newpos;
+       }
+
+       /**
+        * @param $keylen
+        * @param $datalen
+        * @param $h
+        */
+       protected function addend( $keylen, $datalen, $h ) {
+               $this->hplist[] = array(
+                       'h' => $h,
+                       'p' => $this->pos
+               );
+
+               $this->numentries++;
+               $this->posplus( 8 );
+               $this->posplus( $keylen );
+               $this->posplus( $datalen );
+       }
+
+       /**
+        * @throws MWException
+        * @param $keylen
+        * @param $datalen
+        */
+       protected function addbegin( $keylen, $datalen ) {
+               if ( $keylen > 0x7fffffff ) {
+                       $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' );
+               }
+               if ( $datalen > 0x7fffffff ) {
+                       $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' );
+               }
+               $buf = pack( 'VV', $keylen, $datalen );
+               $this->write( $buf );
+       }
+
+       /**
+        * @throws MWException
+        */
+       protected function finish() {
+               // Hack for DBA cross-check
+               $this->hplist = array_reverse( $this->hplist );
+
+               // Calculate the number of items that will be in each hashtable
+               $counts = array_fill( 0, 256, 0 );
+               foreach ( $this->hplist as $item ) {
+                       ++$counts[255 & $item['h']];
+               }
+
+               // Fill in $starts with the *end* indexes
+               $starts = array();
+               $pos = 0;
+               for ( $i = 0; $i < 256; ++$i ) {
+                       $pos += $counts[$i];
+                       $starts[$i] = $pos;
+               }
+
+               // Excessively clever and indulgent code to simultaneously fill $packedTables
+               // with the packed hashtables, and adjust the elements of $starts
+               // to actually point to the starts instead of the ends.
+               $packedTables = array_fill( 0, $this->numentries, false );
+               foreach ( $this->hplist as $item ) {
+                       $packedTables[--$starts[255 & $item['h']]] = $item;
+               }
+
+               $final = '';
+               for ( $i = 0; $i < 256; ++$i ) {
+                       $count = $counts[$i];
+
+                       // The size of the hashtable will be double the item count.
+                       // The rest of the slots will be empty.
+                       $len = $count + $count;
+                       $final .= pack( 'VV', $this->pos, $len );
+
+                       $hashtable = array();
+                       for ( $u = 0; $u < $len; ++$u ) {
+                               $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
+                       }
+
+                       // Fill the hashtable, using the next empty slot if the hashed slot
+                       // is taken.
+                       for ( $u = 0; $u < $count; ++$u ) {
+                               $hp = $packedTables[$starts[$i] + $u];
+                               $where = CdbFunctions::unsignedMod(
+                                       CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
+                               while ( $hashtable[$where]['p'] ) {
+                                       if ( ++$where == $len ) {
+                                               $where = 0;
+                                       }
+                               }
+                               $hashtable[$where] = $hp;
+                       }
+
+                       // Write the hashtable
+                       for ( $u = 0; $u < $len; ++$u ) {
+                               $buf = pack( 'vvV',
+                                       $hashtable[$u]['h'] & 0xffff,
+                                       CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
+                                       $hashtable[$u]['p'] );
+                               $this->write( $buf );
+                               $this->posplus( 8 );
+                       }
+               }
+
+               // Write the pointer array at the start of the file
+               rewind( $this->handle );
+               if ( ftell( $this->handle ) != 0 ) {
+                       $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' );
+               }
+               $this->write( $final );
+       }
+
+       /**
+        * Clean up the temp file and throw an exception
+        *
+        * @param $msg string
+        * @throws MWException
+        */
+       protected function throwException( $msg ) {
+               if ( $this->handle ) {
+                       fclose( $this->handle );
+                       unlink( $this->tmpFileName );
+               }
+               throw new MWException( $msg );
+       }
+}
diff --git a/includes/utils/Cdb_PHP.php b/includes/utils/Cdb_PHP.php
deleted file mode 100644 (file)
index a38b9a8..0000000
+++ /dev/null
@@ -1,493 +0,0 @@
-<?php
-/**
- * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
- * appears in PHP 5.3. Changes are:
- *    * Error returns replaced with exceptions
- *    * Exception thrown if sizes or offsets are between 2GB and 4GB
- *    * Some variables renamed
- *
- * 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
- *
- * @file
- */
-
-/**
- * Common functions for readers and writers
- */
-class CdbFunctions {
-       /**
-        * Take a modulo of a signed integer as if it were an unsigned integer.
-        * $b must be less than 0x40000000 and greater than 0
-        *
-        * @param $a
-        * @param $b
-        *
-        * @return int
-        */
-       public static function unsignedMod( $a, $b ) {
-               if ( $a & 0x80000000 ) {
-                       $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
-                       return $m % $b;
-               } else {
-                       return $a % $b;
-               }
-       }
-
-       /**
-        * Shift a signed integer right as if it were unsigned
-        * @param $a
-        * @param $b
-        * @return int
-        */
-       public static function unsignedShiftRight( $a, $b ) {
-               if ( $b == 0 ) {
-                       return $a;
-               }
-               if ( $a & 0x80000000 ) {
-                       return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
-               } else {
-                       return $a >> $b;
-               }
-       }
-
-       /**
-        * The CDB hash function.
-        *
-        * @param $s string
-        *
-        * @return
-        */
-       public static function hash( $s ) {
-               $h = 5381;
-               for ( $i = 0; $i < strlen( $s ); $i++ ) {
-                       $h5 = ( $h << 5 ) & 0xffffffff;
-                       // Do a 32-bit sum
-                       // Inlined here for speed
-                       $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
-                       $h =
-                               (
-                                       ( $sum & 0x40000000 ? 1 : 0 )
-                                       + ( $h & 0x80000000 ? 2 : 0 )
-                                       + ( $h & 0x40000000 ? 1 : 0 )
-                                       + ( $h5 & 0x80000000 ? 2 : 0 )
-                                       + ( $h5 & 0x40000000 ? 1 : 0 )
-                               ) << 30
-                               | ( $sum & 0x3fffffff );
-                       $h ^= ord( $s[$i] );
-                       $h &= 0xffffffff;
-               }
-               return $h;
-       }
-}
-
-/**
- * CDB reader class
- */
-class CdbReader_PHP extends CdbReader {
-       /** The filename */
-       var $fileName;
-
-       /** The file handle */
-       var $handle;
-
-       /* number of hash slots searched under this key */
-       var $loop;
-
-       /* initialized if loop is nonzero */
-       var $khash;
-
-       /* initialized if loop is nonzero */
-       var $kpos;
-
-       /* initialized if loop is nonzero */
-       var $hpos;
-
-       /* initialized if loop is nonzero */
-       var $hslots;
-
-       /* initialized if findNext() returns true */
-       var $dpos;
-
-       /* initialized if cdb_findnext() returns 1 */
-       var $dlen;
-
-       /**
-        * @param $fileName string
-        * @throws MWException
-        */
-       function __construct( $fileName ) {
-               $this->fileName = $fileName;
-               $this->handle = fopen( $fileName, 'rb' );
-               if ( !$this->handle ) {
-                       throw new MWException( 'Unable to open CDB file "' . $this->fileName . '".' );
-               }
-               $this->findStart();
-       }
-
-       function close() {
-               if ( isset( $this->handle ) ) {
-                       fclose( $this->handle );
-               }
-               unset( $this->handle );
-       }
-
-       /**
-        * @param $key
-        * @return bool|string
-        */
-       public function get( $key ) {
-               // strval is required
-               if ( $this->find( strval( $key ) ) ) {
-                       return $this->read( $this->dlen, $this->dpos );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * @param $key
-        * @param $pos
-        * @return bool
-        */
-       protected function match( $key, $pos ) {
-               $buf = $this->read( strlen( $key ), $pos );
-               return $buf === $key;
-       }
-
-       protected function findStart() {
-               $this->loop = 0;
-       }
-
-       /**
-        * @throws MWException
-        * @param $length
-        * @param $pos
-        * @return string
-        */
-       protected function read( $length, $pos ) {
-               if ( fseek( $this->handle, $pos ) == -1 ) {
-                       // This can easily happen if the internal pointers are incorrect
-                       throw new MWException(
-                               'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
-               }
-
-               if ( $length == 0 ) {
-                       return '';
-               }
-
-               $buf = fread( $this->handle, $length );
-               if ( $buf === false || strlen( $buf ) !== $length ) {
-                       throw new MWException(
-                               'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
-               }
-               return $buf;
-       }
-
-       /**
-        * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
-        * @param $s
-        * @throws MWException
-        * @return mixed
-        */
-       protected function unpack31( $s ) {
-               $data = unpack( 'V', $s );
-               if ( $data[1] > 0x7fffffff ) {
-                       throw new MWException(
-                               'Error in CDB file "' . $this->fileName . '", integer too big.' );
-               }
-               return $data[1];
-       }
-
-       /**
-        * Unpack a 32-bit signed integer
-        * @param $s
-        * @return int
-        */
-       protected function unpackSigned( $s ) {
-               $data = unpack( 'va/vb', $s );
-               return $data['a'] | ( $data['b'] << 16 );
-       }
-
-       /**
-        * @param $key
-        * @return bool
-        */
-       protected function findNext( $key ) {
-               if ( !$this->loop ) {
-                       $u = CdbFunctions::hash( $key );
-                       $buf = $this->read( 8, ( $u << 3 ) & 2047 );
-                       $this->hslots = $this->unpack31( substr( $buf, 4 ) );
-                       if ( !$this->hslots ) {
-                               return false;
-                       }
-                       $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
-                       $this->khash = $u;
-                       $u = CdbFunctions::unsignedShiftRight( $u, 8 );
-                       $u = CdbFunctions::unsignedMod( $u, $this->hslots );
-                       $u <<= 3;
-                       $this->kpos = $this->hpos + $u;
-               }
-
-               while ( $this->loop < $this->hslots ) {
-                       $buf = $this->read( 8, $this->kpos );
-                       $pos = $this->unpack31( substr( $buf, 4 ) );
-                       if ( !$pos ) {
-                               return false;
-                       }
-                       $this->loop += 1;
-                       $this->kpos += 8;
-                       if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
-                               $this->kpos = $this->hpos;
-                       }
-                       $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
-                       if ( $u === $this->khash ) {
-                               $buf = $this->read( 8, $pos );
-                               $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
-                               if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
-                                       // Found
-                                       $this->dlen = $this->unpack31( substr( $buf, 4 ) );
-                                       $this->dpos = $pos + 8 + $keyLen;
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * @param $key
-        * @return bool
-        */
-       protected function find( $key ) {
-               $this->findStart();
-               return $this->findNext( $key );
-       }
-}
-
-/**
- * CDB writer class
- */
-class CdbWriter_PHP extends CdbWriter {
-       var $handle, $realFileName, $tmpFileName;
-
-       var $hplist;
-       var $numentries, $pos;
-
-       /**
-        * @param $fileName string
-        */
-       function __construct( $fileName ) {
-               $this->realFileName = $fileName;
-               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
-               $this->handle = fopen( $this->tmpFileName, 'wb' );
-               if ( !$this->handle ) {
-                       $this->throwException(
-                               'Unable to open CDB file "' . $this->tmpFileName . '" for write.' );
-               }
-               $this->hplist = array();
-               $this->numentries = 0;
-               $this->pos = 2048; // leaving space for the pointer array, 256 * 8
-               if ( fseek( $this->handle, $this->pos ) == -1 ) {
-                       $this->throwException( 'fseek failed in file "' . $this->tmpFileName . '".' );
-               }
-       }
-
-       function __destruct() {
-               if ( isset( $this->handle ) ) {
-                       $this->close();
-               }
-       }
-
-       /**
-        * @param $key
-        * @param $value
-        * @return
-        */
-       public function set( $key, $value ) {
-               if ( strval( $key ) === '' ) {
-                       // DBA cross-check hack
-                       return;
-               }
-               $this->addbegin( strlen( $key ), strlen( $value ) );
-               $this->write( $key );
-               $this->write( $value );
-               $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
-       }
-
-       /**
-        * @throws MWException
-        */
-       public function close() {
-               $this->finish();
-               if ( isset( $this->handle ) ) {
-                       fclose( $this->handle );
-               }
-               if ( wfIsWindows() && file_exists( $this->realFileName ) ) {
-                       unlink( $this->realFileName );
-               }
-               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
-                       $this->throwException( 'Unable to move the new CDB file into place.' );
-               }
-               unset( $this->handle );
-       }
-
-       /**
-        * @throws MWException
-        * @param $buf
-        */
-       protected function write( $buf ) {
-               $len = fwrite( $this->handle, $buf );
-               if ( $len !== strlen( $buf ) ) {
-                       $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' );
-               }
-       }
-
-       /**
-        * @throws MWException
-        * @param $len
-        */
-       protected function posplus( $len ) {
-               $newpos = $this->pos + $len;
-               if ( $newpos > 0x7fffffff ) {
-                       $this->throwException(
-                               'A value in the CDB file "' . $this->tmpFileName . '" is too large.' );
-               }
-               $this->pos = $newpos;
-       }
-
-       /**
-        * @param $keylen
-        * @param $datalen
-        * @param $h
-        */
-       protected function addend( $keylen, $datalen, $h ) {
-               $this->hplist[] = array(
-                       'h' => $h,
-                       'p' => $this->pos
-               );
-
-               $this->numentries++;
-               $this->posplus( 8 );
-               $this->posplus( $keylen );
-               $this->posplus( $datalen );
-       }
-
-       /**
-        * @throws MWException
-        * @param $keylen
-        * @param $datalen
-        */
-       protected function addbegin( $keylen, $datalen ) {
-               if ( $keylen > 0x7fffffff ) {
-                       $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' );
-               }
-               if ( $datalen > 0x7fffffff ) {
-                       $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' );
-               }
-               $buf = pack( 'VV', $keylen, $datalen );
-               $this->write( $buf );
-       }
-
-       /**
-        * @throws MWException
-        */
-       protected function finish() {
-               // Hack for DBA cross-check
-               $this->hplist = array_reverse( $this->hplist );
-
-               // Calculate the number of items that will be in each hashtable
-               $counts = array_fill( 0, 256, 0 );
-               foreach ( $this->hplist as $item ) {
-                       ++ $counts[255 & $item['h']];
-               }
-
-               // Fill in $starts with the *end* indexes
-               $starts = array();
-               $pos = 0;
-               for ( $i = 0; $i < 256; ++$i ) {
-                       $pos += $counts[$i];
-                       $starts[$i] = $pos;
-               }
-
-               // Excessively clever and indulgent code to simultaneously fill $packedTables
-               // with the packed hashtables, and adjust the elements of $starts
-               // to actually point to the starts instead of the ends.
-               $packedTables = array_fill( 0, $this->numentries, false );
-               foreach ( $this->hplist as $item ) {
-                       $packedTables[--$starts[255 & $item['h']]] = $item;
-               }
-
-               $final = '';
-               for ( $i = 0; $i < 256; ++$i ) {
-                       $count = $counts[$i];
-
-                       // The size of the hashtable will be double the item count.
-                       // The rest of the slots will be empty.
-                       $len = $count + $count;
-                       $final .= pack( 'VV', $this->pos, $len );
-
-                       $hashtable = array();
-                       for ( $u = 0; $u < $len; ++$u ) {
-                               $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
-                       }
-
-                       // Fill the hashtable, using the next empty slot if the hashed slot
-                       // is taken.
-                       for ( $u = 0; $u < $count; ++$u ) {
-                               $hp = $packedTables[$starts[$i] + $u];
-                               $where = CdbFunctions::unsignedMod(
-                                       CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
-                               while ( $hashtable[$where]['p'] ) {
-                                       if ( ++$where == $len ) {
-                                               $where = 0;
-                                       }
-                               }
-                               $hashtable[$where] = $hp;
-                       }
-
-                       // Write the hashtable
-                       for ( $u = 0; $u < $len; ++$u ) {
-                               $buf = pack( 'vvV',
-                                       $hashtable[$u]['h'] & 0xffff,
-                                       CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
-                                       $hashtable[$u]['p'] );
-                               $this->write( $buf );
-                               $this->posplus( 8 );
-                       }
-               }
-
-               // Write the pointer array at the start of the file
-               rewind( $this->handle );
-               if ( ftell( $this->handle ) != 0 ) {
-                       $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' );
-               }
-               $this->write( $final );
-       }
-
-       /**
-        * Clean up the temp file and throw an exception
-        *
-        * @param $msg string
-        * @throws MWException
-        */
-       protected function throwException( $msg ) {
-               if ( $this->handle ) {
-                       fclose( $this->handle );
-                       unlink( $this->tmpFileName );
-               }
-               throw new MWException( $msg );
-       }
-}
index 67cb87d..a2fe507 100644 (file)
@@ -115,6 +115,7 @@ class ConfEditor {
                } catch ( ConfEditorParseError $e ) {
                        return $e->getMessage() . "\n" . $e->highlight( $text );
                }
+
                return "OK";
        }
 
@@ -174,78 +175,78 @@ class ConfEditor {
                        $key = isset( $op['key'] ) ? $op['key'] : null;
 
                        switch ( $type ) {
-                       case 'delete':
-                               list( $start, $end ) = $this->findDeletionRegion( $path );
-                               $this->replaceSourceRegion( $start, $end, false );
-                               break;
-                       case 'set':
-                               if ( isset( $this->pathInfo[$path] ) ) {
-                                       list( $start, $end ) = $this->findValueRegion( $path );
-                                       $encValue = $value; // var_export( $value, true );
-                                       $this->replaceSourceRegion( $start, $end, $encValue );
+                               case 'delete':
+                                       list( $start, $end ) = $this->findDeletionRegion( $path );
+                                       $this->replaceSourceRegion( $start, $end, false );
                                        break;
-                               }
-                               // No existing path, fall through to append
-                               $slashPos = strrpos( $path, '/' );
-                               $key = var_export( substr( $path, $slashPos + 1 ), true );
-                               $path = substr( $path, 0, $slashPos );
-                               // Fall through
-                       case 'append':
-                               // Find the last array element
-                               $lastEltPath = $this->findLastArrayElement( $path );
-                               if ( $lastEltPath === false ) {
-                                       throw new MWException( "Can't find any element of array \"$path\"" );
-                               }
-                               $lastEltInfo = $this->pathInfo[$lastEltPath];
+                               case 'set':
+                                       if ( isset( $this->pathInfo[$path] ) ) {
+                                               list( $start, $end ) = $this->findValueRegion( $path );
+                                               $encValue = $value; // var_export( $value, true );
+                                               $this->replaceSourceRegion( $start, $end, $encValue );
+                                               break;
+                                       }
+                                       // No existing path, fall through to append
+                                       $slashPos = strrpos( $path, '/' );
+                                       $key = var_export( substr( $path, $slashPos + 1 ), true );
+                                       $path = substr( $path, 0, $slashPos );
+                                       // Fall through
+                               case 'append':
+                                       // Find the last array element
+                                       $lastEltPath = $this->findLastArrayElement( $path );
+                                       if ( $lastEltPath === false ) {
+                                               throw new MWException( "Can't find any element of array \"$path\"" );
+                                       }
+                                       $lastEltInfo = $this->pathInfo[$lastEltPath];
 
-                               // Has it got a comma already?
-                               if ( strpos( $lastEltPath, '@extra' ) === false && !$lastEltInfo['hasComma'] ) {
-                                       // No comma, insert one after the value region
-                                       list( , $end ) = $this->findValueRegion( $lastEltPath );
-                                       $this->replaceSourceRegion( $end - 1, $end - 1, ',' );
-                               }
+                                       // Has it got a comma already?
+                                       if ( strpos( $lastEltPath, '@extra' ) === false && !$lastEltInfo['hasComma'] ) {
+                                               // No comma, insert one after the value region
+                                               list( , $end ) = $this->findValueRegion( $lastEltPath );
+                                               $this->replaceSourceRegion( $end - 1, $end - 1, ',' );
+                                       }
 
-                               // Make the text to insert
-                               list( $start, $end ) = $this->findDeletionRegion( $lastEltPath );
+                                       // Make the text to insert
+                                       list( $start, $end ) = $this->findDeletionRegion( $lastEltPath );
 
-                               if ( $key === null ) {
-                                       list( $indent, ) = $this->getIndent( $start );
-                                       $textToInsert = "$indent$value,";
-                               } else {
-                                       list( $indent, $arrowIndent ) =
-                                               $this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
-                                       $textToInsert = "$indent$key$arrowIndent=> $value,";
-                               }
-                               $textToInsert .= ( $indent === false ? ' ' : "\n" );
+                                       if ( $key === null ) {
+                                               list( $indent, ) = $this->getIndent( $start );
+                                               $textToInsert = "$indent$value,";
+                                       } else {
+                                               list( $indent, $arrowIndent ) =
+                                                       $this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
+                                               $textToInsert = "$indent$key$arrowIndent=> $value,";
+                                       }
+                                       $textToInsert .= ( $indent === false ? ' ' : "\n" );
 
-                               // Insert the item
-                               $this->replaceSourceRegion( $end, $end, $textToInsert );
-                               break;
-                       case 'insert':
-                               // Find first array element
-                               $firstEltPath = $this->findFirstArrayElement( $path );
-                               if ( $firstEltPath === false ) {
-                                       throw new MWException( "Can't find array element of \"$path\"" );
-                               }
-                               list( $start, ) = $this->findDeletionRegion( $firstEltPath );
-                               $info = $this->pathInfo[$firstEltPath];
-
-                               // Make the text to insert
-                               if ( $key === null ) {
-                                       list( $indent, ) = $this->getIndent( $start );
-                                       $textToInsert = "$indent$value,";
-                               } else {
-                                       list( $indent, $arrowIndent ) =
-                                               $this->getIndent( $start, $key, $info['arrowByte'] );
-                                       $textToInsert = "$indent$key$arrowIndent=> $value,";
-                               }
-                               $textToInsert .= ( $indent === false ? ' ' : "\n" );
+                                       // Insert the item
+                                       $this->replaceSourceRegion( $end, $end, $textToInsert );
+                                       break;
+                               case 'insert':
+                                       // Find first array element
+                                       $firstEltPath = $this->findFirstArrayElement( $path );
+                                       if ( $firstEltPath === false ) {
+                                               throw new MWException( "Can't find array element of \"$path\"" );
+                                       }
+                                       list( $start, ) = $this->findDeletionRegion( $firstEltPath );
+                                       $info = $this->pathInfo[$firstEltPath];
 
-                               // Insert the item
-                               $this->replaceSourceRegion( $start, $start, $textToInsert );
-                               break;
-                       default:
-                               throw new MWException( "Unrecognised operation: \"$type\"" );
+                                       // Make the text to insert
+                                       if ( $key === null ) {
+                                               list( $indent, ) = $this->getIndent( $start );
+                                               $textToInsert = "$indent$value,";
+                                       } else {
+                                               list( $indent, $arrowIndent ) =
+                                                       $this->getIndent( $start, $key, $info['arrowByte'] );
+                                               $textToInsert = "$indent$key$arrowIndent=> $value,";
+                                       }
+                                       $textToInsert .= ( $indent === false ? ' ' : "\n" );
+
+                                       // Insert the item
+                                       $this->replaceSourceRegion( $start, $start, $textToInsert );
+                                       break;
+                               default:
+                                       throw new MWException( "Unrecognised operation: \"$type\"" );
                        }
                }
 
@@ -268,6 +269,7 @@ class ConfEditor {
                                "Sorry, ConfEditor broke the file during editing and it won't parse anymore: " .
                                $e->getMessage() );
                }
+
                return $out;
        }
 
@@ -302,6 +304,7 @@ class ConfEditor {
                        $this->setVar( $vars, $parentPath, $name,
                                $this->parseScalar( $value ) );
                }
+
                return $vars;
        }
 
@@ -359,6 +362,7 @@ class ConfEditor {
                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;
@@ -440,7 +444,8 @@ class ConfEditor {
                        $this->nextToken();
                }
                $regionEnd = $path['endByte']; // past the end
-               for ( $offset = 0; $offset < count( $this->tokens ) - $this->pos; $offset++ ) {
+               $count = count( $this->tokens );
+               for ( $offset = 0; $offset < $count - $this->pos; $offset++ ) {
                        $token = $this->getTokenAhead( $offset );
                        if ( !$token->isSkip() ) {
                                break;
@@ -454,6 +459,7 @@ class ConfEditor {
                                break;
                        }
                }
+
                return array( $regionStart, $regionEnd );
        }
 
@@ -475,6 +481,7 @@ class ConfEditor {
                if ( $path['valueStartByte'] === false || $path['valueEndByte'] === false ) {
                        throw new MWException( "Can't find value region for path \"$pathName\"" );
                }
+
                return array( $path['valueStartByte'], $path['valueEndByte'] );
        }
 
@@ -512,6 +519,7 @@ class ConfEditor {
                                break;
                        }
                }
+
                return $extraPath;
        }
 
@@ -538,6 +546,7 @@ class ConfEditor {
                                return $candidatePath;
                        }
                }
+
                return false;
        }
 
@@ -560,6 +569,7 @@ class ConfEditor {
                                $arrowIndent = str_repeat( ' ', $arrowIndentLength );
                        }
                }
+
                return array( $indent, $arrowIndent );
        }
 
@@ -580,32 +590,137 @@ class ConfEditor {
                        }
 
                        switch ( $state ) {
-                       case 'file':
-                               $this->expect( T_OPEN_TAG );
-                               $token = $this->skipSpace();
-                               if ( $token->isEnd() ) {
-                                       break 2;
-                               }
-                               $this->pushState( 'statement', 'file 2' );
-                               break;
-                       case 'file 2':
-                               $token = $this->skipSpace();
-                               if ( $token->isEnd() ) {
-                                       break 2;
-                               }
-                               $this->pushState( 'statement', 'file 2' );
-                               break;
-                       case 'statement':
-                               $token = $this->skipSpace();
-                               if ( !$this->validatePath( $token->text ) ) {
-                                       $this->error( "Invalid variable name \"{$token->text}\"" );
-                               }
-                               $this->nextPath( $token->text );
-                               $this->expect( T_VARIABLE );
-                               $this->skipSpace();
-                               $arrayAssign = false;
-                               if ( $this->currentToken()->type == '[' ) {
-                                       $this->nextToken();
+                               case 'file':
+                                       $this->expect( T_OPEN_TAG );
+                                       $token = $this->skipSpace();
+                                       if ( $token->isEnd() ) {
+                                               break 2;
+                                       }
+                                       $this->pushState( 'statement', 'file 2' );
+                                       break;
+                               case 'file 2':
+                                       $token = $this->skipSpace();
+                                       if ( $token->isEnd() ) {
+                                               break 2;
+                                       }
+                                       $this->pushState( 'statement', 'file 2' );
+                                       break;
+                               case 'statement':
+                                       $token = $this->skipSpace();
+                                       if ( !$this->validatePath( $token->text ) ) {
+                                               $this->error( "Invalid variable name \"{$token->text}\"" );
+                                       }
+                                       $this->nextPath( $token->text );
+                                       $this->expect( T_VARIABLE );
+                                       $this->skipSpace();
+                                       $arrayAssign = false;
+                                       if ( $this->currentToken()->type == '[' ) {
+                                               $this->nextToken();
+                                               $token = $this->skipSpace();
+                                               if ( !$token->isScalar() ) {
+                                                       $this->error( "expected a string or number for the array key" );
+                                               }
+                                               if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
+                                                       $text = $this->parseScalar( $token->text );
+                                               } else {
+                                                       $text = $token->text;
+                                               }
+                                               if ( !$this->validatePath( $text ) ) {
+                                                       $this->error( "Invalid associative array name \"$text\"" );
+                                               }
+                                               $this->pushPath( $text );
+                                               $this->nextToken();
+                                               $this->skipSpace();
+                                               $this->expect( ']' );
+                                               $this->skipSpace();
+                                               $arrayAssign = true;
+                                       }
+                                       $this->expect( '=' );
+                                       $this->skipSpace();
+                                       $this->startPathValue();
+                                       if ( $arrayAssign ) {
+                                               $this->pushState( 'expression', 'array assign end' );
+                                       } else {
+                                               $this->pushState( 'expression', 'statement end' );
+                                       }
+                                       break;
+                               case 'array assign end':
+                               case 'statement end':
+                                       $this->endPathValue();
+                                       if ( $state == 'array assign end' ) {
+                                               $this->popPath();
+                                       }
+                                       $this->skipSpace();
+                                       $this->expect( ';' );
+                                       $this->nextPath( '@extra-' . ( $this->serial++ ) );
+                                       break;
+                               case 'expression':
+                                       $token = $this->skipSpace();
+                                       if ( $token->type == T_ARRAY ) {
+                                               $this->pushState( 'array' );
+                                       } elseif ( $token->isScalar() ) {
+                                               $this->nextToken();
+                                       } elseif ( $token->type == T_VARIABLE ) {
+                                               $this->nextToken();
+                                       } else {
+                                               $this->error( "expected simple expression" );
+                                       }
+                                       break;
+                               case 'array':
+                                       $this->skipSpace();
+                                       $this->expect( T_ARRAY );
+                                       $this->skipSpace();
+                                       $this->expect( '(' );
+                                       $this->skipSpace();
+                                       $this->pushPath( '@extra-' . ( $this->serial++ ) );
+                                       if ( $this->isAhead( ')' ) ) {
+                                               // Empty array
+                                               $this->pushState( 'array end' );
+                                       } else {
+                                               $this->pushState( 'element', 'array end' );
+                                       }
+                                       break;
+                               case 'array end':
+                                       $this->skipSpace();
+                                       $this->popPath();
+                                       $this->expect( ')' );
+                                       break;
+                               case 'element':
+                                       $token = $this->skipSpace();
+                                       // Look ahead to find the double arrow
+                                       if ( $token->isScalar() && $this->isAhead( T_DOUBLE_ARROW, 1 ) ) {
+                                               // Found associative element
+                                               $this->pushState( 'assoc-element', 'element end' );
+                                       } else {
+                                               // Not associative
+                                               $this->nextPath( '@next' );
+                                               $this->startPathValue();
+                                               $this->pushState( 'expression', 'element end' );
+                                       }
+                                       break;
+                               case 'element end':
+                                       $token = $this->skipSpace();
+                                       if ( $token->type == ',' ) {
+                                               $this->endPathValue();
+                                               $this->markComma();
+                                               $this->nextToken();
+                                               $this->nextPath( '@extra-' . ( $this->serial++ ) );
+                                               // Look ahead to find ending bracket
+                                               if ( $this->isAhead( ")" ) ) {
+                                                       // Found ending bracket, no continuation
+                                                       $this->skipSpace();
+                                               } else {
+                                                       // No ending bracket, continue to next element
+                                                       $this->pushState( 'element' );
+                                               }
+                                       } elseif ( $token->type == ')' ) {
+                                               // End array
+                                               $this->endPathValue();
+                                       } else {
+                                               $this->error( "expected the next array element or the end of the array" );
+                                       }
+                                       break;
+                               case 'assoc-element':
                                        $token = $this->skipSpace();
                                        if ( !$token->isScalar() ) {
                                                $this->error( "expected a string or number for the array key" );
@@ -618,120 +733,15 @@ class ConfEditor {
                                        if ( !$this->validatePath( $text ) ) {
                                                $this->error( "Invalid associative array name \"$text\"" );
                                        }
-                                       $this->pushPath( $text );
+                                       $this->nextPath( $text );
                                        $this->nextToken();
                                        $this->skipSpace();
-                                       $this->expect( ']' );
+                                       $this->markArrow();
+                                       $this->expect( T_DOUBLE_ARROW );
                                        $this->skipSpace();
-                                       $arrayAssign = true;
-                               }
-                               $this->expect( '=' );
-                               $this->skipSpace();
-                               $this->startPathValue();
-                               if ( $arrayAssign ) {
-                                       $this->pushState( 'expression', 'array assign end' );
-                               } else {
-                                       $this->pushState( 'expression', 'statement end' );
-                               }
-                               break;
-                       case 'array assign end':
-                       case 'statement end':
-                               $this->endPathValue();
-                               if ( $state == 'array assign end' ) {
-                                       $this->popPath();
-                               }
-                               $this->skipSpace();
-                               $this->expect( ';' );
-                               $this->nextPath( '@extra-' . ( $this->serial++ ) );
-                               break;
-                       case 'expression':
-                               $token = $this->skipSpace();
-                               if ( $token->type == T_ARRAY ) {
-                                       $this->pushState( 'array' );
-                               } elseif ( $token->isScalar() ) {
-                                       $this->nextToken();
-                               } elseif ( $token->type == T_VARIABLE ) {
-                                       $this->nextToken();
-                               } else {
-                                       $this->error( "expected simple expression" );
-                               }
-                               break;
-                       case 'array':
-                               $this->skipSpace();
-                               $this->expect( T_ARRAY );
-                               $this->skipSpace();
-                               $this->expect( '(' );
-                               $this->skipSpace();
-                               $this->pushPath( '@extra-' . ( $this->serial++ ) );
-                               if ( $this->isAhead( ')' ) ) {
-                                       // Empty array
-                                       $this->pushState( 'array end' );
-                               } else {
-                                       $this->pushState( 'element', 'array end' );
-                               }
-                               break;
-                       case 'array end':
-                               $this->skipSpace();
-                               $this->popPath();
-                               $this->expect( ')' );
-                               break;
-                       case 'element':
-                               $token = $this->skipSpace();
-                               // Look ahead to find the double arrow
-                               if ( $token->isScalar() && $this->isAhead( T_DOUBLE_ARROW, 1 ) ) {
-                                       // Found associative element
-                                       $this->pushState( 'assoc-element', 'element end' );
-                               } else {
-                                       // Not associative
-                                       $this->nextPath( '@next' );
                                        $this->startPathValue();
-                                       $this->pushState( 'expression', 'element end' );
-                               }
-                               break;
-                       case 'element end':
-                               $token = $this->skipSpace();
-                               if ( $token->type == ',' ) {
-                                       $this->endPathValue();
-                                       $this->markComma();
-                                       $this->nextToken();
-                                       $this->nextPath( '@extra-' . ( $this->serial++ ) );
-                                       // Look ahead to find ending bracket
-                                       if ( $this->isAhead( ")" ) ) {
-                                               // Found ending bracket, no continuation
-                                               $this->skipSpace();
-                                       } else {
-                                               // No ending bracket, continue to next element
-                                               $this->pushState( 'element' );
-                                       }
-                               } elseif ( $token->type == ')' ) {
-                                       // End array
-                                       $this->endPathValue();
-                               } else {
-                                       $this->error( "expected the next array element or the end of the array" );
-                               }
-                               break;
-                       case 'assoc-element':
-                               $token = $this->skipSpace();
-                               if ( !$token->isScalar() ) {
-                                       $this->error( "expected a string or number for the array key" );
-                               }
-                               if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
-                                       $text = $this->parseScalar( $token->text );
-                               } else {
-                                       $text = $token->text;
-                               }
-                               if ( !$this->validatePath( $text ) ) {
-                                       $this->error( "Invalid associative array name \"$text\"" );
-                               }
-                               $this->nextPath( $text );
-                               $this->nextToken();
-                               $this->skipSpace();
-                               $this->markArrow();
-                               $this->expect( T_DOUBLE_ARROW );
-                               $this->skipSpace();
-                               $this->startPathValue();
-                               $this->pushState( 'expression' );
-                               break;
+                                       $this->pushState( 'expression' );
+                                       break;
                        }
                }
                if ( count( $this->stateStack ) ) {
@@ -763,6 +773,7 @@ class ConfEditor {
                } else {
                        $this->currentToken = $this->newTokenObj( $this->tokens[$this->pos] );
                }
+
                return $this->currentToken;
        }
 
@@ -787,6 +798,7 @@ class ConfEditor {
                $this->lineNum = 1;
                $this->colNum = 1;
                $this->byteNum = 0;
+
                return $this->currentToken;
        }
 
@@ -814,6 +826,7 @@ class ConfEditor {
                }
                $this->prevToken = $this->currentToken;
                $this->setPos( $this->pos + 1 );
+
                return $this->currentToken;
        }
 
@@ -838,6 +851,7 @@ class ConfEditor {
                while ( $this->currentToken && $this->currentToken->isSkip() ) {
                        $this->nextToken();
                }
+
                return $this->currentToken;
        }
 
@@ -1028,6 +1042,7 @@ class ConfEditor {
                                return false;
                        }
                }
+
                return false;
        }
 
@@ -1058,6 +1073,7 @@ class ConfEditor {
  */
 class ConfEditorParseError extends MWException {
        var $lineNum, $colNum;
+
        function __construct( $editor, $msg ) {
                $this->lineNum = $editor->lineNum;
                $this->colNum = $editor->colNum;
@@ -1072,9 +1088,9 @@ class ConfEditorParseError extends MWException {
                                return "$line\n" . str_repeat( ' ', $this->colNum - 1 ) . "^\n";
                        }
                }
+
                return '';
        }
-
 }
 
 /**
index 930f8c0..c152d41 100644 (file)
@@ -38,7 +38,9 @@ class HashRing {
         * @param array $map (location => weight)
         */
        public function __construct( array $map ) {
-               $map = array_filter( $map, function( $w ) { return $w > 0; } );
+               $map = array_filter( $map, function ( $w ) {
+                       return $w > 0;
+               } );
                if ( !count( $map ) ) {
                        throw new MWException( "Ring is empty or all weights are zero." );
                }
@@ -77,6 +79,7 @@ class HashRing {
         */
        public function getLocation( $item ) {
                $locations = $this->getLocations( $item, 1 );
+
                return $locations[0];
        }
 
@@ -113,6 +116,7 @@ class HashRing {
                        }
                        $locations[] = $location;
                }
+
                return $locations;
        }
 
@@ -137,6 +141,7 @@ class HashRing {
                if ( count( $map ) ) {
                        return new self( $map );
                }
+
                return false;
        }
 }
index 73834a5..002dcd9 100644 (file)
@@ -175,6 +175,7 @@ class IP {
                }
                // Remove leading zeros from each bloc as needed
                $ip = preg_replace( '/(^|:)0+(' . RE_IPV6_WORD . ')/', '$1$2', $ip );
+
                return $ip;
        }
 
@@ -218,6 +219,7 @@ class IP {
                        // Convert to lower case to make it more readable
                        $ip = strtolower( $ip );
                }
+
                return $ip;
        }
 
@@ -270,6 +272,7 @@ class IP {
                                return false;
                        }
                }
+
                // Plain hostname
                return array( $both, false );
        }
@@ -336,6 +339,7 @@ class IP {
                }
                // NO leading zeroes
                $ip_oct = preg_replace( '/(^|:)0+(' . RE_IPV6_WORD . ')/', '$1$2', $ip_oct );
+
                return $ip_oct;
        }
 
@@ -356,6 +360,7 @@ class IP {
                        }
                        $s .= base_convert( substr( $ip_hex, $i * 2, 2 ), 16, 10 );
                }
+
                return $s;
        }
 
@@ -400,6 +405,7 @@ class IP {
                                return false;
                        }
                }
+
                return true;
        }
 
@@ -426,6 +432,7 @@ class IP {
                                return false;
                        }
                }
+
                return true;
        }
 
@@ -449,6 +456,7 @@ class IP {
                                $n = wfBaseConvert( $n, 10, 16, 8, false );
                        }
                }
+
                return $n;
        }
 
@@ -467,6 +475,7 @@ class IP {
                foreach ( explode( ':', $ip ) as $v ) {
                        $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
                }
+
                return $r_ip;
        }
 
@@ -492,6 +501,7 @@ class IP {
                                }
                        }
                }
+
                return $n;
        }
 
@@ -534,6 +544,7 @@ class IP {
                        $network = false;
                        $bits = false;
                }
+
                return array( $network, $bits );
        }
 
@@ -546,9 +557,9 @@ class IP {
         *     1.2.3.4 - 1.2.3.5   Explicit range
         *     1.2.3.4             Single IP
         *
-        *     2001:0db8:85a3::7344/96                                   CIDR
+        *     2001:0db8:85a3::7344/96                       CIDR
         *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
-        *     2001:0db8:85a3::7344                                      Single IP
+        *     2001:0db8:85a3::7344                          Single IP
         * @param string $range IP range
         * @return array(string, string)
         */
@@ -626,6 +637,7 @@ class IP {
                        $network = false;
                        $bits = false;
                }
+
                return array( $network, (int)$bits );
        }
 
@@ -634,9 +646,9 @@ class IP {
         * start and end of the range in hexadecimal. For IPv6.
         *
         * Formats are:
-        *     2001:0db8:85a3::7344/96                                   CIDR
+        *     2001:0db8:85a3::7344/96                       CIDR
         *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
-        *     2001:0db8:85a3::7344/96                                   Single IP
+        *     2001:0db8:85a3::7344/96                       Single IP
         *
         * @param $range
         *
@@ -662,7 +674,7 @@ class IP {
                                $start = "v6-$start";
                                $end = "v6-$end";
                        }
-               // Explicit range notation...
+       // Explicit range notation...
                } elseif ( strpos( $range, '-' ) !== false ) {
                        list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
                        $start = self::toUnsigned6( $start );
@@ -697,6 +709,7 @@ class IP {
        public static function isInRange( $addr, $range ) {
                $hexIP = self::toHex( $addr );
                list( $start, $end ) = self::parseRange( $range );
+
                return ( strcmp( $hexIP, $start ) >= 0 &&
                        strcmp( $hexIP, $end ) <= 0 );
        }
@@ -735,8 +748,8 @@ class IP {
                        return $m[1];
                }
                if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD .
-                       ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
-               {
+                       ':' . RE_IPV6_WORD . '$/i', $addr, $m )
+               {
                        return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
                }
 
@@ -756,6 +769,7 @@ class IP {
                if ( $bits === false ) {
                        return $start; // wasn't actually a range
                }
+
                return "$start/$bits";
        }
 }
index bac018e..0172974 100644 (file)
@@ -25,7 +25,6 @@
  */
 
 class MWCryptRand {
-
        /**
         * Minimum number of iterations we want to make in our drift calculations.
         */
@@ -86,10 +85,11 @@ class MWCryptRand {
                $files[] = __DIR__;
                $files[] = dirname( __DIR__ );
 
-               // The config file is likely the most often edited file we know should be around
-               // so include its stat info into the state.
-               // The constant with its location will almost always be defined, as WebStart.php defines
-               // MW_CONFIG_FILE to $IP/LocalSettings.php unless being configured with MW_CONFIG_CALLBACK (eg. the installer)
+               // The config file is likely the most often edited file we know should
+               // be around so include its stat info into the state.
+               // The constant with its location will almost always be defined, as
+               // WebStart.php defines MW_CONFIG_FILE to $IP/LocalSettings.php unless
+               // being configured with MW_CONFIG_CALLBACK (e.g. the installer).
                if ( defined( 'MW_CONFIG_FILE' ) ) {
                        $files[] = MW_CONFIG_FILE;
                }
@@ -153,7 +153,8 @@ class MWCryptRand {
         * @author Tim Starling
         */
        protected function driftHash( $data ) {
-               // Minimum number of iterations (to avoid slow operations causing the loop to gather little entropy)
+               // Minimum number of iterations (to avoid slow operations causing the
+               // loop to gather little entropy)
                $minIterations = self::MIN_ITERATIONS;
                // Duration of time to spend doing calculations (in seconds)
                $duration = ( self::MSEC_PER_BYTE / 1000 ) * $this->hashLength();
@@ -189,6 +190,7 @@ class MWCryptRand {
                        "(time-taken=" . ( $timeTaken * 1000 ) . "ms, " .
                        "iterations=$iterations, " .
                        "time-per-iteration=" . ( $timeTaken / $iterations * 1e6 ) . "us)\n" );
+
                return $data;
        }
 
@@ -207,6 +209,7 @@ class MWCryptRand {
                // Generate a new random state based on the initial random state or previous
                // random state by combining it with clock drift
                $state = $this->driftHash( $state );
+
                return $state;
        }
 
@@ -227,6 +230,7 @@ class MWCryptRand {
                        if ( in_array( $algorithm, $algos ) ) {
                                $this->algo = $algorithm;
                                wfDebug( __METHOD__ . ": Using the {$this->algo} hash algorithm.\n" );
+
                                return $this->algo;
                        }
                }
@@ -249,6 +253,7 @@ class MWCryptRand {
                if ( is_null( $this->hashLength ) ) {
                        $this->hashLength = strlen( $this->hash( '' ) );
                }
+
                return $this->hashLength;
        }
 
@@ -282,6 +287,7 @@ class MWCryptRand {
                if ( is_null( $this->strong ) ) {
                        throw new MWException( __METHOD__ . ' called before generation of random data' );
                }
+
                return $this->strong;
        }
 
@@ -291,7 +297,8 @@ class MWCryptRand {
        public function realGenerate( $bytes, $forceStrong = false ) {
                wfProfileIn( __METHOD__ );
 
-               wfDebug( __METHOD__ . ": Generating cryptographic random bytes for " . wfGetAllCallers( 5 ) . "\n" );
+               wfDebug( __METHOD__ . ": Generating cryptographic random bytes for " .
+                       wfGetAllCallers( 5 ) . "\n" );
 
                $bytes = floor( $bytes );
                static $buffer = '';
@@ -315,15 +322,17 @@ class MWCryptRand {
                                        wfDebug( __METHOD__ . ": mcrypt_create_iv returned false.\n" );
                                } else {
                                        $buffer .= $iv;
-                                       wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) . " bytes of randomness.\n" );
+                                       wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) .
+                                               " bytes of randomness.\n" );
                                }
                                wfProfileOut( __METHOD__ . '-mcrypt' );
                        }
                }
 
                if ( strlen( $buffer ) < $bytes ) {
-                       // If available make use of openssl's random_pseudo_bytes method to attempt to generate randomness.
-                       // However don't do this on Windows with PHP < 5.3.4 due to a bug:
+                       // If available make use of openssl's random_pseudo_bytes method to
+                       // attempt to generate randomness. However don't do this on Windows
+                       // with PHP < 5.3.4 due to a bug:
                        // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
                        // http://git.php.net/?p=php-src.git;a=commitdiff;h=cd62a70863c261b07f6dadedad9464f7e213cad5
                        if ( function_exists( 'openssl_random_pseudo_bytes' )
@@ -336,7 +345,9 @@ class MWCryptRand {
                                        wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes returned false.\n" );
                                } else {
                                        $buffer .= $openssl_bytes;
-                                       wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes generated " . strlen( $openssl_bytes ) . " bytes of " . ( $openssl_strong ? "strong" : "weak" ) . " randomness.\n" );
+                                       wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes generated " .
+                                               strlen( $openssl_bytes ) . " bytes of " .
+                                               ( $openssl_strong ? "strong" : "weak" ) . " randomness.\n" );
                                }
                                if ( strlen( $buffer ) >= $bytes ) {
                                        // openssl tells us if the random source was strong, if some of our data was generated
@@ -348,11 +359,14 @@ class MWCryptRand {
                }
 
                // Only read from urandom if we can control the buffer size or were passed forceStrong
-               if ( strlen( $buffer ) < $bytes && ( function_exists( 'stream_set_read_buffer' ) || $forceStrong ) ) {
+               if ( strlen( $buffer ) < $bytes &&
+                       ( function_exists( 'stream_set_read_buffer' ) || $forceStrong )
+               ) {
                        wfProfileIn( __METHOD__ . '-fopen-urandom' );
                        $rem = $bytes - strlen( $buffer );
                        if ( !function_exists( 'stream_set_read_buffer' ) && $forceStrong ) {
-                               wfDebug( __METHOD__ . ": Was forced to read from /dev/urandom without control over the buffer size.\n" );
+                               wfDebug( __METHOD__ . ": Was forced to read from /dev/urandom " .
+                                       "without control over the buffer size.\n" );
                        }
                        // /dev/urandom is generally considered the best possible commonly
                        // available random source, and is available on most *nix systems.
@@ -377,7 +391,9 @@ class MWCryptRand {
                                $random_bytes = fread( $urandom, max( $chunk_size, $rem ) );
                                $buffer .= $random_bytes;
                                fclose( $urandom );
-                               wfDebug( __METHOD__ . ": /dev/urandom generated " . strlen( $random_bytes ) . " bytes of randomness.\n" );
+                               wfDebug( __METHOD__ . ": /dev/urandom generated " . strlen( $random_bytes ) .
+                                       " bytes of randomness.\n" );
+
                                if ( strlen( $buffer ) >= $bytes ) {
                                        // urandom is always strong, set to true if all our data was generated using it
                                        $this->strong = true;
@@ -395,7 +411,8 @@ class MWCryptRand {
                // We hash the random state with more salt to avoid the state from leaking
                // out and being used to predict the /randomness/ that follows.
                if ( strlen( $buffer ) < $bytes ) {
-                       wfDebug( __METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n" );
+                       wfDebug( __METHOD__ .
+                               ": Falling back to using a pseudo random state to generate randomness.\n" );
                }
                while ( strlen( $buffer ) < $bytes ) {
                        wfProfileIn( __METHOD__ . '-fallback' );
@@ -412,9 +429,11 @@ class MWCryptRand {
                $generated = substr( $buffer, 0, $bytes );
                $buffer = substr( $buffer, $bytes );
 
-               wfDebug( __METHOD__ . ": " . strlen( $buffer ) . " bytes of randomness leftover in the buffer.\n" );
+               wfDebug( __METHOD__ . ": " . strlen( $buffer ) .
+                       " bytes of randomness leftover in the buffer.\n" );
 
                wfProfileOut( __METHOD__ );
+
                return $generated;
        }
 
@@ -428,6 +447,7 @@ class MWCryptRand {
                $bytes = ceil( $chars / 2 );
                // Generate the data and then convert it to a hex string
                $hex = bin2hex( $this->generate( $bytes, $forceStrong ) );
+
                // A bit of paranoia here, the caller asked for a specific length of string
                // here, and it's possible (eg when given an odd number) that we may actually
                // have at least 1 char more than they asked for. Just in case they made this
@@ -448,6 +468,7 @@ class MWCryptRand {
                if ( is_null( self::$singleton ) ) {
                        self::$singleton = new self;
                }
+
                return self::$singleton;
        }
 
@@ -493,5 +514,4 @@ class MWCryptRand {
        public static function generateHex( $chars, $forceStrong = false ) {
                return self::singleton()->realGenerateHex( $chars, $forceStrong );
        }
-
 }
index 6d11d17..7105f6c 100644 (file)
@@ -30,6 +30,7 @@ class MWFunction {
        public static function call( $callback ) {
                wfDeprecated( __METHOD__, '1.22' );
                $args = func_get_args();
+
                return call_user_func_array( 'call_user_func', $args );
        }
 
@@ -41,6 +42,7 @@ class MWFunction {
         */
        public static function callArray( $callback, $argsarams ) {
                wfDeprecated( __METHOD__, '1.22' );
+
                return call_user_func_array( $callback, $argsarams );
        }
 
@@ -55,7 +57,7 @@ class MWFunction {
                }
 
                $ref = new ReflectionClass( $class );
+
                return $ref->newInstanceArgs( $args );
        }
-
 }
index 70d2032..f2e6df6 100644 (file)
@@ -81,16 +81,19 @@ class MappedIterator extends FilterIterator {
                if ( $ok ) {
                        $this->cache['current'] = $value;
                }
+
                return $ok;
        }
 
        public function key() {
                $this->init();
+
                return parent::key();
        }
 
        public function valid() {
                $this->init();
+
                return parent::valid();
        }
 
index c1545e6..9cd3d3f 100644 (file)
@@ -24,7 +24,6 @@
  * A collection of static methods to play with strings.
  */
 class StringUtils {
-
        /**
         * Test whether a string is valid UTF-8.
         *
@@ -107,6 +106,7 @@ class StringUtils {
                                return false;
                        }
                }
+
                return true;
        }
 
@@ -139,6 +139,7 @@ class StringUtils {
                                $output .= $replace . substr( $s, $endDelimPos + strlen( $endDelim ) );
                        }
                }
+
                return $output;
        }
 
@@ -164,7 +165,9 @@ class StringUtils {
         * @throws MWException
         * @return string
         */
-       static function delimiterReplaceCallback( $startDelim, $endDelim, $callback, $subject, $flags = '' ) {
+       static function delimiterReplaceCallback( $startDelim, $endDelim, $callback,
+               $subject, $flags = ''
+       ) {
                $inputPos = 0;
                $outputPos = 0;
                $output = '';
@@ -176,13 +179,13 @@ class StringUtils {
                $m = array();
 
                while ( $inputPos < strlen( $subject ) &&
-                       preg_match( "!($encStart)|($encEnd)!S$flags", $subject, $m, PREG_OFFSET_CAPTURE, $inputPos ) )
-               {
+                       preg_match( "!($encStart)|($encEnd)!S$flags", $subject, $m, PREG_OFFSET_CAPTURE, $inputPos )
+               {
                        $tokenOffset = $m[0][1];
                        if ( $m[1][0] != '' ) {
                                if ( $foundStart &&
-                                       $strcmp( $endDelim, substr( $subject, $tokenOffset, $endLength ) ) == 0 )
-                               {
+                                       $strcmp( $endDelim, substr( $subject, $tokenOffset, $endLength ) ) == 0
+                               {
                                        # An end match is present at the same location
                                        $tokenType = 'end';
                                        $tokenLength = $endLength;
@@ -219,7 +222,7 @@ class StringUtils {
                                        $output .= call_user_func( $callback, array(
                                                substr( $subject, $outputPos, $tokenOffset + $tokenLength - $outputPos ),
                                                substr( $subject, $contentPos, $tokenOffset - $contentPos )
-                                       ));
+                                       ) );
                                        $foundStart = false;
                                } else {
                                        # Non-matching end, write it out
@@ -233,6 +236,7 @@ class StringUtils {
                if ( $outputPos < strlen( $subject ) ) {
                        $output .= substr( $subject, $outputPos );
                }
+
                return $output;
        }
 
@@ -251,6 +255,7 @@ class StringUtils {
         */
        static function delimiterReplace( $startDelim, $endDelim, $replace, $subject, $flags = '' ) {
                $replacer = new RegexlikeReplacer( $replace );
+
                return self::delimiterReplaceCallback( $startDelim, $endDelim,
                        $replacer->cb(), $subject, $flags );
        }
@@ -291,6 +296,7 @@ class StringUtils {
        static function escapeRegexReplacement( $string ) {
                $string = str_replace( '\\', '\\\\', $string );
                $string = str_replace( '$', '\\$', $string );
+
                return $string;
        }
 
@@ -315,7 +321,6 @@ class StringUtils {
  * StringUtils::delimiterReplaceCallback()
  */
 class Replacer {
-
        /**
         * @return array
         */
@@ -328,7 +333,7 @@ class Replacer {
  * Class to replace regex matches with a string similar to that used in preg_replace()
  */
 class RegexlikeReplacer extends Replacer {
-       var $r;
+       private $r;
 
        /**
         * @param string $r
@@ -346,16 +351,15 @@ class RegexlikeReplacer extends Replacer {
                foreach ( $matches as $i => $match ) {
                        $pairs["\$$i"] = $match;
                }
+
                return strtr( $this->r, $pairs );
        }
-
 }
 
 /**
  * Class to perform secondary replacement within each replacement string
  */
 class DoubleReplacer extends Replacer {
-
        /**
         * @param $from
         * @param $to
@@ -380,7 +384,7 @@ class DoubleReplacer extends Replacer {
  * Class to perform replacement based on a simple hashtable lookup
  */
 class HashtableReplacer extends Replacer {
-       var $table, $index;
+       private $table, $index;
 
        /**
         * @param $table
@@ -405,8 +409,8 @@ class HashtableReplacer extends Replacer {
  * Supports lazy initialisation of FSS resource
  */
 class ReplacementArray {
-       /*mostly private*/ var $data = false;
-       /*mostly private*/ var $fss = false;
+       private $data = false;
+       private $fss = false;
 
        /**
         * Create an object with the specified replacement array
@@ -505,6 +509,7 @@ class ReplacementArray {
                        $result = strtr( $subject, $this->data );
                        wfProfileOut( __METHOD__ . '-strtr' );
                }
+
                return $result;
        }
 }
@@ -520,19 +525,19 @@ class ReplacementArray {
  */
 class ExplodeIterator implements Iterator {
        // The subject string
-       var $subject, $subjectLength;
+       private $subject, $subjectLength;
 
        // The delimiter
-       var $delim, $delimLength;
+       private $delim, $delimLength;
 
        // The position of the start of the line
-       var $curPos;
+       private $curPos;
 
        // The position after the end of the next delimiter
-       var $endPos;
+       private $endPos;
 
        // The current token
-       var $current;
+       private $current;
 
        /**
         * Construct a DelimIterator
@@ -594,6 +599,7 @@ class ExplodeIterator implements Iterator {
                        }
                }
                $this->refreshCurrent();
+
                return $this->current;
        }
 
index 963e51a..10ff957 100644 (file)
@@ -82,6 +82,7 @@ class UIDGenerator {
                if ( self::$instance === null ) {
                        self::$instance = new self();
                }
+
                return self::$instance;
        }
 
@@ -106,6 +107,7 @@ class UIDGenerator {
                }
                $gen = self::singleton();
                $time = $gen->getTimestampAndDelay( 'lockFile88', 1, 1024 );
+
                return wfBaseConvert( $gen->getTimestampedID88( $time ), 2, $base );
        }
 
@@ -125,6 +127,7 @@ class UIDGenerator {
                if ( strlen( $id_bin ) !== 88 ) {
                        throw new MWException( "Detected overflow for millisecond timestamp." );
                }
+
                return $id_bin;
        }
 
@@ -148,6 +151,7 @@ class UIDGenerator {
                }
                $gen = self::singleton();
                $time = $gen->getTimestampAndDelay( 'lockFile128', 16384, 1048576 );
+
                return wfBaseConvert( $gen->getTimestampedID128( $time ), 2, $base );
        }
 
@@ -169,6 +173,7 @@ class UIDGenerator {
                if ( strlen( $id_bin ) !== 128 ) {
                        throw new MWException( "Detected overflow for millisecond timestamp." );
                }
+
                return $id_bin;
        }
 
@@ -320,6 +325,7 @@ class UIDGenerator {
                        throw new MWException( __METHOD__ .
                                ': sorry, this function doesn\'t work after the year 144680' );
                }
+
                return substr( wfBaseConvert( $ts, 10, 2, 46 ), -46 );
        }
 
@@ -328,6 +334,7 @@ class UIDGenerator {
         */
        protected static function millitime() {
                list( $msec, $sec ) = explode( ' ', microtime() );
+
                return array( (int)$sec, (int)( $msec * 1000 ) );
        }
 
index 307efce..1419bbb 100644 (file)
@@ -88,31 +88,32 @@ class ZipDirectoryReader {
         */
        public static function read( $fileName, $callback, $options = array() ) {
                $zdr = new self( $fileName, $callback, $options );
+
                return $zdr->execute();
        }
 
        /** The file name */
-       var $fileName;
+       protected $fileName;
 
        /** The opened file resource */
-       var $file;
+       protected $file;
 
        /** The cached length of the file, or null if it has not been loaded yet. */
-       var $fileLength;
+       protected $fileLength;
 
        /** A segmented cache of the file contents */
-       var $buffer;
+       protected $buffer;
 
        /** The file data callback */
-       var $callback;
+       protected $callback;
 
        /** The ZIP64 mode */
-       var $zip64 = false;
+       protected $zip64 = false;
 
        /** Stored headers */
-       var $eocdr, $eocdr64, $eocdr64Locator;
+       protected $eocdr, $eocdr64, $eocdr64Locator;
 
-       var $data;
+       protected $data;
 
        /** The "extra field" ID for ZIP64 central directory entries */
        const ZIP64_EXTRA_HEADER = 0x0001;
@@ -159,8 +160,8 @@ class ZipDirectoryReader {
                        } else {
                                if ( $this->eocdr['CD size'] == 0xffffffff
                                        || $this->eocdr['CD offset'] == 0xffffffff
-                                       || $this->eocdr['CD entries total'] == 0xffff )
-                               {
+                                       || $this->eocdr['CD entries total'] == 0xffff
+                               {
                                        $this->error( 'zip-unsupported', 'Central directory header indicates ZIP64, ' .
                                                'but we are in legacy mode. Rejecting this upload is necessary to avoid ' .
                                                'opening vulnerabilities on clients using OpenJDK 7 or later.' );
@@ -174,6 +175,7 @@ class ZipDirectoryReader {
                }
 
                fclose( $this->file );
+
                return $status;
        }
 
@@ -221,8 +223,8 @@ class ZipDirectoryReader {
                        $this->error( 'zip-bad', 'trailing bytes after the end of the file comment' );
                }
                if ( $this->eocdr['disk'] !== 0
-                       || $this->eocdr['CD start disk'] !== 0 )
-               {
+                       || $this->eocdr['CD start disk'] !== 0
+               {
                        $this->error( 'zip-unsupported', 'more than one disk (in EOCDR)' );
                }
                $this->eocdr += $this->unpack(
@@ -245,8 +247,8 @@ class ZipDirectoryReader {
                );
                $structSize = $this->getStructSize( $info );
 
-               $block = $this->getBlock( $this->getFileLength() - $this->eocdr['EOCDR size']
-                       - $structSize, $structSize );
+               $start = $this->getFileLength() - $this->eocdr['EOCDR size'] - $structSize;
+               $block = $this->getBlock( $start, $structSize );
                $this->eocdr64Locator = $data = $this->unpack( $block, $info );
 
                if ( $data['signature'] !== "PK\x06\x07" ) {
@@ -263,8 +265,8 @@ class ZipDirectoryReader {
         */
        function readZip64EndOfCentralDirectoryRecord() {
                if ( $this->eocdr64Locator['eocdr64 start disk'] != 0
-                       || $this->eocdr64Locator['number of disks'] != 0 )
-               {
+                       || $this->eocdr64Locator['number of disks'] != 0
+               {
                        $this->error( 'zip-unsupported', 'more than one disk (in EOCDR64 locator)' );
                }
 
@@ -287,8 +289,8 @@ class ZipDirectoryReader {
                        $this->error( 'zip-bad', 'wrong signature on Zip64 end of central directory record' );
                }
                if ( $data['disk'] !== 0
-                       || $data['CD start disk'] !== 0 )
-               {
+                       || $data['CD start disk'] !== 0
+               {
                        $this->error( 'zip-unsupported', 'more than one disk (in EOCDR64)' );
                }
        }
@@ -310,6 +312,7 @@ class ZipDirectoryReader {
                        $this->error( 'zip-bad', 'the central directory does not immediately precede the end ' .
                                'of central directory record' );
                }
+
                return array( $offset, $size );
        }
 
@@ -329,8 +332,8 @@ class ZipDirectoryReader {
                $endPos = $this->eocdr['position'];
                if ( $size == 0xffffffff
                        || $offset == 0xffffffff
-                       || $numEntries == 0xffff )
-               {
+                       || $numEntries == 0xffff
+               {
                        $this->readZip64EndOfCentralDirectoryLocator();
 
                        if ( isset( $this->eocdr64Locator['eocdr64 offset'] ) ) {
@@ -348,6 +351,7 @@ class ZipDirectoryReader {
                        $this->error( 'zip-bad', 'the central directory does not immediately precede the end ' .
                                'of central directory record' );
                }
+
                return array( $offset, $size );
        }
 
@@ -396,10 +400,10 @@ class ZipDirectoryReader {
                        $pos += $this->getStructSize( $variableInfo );
 
                        if ( $this->zip64 && (
-                                  $data['compressed size'] == 0xffffffff
-                               || $data['uncompressed size'] == 0xffffffff
-                               || $data['local header offset'] == 0xffffffff ) )
-                       {
+                                       $data['compressed size'] == 0xffffffff
+                                       || $data['uncompressed size'] == 0xffffffff
+                                       || $data['local header offset'] == 0xffffffff )
+                       {
                                $zip64Data = $this->unpackZip64Extra( $data['extra field'] );
                                if ( $zip64Data ) {
                                        $data = $zip64Data + $data;
@@ -427,8 +431,8 @@ class ZipDirectoryReader {
 
                        // Convert the character set in the file name
                        if ( !function_exists( 'iconv' )
-                               || $this->testBit( $data['general bits'], self::GENERAL_UTF8 ) )
-                       {
+                               || $this->testBit( $data['general bits'], self::GENERAL_UTF8 )
+                       {
                                $name = $data['name'];
                        } else {
                                $name = iconv( 'CP437', 'UTF-8', $data['name'] );
@@ -487,6 +491,7 @@ class ZipDirectoryReader {
                        $stat = fstat( $this->file );
                        $this->fileLength = $stat['size'];
                }
+
                return $this->fileLength;
        }
 
@@ -548,6 +553,7 @@ class ZipDirectoryReader {
                        $bytePos = $segIndex * self::SEGSIZE;
                        if ( $bytePos >= $this->getFileLength() ) {
                                $this->buffer[$segIndex] = '';
+
                                return '';
                        }
                        if ( fseek( $this->file, $bytePos ) ) {
@@ -559,6 +565,7 @@ class ZipDirectoryReader {
                        }
                        $this->buffer[$segIndex] = $seg;
                }
+
                return $this->buffer[$segIndex];
        }
 
@@ -576,6 +583,7 @@ class ZipDirectoryReader {
                                $size += $type;
                        }
                }
+
                return $size;
        }
 
@@ -613,12 +621,12 @@ class ZipDirectoryReader {
                        if ( is_array( $type ) ) {
                                list( $typeName, $fieldSize ) = $type;
                                switch ( $typeName ) {
-                               case 'string':
-                                       $data[$key] = substr( $string, $pos, $fieldSize );
-                                       $pos += $fieldSize;
-                                       break;
-                               default:
-                                       throw new MWException( __METHOD__ . ": invalid type \"$typeName\"" );
+                                       case 'string':
+                                               $data[$key] = substr( $string, $pos, $fieldSize );
+                                               $pos += $fieldSize;
+                                               break;
+                                       default:
+                                               throw new MWException( __METHOD__ . ": invalid type \"$typeName\"" );
                                }
                        } else {
                                // Unsigned little-endian integer
@@ -696,7 +704,7 @@ class ZipDirectoryReader {
  * Internal exception class. Will be caught by private code.
  */
 class ZipDirectoryReaderError extends Exception {
-       var $errorCode;
+       protected $errorCode;
 
        function __construct( $code ) {
                $this->errorCode = $code;
index dc87bc8..f54ce83 100644 (file)
@@ -63,6 +63,7 @@ class FakeConverter {
        function markNoConversion( $text, $noParse = false ) { return $text; }
        function convertCategoryKey( $key ) { return $key; }
        function convertLinkToAllVariants( $text ) { return $this->autoConvertToAllVariants( $text ); }
+       /** @deprecated since 1.22 is no longer used */
        function armourMath( $text ) { return $text; }
        function validateVariant( $variant = null ) { return $variant === $this->mLang->getCode() ? $variant : null; }
        function translate( $text, $variant ) { return $text; }
@@ -3811,6 +3812,7 @@ class Language {
         *
         * @param $text string
         * @return string
+        * @deprecated since 1.22 is no longer used
         */
        public function armourMath( $text ) {
                return $this->mConverter->armourMath( $text );
index ccf9b1e..96a71a0 100644 (file)
@@ -1103,6 +1103,7 @@ class LanguageConverter {
         * @param $text String: text to armour against conversion
         * @return String: armoured text where { and } have been converted to
         *                 &#123; and &#125;
+        * @deprecated since 1.22 is no longer used
         */
        public function armourMath( $text ) {
                // convert '-{' and '}-' to '-&#123;' and '&#125;-' to prevent
index 3848a0b..0bf96d4 100644 (file)
@@ -47,7 +47,7 @@
        'als' => 'Alemannisch', # Alemannic -- not a valid code, for compatibility. See gsw.
        'am' => 'አማርኛ', # Amharic
        'an' => 'aragonés',    # Aragonese
-       'ang' => 'Ænglisc',    # Old English (Bug 23283)
+       'ang' => 'Ænglisc',    # Old English, bug 23283
        'anp' => 'अङ्गिका',       # Angika
        'ar' => 'العربية',       # Arabic
        'arc' => 'ܐܪܡܝܐ',  # Aramaic
@@ -70,9 +70,9 @@
        'bcl' => 'Bikol Central', # Bikol: Central Bicolano language
        'be' => 'беларуская', #  Belarusian normative
        'be-tarask' => "беларуская (тарашкевіца)\xE2\x80\x8E",     # Belarusian in Taraskievica orthography
-       'be-x-old' => "беларуская (тарашкевіца)\xE2\x80\x8E",      # Belarusian in Taraskievica orthography; compat link
+       'be-x-old' => "беларуская (тарашкевіца)\xE2\x80\x8E",      # (be-tarask compat)
        'bg' => 'български',   # Bulgarian
-       'bh' => 'भोजपुरी',        # Bihari macro language. Falls back to Bhojpuri (bho). The name actually says "Bhojpuri".
+       'bh' => 'भोजपुरी',        # Bihari macro language. Falls back to Bhojpuri (bho)
        'bho' => 'भोजपुरी',       # Bhojpuri
        'bi' => 'Bislama',              # Bislama
        'bjn' => 'Bahasa Banjar',       # Banjarese
        'gl' => 'galego',               # Galician
        'glk' => 'گیلکی',  # Gilaki
        'gn' => 'Avañe\'ẽ',  # Guaraní, Paraguayan
+       'gom-latn' => 'Konknni',        # Goan Konkani (Latin script)
        'got' => '𐌲𐌿𐍄𐌹𐍃𐌺',    # Gothic
        'grc' => 'Ἀρχαία ἑλληνικὴ', # Ancient Greek
        'gsw' => 'Alemannisch', # Alemannic
        'ig' => 'Igbo',                 # Igbo
        'ii' => 'ꆇꉙ',       # Sichuan Yi
        'ik' => 'Iñupiak',     # Inupiak (Inupiatun, Northwest Alaska / Inupiatun, North Alaskan)
-       'ike-cans' => 'ᐃᓄᒃᑎᑐᑦ',     # Inuktitut, Eastern Canadian/Eastern Canadian "Eskimo"/"Eastern Arctic Eskimo"/Inuit (Unified Canadian Aboriginal Syllabics)
+       'ike-cans' => 'ᐃᓄᒃᑎᑐᑦ',     # Inuktitut, Eastern Canadian (Unified Canadian Aboriginal Syllabics)
        'ike-latn' => 'inuktitut',      # Inuktitut, Eastern Canadian (Latin script)
        'ilo' => 'Ilokano',     # Ilokano
        'inh' => 'ГӀалгӀай',    # Ingush
        'io' => 'Ido',                  # Ido
        'is' => 'íslenska',    # Icelandic
        'it' => 'italiano',             # Italian
-       'iu' => 'ᐃᓄᒃᑎᑐᑦ/inuktitut', # Inuktitut (macro language - do no localise, see ike/ikt - falls back to ike-cans)
+       'iu' => 'ᐃᓄᒃᑎᑐᑦ/inuktitut', # Inuktitut (macro language, see ike/ikt, falls back to ike-cans)
        'ja' => '日本語',    # Japanese
        'jam' => 'Patois',      # Jamaican Creole English
        'jbo' => 'Lojban',              # Lojban
        'ltg' => 'latgaļu',    # Latgalian
        'lus' => 'Mizo ţawng', # Mizo/Lushai
        'lv' => 'latviešu',    # Latvian
-       'lzh' => '文言',      # Literary Chinese -- (bug 8217) lzh instead of zh-classical, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=l
+       'lzh' => '文言',      # Literary Chinese, bug 8217
        'lzz' => 'Lazuri',      # Laz
        'mai' => 'मैथिली', # Maithili
        'map-bms' => 'Basa Banyumasan', # Banyumasan
        'myv' => 'эрзянь',        # Erzya
        'mzn' => 'مازِرونی',            # Mazanderani
        'na' => 'Dorerin Naoero',               # Nauruan
-       'nah' => 'Nāhuatl',            # Nahuatl, en:Wikipedia writes Nahuatlahtolli, while another form is Náhuatl
-       'nan' => 'Bân-lâm-gú', # Min-nan -- (bug 8217) nan instead of zh-min-nan, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=n
+       'nah' => 'Nāhuatl',            # Nahuatl (not in ISO 639-3)
+       'nan' => 'Bân-lâm-gú', # Min-nan, bug 8217
        'nap' => 'Napulitano',  # Neapolitan, bug 43793
        'nb' => "norsk bokmål",                # Norwegian (Bokmal)
        'nds' => 'Plattdüütsch',      # Low German ''or'' Low Saxon
        'oc' => 'occitan',              # Occitan
        'om' => 'Oromoo',               # Oromo
        'or' => 'ଓଡ଼ିଆ',              # Oriya
-       'os' => 'Ирон', # Ossetic -- fixed per bug 29091
+       'os' => 'Ирон', # Ossetic, bug 29091
        'pa' => 'ਪੰਜਾਬੀ', # Eastern Punjabi (Gurmukhi script) (pan)
        'pag' => 'Pangasinan',  # Pangasinan
        'pam' => 'Kapampangan',   # Pampanga
        'pt' => 'português',   # Portuguese
        'pt-br' => 'português do Brasil',      # Brazilian Portuguese
        'qu' => 'Runa Simi',    # Southern Quechua
-       'qug' => 'Runa shimi',  # Kichwa/Northern Quechua (one of the codes it covers; temporarily used until Kichwa has its own)
+       'qug' => 'Runa shimi',  # Kichwa/Northern Quechua (temporarily used until Kichwa has its own)
        'rgn' => 'Rumagnôl',   # Romagnol
        'rif' => 'Tarifit',     # Tarifit
        'rm' => 'rumantsch',    # Raeto-Romance
        'war' => 'Winaray', # Waray-Waray
        'wo' => 'Wolof',                # Wolof
        'wuu' => '吴语',              # Wu Chinese
-       'xal' => 'хальмг',                # Kalmyk-Oirat (Kalmuk, Kalmuck, Kalmack, Qalmaq, Kalmytskii Jazyk, Khal:mag, Oirat, Volga Oirat, European Oirat, Western Mongolian)
+       'xal' => 'хальмг',                # Kalmyk-Oirat
        'xh' => 'isiXhosa',             # Xhosan
        'xmf' => 'მარგალური', # Mingrelian
        'yi' => 'ייִדיש', # Yiddish
        'yo' => 'Yorùbá',     # Yoruba
-       'yue' => '粵語',      # Cantonese -- (bug 8217) yue instead of zh-yue, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=y
+       'yue' => '粵語',      # Cantonese
        'za' => 'Vahcuengh',    # Zhuang
        'zea' => 'Zeêuws',     # Zeeuws/Zeaws
        'zh' => '中文',                                               # (Zhōng Wén) - Chinese
index 0038b29..7b9dcc3 100644 (file)
@@ -158,12 +158,12 @@ $specialPageAliases = array(
 
 $messages = array(
 # User preference toggles
-'tog-underline' => 'Bôh garéh yup bak hubông:',
+'tog-underline' => 'Bôh garéh yup peunawôt:',
 'tog-justify' => 'Peurata paragraf',
-'tog-hideminor' => 'Peusom neuandam bacut bak neuubah paléng barô',
-'tog-hidepatrolled' => 'Peusom neuandam teupatroli bak neuubah paléng barô',
-'tog-newpageshidepatrolled' => 'Peusom ôn teupatroli nibak dapeuta ôn barô',
-'tog-extendwatchlist' => 'Peuhah dapeuta keunalön keu peuleumah ban dum neuubah, kon nyang paléng barô mantöng',
+'tog-hideminor' => 'Peusom neuandam bacut bak neuubah barô',
+'tog-hidepatrolled' => 'Peusom neuandam teurunda bak neuubah barô',
+'tog-newpageshidepatrolled' => 'Peusom laman teurunda nibak dapeuta ôn barô',
+'tog-extendwatchlist' => 'Peuhah dapeuta keunalön keu peuleumah ban dum neuubah, kon nyang barô mantöng',
 'tog-usenewrc' => 'Peusaho neuandam bak neuleumah neuubah barô ngon dapeuta keunalon meunurot ôn',
 'tog-numberheadings' => 'Bôh numbô nan keudroë',
 'tog-showtoolbar' => 'Peuleumah bateuëng alat andam',
@@ -348,7 +348,7 @@ $messages = array(
 'history_short' => 'Riwayat',
 'updatedmarker' => 'geuubah yoh seunaweue keuneulheueh lon phon kon',
 'printableversion' => 'Seunalén rakam',
-'permalink' => 'Seuneumat teutap',
+'permalink' => 'Peunawôt teutap',
 'print' => 'Rakam',
 'view' => 'Beuet',
 'edit' => 'Andam',
@@ -386,7 +386,7 @@ $messages = array(
 'otherlanguages' => 'Bahsa la’én',
 'redirectedfrom' => '(Geupeupinah nibak $1)',
 'redirectpagesub' => 'Ôn peuninah',
-'lastmodifiedat' => 'Ôn nyoë seuneulheuëh geuubah bak $2, $1.',
+'lastmodifiedat' => 'Laman nyoë seuneulheuëh geuubah bak $1 poh $2.',
 'viewcount' => 'On nyoe ka geusaweue {{PLURAL:$1|sigo|$sigo}}.<br />',
 'protectedpage' => 'Ôn teupeulindông',
 'jumpto' => 'Grôp u:',
@@ -464,7 +464,7 @@ $1",
 
 # Short words for each namespace, by default used in the namespace tab in monobook
 'nstab-main' => 'Ôn',
-'nstab-user' => 'Ureuëng nguy',
+'nstab-user' => 'Ureuëng ngui',
 'nstab-media' => 'Ôn media',
 'nstab-special' => 'Kusuih',
 'nstab-project' => 'Buët ôn',
@@ -556,7 +556,7 @@ Bèk tuwo neuatô [[Special:Preferences|geunalak {{SITENAME}}]] droëneuh.',
 'userlogin-yourpassword' => 'Lageuëm rahsia',
 'userlogin-yourpassword-ph' => 'Pasoë lageuëm rahsia droëneuh',
 'createacct-yourpassword-ph' => 'Pasoë lageuëm rahsia',
-'yourpasswordagain' => 'Pasoë lom lageuëm:',
+'yourpasswordagain' => 'Pasoë lom lageuëm rahsia:',
 'createacct-yourpasswordagain' => 'Peunyo lageuëm rahsia',
 'createacct-yourpasswordagain-ph' => 'Pasoë lom lageuëm rahsia',
 'remembermypassword' => 'Ingat lôn tamong bak peuramban nyoë (keu paleng trep $1 {{PLURAL:$1|uroë|uroë}})',
@@ -581,7 +581,7 @@ Bèk tuwo neuatô [[Special:Preferences|geunalak {{SITENAME}}]] droëneuh.',
 'gotaccount' => "Ka lheuëh neudapeuta? '''$1'''.",
 'gotaccountlink' => 'Tamong',
 'userlogin-resetlink' => 'Tuwo ngon rincian tamong Droeneuh?',
-'userlogin-resetpassword-link' => 'Peugöt lageuëm rahsia la’én',
+'userlogin-resetpassword-link' => 'Tuwö lageuëm rahsia?',
 'helplogin-url' => 'Help:Tamong',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Bantu tamöng]]',
 'userlogin-loggedin' => 'Droëneuh ka neutamöng seubagoë $1. Neunguy blangko di yup keu neutamöng seubagoë ureuëng nguy la’én',
@@ -622,7 +622,7 @@ Préksa keulayi neu’ija Droëneuh.',
 'wrongpassword' => 'Lageuëm nyang neupasoë salah. Neuci lom.',
 'wrongpasswordempty' => 'Droëneuh hana neupasoë lageuëm. Neuci lom.',
 'passwordtooshort' => "Lageuëm paléng h'an haroh na {{PLURAL:$1|1 karakter|$1 karakter}}.",
-'mailmypassword' => "Peu'ét lageuëm barô",
+'mailmypassword' => "Peu'ét lageuëm rahsia barô u surat-e",
 'passwordremindertitle' => 'Lageuëm seumeuntara barô keu {{SITENAME}}',
 'passwordremindertext' => 'Salah sidroë (kadang Droëneuh, ngön alamat IP $1) geulakèë lageuëm barô keu {{SITENAME}} ($4). Lageuëm si\'at keu ureuëng nguy "$2" ka geupeuna ngon ka geuato jeuet keu "$3". Meunyo nyoe nakeuh meukeusud droeneuh, droeneuh peureulee neutamong ngon neupileh lageuem baro jinoe. Lageuem siat droeneuh meung abeh lam {{PLURAL:$5|siuroe|$5 uroe}}.
 
@@ -633,17 +633,27 @@ Meunyo ureueng la\'en nyang peugot neulakee nyoe, atawa meunyo droeneuh ka neuin
 'loginlanguagelabel' => 'Bahsa: $1',
 
 # Change password dialog
-'retypenew' => 'Pasoë teuma lageuëm barô:',
+'resetpass' => 'Gantoë lageuëm rahsia',
+'resetpass_header' => 'Gantoë lageuëm rahsia nan ureuëng ngui',
+'oldpassword' => 'Lageuëm rahsia awai:',
+'newpassword' => 'Lageuëm rahsia barô:',
+'retypenew' => 'Pasoë lom lageuëm barô:',
+'resetpass_submit' => 'Atô lageuëm rahsia lheuëh nyan tamöng',
+'changepassword-success' => 'Lageuëm rahsia droëneuh meuhasé geugantoë!',
+'resetpass_forbidden' => "Lageuëm rahsia h'an jeuët geugantoë",
+'resetpass-no-info' => "Droëneuh suwah neutamöng mangat jeuët neu'eu laman nyoë",
+'resetpass-submit-loggedin' => 'Gantoë lageuëm rahsia',
+'resetpass-submit-cancel' => 'Pubateuë',
 
 # Edit page toolbar
-'bold_sample' => 'Rakam teubay',
+'bold_sample' => 'Rakam teubai',
 'bold_tip' => 'Haraih teubay',
 'italic_sample' => 'Rakam singèt naseukah nyoë',
 'italic_tip' => 'Rakam singèt',
 'link_sample' => 'Nan hubông',
-'link_tip' => 'Hubông dalam',
+'link_tip' => 'Peunawôt dalam',
 'extlink_sample' => 'http://www.example.com nan hubông',
-'extlink_tip' => 'Hubông luwa (bèk tuwoë bôh http:// bak away)',
+'extlink_tip' => 'Peunawôt luwa (neubôh http:// bak awai)',
 'headline_sample' => 'Naseukah nan',
 'headline_tip' => 'Aneuk beunagi tingkat 1',
 'nowiki_sample' => 'Bèk format naseukah nyoë',
@@ -685,7 +695,7 @@ Ji Droëneuh jeuët [[Special:Search/{{PAGENAME}}|neumita keu nan ôn nyoë]] ba
 Droëneuh jeuët [[Special:Search/{{PAGENAME}}|neumita keu nan ôn nyoë]] bak ôn-ôn la\'én,
 atawa <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} neumita log nyang na meuhubông]</span>, tapi Droëneuh hana idin keu neupeugöt ôn nyoë',
 'updated' => '(Seubarô)',
-'note' => "'''Ceunatat:'''",
+'note' => "'''Hareutoë:'''",
 'previewnote' => "'''Beu neuingat meunyo laman nyoë goh lom neukeubah!'''",
 'editing' => 'Andam $1',
 'editingsection' => 'Andam $1 (bideuëng)',
@@ -737,9 +747,9 @@ Alasan-alasan nyan hana geupeureumeuen.",
 
 # Revision deletion
 'rev-delundel' => 'peuleumah/peusom',
-'revdel-restore' => 'Ubah neuleumah',
+'revdel-restore' => 'Gantoë seuneudeuih',
 'revdel-restore-deleted' => 'geunantoe nyang ka geusampôh',
-'revdel-restore-visible' => 'geunantoe nyang leumah',
+'revdel-restore-visible' => 'geunantoë nyang deuih',
 
 # Merge log
 'revertmerge' => 'Hana jadèh peugabông',
@@ -768,7 +778,7 @@ Alasan-alasan nyan hana geupeureumeuen.",
 'searchmenu-exists' => "'''Na on ngon nan \"[[:\$1]]\" bak wiki nyoe.'''",
 'searchmenu-new' => "'''Peugöt ôn \"[[:\$1]]\" bak wiki nyoë!'''",
 'searchprofile-articles' => 'Ôn asoë',
-'searchprofile-project' => 'Ôn Beunantu ngön Buët',
+'searchprofile-project' => 'Laman Beunantu ngön Buët',
 'searchprofile-images' => 'Multimedia',
 'searchprofile-everything' => 'Ban dum',
 'searchprofile-advanced' => 'Tingkat lanjut',
@@ -788,8 +798,9 @@ Alasan-alasan nyan hana geupeureumeuen.",
 'searchrelated' => 'meusambat',
 'searchall' => 'ban dum',
 'showingresultsheader' => "{{PLURAL:$5|Hase '''$1''' nibak '''$3'''|Hase '''$1 - $2''' nibak '''$3'''}} keu '''$4'''",
-'nonefound' => "'''Ceunatat''': Cit ladôm ruweuëng nyang seucara baku geupeutamöng lam meunita. Ci neupuphôn leunakèë Droëneuh ngön ''all:'' keu mita ban dum asoë (rôh cit ôn peugah haba, tèmplat, ngön nyang la’én (nnl)), atawa neunguy ruweuëng nan nyang neumeuh’eut sibagoë neu’away.",
-'search-nonefound' => 'Hana hase nyang paih lagee atra neulakee',
+'nonefound' => "'''Teuneurang''': Ladôm ruweuëng nan mantöng nyang geumita. 
+Neubaci puphôn neulakèë droëneuh ngön ''all:'' keu jak mita ban dum asoë (rôh lam nyan laman marit, seunaleuëk, ngön nyang la’én nibak nyan), atawa neungui ruweuëng nan nyang neumeuh’eut sibagoë neuawai.",
+'search-nonefound' => 'Hana hasé nyang paih lagèë neulakèë',
 'powersearch' => 'Mita lanjut',
 'powersearch-legend' => 'Mita lanjut',
 'powersearch-ns' => 'Mita bak ruweuëng nan:',
@@ -826,11 +837,15 @@ Surat-e droeneuh h'an geupeugah keu ureueng nyan.",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|neuubah}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|yôh seunaweuë seuneulheuëh kön}}',
 'recentchanges' => 'Neuubah barô',
 'recentchanges-legend' => 'Peuniléh neuubah barô',
 'recentchanges-summary' => "Di yup nyoë nakeuh neuubah barô nyang na bak Wikipèdia nyoë.
-Ceunatat: (bida) = neuubah, (riwayat) = riwayat teumuléh, '''B''' = ôn barô, '''u''' = neuandam ubeut, '''b''' = neuandam bot, (± ''bit'') = jumeulah asoë meutamah/meukureuëng, → = neuandam beunagi, ← = mohtasa otomatis.
+
+
+Hareutoë: (bida) = neuubah, (riwayat) = riwayat teumuléh, '''B''' = laman barô, '''u''' = neuandam ubeut, '''b''' = neuandam bot, (± ''bit'') = jumeulah asoë meutamah/meukureuëng, → = neuandam beunagi, ← = mohtasa otomatis.
 ----",
+'recentchanges-noresult' => 'Hana neuubah lam lheuëng watèë nyoë nyang paih ngön syarat',
 'recentchanges-feed-description' => 'Seutot neuubah barô lam wiki bak umpeuën nyoë.',
 'recentchanges-label-newpage' => 'Neuandam nyoe jipeugot on baro',
 'recentchanges-label-minor' => 'Nyoe neuandam ubeut',
@@ -838,14 +853,14 @@ Ceunatat: (bida) = neuubah, (riwayat) = riwayat teumuléh, '''B''' = ôn barô,
 'recentchanges-label-unpatrolled' => 'Neuandam nyoe goh lom geukalon',
 'rcnote' => "Di yup nyoë nakeuh {{PLURAL:$1|nakeuh '''1''' neu’ubah barô |nakeuh '''$1''' neu’ubah barô}} lam {{PLURAL:$2|'''1''' uroë|'''$2''' uroë}} nyoë, trôk ‘an $5, $4.",
 'rcnotefrom' => 'Di yup nyoë nakeuh neuubah yôh <strong>$2</strong> (geupeudeuh trôh ‘an <strong>$1</strong> neuubah).',
-'rclistfrom' => 'Peuleumah neuubah paléng barô yôh $1 kön',
+'rclistfrom' => 'Peudeuih neuubah barô yôh $1 kön',
 'rcshowhideminor' => '$1 andam bacut',
 'rcshowhidebots' => '$1 bot',
 'rcshowhideliu' => '$1 ureuëng nguy tamong',
 'rcshowhideanons' => '$1 ureuëng nguy hana nan',
 'rcshowhidepatr' => '$1 andam teurunda',
 'rcshowhidemine' => '$1 atra lôn andam',
-'rclinks' => 'Peuleumah $1 neuubah paléng barô lam $2 uroë nyoë<br />$3',
+'rclinks' => 'Peudeuih $1 neuubah barô lam $2 uroë nyoë<br />$3',
 'diff' => 'bida',
 'hist' => 'riwayat',
 'hide' => 'Peusom',
@@ -859,7 +874,7 @@ Ceunatat: (bida) = neuubah, (riwayat) = riwayat teumuléh, '''B''' = ôn barô,
 # Recent changes linked
 'recentchangeslinked' => 'Neuubah meuhubông',
 'recentchangeslinked-feed' => 'Neuubah meuhubông',
-'recentchangeslinked-toolbox' => 'Neuubah meuhubông',
+'recentchangeslinked-toolbox' => "Neuubah teukaw'èt",
 'recentchangeslinked-title' => 'Neuubah nyang meuhubông ngön $1',
 'recentchangeslinked-summary' => "Nyoë nakeuh dapeuta neuubah nyang geupeugèt ban-ban nyoë keu on-on nyang meuhubông nibak ôn ka kusuih (atawa keu anggèëta kawan kusuih).
 Ôn-ôn bak [[Special:Watchlist|keunalon droeneuh]] geucitak '''teubay'''.",
@@ -888,13 +903,13 @@ Ceunatat: (bida) = neuubah, (riwayat) = riwayat teumuléh, '''B''' = ôn barô,
 'filehist-datetime' => 'Uroë buleuën/Watèë',
 'filehist-thumb' => 'Beuntuk ubeut',
 'filehist-thumbtext' => 'Beuntuk ubeut keu seunalén tiëp $1',
-'filehist-user' => 'Ureuëng nguy',
+'filehist-user' => 'Ureuëng ngui',
 'filehist-dimensions' => 'Dimènsi',
 'filehist-filesize' => 'Rayek beureukah',
 'filehist-comment' => "Seuneu'ôt",
-'imagelinks' => 'Neunguy beureukaih',
+'imagelinks' => 'Seuneungui beureukaih',
 'linkstoimage' => 'Ôn di yup nyoë na {{PLURAL:$1|hubông|$1 hubông}} u beureukah nyoë:',
-'nolinkstoimage' => 'Hana ôn nyang na hubông u beureukah nyoë.',
+'nolinkstoimage' => 'Hana laman nyang na meupawôt u beureukaih nyoë.',
 'sharedupload' => 'Beureukah nyoë dari $1 ngön kadang geunguy lé buët-buët la’én.',
 'sharedupload-desc-here' => "Beureukaih nyoe nejih nibak $1 ngon kadang geunguy le proyek-proyek la'en.
 Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
@@ -957,7 +972,7 @@ Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
 'move' => 'Peupinah',
 'movethispage' => 'Peupinah ôn nyoë',
 'pager-newer-n' => '{{PLURAL:$1|1 leubèh barô |$1 leubèh barô}}',
-'pager-older-n' => '{{PLURAL:$1|1 leubèh trép|$1 leubèh trép}}',
+'pager-older-n' => '{{PLURAL:$1|1 leubèh awai|$1 leubèh awai}}',
 
 # Book sources
 'booksources' => 'Nè kitab',
@@ -965,10 +980,10 @@ Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
 'booksources-go' => 'Mita',
 
 # Special:Log
-'specialloguserlabel' => 'Ureuëng nguy:',
-'speciallogtitlelabel' => 'Nan:',
+'specialloguserlabel' => 'Ureuëng ngui:',
+'speciallogtitlelabel' => 'Sasaran (nan atawa ureuëng ngui):',
 'log' => 'Log',
-'all-logs-page' => 'Ban dum log',
+'all-logs-page' => 'Ban dum log umom',
 
 # Special:AllPages
 'allpages' => 'Dapeuta ôn',
@@ -987,7 +1002,7 @@ Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
 # Special:LinkSearch
 'linksearch' => 'Mita seuneumat luwa',
 'linksearch-ok' => 'Mita',
-'linksearch-line' => '$1 meusambat nibak $2',
+'linksearch-line' => '$1 meupawôt nibak $2',
 
 # Special:ListGroupRights
 'listgrouprights-members' => '(dapeuta anggèëta)',
@@ -1019,7 +1034,7 @@ Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
 'actioncomplete' => 'Seuleusoë',
 'actionfailed' => 'Hana meuhase',
 'deletedtext' => '"$1" ka geusampôh. Eu $2 keu log paléng barô bak ôn nyang ka geusampôh.',
-'dellogpage' => 'Log seunampoh',
+'dellogpage' => 'Log seunampôh',
 'deletecomment' => 'Choë:',
 'deleteotherreason' => 'Nyang la’én/choë la’én:',
 'deletereasonotherlist' => 'Choë la’én',
@@ -1043,7 +1058,7 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'protect-default' => 'Peuidin ban dum ureuëng nguy',
 'protect-fallback' => 'Peureulèë hak peuhah "$1"',
 'protect-level-autoconfirmed' => 'Theun ureuëng nguy barô ngön hana teudapeuta',
-'protect-level-sysop' => 'Ureuëng urôh mantöng',
+'protect-level-sysop' => 'Peuidin ureuëng urôh mantöng',
 'protect-summary-cascade' => 'riti',
 'protect-expiring' => 'maté tanggay $1 (UTC)',
 'protect-cascade' => 'Peulindông ban mandum ôn nyang rôh lam ôn nyoë (lindông meuturôt).',
@@ -1070,8 +1085,8 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'mycontris' => 'Beuneuri',
 'contribsub2' => 'Keu {{GENDER:$3|$1}} ($2)',
 'uctop' => '(jinoë)',
-'month' => 'Yôh buleuën (ngön yôh goh lom nyan)',
-'year' => 'Yôh thôn (ngön yôh goh lom nyan)',
+'month' => 'Mula phôn buleuën (ngön sigohlomjih)',
+'year' => 'Mula phôn thôn (ngön sigohlomjih)',
 
 'sp-contributions-newbies' => 'Peudeuh beuneuri atra ureuëng ban dapeuta mantöng',
 'sp-contributions-newbies-sub' => 'Keu ureuëng nguy barô',
@@ -1085,20 +1100,20 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'sp-contributions-submit' => 'Mita',
 
 # What links here
-'whatlinkshere' => 'Hubông balék',
+'whatlinkshere' => 'Peunawôt balék',
 'whatlinkshere-title' => 'Ôn nyang na neuhubông u $1',
 'whatlinkshere-page' => 'Ôn:',
-'linkshere' => "Ôn-ôn nyoë meuhubông u '''[[:$1]]''':",
+'linkshere' => "Laman-laman nyoë meupawôt u '''[[:$1]]''':",
 'nolinkshere' => "Hana halaman nyang teukaw'et u '''[[:$1]]'''.",
 'isredirect' => 'ôn peupinah',
 'istemplate' => 'ngön seunaleuëk',
 'isimage' => 'hubông beureukaih',
 'whatlinkshere-prev' => '$1 {{PLURAL:$1|sigohlomjih|sigohlomjih}}',
 'whatlinkshere-next' => '$1 {{PLURAL:$1|lheuëh nyan|lheuëh nyan}}',
-'whatlinkshere-links' => '← hubông',
+'whatlinkshere-links' => '← peunawôt',
 'whatlinkshere-hideredirs' => '$1 peuninah',
 'whatlinkshere-hidetrans' => '$1 transklusi',
-'whatlinkshere-hidelinks' => '$1 hubông',
+'whatlinkshere-hidelinks' => '$1 peunawôt',
 'whatlinkshere-hideimages' => '$1 seuneumat beureukaih',
 'whatlinkshere-filters' => 'Saréng',
 
@@ -1109,7 +1124,7 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'ipblocklist-submit' => 'Mita',
 'blocklink' => 'theun',
 'unblocklink' => 'peugadöh theun',
-'change-blocklink' => 'ubah theun',
+'change-blocklink' => 'gantoë theun',
 'contribslink' => 'beuneuri',
 'blocklogpage' => 'Log peutheun',
 'blocklogentry' => 'theun [[$1]] ngön watèë maté tanggay $2 $3',
@@ -1160,17 +1175,17 @@ Lam masalah nyoë, meunyo neuhawa, Droëneuh jeuët neupeupinah atawa neupeugab
 # Tooltip help for the actions
 'tooltip-pt-userpage' => 'Ôn ureuëng nguy Droëneuh',
 'tooltip-pt-mytalk' => 'Ôn marit Droëneuh',
-'tooltip-pt-preferences' => 'Atô',
-'tooltip-pt-watchlist' => 'Dapeuta ôn nyang lônkalön',
+'tooltip-pt-preferences' => 'Geunalak',
+'tooltip-pt-watchlist' => 'Dapeuta laman nyang lônkalön',
 'tooltip-pt-mycontris' => 'Dapeuta beuneuri Droëneuh',
 'tooltip-pt-login' => 'Droëneuh geupadan keu tamong log, bah pih nyan hana geupeuwajéb.',
 'tooltip-pt-logout' => 'Teubiët',
 'tooltip-ca-talk' => 'Marit ôn asoë',
-'tooltip-ca-edit' => 'Droëneuh jeuët neuandam ôn nyoë. Neunguy tumbôy eu dilèë yôh goh neukeubah.',
+'tooltip-ca-edit' => 'Droëneuh jeuët neuandam laman nyoë. Neungui tumbôi eu dilèë sigoh neukeubah.',
 'tooltip-ca-addsection' => 'Puphôn beunagi barô',
 'tooltip-ca-viewsource' => 'Ôn nyoë geupeulindông.
 Droëneuh jeuët neu’eu nèjih.',
-'tooltip-ca-history' => 'Seunalén away nibak ôn nyoë',
+'tooltip-ca-history' => 'Seunalén awai nibak ôn nyoë',
 'tooltip-ca-protect' => 'Peulindông ôn nyoë',
 'tooltip-ca-delete' => 'Sampôh ôn nyoë',
 'tooltip-ca-move' => 'Peupinah ôn nyoë',
@@ -1182,21 +1197,21 @@ Droëneuh jeuët neu’eu nèjih.',
 'tooltip-p-logo' => 'Saweuë ôn keuë',
 'tooltip-n-mainpage' => 'Jak u ôn keuë',
 'tooltip-n-mainpage-description' => 'Saweuë ôn keuë',
-'tooltip-n-portal' => 'Bhaih buët, peuë nyang jeuët neupubuët, pat keu mita sipeuë hay',
+'tooltip-n-portal' => 'Bhaih buët, peuë nyang jeuët neupubuët, pat keu mita sipeuë hai',
 'tooltip-n-currentevents' => 'Mita haba barô',
-'tooltip-n-recentchanges' => 'Dapeuta neuubah baro lam wiki.',
+'tooltip-n-recentchanges' => 'Dapeuta neuubah barô lam wiki.',
 'tooltip-n-randompage' => 'Peuleumah ôn beurangkari',
 'tooltip-n-help' => 'Bak mita bantu.',
-'tooltip-t-whatlinkshere' => 'Dapeuta ban dum ôn wiki nyang meuhubông keunoë',
-'tooltip-t-recentchangeslinked' => 'Neuubah barô ôn nyang na seuneumat u ôn nyoë',
+'tooltip-t-whatlinkshere' => 'Dapeuta ban dum laman wiki nyang mupawôt keunoë',
+'tooltip-t-recentchangeslinked' => 'Neuubah barô lam laman nyang meupawôt nibak laman nyoë',
 'tooltip-feed-rss' => 'Umpeuën RSS keu ôn nyoë',
-'tooltip-feed-atom' => 'Umpeuën Atom keu ôn nyoë',
+'tooltip-feed-atom' => 'Umpeuën Atom keu laman nyoë',
 'tooltip-t-contributions' => 'Eu dapeuta nyang ka geutuléh lé ureuëng nguy nyoë',
 'tooltip-t-emailuser' => "Peu'ét surat-e keu ureuëng nguy nyoë",
 'tooltip-t-upload' => 'Peutamong beureukaih',
 'tooltip-t-specialpages' => 'Dapeuta ban dum ôn kusuih',
-'tooltip-t-print' => 'Seunalén rakam ôn nyoë',
-'tooltip-t-permalink' => 'Seuneumat teutap keu geunantoë ôn nyoë',
+'tooltip-t-print' => 'Seunalén rakam laman nyoë',
+'tooltip-t-permalink' => 'Peunawôt teutap keu geunantoë laman nyoë',
 'tooltip-ca-nstab-main' => 'Eu ôn asoë',
 'tooltip-ca-nstab-user' => 'Eu ôn ureuëng nguy',
 'tooltip-ca-nstab-special' => 'Nyoë nakeuh ôn kusuih nyang h’an jeuët geu’andam.',
@@ -1215,8 +1230,11 @@ Droëneuh jeuët neu’eu nèjih.',
 'tooltip-undo' => 'Peuriwang geunantoë nyoë ngön peuhah plôk neu’andam ngön cara eu dilèë. Choë jeuët geupeutamah bak plôk ehtisa.',
 'tooltip-summary' => 'Pasoë éhtisa paneuk',
 
+# Info page
+'pageinfo-toolboxlink' => 'Teuneurang laman',
+
 # Browsing diffs
-'previousdiff' => '← Bida away',
+'previousdiff' => '← Bida awai',
 'nextdiff' => 'Geunantoë lheuëh nyan →',
 
 # Media information
@@ -1265,6 +1283,17 @@ Data nyang la'én eunteuk teupeusom keudroë.
 'namespacesall' => 'ban dum',
 'monthsall' => 'ban dum',
 
+# Auto-summaries
+'autosumm-new' => "Geupeugöt laman ngön asoë '$1'",
+
+# Live preview
+'livepreview-loading' => 'Pumasoë...',
+'livepreview-ready' => 'Pumasoë... Ka lheuëh!',
+'livepreview-failed' => 'Peudeuih hasé langsông hana meuhasé!
+Neuci peudeuih hasé biasa.',
+'livepreview-error' => 'H\'an jitém teusambat: $1 "$2"
+Neuci peudeuih hasé biasa.',
+
 # Watchlist editing tools
 'watchlisttools-view' => "Peudeuh neuubah meukaw'èt",
 'watchlisttools-edit' => 'Peudeuh ngön andam dapeuta keunalön',
index ec7eb60..848e65b 100644 (file)
@@ -555,7 +555,7 @@ Dae malingaw na liwaton an saimong [[Special:Preferences|{{SITENAME}} mga kamuya
 'gotaccount' => 'Igwa ka na tabi nin panindog? $1.',
 'gotaccountlink' => 'Maglaog',
 'userlogin-resetlink' => 'Nakalingaw ka sa panlaog mong detalye?',
-'userlogin-resetpassword-link' => 'Pakibaguha an saimong sekretong panlaog',
+'userlogin-resetpassword-link' => 'Nalingawan mo an saimong pasa-taramon?',
 'helplogin-url' => 'Help:Paglalaog',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Tabang sa paglalaog]]',
 'userlogin-loggedin' => 'Ika nakalaog na tabi bilang si {{GENDER:$1|$1}}.
@@ -630,9 +630,9 @@ Tangarig malikayan an abuso, saro sanang e-surat sa pagliliwat kan sekretong pan
 'mailerror' => 'Salâ an pagpadará kan koreo: $1',
 'acct_creation_throttle_hit' => 'Mga bisita kaining wiki na ginagamit an saimong IP address nagmukna nin {{PLURAL:$1|1 panindog|$1 mga panindog}} sa nakaaging aldaw, na iyo ngani an maximum na pinagtutugot sa laog kan peryodong panahon.
 Bilang resulta, an mga bisita na naggagamit kaining IP address dae nguna makakamukna nin mga panindog.',
-'emailauthenticated' => 'An saimong e-koreo awtentikado kan $2 sa $3.',
-'emailnotauthenticated' => 'An saimong e-surat dae pa tabi pinagpatunayan. 
-Mayong e-surat an ipapadara para sa arinman kan minasunod na estima.',
+'emailauthenticated' => 'An saimong e-surat na estada pinagkumpirma kan $2 mga alas $3.',
+'emailnotauthenticated' => 'An saimong e-surat na estada dae pa tabi pinagkumpirma.
+Mayo tabing e-surat na ipagpapadara para sa arinman kan mga minasunod na mga estima.',
 'noemailprefs' => 'Magkaag nin sarong e-koreong address sa saimong mga kabotan para gumana ining mga estima.',
 'emailconfirmlink' => 'Kompirmaron tabî an saimong e-koreong address',
 'invalidemailaddress' => 'An e-koreo dae akseptado habang ini minapahiling na igwa nin imbalidong panugmad.
index f25f067..c1dabfd 100644 (file)
@@ -690,7 +690,7 @@ $2',
 'gotaccount' => "Имате ли вече сметка? '''$1'''.",
 'gotaccountlink' => 'Влизане',
 'userlogin-resetlink' => 'Забравени данни за влизане в системата?',
-'userlogin-resetpassword-link' => 'Ð\92Ñ\8aзÑ\81Ñ\82ановÑ\8fване Ð½Ð° Ð¿Ð°Ñ\80олаÑ\82а',
+'userlogin-resetpassword-link' => 'Ð\97абÑ\80авена Ð¿Ð°Ñ\80ола?',
 'helplogin-url' => 'Help:Влизане',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощ за влизане]] в системата',
 'userlogin-loggedin' => 'Вече сте влезли в системата като {{GENDER:$1|$1}}.
@@ -757,7 +757,8 @@ $2',
 'acct_creation_throttle_hit' => 'През последното денонощие, през този IP-адрес посетители на това уики са създали {{PLURAL:$1|1 сметка |$1 сметки}}, което е максималният допустим брой за този период.
 В резултат, към момента не могат да създават повече потребителски сметки през този IP-адрес.',
 'emailauthenticated' => 'Адресът на електронната ви поща беше потвърден на $2 в $3.',
-'emailnotauthenticated' => 'Адресът на електронната ви поща <strong>не е потвърден</strong>. Няма да получавате писма за никоя от следните възможности.',
+'emailnotauthenticated' => 'Адресът на електронната ви поща все още не е потвърден.
+Няма да получавате писма за никоя от следните възможности.',
 'noemailprefs' => 'За да работят тези функционалности, трябва да посочите адрес на електронна поща в своите настройки.',
 'emailconfirmlink' => 'Потвърждаване на адреса за електронна поща',
 'invalidemailaddress' => 'Въведеният адрес не може да бъде приет, тъй като не съответства на формата на адрес за електронна поща. Въведете коректен адрес или оставете полето празно.',
@@ -771,7 +772,7 @@ $2',
 Можете да пренебрегнете това съобщение, ако сметката е създадена по грешка.',
 'usernamehasherror' => 'Потребителското име не може да съдържа хеш символи',
 'login-throttled' => 'Направили сте твърде много опити да въведете паролата за тази сметка.
\98зÑ\87акайÑ\82е Ð¸Ð·Ð²ÐµÑ\81Ñ\82но Ð²Ñ\80еме преди да опитате отново.',
\9dеобÑ\85одимо Ðµ Ð´Ð° Ð¸Ð·Ñ\87акаÑ\82е $1 преди да опитате отново.',
 'login-abort-generic' => 'Влизането беше неуспешно - Прекратено',
 'loginlanguagelabel' => 'Език: $1',
 'suspicious-userlogout' => 'Заявката ви за излизане от системата беше отхвърлена, тъй като изглежда е била изпратена погрешка от браузъра или кеширащото прокси.',
@@ -3754,9 +3755,9 @@ MediaWiki се разпространява с надеждата, че ще б
 'logentry-newusers-create' => 'Потребителската сметка $1 беше {{GENDER:$2|създадена}}',
 'logentry-newusers-create2' => '$1 {{GENDER:$2|създаде}} потребителска сметка $3',
 'logentry-newusers-byemail' => '$1 {{GENDER:$2|създаде}} потребителската сметка $3, като паролата за нея беше изпратена по е-поща',
-'logentry-newusers-autocreate' => 'Сметката $1 беше създадена автоматично',
-'logentry-rights-rights' => '$1 промени потребителската група на $3 от $4 на $5',
-'logentry-rights-rights-legacy' => '$1 промени потребителската група на $3',
+'logentry-newusers-autocreate' => 'Сметката $1 беше {{GENDER:$2|създадена}} автоматично',
+'logentry-rights-rights' => '$1 {{GENDER:$2|промени}} потребителската група на $3 от $4 на $5',
+'logentry-rights-rights-legacy' => '$1 {{GENDER:$2|промени}} потребителската група на $3',
 'logentry-rights-autopromote' => '
 $1 е автоматично повишен от $4 до $5',
 'rightsnone' => '(никакви)',
index f62a548..ba4745c 100644 (file)
@@ -526,7 +526,7 @@ $2',
 'gotaccount' => "আপনার কি ইতিমধ্যে একটি অ্যাকাউন্ট তৈরি করা আছে? '''$1''' করুন।",
 'gotaccountlink' => 'প্রবেশ',
 'userlogin-resetlink' => 'আপনার লগইনের বিস্তারিত তথ্যাদি ভুলে গেছেন?',
-'userlogin-resetpassword-link' => 'শবà§\8dদà¦\9aাবি à¦ªà§\81নরায় à¦§à¦¾à¦°à§\8dয à¦\95রà§\81ন',
+'userlogin-resetpassword-link' => 'শবà§\8dদà¦\9aাবি à¦­à§\81লà§\87 à¦\97à§\87à¦\9bà§\87ন?',
 'helplogin-url' => 'Help:প্রবেশ',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|লগইন সংক্রান্ত সাহায্য]]',
 'userlogin-loggedin' => 'আপনি বর্তমানে {{GENDER:$1|$1}} হিসাবে লগইন আছেন।
@@ -587,13 +587,14 @@ $2',
 'passwordsent' => 'একটি নতুন শব্দচাবি "$1" ব্যবহারকারীর ই-মেইল ঠিকানায় পাঠানো হয়েছে। দয়াকরে তা পাওয়ার পর আবার লগ-ইন করুন।',
 'blocked-mailpassword' => 'আপনার আইপি ঠিকানাটি থেকে সম্পাদনা করতে বাধা আছে, তাই এই ঠিকানার অপব্যবহার করে শব্দচাবি ফেরত আনতে দেয়া যাবে না।',
 'eauthentsent' => 'মনোনীত ই-মেইল ঠিকানায় একটি নিশ্চিতকরণ ই-মেইল পাঠানো হয়েছে।
-à¦\85à§\8dযাà¦\95াà¦\89নà§\8dà¦\9fà¦\9fিতà§\87 à¦\85নà§\8dয à¦¯à§\87 à¦\95à§\8bন à¦\87-মà§\87à¦\87ল à¦ªà¦¾à¦ à¦¾à¦¨à§\8bর à¦\86à¦\97à§\87 à¦\86পনাà¦\95à§\87 à¦\8fà¦\87 à¦\87-মà§\87à¦\87লà§\87র à¦¨à¦¿à¦°à§\8dদà§\87শà¦\97à§\81লি à¦\85নà§\81সরণ à¦\95রতà§\87 à¦¹à¦¬à§\87, à¦¯à¦¾à¦¤à§\87 à¦\85à§\8dযাà¦\95াà¦\89নà§\8dà¦\9fà¦\9fি à¦¯à§\87 à¦\86সলà§\87à¦\87 à¦\86পনার, à¦¤à¦¾ à¦¨à¦¿à¦¶à§\8dà¦\9aিত à¦¹à¦¯à¦¼à¥¤',
+à¦\90 à¦\85à§\8dযাà¦\95াà¦\89নà§\8dà¦\9fà¦\9fà§\87 à¦\85নà§\8dয à¦\95à§\8bন à¦\87-মà§\87à¦\87ল à¦ªà¦¾à¦ à¦¾à¦¨à§\8bর à¦\86à¦\97à§\87 à¦\86পনাà¦\95à§\87 à¦\87-মà§\87à¦\87লà§\87র à¦¨à¦¿à¦°à§\8dদà§\87শà¦\97à§\81লি à¦\85নà§\81সরণ à¦\95রতà§\87 à¦¹à¦¬à§\87, à¦¯à¦¾à¦¤à§\87 à¦\85à§\8dযাà¦\95াà¦\89নà§\8dà¦\9fà¦\9fি à¦¯à§\87 à¦\86সলà§\87à¦\87 à¦\86পনার, à¦¤à¦¾ à¦¨à¦¿à¦¶à§\8dà¦\9aিত à¦¹à¦¯à¦¼à¥¤',
 'throttled-mailpassword' => 'বিগত {{PLURAL:$1|ঘন্টার|$1 ঘন্টার}} মধ্যে ইতিমধ্যেই একবার শব্দচাবি বদলের তথ্য পাঠানো হয়েছে। অপব্যবহার রোধে প্রতি {{PLURAL:$1|ঘন্টায়|$1 ঘন্টায়}} কেবল একবার শব্দচাবি বদলের তথ্য পাঠানো যাবে।',
 'mailerror' => 'ইমেইল পাঠাতে সমস্যা: $1',
 'acct_creation_throttle_hit' => 'এই উইকির দর্শক আপনার IP থেকে বিগত সময়ে {{PLURAL:$1|1 টি অ্যাকাউন্ট|$1 গুলো অ্যাকাউন্ট}} তৈরি করেছেন, যা এই সময়ের জন্য সর্বোচ্চ অনুমোদনকৃত।
 ফলে, এই IP থেকে দর্শক এই সময়ে নতুন অ্যাকাউন্ট তৈরি করতে পারবেন না।',
-'emailauthenticated' => 'আপনার ই-মেইল ঠিকানাটি $2 তারিখের $3 এ নিশ্চিত করা হয়েছে।',
-'emailnotauthenticated' => 'আপনার ই-মেইলের ঠিকানা <strong>এখনও যাচাই করা হয়নি</strong>। নিচের বৈশিষ্ট্যগুলোর (features) জন্য কোনো ই-মেইল পাঠানো হবে না।',
+'emailauthenticated' => 'আপনার ইমেইল ঠিকানাটি $2 তারিখের $3 এ নিশ্চিত করা হয়েছে।',
+'emailnotauthenticated' => 'আপনার ই-মেইলের ঠিকানা এখনও যাচাই করা হয়নি।
+নিচের বৈশিষ্ট্যগুলোর (features) জন্য কোনো ই-মেইল পাঠানো হবে না।',
 'noemailprefs' => 'এই বৈশিষ্টটি কাজ করাতে হলে একটি ই-মেইল ঠিকানা নির্ধারণ করতে হবে।',
 'emailconfirmlink' => 'আপনার ই-মেইলের ঠিকানা নিশ্চিত করুন',
 'invalidemailaddress' => 'এই ই-মেইল ঠিকানাটি গ্রহণযোগ্য নয়, কারণ সম্ভবত এটি সঠিক ফরম্যাটে লেখা হয়নি। অনুগ্রহ করে সঠিক ফরম্যাটে লেখা ই-মেইল ঠিকানা দিন, অথবা ক্ষেত্রটি খালি রাখুন।',
@@ -1025,15 +1026,15 @@ $3-এর দেয়া কারণ হল ''$2''",
 * ভুল ব্যক্তিগত তথ্য
 *:  ''বাসার ঠিকানা এবং ফোন নম্বর, সোসাল সিকিউরিটি নম্বর, ইত্যাদি।''",
 'revdelete-legend' => 'দৃষ্টিপাত সীমাবদ্ধ করো',
-'revdelete-hide-text' => 'সà¦\82শà§\8bধিত à¦²à§\87à¦\96া à¦\86ড়াল à¦\95রà§\8b',
+'revdelete-hide-text' => 'সà¦\82সà§\8dà¦\95রণà§\87র à¦²à§\87à¦\96া',
 'revdelete-hide-image' => 'ফাইলের বিষয়বস্তু আড়াল করো',
 'revdelete-hide-name' => 'কাজ এবং লক্ষ্য আড়াল করো',
-'revdelete-hide-comment' => 'সম্পাদনা সারাংশ আড়াল করো',
-'revdelete-hide-user' => 'সম্পাদকে ব্যবহারকারীর নাম/আইপি আড়াল করো',
+'revdelete-hide-comment' => 'সম্পাদনা সারাংশ',
+'revdelete-hide-user' => 'সম্পাদকে ব্যবহারকারীর নাম/আইপি',
 'revdelete-hide-restricted' => 'প্রশাসকবৃন্দ এবং অন্যদের ক্ষেত্রে এই ডাটা রোধ করো',
 'revdelete-radio-same' => 'পরিবর্তন নয়',
-'revdelete-radio-set' => 'হà§\8dযাà¦\81',
-'revdelete-radio-unset' => 'না',
+'revdelete-radio-set' => 'দà§\83শà§\8dযমান',
+'revdelete-radio-unset' => 'লà§\81à¦\95ানà§\8b',
 'revdelete-suppress' => 'সব প্রশাসক ও অন্যান্যদের কাছ থেকে উপাত্ত লুকিয়ে রাখা হোক।',
 'revdelete-unsuppress' => 'সংশোধন পুনঃস্থাপনের উপর সীমাবদ্ধতা দূর করো',
 'revdelete-log' => 'কারণ:',
@@ -2901,7 +2902,7 @@ $2',
 'spam_reverting' => '$1-এর প্রতি কোন সংযোগ নেই, এমন সর্বশেষ সংস্করণে ফেরত নেওয়া হচ্ছে।',
 'spam_blanking' => '$1-এর প্রতি সংযোগ অন্তর্ভুক্ত আছে এমন সমস্ত সংশোধন খালি করা হচ্ছে',
 'spam_deleting' => '$1-এর প্রতি সংযোগ অন্তর্ভুক্ত আছে এমন সমস্ত সংশোধন অপসারণ করা হচ্ছে',
-'simpleantispam-label' => "এন্টি-স্প্যাম যাচাই।
+'simpleantispam-label' => "এন্টি স্প্যাম যাচাই।
 এটা পূরণ করবেন '''না'''!",
 
 # Info page
@@ -3710,7 +3711,7 @@ $4-এ নিশ্চিতকরণ কোডটি মেয়াদোত
 # Special:Redirect
 'redirect' => 'ফাইল, ব্যবহারকরী, অথবা রিভিশন আইডি দ্বারা পুনঃনির্দেশ করা হয়েছে',
 'redirect-legend' => 'একটি ফাইল অথবা পাতায় পুনঃনির্দেশ করা হয়েছে',
-'redirect-summary' => 'এই বিশেষ পাতাটি পুনঃনির্দেশিত হয়েছে একটি ফাইলে (ফাইলের নাম), একটি পাতা (রিভিশন আইডি), অথবা একটি ব্যবহারকরী পাতায় (সংখ্যায় লেখা ব্যবহারকারী আইডি)।',
+'redirect-summary' => 'এই বিশেষ পাতাটি পুনঃনির্দেশিত হয়েছে একটি ফাইলে (ফাইলের নাম), একটি পাতা (রিভিশন আইডি), অথবা একটি ব্যবহারকরী পাতায় (সংখ্যায় লেখা ব্যবহারকারী আইডি)। ব্যবহার: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]]।',
 'redirect-submit' => 'যাও',
 'redirect-lookup' => 'দেখুন:',
 'redirect-value' => 'মান:',
index 2c35622..dc57112 100644 (file)
@@ -528,7 +528,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'O projektu {{SITENAME}}',
 'aboutpage' => 'Project:O_projektu_{{SITENAME}}',
-'copyright' => 'Svi sadržaji podliježu "$1" licenci.',
+'copyright' => 'Sadržaj je dostupan pod licencom $1 osim ako je drugačije navedeno.',
 'copyrightpage' => '{{ns:project}}:Autorska_prava',
 'currentevents' => 'Trenutni događaji',
 'currentevents-url' => 'Project:Novosti',
@@ -1233,15 +1233,15 @@ Drugi administratori projekta {{SITENAME}} će i dalje moći pristupiti sakriven
 * Osjetljive korisničke informacije
 *: ''kućne adrese, brojevi telefona, brojevi bankovnih kartica itd.''",
 'revdelete-legend' => 'Postavi ograničenja vidljivosti',
-'revdelete-hide-text' => 'Sakrij tekst revizije',
+'revdelete-hide-text' => 'Tekst revizije',
 'revdelete-hide-image' => 'Sakrij sadržaj datoteke',
 'revdelete-hide-name' => 'Sakrij akciju i cilj',
 'revdelete-hide-comment' => 'Sakrij izmjene komentara',
-'revdelete-hide-user' => 'Sakrij korisničko ime urednika/IP',
+'revdelete-hide-user' => 'Korisničko ime urednika/IP',
 'revdelete-hide-restricted' => 'Ograniči podatke za administratore kao i za druge korisnike',
 'revdelete-radio-same' => '(ne mijenjaj)',
-'revdelete-radio-set' => 'Da',
-'revdelete-radio-unset' => 'Ne',
+'revdelete-radio-set' => 'Vidljivo',
+'revdelete-radio-unset' => 'Sakriveno',
 'revdelete-suppress' => 'Sakrij podatke od administratora kao i od drugih',
 'revdelete-unsuppress' => 'Ukloni ograničenja na vraćenim revizijama',
 'revdelete-log' => 'Razlog:',
@@ -1679,6 +1679,7 @@ Ako izaberete da date ime, biće korišteno za pripisivanje za vaš rad.',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|promjena|promjene|promjena}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|izmjena od vaše posljedne posjete}}',
 'enhancedrc-history' => 'historija',
 'recentchanges' => 'Nedavne izmjene',
 'recentchanges-legend' => 'Postavke nedavnih izmjena',
index 8046029..6e58102 100644 (file)
@@ -373,11 +373,11 @@ $messages = array(
 'vector-action-undelete' => 'Restaura',
 'vector-action-unprotect' => 'Desprotegeix',
 'vector-simplesearch-preference' => 'Activar la barra de cerca simplificada (només aparença Vector)',
-'vector-view-create' => 'Inicia',
+'vector-view-create' => 'Crea',
 'vector-view-edit' => 'Modifica',
 'vector-view-history' => "Mostra l'historial",
 'vector-view-view' => 'Mostra',
-'vector-view-viewsource' => 'Mostra la font',
+'vector-view-viewsource' => 'Mostra el codi',
 'actions' => 'Accions',
 'namespaces' => 'Espais de noms',
 'variants' => 'Variants',
@@ -581,7 +581,7 @@ No ha donat cap explicació.',
 'wrong_wfQuery_params' => 'Paràmetres incorrectes per a wfQuery()<br />
 Funció: $1<br />
 Consulta: $2',
-'viewsource' => 'Mostra la font',
+'viewsource' => 'Mostra el codi',
 'viewsource-title' => 'Mostra la font per a $1',
 'actionthrottled' => 'Acció limitada',
 'actionthrottledtext' => "Com a mesura per a prevenir la propaganda indiscriminada (spam), no podeu fer aquesta acció tantes vegades en un període de temps tan curt. Torneu-ho a intentar d'ací uns minuts.",
index 7c96e25..94734b1 100644 (file)
@@ -437,7 +437,7 @@ Sō̤ kī só-gé̤ṳ-kó gì guāng-lī-uòng cūng-kuāng gāi-sék: $1',
 'gotaccount' => "已經有賬戶了?'''$1'''。",
 'gotaccountlink' => '躒底',
 'userlogin-resetlink' => '躒底其資料𣍐記咯?',
-'userlogin-resetpassword-link' => '重置汝其密碼',
+'userlogin-resetpassword-link' => '密码𣍐記?',
 'helplogin-url' => 'Help: 躒底',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|幫助躒底]]',
 'createacct-join' => '敆下底輸底汝其信息。',
index 7850b31..2639d51 100644 (file)
@@ -2216,9 +2216,12 @@ PICT # тайп тайпан
 
 # Video information, used by Language::formatTimePeriod() to format lengths in the above messages
 'seconds-abbrev' => '$1оцу',
+'days' => '{{PLURAL:$1|$1 де}}',
+'ago' => '$1 хьалха',
 
 # Human-readable timestamps
 'hours-ago' => '$1 {{PLURAL:$1|сахьат}} хьалха',
+'minutes-ago' => '$1 {{PLURAL:$1|минут}} хьалха',
 'yesterday-at' => 'селхана $1 даьлча',
 
 # Bad image list
@@ -2486,6 +2489,9 @@ PICT # тайп тайпан
 # Search suggestions
 'searchsuggest-search' => 'Лаха',
 
+# Durations
+'duration-days' => '$1 {{PLURAL:$1|де}}',
+
 # Limit report
 'limitreport-title' => 'АгӀона хӀоттам къасторан хаамаш:',
 'limitreport-cputime' => 'Процессоран хан лелор',
index 55ad052..a5cedec 100644 (file)
@@ -816,7 +816,7 @@ Nezapomeňte si upravit své [[Special:Preferences|nastavení {{grammar:2sg|{{SI
 'gotaccount' => "Už jste registrováni? '''$1'''.",
 'gotaccountlink' => 'Přihlaste se',
 'userlogin-resetlink' => 'Zapomněli jste přihlašovací údaje?',
-'userlogin-resetpassword-link' => 'Obnovit heslo',
+'userlogin-resetpassword-link' => 'Zapomněli jste heslo?',
 'helplogin-url' => 'Help:Přihlášení',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Nápověda k přihlašování]]',
 'userlogin-loggedin' => 'Již jste {{GENDER:$1|přihlášen|přihlášena}} jako $1.
@@ -885,8 +885,9 @@ a používat staré heslo.',
 Kvůli prevenci zneužívání lze heslo zaslat jen jednou za $1 {{PLURAL:$1|hodinu|hodiny|hodin}}.',
 'mailerror' => 'Chyba při zasílání e-mailu: $1',
 'acct_creation_throttle_hit' => 'Uživatelé přicházející z vaší IP adresy už dnes vytvořili $1 {{PLURAL:$1|účet|účty|účtů}}, což je dovolené maximum. Proto v tuto chvíli není dovoleno z této IP adresy další účty zakládat.',
-'emailauthenticated' => 'Vaše e-mailová adresa byla ověřena dne $2 v $3.',
-'emailnotauthenticated' => 'Vaše e-mailová adresa dosud nebyla ověřena a e-mailové funkce do té doby nejsou dostupné.',
+'emailauthenticated' => 'Vaše e-mailová adresa byla ověřena $2 v $3.',
+'emailnotauthenticated' => 'Vaše e-mailová adresa dosud nebyla ověřena.
+U následujících funkcí nebudou zasílány žádné e-maily.',
 'noemailprefs' => 'Pro zprovoznění následujících možností musíte zadat svou e-mailovou adresu.',
 'emailconfirmlink' => 'Podvrďte svou e-mailovou adresu',
 'invalidemailaddress' => 'Zadaná e-mailová adresa nemůže být přijata, neboť nemá správný formát. Zadejte platnou e-mailovou adresu nebo obsah tohoto pole vymažte.',
index 5d564fb..23948f3 100644 (file)
@@ -569,7 +569,7 @@ Sylwer y bydd rhai tudalennau yn parhau i ymddangos fel ag yr oeddent pan oeddec
 'gotaccount' => 'Oes cyfrif gennych eisoes? $1.',
 'gotaccountlink' => 'Mewngofnodi',
 'userlogin-resetlink' => 'Ydych chi wedi anghofio eich manylion mewngofnodi?',
-'userlogin-resetpassword-link' => 'Ailosod eich cyfrinair',
+'userlogin-resetpassword-link' => 'Wedi anghofio eich cyfrinair?',
 'helplogin-url' => 'Help:Mewngofnodi',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Cymorth i fewngofnodi]]',
 'userlogin-loggedin' => 'Rydych eisoes wedi mewngofnodi wrth yr enw {{GENDER:$1|$1}}.
@@ -636,7 +636,7 @@ Er mwyn rhwystro camddefnydd, dim ond un e-bost i'ch atgoffa o'ch cyfrinair gaif
 'acct_creation_throttle_hit' => "Mae ymwelwyr sy'n defnyddio'ch cyfeiriad IP wedi creu $1 {{PLURAL:$1|cyfrif|cyfrif|gyfrif|chyfrif|chyfrif|cyfrif}} yn ystod y diwrnod diwethaf, sef y mwyafswm a ganiateir mewn diwrnod.
 Felly ni chaiff defnyddwyr sy'n defnyddio'r cyfeiriad IP hwn greu rhagor o gyfrifon ar hyn o bryd.",
 'emailauthenticated' => 'Cadarnhawyd eich cyfeiriad e-bost am $3 ar $2.',
-'emailnotauthenticated' => "Nid yw eich cyfeiriad e-bost wedi'i ddilysu eto. Ni fydd unrhyw negeseuon e-bost yn cael eu hanfon atoch ar gyfer y nodweddion canlynol.",
+'emailnotauthenticated' => "Nid yw eich cyfeiriad e-bost wedi'i gadarnhau eto. Ni fydd unrhyw negeseuon e-bost yn cael eu hanfon atoch ar gyfer y nodweddion canlynol.",
 'noemailprefs' => "Mae'n rhaid i chi gynnig cyfeiriad e-bost er mwyn i'r nodweddion hyn weithio.",
 'emailconfirmlink' => 'Cadarnhewch eich cyfeiriad e-bost',
 'invalidemailaddress' => 'Ni allwn dderbyn y cyfeiriad e-bost gan fod ganddo fformat annilys. Mewnbynnwch cyfeiriad dilys neu gwagiwch y maes hwnnw, os gwelwch yn dda.',
index e373c86..3392d38 100644 (file)
@@ -877,7 +877,7 @@ Vergiss nicht, deine [[Special:Preferences|{{SITENAME}}-Einstellungen]] zu ände
 'gotaccount' => "Du hast bereits ein Benutzerkonto? '''$1'''.",
 'gotaccountlink' => 'Anmelden',
 'userlogin-resetlink' => 'Die Anmeldedaten vergessen?',
-'userlogin-resetpassword-link' => 'Passwort zurücksetzen',
+'userlogin-resetpassword-link' => 'Passwort vergessen?',
 'helplogin-url' => 'Help:Anmelden',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hilfe beim Anmelden]]',
 'userlogin-loggedin' => 'Du bist bereits als {{GENDER:$1|$1}} angemeldet.
@@ -949,7 +949,8 @@ Bevor eine E-Mail von anderen Benutzern über die E-Mail-Funktion empfangen werd
 
 Besucher, die diese IP-Adresse verwenden, können momentan keine Benutzerkonten mehr erstellen.',
 'emailauthenticated' => 'Deine E-Mail-Adresse wurde am $2 um $3 Uhr bestätigt.',
-'emailnotauthenticated' => 'Deine E-Mail-Adresse ist noch nicht bestätigt. Die folgenden E-Mail-Funktionen stehen erst nach erfolgreicher Bestätigung zur Verfügung.',
+'emailnotauthenticated' => 'Deine E-Mail-Adresse ist noch nicht bestätigt.
+Die folgenden E-Mail-Funktionen stehen erst nach erfolgreicher Bestätigung zur Verfügung.',
 'noemailprefs' => 'Gib eine E-Mail-Adresse in den Einstellungen an, damit die nachfolgenden Funktionen zur Verfügung stehen.',
 'emailconfirmlink' => 'E-Mail-Adresse bestätigen (authentifizieren).',
 'invalidemailaddress' => 'Die E-Mail-Adresse wird nicht akzeptiert, weil sie ein ungültiges Format (eventuell ungültige Zeichen) zu haben scheint. Bitte gib eine korrekte Adresse ein oder leere das Feld.',
index 410d7d9..d17d81a 100644 (file)
@@ -10,6 +10,7 @@
  * @author AK
  * @author Aitolos
  * @author Assassingr
+ * @author Astralnet
  * @author Azimout
  * @author Badseed
  * @author Chomwitt
@@ -3934,6 +3935,7 @@ $5
 'version-license' => 'Άδεια χρήσης',
 'version-poweredby-credits' => "Αυτό το wiki λειτουργεί με το λογισμικό '''[//www.mediawiki.org/ MediaWiki]''', πνευματική ιδιοκτησία © 2001-$1 $2.",
 'version-poweredby-others' => 'άλλοι',
+'version-poweredby-translators' => 'translatewiki.net μεταφραστές',
 'version-credits-summary' => 'Θα θέλαμε να αναγνωρίσουμε τη συμβολή των παρακάτω προσώπων στο [[Special:Version|MediaWiki]].',
 'version-license-info' => "Το MediaWiki είναι ελεύθερο λογισμικό. Μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό τους όρους της άδειας GNU General Public License όπως αυτή εκδόθηκε από το Free Software Foundation· είτε της δεύτερης έκδοσης της άδειας, είτε (κατ' επιλογή σας) οποιασδήποτε επόμενης έκδοσης.
 
@@ -4010,6 +4012,8 @@ $5
 'tags-display-header' => 'Εμφάνιση στις λίστες αλλαγής',
 'tags-description-header' => 'Πλήρης περιγραφή του νοήματος',
 'tags-hitcount-header' => 'Αλλαγές με ετικέτα',
+'tags-active-yes' => 'Ναι',
+'tags-active-no' => 'Όχι',
 'tags-edit' => 'επεξεργασία',
 'tags-hitcount' => '$1 {{PLURAL:$1|αλλαγή|αλλαγές}}',
 
index 7b62d29..39f0b8a 100644 (file)
@@ -1134,7 +1134,7 @@ Do not forget to change your [[Special:Preferences|{{SITENAME}} preferences]].',
 'gotaccount'                      => 'Already have an account? $1.',
 'gotaccountlink'                  => 'Log in',
 'userlogin-resetlink'             => 'Forgotten your login details?',
-'userlogin-resetpassword-link'    => 'Reset your password',
+'userlogin-resetpassword-link'    => 'Forgot your password?',
 'helplogin-url'                   => 'Help:Logging in',
 'userlogin-helplink'              => '[[{{MediaWiki:helplogin-url}}|Help with logging in]]',
 'userlogin-loggedin'              => 'You are already logged in as {{GENDER:$1|$1}}.
@@ -1228,8 +1228,8 @@ To prevent abuse, only one password reset email will be sent per {{PLURAL:$1|hou
 'mailerror'                       => 'Error sending mail: $1',
 'acct_creation_throttle_hit'      => 'Visitors to this wiki using your IP address have created {{PLURAL:$1|1 account|$1 accounts}} in the last day, which is the maximum allowed in this time period.
 As a result, visitors using this IP address cannot create any more accounts at the moment.',
-'emailauthenticated'              => 'Your email address was authenticated on $2 at $3.',
-'emailnotauthenticated'           => 'Your email address is not yet authenticated.
+'emailauthenticated'              => 'Your email address was confirmed on $2 at $3.',
+'emailnotauthenticated'           => 'Your email address is not yet confirmed.
 No email will be sent for any of the following features.',
 'noemailprefs'                    => 'Specify an email address in your preferences for these features to work.',
 'emailconfirmlink'                => 'Confirm your email address',
index ecdae41..84d149c 100644 (file)
@@ -7,6 +7,7 @@
  * @ingroup Language
  * @file
  *
+ * @author AivoK
  * @author Avjoska
  * @author Cylly1512
  * @author Geitost
@@ -746,7 +747,7 @@ Pane tähele, et seni kuni sa pole oma võrgulehitseja puhvrit tühjendanud, võ
 'gotaccount' => "Kui sul on juba konto, '''$1'''.",
 'gotaccountlink' => 'logi sisse',
 'userlogin-resetlink' => 'Kas oled unustanud oma sisselogimisandmed?',
-'userlogin-resetpassword-link' => 'Lähtesta oma parool',
+'userlogin-resetpassword-link' => 'Unustasid parooli?',
 'helplogin-url' => 'Help:Sisselogimine',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Sisselogimisabi]]',
 'userlogin-loggedin' => 'Oled juba sisse logitud nimega {{GENDER:$1|$1}}.
index 1848261..9d5c753 100644 (file)
@@ -741,7 +741,7 @@ Behin-behineko pasahitza: $2',
 'subject' => 'Izenburua:',
 'minoredit' => 'Hau aldaketa txikia da',
 'watchthis' => 'Orrialde hau jarraitu',
-'savearticle' => 'Gorde orrialdea',
+'savearticle' => 'Gorde orria',
 'preview' => 'Aurrebista erakutsi',
 'showpreview' => 'Aurrebista erakutsi',
 'showlivepreview' => 'Zuzeneko aurrebista',
@@ -1069,6 +1069,7 @@ Ezin duzu atzitu.',
 'revdelete-no-change' => "'''Abisua:''' $1 $2 data duen elementuak jadanik bazituen eskatutako ikusgaitasun ezarpenak.",
 'revdelete-concurrent-change' => 'Errorea, $1 $2 data duen elementua aldatzean: badirudi haren egoera aldatu duela nor edo nork, zu aldatzen saiatzen ari zinela.
 Begira itzazu erregistroak.',
+'revdelete-only-restricted' => '$2 data duen $1 elementua ezkutatzen arazoa: ezin dira kendu adminstratzaileen ikuskaritzatik elementuak ez badago beste ikusgarritasun aukerarik hautatua.',
 'revdelete-reason-dropdown' => '*Ezabatzeko ohiko arrazoiak
 ** Egile eskubideak urratzea
 ** Informazio pertsonal edo iruzkin desegokia
@@ -1271,11 +1272,13 @@ Saia zaitez zure eskeraren aurretik ''all:'' jartzen eduki guztien artean bilatz
 'badsig' => 'Baliogabeko sinadura; egiaztatu HTML etiketak.',
 'badsiglength' => 'Zure sinadura luzeegia da.
 $1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.',
-'yourgender' => 'Generoa:',
-'gender-unknown' => 'Zehaztugabea',
-'gender-male' => 'Gizona',
-'gender-female' => 'Emakumea',
-'prefs-help-gender' => 'Hautazkoa: softwareak generoa zehazteko erabilia. Informazio hau publikoa da.',
+'yourgender' => 'Nola nahiagu duzu deskribatua izatea?',
+'gender-unknown' => 'Nahiago dut ez esatea',
+'gender-male' => 'Wiki orrialdeak editatzen dituen gizona',
+'gender-female' => 'Wiki orrialdeak editatzen dituen emakumea',
+'prefs-help-gender' => 'Hobespen hau jartzea aukerazkoa da.
+Softwareak bere balioak erabiltzen ditu zu aipatzeko eta beste batzuek genero gramatikala erabiltzeko aukera izan dezaten.
+Informazio hau publikoa da.',
 'email' => 'E-posta',
 'prefs-help-realname' => '* Benetako izena (aukerakoa): zehaztea erabakiz gero, zure lanarentzako atribuzio bezala balioko du.',
 'prefs-help-email' => 'E-posta helbidea aukerakoa da, baina zure pasahitza ahaztekotan berriro zure e-postara bidaltzeko aukera ematen dizu.',
@@ -1286,7 +1289,7 @@ $1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.',
 'prefs-signature' => 'Sinadura',
 'prefs-dateformat' => 'Data-formatua',
 'prefs-timeoffset' => 'Denbora ezberdintasuna',
-'prefs-advancedediting' => 'Aukera aurreratuak',
+'prefs-advancedediting' => 'Genero aukerak',
 'prefs-advancedrc' => 'Aukera aurreratuak',
 'prefs-advancedrendering' => 'Aukera aurreratuak',
 'prefs-advancedsearchoptions' => 'Aukera aurreratuak',
@@ -1486,7 +1489,7 @@ $1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.',
 'rc_categories_any' => 'Edozein',
 'rc-change-size-new' => '{{PLURAL:$1|Byte 1|$1 byte}} aldaketaren ostean',
 'newsectionsummary' => '/* $1 */ atal berria',
-'rc-enhanced-expand' => 'Erakutsi xehetasunak (JavaScript beharrezkoa da)',
+'rc-enhanced-expand' => 'Erakutsi xehetasunak',
 'rc-enhanced-hide' => 'Xehetasunak ezkutatu',
 'rc-old-title' => 'hasiera batean "$1" gisa sortua',
 
@@ -1616,6 +1619,7 @@ $1',
 'upload-too-many-redirects' => 'URLak birzuzenketa gehiegi zituen',
 'upload-unknown-size' => 'Tamaina ezezaguna',
 'upload-http-error' => 'HTTP errorea gertatu da: $1',
+'upload-copy-upload-invalid-domain' => 'Domeinu honetan ezin dira igoerak kopiatu.',
 
 # File backend
 'backend-fail-stream' => 'Ezin izan da "$1" fitxategiaren stream egin.',
@@ -1625,6 +1629,7 @@ $1',
 'backend-fail-notsame' => 'Berdina ez den beste fitxategi bat dago "$1"n',
 'backend-fail-invalidpath' => '"$1" ez da gordetzeko helbide baliagarria.',
 'backend-fail-delete' => 'Ezin izan da ezabatu "$1" fitxategia.',
+'backend-fail-describe' => 'Ezin dira "$1" fitxategiaren metadatuak aldatu.',
 'backend-fail-alreadyexists' => '"$1" fitxategia jadanik badago.',
 'backend-fail-store' => 'Ezin izan da gorde "$1" fitxategia "$2" helbidean.',
 'backend-fail-copy' => 'Ezin izan da kopiatu "$1" fitxategia "$2" helbidean.',
@@ -1639,11 +1644,15 @@ $1',
 # Lock manager
 'lockmanager-notlocked' => 'Ezin izan da "$1" askatu; ez dago itxita.',
 'lockmanager-fail-closelock' => 'Ezin izan da "$1" fitxategiaren giltza itxi.',
+'lockmanager-fail-deletelock' => 'Ezin izan da "$1" fitxategia desblokeatu.',
+'lockmanager-fail-acquirelock' => 'Ezin izan da "$1" blokeoa eskuratu.',
+'lockmanager-fail-openlock' => 'Ezin izan da "$1" blokeo fitxategia ireki.',
 
 # ZipDirectoryReader
 'zip-wrong-format' => 'Zehaztutako fitxategia ez zen ZIP motakoa.',
 
 # Special:UploadStash
+'uploadstash' => 'Gordailu bat igo',
 'uploadstash-refresh' => 'Fitxategien zerrenda eguneratu',
 
 # img_auth script messages
@@ -2288,7 +2297,7 @@ $1',
 'contributions' => '{{GENDER:$1|Lankidearen}} ekarpenak',
 'contributions-title' => '$1(r)entzat lankidearen ekarpenak',
 'mycontris' => 'Ekarpenak',
-'contribsub2' => '$1 ($2)',
+'contribsub2' => '{{GENDER:$3|$1(r)entzat}} ($2)',
 'nocontribs' => 'Ez da ezaugarri horiekin bat datorren aldaketarik aurkitu.',
 'uctop' => '(azken aldaketa)',
 'month' => 'Hilabetea (eta lehenagokoak):',
@@ -2551,6 +2560,7 @@ Horrez gain, lotura zuzena ere erabil dezakezu; adibidez, [[{{#Special:Export}}/
 'exportcuronly' => 'Oraingo berrikuspena bakarrik hartu, ez historia guztia',
 'exportnohistory' => "----
 '''Oharra:''' Formulario honen bitartez orrialdeen historia osoak esportatzeko aukera ezgaitu egin da, errendimendua dela-eta.",
+'exportlistauthors' => 'Orrialde bakoitzaren lankideen zerrenda osoa sartu',
 'export-submit' => 'Esportatu',
 'export-addcattext' => 'Orrialdeak gehitu kategoria honetatik:',
 'export-addcat' => 'Gehitu',
@@ -2583,6 +2593,7 @@ Mesedez bisitatu [//www.mediawiki.org/wiki/Localisation MediaWiki] eta [//transl
 'thumbnail_error' => 'Errorea irudi txikia sortzerakoan: $1',
 'djvu_page_error' => 'DjVu orrialdea eremuz kanpo',
 'djvu_no_xml' => 'Ezinezkoa izan da DjVu fitxategiaren XML lortzea',
+'thumbnail-temp-create' => 'Ezin izan da behin-behineko iruditxoa sortu',
 'thumbnail-dest-create' => 'Ezin izan da iruditxoa gorde helburuan',
 'thumbnail_invalid_params' => 'Irudi txikiaren ezarpenak ez dira baliagarriak',
 'thumbnail_dest_directory' => 'Ezinezkoa izan da helburu direktorioa sortu',
@@ -2741,6 +2752,7 @@ Baliteke zerrenda beltzean dagoen kanpo lotura batek sortzea arazo hori.',
 'spambot_username' => 'MediaWikiren spam garbiketa',
 'spam_reverting' => '$1(e)rako loturarik ez daukan azken bertsiora itzultzen',
 'spam_blanking' => 'Berrikuspen guztiek $1(e)rako lotura zeukaten, husten',
+'spam_deleting' => '$1(e)ra loturak dituzten errebisio guztiak ezabatzen',
 'simpleantispam-label' => "Anti-spam egiaztapena.
 Atal hau '''EZ''' bete!",
 
@@ -3047,6 +3059,7 @@ Zerrenda elementuak (hasieran * duten lerroak) baino ez dira kontuan hartzen. Le
 'exif-source' => 'Jatorria',
 'exif-editstatus' => 'Irudiaren egoera editoriala',
 'exif-urgency' => 'Larrialdia',
+'exif-fixtureidentifier' => 'Konpontzearen izena',
 'exif-locationdest' => 'Agertzen den lekua',
 'exif-locationdestcode' => 'Agertzen den lekuaren kodea',
 'exif-objectcycle' => 'Media hau baliagarria den egunaren ordua',
@@ -3058,11 +3071,14 @@ Zerrenda elementuak (hasieran * duten lerroak) baino ez dira kontuan hartzen. Le
 'exif-iimsupplementalcategory' => 'Kategoria gehigarriak',
 'exif-datetimeexpires' => 'Ez erabili data hau pasata:',
 'exif-datetimereleased' => 'Ekoizpen data:',
+'exif-originaltransmissionref' => 'Trasmisio originalaren kokapen kodea',
 'exif-identifier' => 'Identifikatzailea',
 'exif-lens' => 'Erabilitako lentea',
 'exif-serialnumber' => 'Kameraren serie-zenbakia',
 'exif-cameraownername' => 'Kameraren jabea',
 'exif-label' => 'Etiketa',
+'exif-datetimemetadata' => 'Datuaren metadata azken aldiz aldatu da',
+'exif-nickname' => 'Irudiaren izen ez-formala',
 'exif-rating' => 'Balorazioa (5 arte)',
 'exif-rightscertificate' => 'Eskubideen kudeaketa ziurtagiria',
 'exif-copyrighted' => 'Copyright egoera',
@@ -3089,6 +3105,7 @@ Zerrenda elementuak (hasieran * duten lerroak) baino ez dira kontuan hartzen. Le
 
 # Exif attributes
 'exif-compression-1' => 'Konprimatu gabe',
+'exif-compression-2' => 'CCITT Group 3 1-Dimensional Modified Huffman kodetzea abiatu da',
 'exif-compression-6' => 'JPEG',
 
 'exif-copyrighted-true' => 'Copyrightduna',
@@ -3598,7 +3615,9 @@ Halaber [[Special:EditWatchlist|aldatzaile estandarra]] erabil dezakezu.',
 'feedback-message' => 'Mezua:',
 'feedback-cancel' => 'Utzi',
 'feedback-submit' => 'Feedbacka bidali',
+'feedback-error1' => 'Akatsa: APIaren emaitza ez ezagunak',
 'feedback-error2' => 'Akatsa: Aldaketa ez da egin',
+'feedback-error3' => 'Akatsa: APIaren erantzunik gabe',
 'feedback-close' => 'Egina',
 'feedback-bugnew' => 'Txekeatu dut. Bug berria bidaliko',
 
@@ -3609,11 +3628,25 @@ Halaber [[Special:EditWatchlist|aldatzaile estandarra]] erabil dezakezu.',
 # API errors
 'api-error-badaccess-groups' => 'Ez duzu baimendik fitxategi hauek wiki honetara igotzeko.',
 'api-error-badtoken' => 'Barne akatsa: token okerra.',
+'api-error-empty-file' => 'Bidali duzun fitxategia hutsik dago.',
+'api-error-emptypage' => 'Berria sortzerako garaian orrialde hutsak ezin dira erabili.',
+'api-error-fetchfileerror' => 'Barne akatsa: zerbait gaizki joan da fitxategia eskuratzerakoan.',
+'api-error-file-too-large' => 'Bidali duzun fitxategia handiegia zen.',
 'api-error-filename-tooshort' => 'Fitxategiaren izena laburregia da.',
 'api-error-filetype-banned' => 'Mota horretako fitxategiak debekatuta daude.',
+'api-error-filetype-missing' => 'Fitxategiak ez zuen luzapenik.',
 'api-error-illegal-filename' => 'Fitxategiaren izena ez da onartzen.',
+'api-error-mustbeloggedin' => 'Fitxategiak igotzeko izena emanda eduki behar duzu.',
+'api-error-mustbeposted' => 'Barne arazoa: HTTP POST beharrezkoa da.',
+'api-error-noimageinfo' => 'Igoera ondo egin da, baina zerbitzariak ez digu informaziorik eman zerbitzariaren inguruan.',
+'api-error-nomodule' => 'Barne arazoa: igoera modulurik ez dago.',
+'api-error-ok-but-empty' => 'Barne arazoa: zerbitzariaren erantzunik ez.',
+'api-error-overwrite' => 'Existitzen den fitxategi bat gain-idaztea ez da posible.',
+'api-error-stashfailed' => 'Barne arazoa: Zerbitzariak ezin izan du behin-behineko fitxategia gorde',
+'api-error-timeout' => 'Zerbitzariak ez du erantzun espero zitekeen denboran.',
 'api-error-unclassified' => 'Ezezaguna den errorea gertatu da.',
 'api-error-unknown-code' => 'Akats ezezaguna: "$1".',
+'api-error-unknown-error' => 'Barne arazoa: fitxategia igotzen saiatzerakoan zerbait gaizki egon da.',
 'api-error-unknown-warning' => 'Ohartarazpen ezezaguna: "$1".',
 'api-error-unknownerror' => 'Akats ezezaguna: "$1".',
 'api-error-uploaddisabled' => 'Wiki honetan ezin dira igoerak egin.',
index 90a1ea9..2803611 100644 (file)
@@ -1053,7 +1053,7 @@ $2
 شما باید هم‌اکنون وارد شده و یک گذرواژهٔ جدید برگزینید. اگر شخص دیگری این درخواست را داده است، یا اگر گذرواژهٔ اصلی‌تان را به خاطر آوردید و دیگر نمی‌خواهید آن را تغییر دهید، می‌توانید این پیغام را نادیده بگیرید و به استفاده از گذرواژهٔ قبلی‌تان ادامه دهید.',
 'passwordreset-emailelement' => 'نام کاربری: $1
 گذرواژهٔ موقت: $2',
-'passwordreset-emailsent' => 'یک نامهٔ بازنشانی گذرواژه فرستاده شده است.',
+'passwordreset-emailsent' => 'یک نامهٔ بازنشانی گذرواژه فرستاده شدهاست.',
 'passwordreset-emailsent-capture' => 'یک رایانامهٔ بازنشانی که در پایین نمایش داده شده، فرستاده شده است.',
 'passwordreset-emailerror-capture' => 'رایانامهٔ بازنشانی، که در زیر نمایش داده شده، ایجاد شد، ولی ارسال آن به {{GENDER:$2|کاربر}} موفقیت‌آمیز نبود: $1',
 
@@ -1250,8 +1250,8 @@ $2
 اگر می‌خواهید متن را در یک پروندهٔ متنی کپی کنید و برای آینده ذخیره‌اش کنید.
 
 مدیری که آن را قفل کرده این توضیح را ارائه کرده‌است: $1",
-'protectedpagewarning' => "'''هشدار: این صفحه قفل شده است تا فقط کاربران با امتیاز مدیر بتوانند ویرایشش کنند.'''
-آخرین موارد سیاهه در زیر آمده است:",
+'protectedpagewarning' => "'''هشدار: این صفحه قفل شده‌است تا فقط کاربران با دسترسی مدیریت بتوانند ویرایشش کنند.'''
+آخرین موارد سیاهه در زیر آمدهاست:",
 'semiprotectedpagewarning' => "'''توجه:''' این صفحه قفل شده‌است تا تنها کاربران ثبت‌نام‌کرده قادر به ویرایش آن باشند.
 آخرین موارد سیاهه در زیر آمده‌است:",
 'cascadeprotectedwarning' => "'''هشدار:''' این صفحه به علت قرارگرفتن در {{PLURAL:$1|صفحهٔ|صفحه‌های}} آبشاری-محافظت‌شدهٔ زیر قفل شده‌است تا فقط مدیران بتوانند ویرایشش کنند.",
@@ -1794,7 +1794,7 @@ $1",
 'right-ipblock-exempt' => 'تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای',
 'right-proxyunbannable' => 'تاثیر نپذیرفتن از قطع دسترسی خودکار پروکسی‌ها',
 'right-unblockself' => 'بازکردن دسترسی خود',
-'right-protect' => 'تغییر میزان محافظت صفحه‌ها و ویرایش صفحه‌های محافظت شده آبشاری',
+'right-protect' => 'تغییر میزان محافظت صفحه‌ها و ویرایش صفحه‌های محافظتشده آبشاری',
 'right-editprotected' => 'ویرایش صفحه‌های محافظت شده به عنوان "{{int:protect-level-sysop}}"',
 'right-editsemiprotected' => 'ویرایش صفحه حفاظت‌شده به عنوان "{{int:protect-level-autoconfirmed}}"',
 'right-editinterface' => 'ویرایش واسط کاربری',
@@ -1852,7 +1852,7 @@ $1",
 'action-delete' => 'حذف این صفحه',
 'action-deleterevision' => 'حذف این نسخه',
 'action-deletedhistory' => 'مشاهدهٔ تاریخچهٔ حذف شدهٔ این صفحه',
-'action-browsearchive' => 'جستجوی صفحه‌های حذف شده',
+'action-browsearchive' => 'جستجوی صفحه‌های حذفشده',
 'action-undelete' => 'احیای این صفحه',
 'action-suppressrevision' => 'مشاهده و احیای ویرایش‌های حذف شده',
 'action-suppressionlog' => 'مشاهدهٔ این سیاههٔ خصوصی',
@@ -2121,10 +2121,10 @@ $1',
 # Special:UploadStash
 'uploadstash' => 'انبار بارگذاری',
 'uploadstash-summary' => 'این صفحه دسترسی به پرونده‌هایی که بارگذاری شده‌اند (یا در حال بارگذاری هستند) اما هنوز در ویکی منتشر نشده‌اند را فراهم می‌کند. این پرونده‌ها توسط هیچ کاربری به جز کسی که آن‌ها را بارگذاری کرده قابل دیدن نیستند.',
-'uploadstash-clear' => 'پاک کردن پرونده‌های انبارشده',
+'uploadstash-clear' => 'پاککردن پرونده‌های انبارشده',
 'uploadstash-nofiles' => 'شما هیچ پروندهٔ انبارشده‌ای ندارید.',
 'uploadstash-badtoken' => 'انجام این اقدام ناموفق بود، احتمالاً به این دلیل که اعتبار ویرایش شما به اتمام رسیده است. دوباره امتحان کنید.',
-'uploadstash-errclear' => 'پاک کردن پرونده‌ها ناموفق بود.',
+'uploadstash-errclear' => 'پاککردن پرونده‌ها ناموفق بود.',
 'uploadstash-refresh' => 'تازه‌کردن فهرست پرونده‌ها',
 'invalid-chunk-offset' => 'جابجایی نامعتبر قطعه',
 
@@ -2802,7 +2802,7 @@ $1',
 
 برای دیدن سیاههٔ حذف‌ها و احیاهای اخیر به  [[Special:Log/delete|سیاههٔ حذف]] رجوع کنید.",
 'undelete-header' => 'برای دیدن صفحه‌های حذف‌شدهٔ اخیر [[Special:Log/delete|سیاههٔ حذف]] را ببینید.',
-'undelete-search-title' => 'جستجوی صفحه‌های حذف شده',
+'undelete-search-title' => 'جستجوی صفحه‌های حذفشده',
 'undelete-search-box' => 'جستجوی صفحه‌های حذف‌شده.',
 'undelete-search-prefix' => 'نمایش صفحه‌ها با شروع از:',
 'undelete-search-submit' => 'برو',
@@ -2890,8 +2890,8 @@ $1',
 'ipbreason' => 'دلیل:',
 'ipbreasonotherlist' => 'دلیل دیگر',
 'ipbreason-dropdown' => '*دلایل متداول قطع دسترسی
-**وارد کردن اطلاعات نادرست
-**پاک کردن اطلاعات مفید از صفحه‌ها
+**واردکردن اطلاعات نادرست
+**پاککردن اطلاعات مفید از صفحه‌ها
 **هرزنگاری از طریق درج مکرر پیوند به وب‌گاه‌ها
 **درج چرندیات یا نوشته‌های بی‌معنا در صفحه‌ها
 **تهدید یا ارعاب دیگر کاربران
@@ -3240,7 +3240,7 @@ $2',
 'javascripttest-title' => 'در حال اجرای آزمایش‌های $1',
 'javascripttest-pagetext-noframework' => 'این صفحه برای اجرای آزمایش‌های جاوا اسکریپت کنار گذاشته شده‌است.',
 'javascripttest-pagetext-unknownframework' => 'چارچوب آزمایشی ناشناخته «$1».',
-'javascripttest-pagetext-frameworks' => 'لطفاً یکی از فریم‌ورک‌های آزمایشی زیر را انتخاب کنید: $1',
+'javascripttest-pagetext-frameworks' => 'لطفاً یکی از چارچوب‌های آزمایش زیر را انتخاب کنید: $1',
 'javascripttest-pagetext-skins' => 'پوسته‌ای را برای اجرای آزمایش‌ها انتخاب کنید:',
 'javascripttest-qunit-intro' => '[$1 مستندات آزمایش] را در mediawiki.org ببینید.',
 'javascripttest-qunit-heading' => 'مجموعه آزمایش QUnit جاوااسکریپت برای مدیاویکی',
@@ -3470,6 +3470,10 @@ $1',
 'sp-newimages-showfrom' => 'نشان‌دادن تصویرهای جدید از $2، $1 به بعد',
 
 # Video information, used by Language::formatTimePeriod() to format lengths in the above messages
+'seconds-abbrev' => '$1 ثانیه',
+'minutes-abbrev' => '$1 دقیقه',
+'hours-abbrev' => '$1 ساعت',
+'days-abbrev' => '$1 روز',
 'seconds' => '{{PLURAL:$1|$1ثانیه| $1  ثانیه}}',
 'minutes' => '{{PLURAL: $1|دقیقه|دقیقه}}',
 'hours' => '{{PLURAL: $1|ساعت|ساعت}}',
@@ -3999,7 +4003,7 @@ $5
 
 # action=purge
 'confirm_purge_button' => 'تأیید',
-'confirm-purge-top' => 'پاک کردن نسخهٔ حافظهٔ نهانی (Cache) این صفحه را تأیید می‌کنید؟',
+'confirm-purge-top' => 'پاککردن نسخهٔ حافظهٔ نهانی (Cache) این صفحه را تأیید می‌کنید؟',
 'confirm-purge-bottom' => 'خالی کردن میانگیر یک صفحه باعث می‌شود که آخرین نسخهٔ آن نمایش یابد.',
 
 # action=watch/unwatch
index 8a62105..d74827e 100644 (file)
@@ -771,7 +771,7 @@ Huomaa, että jotkut sivut saattavat näkyä edelleen kuin olisit kirjautunut si
 'gotaccount' => "Jos sinulla on jo tunnus, voit '''$1'''.",
 'gotaccountlink' => 'kirjautua sisään',
 'userlogin-resetlink' => 'Unohditko salasanasi?',
-'userlogin-resetpassword-link' => 'Vaihda salasanaasi',
+'userlogin-resetpassword-link' => 'Unohditko salasanasi?',
 'helplogin-url' => 'Help:Sisäänkirjautuminen',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Auta sisäänkirjautumisessa]]',
 'userlogin-loggedin' => 'Olet jo kirjautunut sisään tunnuksella {{GENDER:$1|$1}}.
index 794d86d..b848b16 100644 (file)
@@ -566,9 +566,12 @@ Gloym ikki at broyta tínar [[Special:Preferences|{{SITENAME}}-innstillingar]].'
 'gotaccount' => "Hevur tú longu eina kontu? '''$1'''.",
 'gotaccountlink' => 'Rita inn',
 'userlogin-resetlink' => 'Hevur tú gloymt tínar logg inn upplýsingar',
-'userlogin-resetpassword-link' => 'Nullstilla títt loyniorð',
+'userlogin-resetpassword-link' => 'Hevur tú gloymt títt loyniorð?',
 'helplogin-url' => 'Help:Innritan',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hjálp til innritan]]',
+'userlogin-loggedin' => 'Tú ert longu innritað/ur sum {{GENDER:$1|$1}}.
+Nýt formularin niðanfyri fyri at rita inn sum ein annar brúkari.',
+'userlogin-createanother' => 'Stovna eina aðra kontu',
 'createacct-join' => 'Skrivað tínar upplýsingar niðanfyri.',
 'createacct-another-join' => 'Skriva upplýsingarnar fyri tað nýggju kontuna niðanfyri.',
 'createacct-emailrequired' => 'Teldupost adressa',
@@ -631,16 +634,16 @@ og tú ikki longur ynskir at broyta tað, so skal tú síggja burtur frá hesum
 sum er skrásett fyri "$1".
 Vinarliga rita inn eftir at tú hevur fingið hana.',
 'blocked-mailpassword' => 'Tín IP adressa er stongd fyri at gera rættingar á síðum, og tí er tað ikki loyvt at brúka funkuna fyri endurskapan av loyniorði, hetta fyri at forða fyri misnýtslu.',
-'eauthentsent' => '↓ Ein váttanar t-postur er sendur til givna t-post bústaðin.
-Áðrenn aðrir teldupostar verða sendir til kontuna, mást tú fylgja leiðbeiningunum í t-postinum, fyri at vátta at kontoin veruliga er tín.',
+'eauthentsent' => 'Ein váttanar teldupostur er sendur til givna teldupost bústaðin.
+Áðrenn nakað annað teldubræv verður sent til kontuna, mást tú fylgja leiðbeiningunum í teldupostinum, fyri at vátta at kontoin veruliga er tín.',
 'throttled-mailpassword' => 'Ein teldupostur har loyniorðið verður nullstillað er longu sendur fyri bert {{PLURAL:$1|tíma|$1 tímum}} síðan.
 Fyri at fyribyrja misnýtslu, verður bert ein teldupostur við nullstillaðum loyniorði sendur fyri pr. {{PLURAL:$1|tíma|$1 tímar}}.',
 'mailerror' => 'Villa tá t-postur var sendur: $1',
 'acct_creation_throttle_hit' => 'Vitjandi á hesi wiki, sum nýta tína IP addressu, hava stovnað {{PLURAL:$1|1 kontu|$1 kontur}} seinastu dagarnar, sum er mest loyvda hetta tíðarskeið.
 Sum eitt úrslit av hesum, kunnu vitjandi sum brúka hesa IP adressuna ikki stovna fleiri kontur í løtuni.',
-'emailauthenticated' => 'Tín t-post adressa varð váttað hin $2 kl. $3.',
-'emailnotauthenticated' => 'Tín t-post adressa er enn ikki komin í gildi. Ongin t-postur
-verður sendur fyri nak av fylgjandi hentleikum.',
+'emailauthenticated' => 'Tín teldupost adressa varð váttað hin $2 kl. $3.',
+'emailnotauthenticated' => 'Tín teldupost adressa er enn ikki váttað. Ongin teldupostur
+verður sendur fyri nakran av fylgjandi hentleikum.',
 'noemailprefs' => 'Skriva eina t-post adressu, so hesar funktiónir fara at virka.',
 'emailconfirmlink' => 'Vátta tína t-post adressu',
 'invalidemailaddress' => 'T-post bústaðurin kann ikki verða góðtikin, tí hann sær út til at hava ógyldugt format.
@@ -1066,15 +1069,15 @@ Tú kanst síggja munin; smálutir eru at finna í [{{fullurl:{{#Special:Log}}/s
 'logdelete-selected' => "'''{{PLURAL:$1|Útvald logghending|Útvaldar logghendingar}}:'''",
 'revdelete-confirm' => 'Vinarliga vátta, at tú ætlar at gera hetta, at tú skilir avleiðingarnar, og at tú ger hetta í samsvari við [[{{MediaWiki:Policy-url}}|mannagongdirnar]].',
 'revdelete-legend' => 'Set avmarkinga fyri sjónligheit',
-'revdelete-hide-text' => 'Goym burtur tekstin á hesi versjónini',
+'revdelete-hide-text' => 'Versjónstekstur',
 'revdelete-hide-image' => 'Fjal fílu innihald',
 'revdelete-hide-name' => 'Fjal handling og mál',
-'revdelete-hide-comment' => 'Fjal rættingar frágreiðing',
-'revdelete-hide-user' => 'Fjal brúkaranavn/IP adressu hjá tí sum rættar',
+'revdelete-hide-comment' => 'Samandráttur um rættingar',
+'revdelete-hide-user' => 'Brúkaranavn/IP adressa hjá tí sum rættar',
 'revdelete-hide-restricted' => 'Síggj burtur frá data frá administratorum líka væl sum frá øðrum',
 'revdelete-radio-same' => '(ikki broyta)',
-'revdelete-radio-set' => 'Ja',
-'revdelete-radio-unset' => 'Nei',
+'revdelete-radio-set' => 'Sjónligt',
+'revdelete-radio-unset' => 'Fjalt',
 'revdelete-suppress' => 'Síggj burtur frá data frá administratorum líka væl sum frá øðrum',
 'revdelete-unsuppress' => 'Tak burtur avmarkingar á endurskaptum versjónum',
 'revdelete-log' => 'Orsøk:',
index 07f3e9b..d643f59 100644 (file)
@@ -845,7 +845,7 @@ N'oubliez pas de modifier [[Special:Preferences|vos préférences pour {{SITENAM
 'gotaccount' => "Vous avez déjà un compte ? '''$1'''.",
 'gotaccountlink' => 'Connectez-vous',
 'userlogin-resetlink' => 'Vous avez oublié vos détails de connexion ?',
-'userlogin-resetpassword-link' => 'Réinitialiser le mot de passe',
+'userlogin-resetpassword-link' => 'Mot de passe oublié ?',
 'helplogin-url' => 'Help:Connexion',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Aide à la connexion]]',
 'userlogin-loggedin' => 'Vous êtes déjà connecté en tant que {{GENDER:$1|$1}}.
@@ -915,8 +915,8 @@ Avant qu’un autre courriel ne soit envoyé à ce compte, vous devrez suivre le
 'mailerror' => "Erreur lors de l'envoi du courriel : $1",
 'acct_creation_throttle_hit' => "Quelqu'un utilisant votre adresse IP a créé {{PLURAL:$1|un compte|$1 comptes}} au cours des dernières 24 heures, ce qui constitue la limite autorisée dans cet intervalle de temps.
 Par conséquent, la création de compte a été temporairement désactivée pour cette adresse IP.",
-'emailauthenticated' => 'Votre adresse de courriel a été authentifiée le $2 à $3.',
-'emailnotauthenticated' => "Votre adresse de courriel n'est <strong>pas encore authentifiée</strong>. Aucun courriel ne sera envoyé pour chacune des fonctions suivantes.",
+'emailauthenticated' => 'Votre adresse de courriel a été confirmée le $2 à $3.',
+'emailnotauthenticated' => "Votre adresse de courriel n'est pas encore confirmée. Aucun courriel ne sera envoyé pour chacune des fonctions suivantes.",
 'noemailprefs' => 'Indiquez une adresse de courriel dans vos préférences pour utiliser ces fonctions.',
 'emailconfirmlink' => 'Confirmez votre adresse de courriel',
 'invalidemailaddress' => 'Cette adresse courriel ne peut pas être acceptée car elle semble avoir un format incorrect.
index bef6c2f..ac19fe0 100644 (file)
@@ -1063,7 +1063,7 @@ $2',
 'randompage-nopages' => '个只名字空间冇𠮶页面。',
 
 # Random redirect
-'randomredirect' => '随机重定向页面',
+'randomredirect' => '随机重定向',
 'randomredirect-nopages' => '个只名字空间冇重定向页面。',
 
 # Statistics
@@ -1438,7 +1438,7 @@ $1',
 'linkshere' => '下底𠮶页面链接到[[:$1]]:',
 'nolinkshere' => '冇页面链接到[[:$1]]。',
 'nolinkshere-ns' => '选正𠮶空间名内冇页面链接到[[:$1]]。',
-'isredirect' => '重定向页',
+'isredirect' => '重定向页',
 'istemplate' => '含到',
 'isimage' => '档案连结',
 'whatlinkshere-prev' => '先$1只',
index 12751d5..00a058c 100644 (file)
@@ -670,6 +670,9 @@ Bu chòir dhut seo a dhèanamh ma thug thu do chuideigin e air mhearachd no ma b
 'resettokens-legend' => 'Ath-shuidhich na tòcanan',
 'resettokens-tokens' => 'Tòcanan:',
 'resettokens-token-label' => "$1 ('s e $2 an luach làithreach)",
+'resettokens-watchlist-token' => "Tòcan airson an inbhir-lìn (Atom/RSS) a sheallas dhut [[Special:Watchlist|atharraichean air duilleagan a tha air a' chlàr-fhaire agad]]",
+'resettokens-done' => 'Chaidh na tòcanan ath-shuidheachadh.',
+'resettokens-resetbutton' => 'Ath-shuidhich na tòcanan a chaidh a thaghadh',
 
 # Edit page toolbar
 'bold_sample' => 'Teacs trom',
@@ -878,6 +881,7 @@ Cha deach adhbhar a thoirt seachad.',
 Tha coltas gun deach a sguabadh às.",
 'edit-conflict' => 'Còmhstri deasachaidh.',
 'edit-no-change' => "Chaidh an obair-dheasachaidh agad a leigeil seachad a chionn 's nach do dh'atharraich thu dad.",
+'postedit-confirmation' => 'Chaidh na dheasaich thu a shàbhaladh.',
 'edit-already-exists' => "Cha b' urrainn dhuinn an duilleag ùr a chruthachadh.
 Tha e ann mu thràth.",
 'defaultmessagetext' => 'Teacsa bunaiteach na teachdaireachd',
@@ -905,10 +909,29 @@ Cha dèid cuid dhith a ghabhail a-steach.",
 Chaidh na h-argamaidean sinn a leigeil seachad.",
 'post-expand-template-argument-category' => 'Duilleagan air an deach argamaidean teamplaidean fhàgail às',
 'parser-template-loop-warning' => 'Mhothaicheadh do lùb teamplaid: [[$1]]',
+'parser-template-recursion-depth-warning' => 'Chaidh thu thairis air crìoch doimhne nan ath-chùrsaidhean teamplaid ($1)',
+'language-converter-depth-warning' => 'Chaidh thu thairis air crìoch doimhne an iompachair chànain ($1)',
+'node-count-exceeded-category' => 'Duilleagan far an deachas thairis air cunntas nan nòdan',
+'node-count-exceeded-warning' => 'Chaidh an duilleag thairis air cunntas nan nòdan',
+'expansion-depth-exceeded-category' => "Duilleagan far an deachas thairis air a' chrìoch leudachaidh",
+'expansion-depth-exceeded-warning' => 'Chaidh an duilleag thairis air an doimhne leudachaidh',
 'parser-unstrip-loop-warning' => 'Mhothaich sinn do lùb unstrip',
+'parser-unstrip-recursion-limit' => 'Chaidheas thairis air crìoch unstrip recursion ($1)',
+'converter-manual-rule-error' => 'Mhothaich sinn do mhearachd san riaghailt iompachadh làimhe airson cànan',
+
+# "Undo" feature
+'undo-success' => "Gabhaidh an deasachadh seo a neo-dhèanamh.
+Thoir sùil air a' choimeas gu h-ìosal is dearbh gur e sin a tha fa-near dhut agus sàbhail na h-atharraichean gu h-ìosal gus neo-dhèanamh an deasachaidh a choileanadh.",
+'undo-failure' => "Cha b' urrainn dhuinn an deasachadh a neo-dhèanamh air sgàth 's gun robh deasachaidhean eile sa mheadhan.",
+'undo-norev' => "Cha b' urrainn dhuinn an deasachadh a neo-dhèanamh a chionn 's nach robh e ann no gun deach a sguabadh às.",
+'undo-summary' => 'Neo-dhèan mùthadh $1 le [[Special:Contributions/$2|$2]] ([[User talk:$2|Deasbaireachd]])',
+'undo-summary-username-hidden' => 'Neo-dhèan am mùthadh $1 le cleachdaiche falaichte',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Cha ghabh an cunntas a chruthachadh',
+'cantcreateaccount-text' => "Chuir [[User:$3|$3]] bacadh air cruthachadh chunntasan on t-seòladh IP seo ('''$1''').
+
+Dh'innise $3 gun do rinn iad seo air sgàth: ''$2''",
 
 # History pages
 'viewpagelogs' => 'Seall logaichean na duilleige seo',
@@ -946,20 +969,70 @@ Feuch is [[Special:Search|lorg duilleagan ùra iomachaidh air an uici]]",
 'rev-deleted-comment' => '(chaidh gearr-chunntas an deasachaidh a thoirt air falbh)',
 'rev-deleted-user' => '(chaidh an t-ainm-cleachdaiche a thoirt air falbh)',
 'rev-deleted-event' => '(chaidh gnìomh an loga a thoirt air falbh)',
+'rev-deleted-user-contribs' => '[chaidh an t-ainm-cleachdaiche no an seòladh IP a thoirt air falbh - chan fhaic na com-pàirtichean an deasachadh]',
+'rev-deleted-text-permission' => "Chaidh mùthadh na duilleige seo '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-deleted-text-unhide' => "Chaidh mùthadh na duilleige seo '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].
+'S urrainn dhut [$1 am mùthadh seo fhaicinn fhathast] ma tha thu airson leantainn air adhart.",
+'rev-suppressed-text-unhide' => "Chaidh mùthadh na duilleige seo '''a mhùchadh'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].
+'S urrainn dhut [$1 am mùthadh seo fhaicinn fhathast] ma tha thu airson leantainn air adhart.",
+'rev-deleted-text-view' => "Chaidh mùthadh na duilleige seo '''a sguabadh às'''.
+'S urrainn dhut coimhead air, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-suppressed-text-view' => "Chaidh mùthadh na duilleige seo '''a mhùchadh'''.
+'S urrainn dhut coimhead air, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].",
+'rev-deleted-no-diff' => "Chan fhaic thu an diff seo a chionn 's gun deach aon dhe na mùthaidhean '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-suppressed-no-diff' => "Chan fhaic thu an diff seo a chionn 's gun deach aon dhe na mùthaidhean '''a sguabadh às'''.",
+'rev-deleted-unhide-diff' => "Chaidh mùthadh dhen diff seo '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].
+'S urrainn dhut [$1 coimhead air an diff seo fhathast] ma tha thu airson leantainn air adhart.",
+'rev-suppressed-unhide-diff' => "Chaidh mùthadh an diff seo '''a mhùchadh'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].
+'S urrainn dhut [$1 coimhead air an diff seo fhathast] ma tha thu airson leantainn air adhart.",
+'rev-deleted-diff-view' => "Chaidh mùthadh an diff seo '''a sguabadh às'''.
+'S urrainn dhut coimhead air an diff seo, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-suppressed-diff-view' => "Chaidh mùthadh an diff seo '''a mhùchadh'''.
+'S urrainn dhut coimhead air an diff seo, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].",
 'rev-delundel' => 'seall/falaich',
 'rev-showdeleted' => 'seall',
+'revisiondelete' => 'Sguab às/neo-dhèan sguabadh às mhùthaidhean',
+'revdelete-nooldid-title' => 'Tha am mùthadh seo mì-dhligheach',
+'revdelete-nooldid-text' => "Cha do shònraich thu mùthadh airson seo a dhèanamh, chan eil e ann no tha thu a' feuchainn ris am mùthadh làithreach a chur am falach.",
+'revdelete-nologtype-title' => 'Cha deach seòrsa an loga a shònrachadh',
+'revdelete-nologtype-text' => 'Cha do shònraich thu seòrsa an loga air an dèanar seo.',
+'revdelete-nologid-title' => 'Innteart mì-dhligheach an loga',
+'revdelete-nologid-text' => 'Cha do shònraich thu tachartas loga targaide gus seo a dhèanamh no chan eil an t-innteart seo ann.',
+'revdelete-no-file' => 'Chan eil am faidhle a shònraich thu ann.',
+'revdelete-show-file-confirm' => 'A bheil thu cinnteach gu bheil thu airson coimhead air mùthadh an fhaidhle "<nowiki>$1</nowiki>" a chaidh a sguabadh às $2 aig $3?',
+'revdelete-show-file-submit' => 'Tha',
 'revdelete-selected' => "'''{{PLURAL:$2|Lèirmheas|Lèirmheasan}} de [[:$1]] a thagh thu:'''",
 'logdelete-selected' => "'''{{PLURAL:$1|An tachartas loga|Na tachartasan loga}} a thagh thu:'''",
+'revdelete-hide-text' => "Teacsa a' mhùthaidh",
+'revdelete-hide-image' => 'Falaich susbaint an fhaidhle',
+'revdelete-hide-name' => 'Falaich an gnìomh agus an targaid',
+'revdelete-hide-comment' => 'Gearr-chunntas an deasachaidh',
 'revdelete-hide-user' => 'Ainm-cleachdaiche/seòladh IP an deasaiche',
+'revdelete-hide-restricted' => 'Mùch dàta o rianairean agus càch',
 'revdelete-radio-same' => '(na atharraich)',
 'revdelete-radio-set' => 'Ri fhaicinn',
 'revdelete-radio-unset' => 'Falaichte',
+'revdelete-suppress' => 'Mùch dàta o rianairean agus càch',
+'revdelete-unsuppress' => 'Thoir air falbh na bacaidhean air mùthaidhean a chaidh aiseag',
 'revdelete-log' => 'Adhbhar:',
 'revdelete-submit' => 'Cuir air {{PLURAL:$1|an lèirmheas|na lèirmheasan}} a thagh thu',
+'revdelete-success' => "'''Chaidh so-fhaicsinneachd a' mhùthaidh ùrachadh.'''",
+'revdelete-failure' => "'''Cha b' urrainn dhuinn so-fhaicsinneachd a' mhùthaidh ùrachadh:'''
+$1",
+'logdelete-success' => "'''Chaidh faicsinneachd an loga a shuidheachadh.'''",
+'logdelete-failure' => "'''Cha b' urrainn dhuinn faicsinneachd an loga a shuidheachadh:'''
+$1",
 'revdel-restore' => 'mùth follaiseachd',
 'revdel-restore-deleted' => 'mùthaidhean a chaidh a sguabadh às',
 'revdel-restore-visible' => 'mùthaidhean faicsinneach',
 'pagehist' => 'Eachdraidh na duilleige',
+'deletedhist' => 'Eachdraidh a chaidh a sguabadh às',
 'revdelete-otherreason' => 'Adhbhar eile/a bharrachd:',
 'revdelete-reasonotherlist' => 'Adhbhar eile',
 'revdelete-edit-reasonlist' => 'Deasaich adhbharan an sguabaidh às',
@@ -967,9 +1040,12 @@ Feuch is [[Special:Search|lorg duilleagan ùra iomachaidh air an uici]]",
 
 # History merging
 'mergehistory-from' => 'An duilleag thùsail:',
+'mergehistory-autocomment' => 'Chaidh [[:$1]] a cho-aonadh dha [[:$2]]',
+'mergehistory-comment' => 'Chaidh [[:$1]] a cho-aonadh dha [[:$2]]: $3',
 'mergehistory-reason' => 'Adhbhar:',
 
 # Merge log
+'mergelog' => "Loga a' cho-aonaidh",
 'revertmerge' => 'Dì-aontaich',
 
 # Diffs
@@ -979,6 +1055,7 @@ Feuch is [[Special:Search|lorg duilleagan ùra iomachaidh air an uici]]",
 'compareselectedversions' => 'Dèan coimeas eadar na mùthaidhean a thagh thu',
 'showhideselectedversions' => 'Seall/Falaich na lèirmheasan a thagh thu',
 'editundo' => 'neo-dhèan',
+'diff-empty' => '(Gun diofar eatarra)',
 'diff-multi' => '({{PLURAL:$1|Aon lèirmheas eadar-mheadhanach||$1 lèirmheasan eadar-mheadhanach|$1 lèirmheas eadar-mheadhanach}} le {{PLURAL:$2|aon chleachdaiche|$2 chleachdaiche|$2 cleachdaichean|$2 cleachdaiche}} gun sealltainn)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Aon lèirmheas eadar-mheadhanach||$1 lèirmheasan eadar-mheadhanach|$1 lèirmheas eadar-mheadhanach}} le {{PLURAL:$2|aon chleachdaiche|$2 chleachdaiche|$2 cleachdaichean|$2 cleachdaiche}} gun sealltainn)',
 
@@ -1064,6 +1141,9 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 'prefs-watchlist' => 'An clàr-faire',
 'prefs-watchlist-days' => "Co mheud latha a sheallar air a' chlàr-fhaire:",
 'prefs-watchlist-days-max' => "{{PLURAL:$1|latha|latha|làithean|latha}} air a' char as motha",
+'prefs-watchlist-edits-max' => 'Àireamh as motha: 1000',
+'prefs-watchlist-token' => "Tòcan a' chlàir-fhaire:",
+'prefs-misc' => 'Measgachadh',
 'prefs-resetpass' => 'Atharraich am facal-faire',
 'prefs-changeemail' => 'Atharraich am post-d',
 'prefs-setemail' => 'Suidhich seòladh puist-d',
@@ -1077,10 +1157,14 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 'columns' => 'Colbhan',
 'searchresultshead' => 'Lorg',
 'stub-threshold-disabled' => 'À comas',
+'recentchangesdays-max' => "{{PLURAL:$1|latha|latha|làithean|latha}} air a' char as motha",
+'recentchangescount' => 'Uiread a dheasachaidhean a thèid a shealltainn a ghnàth:',
 'savedprefs' => 'Tha na roghainnean agad air an sàbhaladh.',
 'timezonelegend' => 'Roinn-tìde:',
 'localtime' => 'An t-àm ionadail:',
+'timezoneuseserverdefault' => 'Cleachd bun-roghainn na h-Uicipeid ($1)',
 'servertime' => 'Àm an fhrithealaichte:',
+'guesstimezone' => 'Lìon on bhrabhsair',
 'timezoneregion-africa' => 'Afraga',
 'timezoneregion-america' => 'Aimeireaga',
 'timezoneregion-antarctica' => 'An Antartaig',
@@ -1091,6 +1175,8 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 'timezoneregion-europe' => 'An Roinn-Eòrpa',
 'timezoneregion-indian' => 'An Cuan Innseanach',
 'timezoneregion-pacific' => 'An Cuan Sèimh',
+'allowemail' => 'Ceadaich post-d o chleachdaichean eile',
+'prefs-searchoptions' => 'Lorg',
 'prefs-namespaces' => 'Namespaces',
 'default' => 'an roghainn bhunaiteach',
 'prefs-files' => 'Faidhlichean',
@@ -1129,6 +1215,8 @@ Chan fhaicear an seòladh fhèin nuair a chuireas cuideigin post-dealain thugad.
 'prefs-dateformat' => "Fòrmat a' chinn-là",
 'prefs-timeoffset' => 'Diofar ama',
 'prefs-advancedediting' => 'Roghainnean coitcheann',
+'prefs-editor' => 'Deasaiche',
+'prefs-preview' => 'Ro-shealladh',
 'prefs-advancedrc' => 'Roghainnean adhartach',
 'prefs-advancedrendering' => 'Roghainnean adhartach',
 'prefs-advancedsearchoptions' => 'Roghainnean adhartach',
@@ -1136,6 +1224,7 @@ Chan fhaicear an seòladh fhèin nuair a chuireas cuideigin post-dealain thugad.
 'prefs-displayrc' => 'Roghainnean taisbeanaidh',
 'prefs-displaysearchoptions' => 'Roghainnean taisbeanaidh',
 'prefs-displaywatchlist' => 'Roghainnean taisbeanaidh',
+'prefs-tokenwatchlist' => 'Tòcan',
 'prefs-diffs' => 'Diffs',
 
 # User preference: email validation using jQuery
diff --git a/languages/messages/MessagesGom_latn.php b/languages/messages/MessagesGom_latn.php
new file mode 100644 (file)
index 0000000..fe133ad
--- /dev/null
@@ -0,0 +1,749 @@
+<?php
+/** Goan Konkani (Latin script) (Konknni)
+ *
+ * See MessagesQqq.php for message documentation incl. usage of parameters
+ * To improve a translation please visit http://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ * @author Deepak D'Souza
+ * @author Isidore Dantas
+ * @author The Discoverer
+ */
+
+$messages = array(
+'underline-always' => 'Soddankal',
+'underline-never' => 'Kednach na',
+
+# Dates
+'sunday' => 'Aitar',
+'monday' => 'Somar',
+'tuesday' => 'Munglar',
+'wednesday' => 'Budhwar',
+'thursday' => 'Birestar',
+'friday' => 'Sukrar',
+'saturday' => 'Sonvar',
+'sun' => 'Ait',
+'mon' => 'Som',
+'tue' => 'Mung',
+'wed' => 'Budh',
+'thu' => 'Bres',
+'fri' => 'Sukr',
+'sat' => 'Son',
+'january' => 'Janer',
+'february' => 'Febrer',
+'march' => 'Mars',
+'april' => 'Abril',
+'may_long' => 'Mai',
+'june' => 'Jun',
+'july' => 'Julai',
+'august' => 'Agost',
+'september' => 'Setembr',
+'october' => 'Otubr',
+'november' => 'Novembr',
+'december' => 'Dezembr',
+'january-gen' => 'Janer',
+'february-gen' => 'Febrer',
+'march-gen' => 'Mars',
+'april-gen' => 'Abril',
+'may-gen' => 'Mai',
+'june-gen' => 'Jun',
+'july-gen' => 'Julai',
+'august-gen' => 'Agost',
+'september-gen' => 'Setembr',
+'october-gen' => 'Otubr',
+'november-gen' => 'Novembr',
+'december-gen' => 'Dezembr',
+'jan' => 'Jan',
+'feb' => 'Feb',
+'mar' => 'Mar',
+'apr' => 'Abr',
+'may' => 'Mai',
+'jun' => 'Jun',
+'jul' => 'Jul',
+'aug' => 'Ago',
+'sep' => 'Set',
+'oct' => 'Otu',
+'nov' => 'Nov',
+'dec' => 'Dez',
+'january-date' => '$1 Janer',
+'february-date' => '$1 Febrer',
+'march-date' => '$1 Mars',
+'april-date' => '$1 Abril',
+'may-date' => '$1 Mai',
+'june-date' => '$1 Jun',
+'july-date' => '$1 Julai',
+'august-date' => '$1 Agost',
+'september-date' => '$1 Setembr',
+'october-date' => '$1 Otubr',
+'november-date' => '$1 Novembr',
+'december-date' => '$1 Dezembr',
+
+# Categories related messages
+'pagecategories' => '{{PLURAL:$1|Vorg|Vorgam}}',
+'category_header' => '"$1" vorgantlim panam',
+'subcategories' => 'Upvorg',
+'category-media-header' => '"$1" hea vorgan madheom\'ma',
+'category-empty' => "''Hea vorgan sodhea ekui pan vo madheom na''",
+'hidden-categories' => '{{PLURAL:$1|Lipoilolo vorg|Lipoilole vorg}}',
+'category-subcat-count' => '{{PLURAL:$2|Hea vorgan fokot hi ek upvorg asa.|Hea vorgan {{PLURAL:$1|hi upvorg asa|heo $1 upvorg asat}}, beriz $2 upvorga modem.}}',
+'category-article-count' => '{{PLURAL:$2|Hea vorgan fokot hi ek pan asa.|Hea vorgan {{PLURAL:$1|hi pan asa|him $1 panam asat}} beriz $2 panam modem.}}',
+'category-file-count' => '{{PLURAL:$2|Hea vorgan fokot hi ek fail asa.|Hea vorgan {{PLURAL:$1|hi fail asa|heo $1 faili asat}}, beriz $2 faili modem.}}',
+'listingcontinuesabbrev' => 'chalu',
+'noindex-category' => 'Suchi-potran zoddunk-naslelim panam',
+
+'about' => 'Hea vixoiavoir',
+'article' => 'Vixoi sombondhi pan',
+'newwindow' => '(novea zonelant uktem zata)',
+'cancel' => "Rod'd kor",
+'mytalk' => 'Bhasabhas',
+'navigation' => 'Dixa-niontronn',
+
+# Cologne Blue skin
+'qbfind' => 'Sod',
+'qbedit' => 'Bodol',
+'qbspecialpages' => 'Khaxelim panam',
+'faq' => 'Choddxe vicharlole prosn',
+
+# Vector skin
+'vector-action-addsection' => 'Vixoi zodd',
+'vector-action-delete' => 'Kadd',
+'vector-action-move' => 'Fuddem voch',
+'vector-action-protect' => 'Rakh',
+'vector-view-create' => 'Roch',
+'vector-view-edit' => 'sudar',
+'vector-view-history' => 'Itihas polloi',
+'vector-view-view' => 'Vach',
+'vector-view-viewsource' => 'Mull polloi',
+'actions' => 'Karvaio',
+'namespaces' => 'Nanv-thollam',
+'variants' => 'Dusre',
+
+'errorpagetitle' => 'Chuk',
+'returnto' => '$1 hanga porot voch.',
+'tagline' => '{{SITENAME}} savn',
+'help' => 'Adar',
+'search' => 'Sod',
+'searchbutton' => 'Sod',
+'go' => 'Pavl mar',
+'searcharticle' => 'Fuddem voch',
+'history' => 'Panacho itihas',
+'history_short' => 'Itihas',
+'printableversion' => "Chapp'pachi avruti",
+'permalink' => 'Togpi zodd',
+'view' => 'Poloi',
+'edit' => 'Sudar',
+'create' => 'Roch',
+'editthispage' => 'Hem pan bodol',
+'create-this-page' => 'Ho pan roch',
+'delete' => 'Vogllai',
+'deletethispage' => 'Hem pan kad',
+'protect' => 'Rakh',
+'protect_change' => 'bodol',
+'protectthispage' => 'Hem pan rakh',
+'newpage' => 'Novem pan',
+'talkpage' => 'Hea panachem bhasabhas kor',
+'talkpagelinktext' => 'Bhasabhas',
+'specialpage' => 'Khaxhel pan',
+'personaltools' => 'Khasgi avtam',
+'articlepage' => 'Vixoi sombondhi pan poloi',
+'talk' => 'Bhasabhas',
+'views' => 'Niall',
+'toolbox' => 'Avtam',
+'userpage' => 'Vangddiacho pan poloi',
+'imagepage' => 'Imazichem pan poloi',
+'viewhelppage' => 'Adar pan poloi',
+'categorypage' => 'Vorgachem pan poloi',
+'otherlanguages' => 'Dusrea bhasanim',
+'redirectedfrom' => '($1 savn porot dixent)',
+'redirectpagesub' => 'Punornirdexan pan',
+'lastmodifiedat' => 'Hem pan xevtim $1 disa, $2 vazta bodolelem.',
+'jumpto' => 'Hangachean voch',
+'jumptonavigation' => 'Dixa-niontronn',
+'jumptosearch' => 'sod',
+
+# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
+'aboutsite' => '(Thollachea) babtint',
+'aboutpage' => 'Project:Vixoiavixim',
+'copyrightpage' => '{{ns:project}}:Prat-hokk',
+'currentevents' => 'Chalu ghoddnneo',
+'currentevents-url' => 'Project:Chalu ghoddneo',
+'disclaimers' => 'Chotraio',
+'disclaimerpage' => 'Project:Sadeo chotraio',
+'edithelp' => 'Sudarunk palov',
+'helppage' => 'Help:Mozkur',
+'mainpage' => 'Mukhel Pan',
+'mainpage-description' => 'Mukhel Pan',
+'portal' => 'Somudaik proves-dar',
+'portal-url' => 'Project:Somudaik proves-dar',
+'privacy' => 'Gupitaiechem dhoronn',
+'privacypage' => 'Project:Gupitachem dhoronn',
+
+'badaccess-group0' => "Tumi tillson kel'lem kary korunk tumkam permissanv na.",
+'badaccess-groups' => "Tumi tillson kel'lem kary mat {{PLURAL:$2|the group|one of the groups}}: $1 -ak permissanv asa.",
+
+'versionrequired' => 'MediaWikichem $1 version zai',
+'versionrequiredtext' => 'Hem pan vaprunk MediaWikichem $1 version zai.
+[[Special:Version|version page]] pan poloea.',
+
+'ok' => 'Zait',
+'retrievedfrom' => '"$1" savn prapt kelam',
+'youhavenewmessages' => 'Tumkam $1 ($2) asat.',
+'newmessageslink' => 'nove sondex',
+'newmessagesdifflink' => 'nimannem bodlop',
+'editsection' => 'sudar',
+'editold' => 'sudar',
+'viewsourceold' => 'mull poloi',
+'editlink' => 'sudar',
+'viewsourcelink' => 'mull polloi',
+'editsectionhint' => 'khondd sudar: $1',
+'toc' => 'Suchi potr',
+'showtoc' => 'dakhoi',
+'hidetoc' => 'lipoi',
+'site-atom-feed' => '$1 Atom purvoi',
+'page-atom-feed' => '"$1" Atom purvonn',
+'red-link-title' => '$1 (hea nanvachem pan na)',
+
+# Short words for each namespace, by default used in the namespace tab in monobook
+'nstab-main' => 'Pan',
+'nstab-user' => 'Vapuddpeachem pan',
+'nstab-special' => 'Kherit pan',
+'nstab-project' => 'Project-ache pan',
+'nstab-image' => 'Fail',
+'nstab-mediawiki' => 'Sondex',
+'nstab-template' => 'Saacho',
+'nstab-category' => 'Vorg',
+
+# General errors
+'missing-article' => 'Totv-kox (Database) hantun mellunk zai aslem tem mozkur "$1" $2 mellunk-nam.
+
+Horxim, oxem ek pornem frk vo eka panachem itihasachem zodd vogllailem, tedna zata.
+
+Oxem nhoi zalear, tuka softwer-an chuk sampodlam zait.
+Upkar korun eka [[Special:ListUsers/sysop|karbhari]]chea nodrek hadd, Internet Zago Sodpi (URL) hachi nond gheun.',
+'missingarticle-rev' => '(uzollnni#: $1)',
+'badtitle' => 'Chukichem nanv',
+'badtitletext' => 'Tuven maglelem panache nanv chukichem, rintem, vo ek sarkem zodunk-naslelem bhase-modlem vo wiki-modlem nanv.
+
+Tantun ek vo sabaar okxor asot jenka nanvanim uzar korunk zainan.',
+'viewsource' => 'Mull polloi',
+
+# Login and logout pages
+'welcomeuser' => 'Ievkar, $1!',
+'yourname' => 'Vapuddpeachem nanv:',
+'yourpassword' => 'Gupitutor:',
+'yourpasswordagain' => 'Gupit utor porot boroi:',
+'remembermypassword' => 'Hea internet browseran mhojem sotrachem ugdas dovor (chodan chod $1 {{PLURAL:$1|disak|disank}})',
+'login' => 'Sotrromb kor',
+'nav-login-createaccount' => 'Sotrrombh kor / khato roch',
+'loginprompt' => "{{SITENAME}}, hea siticher sotrorombh korunktujea internet browseran ''cookies'' suru asunk zai.",
+'userlogin' => 'Sotrrombh kor / khatem roch',
+'logout' => 'Bhair podd',
+'userlogout' => 'Sotracho xevott',
+'nologin' => 'Tuje kodde khatem na? $1.',
+'nologinlink' => 'Novem khatem ughodd',
+'createaccount' => 'Khatem roch',
+'gotaccount' => 'Tuje kodem khatem asa? $1.',
+'gotaccountlink' => 'Sotrorombh kor',
+'userlogin-resetlink' => 'Sotrorombh korpacheo bariksai visorlai?',
+'mailmypassword' => 'Novem gupitutor email kor',
+'loginlanguagelabel' => 'Bhas: $1',
+
+# Edit page toolbar
+'bold_sample' => 'Datt mozkur',
+'bold_tip' => 'Datt mozkur',
+'italic_sample' => 'Palso mozkur',
+'italic_tip' => 'Palso mozkur',
+'link_sample' => 'Zoddachem nanv',
+'link_tip' => 'Bhitorlem zoddop',
+'extlink_sample' => 'http://www.udaronn.in zoddachem nanv',
+'extlink_tip' => 'Bhailem site (survatek http:// visronakai)',
+'headline_sample' => 'Mathalleacho mozkur',
+'headline_tip' => 'Dusrea patllicho mathallo',
+'nowiki_sample' => 'Sworup diunk naslelem mozkur hanga ghal',
+'nowiki_tip' => 'Wiki sworup durlokx kor',
+'image_tip' => 'Bosoileli fail',
+'media_tip' => 'Failichem zodd',
+'sig_tip' => 'Tuji soi, vell-chaap soit',
+'hr_tip' => 'Adhvem rang (Komi uzar kor)',
+
+# Edit pages
+'summary' => 'Sar:',
+'subject' => 'Vishoy:',
+'minoredit' => 'Hem ek dhaktem sudarop',
+'watchthis' => 'Hea panar nodor dovor',
+'savearticle' => 'Pan samball',
+'preview' => 'Zholok',
+'showpreview' => 'Zholok dakhoi',
+'showdiff' => 'Bodolpam dakhoi',
+'anoneditwarning' => "'''Chotrai:''' Tuven sotrorombh korunk nai.
+Tuzo internet potteachi nond panachem itihasan zatelem.",
+'newarticle' => '(Novem)',
+'newarticletext' => "Tuven ek zoddache patlav kelai, zachem pan azun rochunk na.
+Pan rochunk, khallchea chovkottan boroi (anik mahitik [[{{MediaWiki:Helppage}}|adar pan]] polloi).
+Tu hangasor chukin pavlai zalear tujea internet browser-achi '''Fatim'' vo '''Back''' butao dab.",
+'noarticletext' => 'Sodheak hem pan rinte asa.
+Tujean dusrea panani [[Special:Search/{{PAGENAME}}|hea panache nanv sodunk zata]], <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sombondhi sotrani sodunk zata], vo [{{fullurl:{{FULLPAGENAME}}|action=edit}} hem pan sudharunk zata]</span>.',
+'noarticletext-nopermission' => 'Sodheak hem pan rinte asa.
+Tujean dusrea panani [[Special:Search/{{PAGENAME}}|hea panache nanv sodunk zata]], vo <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sombondhi sotrani sodunk zata], pun tuka hem pan rochunk porvangi na.',
+'previewnote' => "'''Hi fokot ek zholok mhonn ugddas dhor.'''
+Tujim bodolpam azun sambhallun dovrunk nant!",
+'editing' => '$1 sudarop',
+'editingsection' => '(Vibhag) $1 sudar',
+'templatesused' => "Hea panant uzar {{PLURAL:$1|kel'lo sancho|kel'le sanche}}:",
+'template-protected' => '(rakhlelem)',
+'template-semiprotected' => '(ordhem rakhun dovorlelem)',
+'hiddencategories' => 'Hem pan {{PLURAL:$1|1 lipoilelea vorgacho vangddi|$1 lipoileleam vorgancho vangddi}}:',
+'permissionserrorstext-withaction' => '$2, hem korpak tuka porvangi na, {{PLURAL:$1|hea karnnak lagon|hea karnnank lagun}}:',
+'recreate-moveddeleted-warn' => "'''Xittkavnni: Tum ek pan porot rochtai jem fattim vogllailelem .'''
+
+Panacho sudar korop sarkem zalear dhean di.
+Pan vogllavpachem ani sotr halovpachem, sovloti khatir hangasor dilelem asa:",
+'moveddeleted-notice' => 'Hem pan vogllailem asa.
+Panachea vogllaonechi ani hallonechi sotr mahiti khatir sokoil sondorba khatir dilea.',
+
+# Parser/template warnings
+'post-expand-template-inclusion-warning' => "'''Chotrai:''' Sacho zoddpacho akar chod vhodlem asa.
+Thodde sache zoddchenant",
+'post-expand-template-inclusion-category' => 'Zea panani sache zoddpachem akarachem merakin chod zala',
+'post-expand-template-argument-warning' => "'''Chotrai:''' Hea panan ek tori oslo sacheacho parametro asa zacho patlloylea uprant akar chod vhoddlo zata.
+Heo parametrank durlokx keleat.",
+'post-expand-template-argument-category' => 'Sacheache parametro zoddunknan osle panam',
+
+# History pages
+'viewpagelogs' => 'Hea panachim sotram polloi',
+'currentrev-asof' => '$1, hachi halinchi uzollnni',
+'revisionasof' => '$1 hachi uzollnni',
+'revision-info' => '$2 hannem $1, hachi uzollnni',
+'previousrevision' => '← Adli uzollnni',
+'nextrevision' => 'Novi uzolnni →',
+'currentrevisionlink' => 'Sogleanvon novi uzollnni',
+'cur' => 'chal',
+'last' => 'adl',
+'page_first' => 'poilem',
+'page_last' => 'akhirchem',
+'histlegend' => "Frk nivoddni: Jeo uzollneo tuka comparar korunk zai, tenche fudle ''radio'' butao petoi
+Vivron: '''({{int:cur}})''' = halinchi uzollnie borobor forok, '''({{int:last}})''' = adli uzollnie borobor forok, '''{{int:minoreditletter}}''' = dhaktem sudharop.",
+'history-fieldset-title' => 'Itihas chall',
+'history-show-deleted' => 'Fokot vogllailelem',
+'histfirst' => 'sogleavon adhlem',
+'histlast' => 'sogleavon novem',
+
+# Revision feed
+'history-feed-item-nocomment' => '$1 hannem $4, $3 hea vellar',
+
+# Revision deletion
+'rev-delundel' => 'dakhoi/lipoi',
+'revdel-restore' => 'Disnnem bodol',
+'revdel-restore-deleted' => "rod'd kelelo uzollnneo",
+'revdel-restore-visible' => 'Dispi uzollnneo',
+
+# Merge log
+'revertmerge' => 'Doxim kor',
+
+# Diffs
+'history-title' => '"$1" hachea uzollnnecho itihas',
+'lineno' => 'Line ank $1:',
+'compareselectedversions' => 'Nivodloleo uzollneo comparar kor',
+'editundo' => "rod'd kor",
+'diff-multi' => "({{PLURAL:$2|Eka vapuddpean|$2 vapuddpeamni}} {{PLURAL:$1|kel'li ek modli uzollnni|kel'leo $1 modleo uzollnneo}} dakhonk nant)",
+
+# Search results
+'searchresults' => 'Sodacho nikal',
+'searchresults-title' => '"$1" -khatir sodacho nikal',
+'prevn' => 'adlem {{PLURAL:$1|$1}}',
+'nextn' => 'fuddlem {{PLURAL:$1|$1}}',
+'prevn-title' => '{{PLURAL:$1|Fattlem $1 porinnam|Fattlem $1 porinam}}',
+'nextn-title' => '{{PLURAL:$1|Fuddlem $1 porinnam|Fudnlim $1 porinnam}}',
+'shown-title' => 'Dor panar {{PLURAL:$1|porinam|porinam}} dakhoi',
+'viewprevnext' => '($1 {{int:pipe-separator}} $2) ($3) poloi',
+'searchmenu-exists' => "'''Hea Wikicher \"[[:\$1]]\" nanvanche pan asa.'''",
+'searchmenu-new' => "'''\"[[:\$1]]\" hem pan hea vikint roch!'''",
+'searchprofile-articles' => 'Mozkurachim panam',
+'searchprofile-project' => 'Adar ani Project panam',
+'searchprofile-images' => 'Bhovmadhiom',
+'searchprofile-everything' => 'Sogllem',
+'searchprofile-advanced' => 'Sodpache poryay',
+'searchprofile-articles-tooltip' => '$1 hantunt sod',
+'searchprofile-project-tooltip' => '$1 hantunt sod',
+'searchprofile-images-tooltip' => 'Faili sod',
+'searchprofile-everything-tooltip' => "Akhea sitin sod (Bhasabhas panant'ui)",
+'searchprofile-advanced-tooltip' => 'chalu nanvthollancher sod',
+'search-result-size' => '$1 {{PLURAL:$2|1 utor|$2 utram}}',
+'search-result-category-size' => '{{PLURAL:$1|1 vangddi|$1 vangddi}} ({{PLURAL:$2|1 upvorg|$2 upvorg}}, {{PLURAL:$3|1 fichier|$3 fichieri}})',
+'search-redirect' => '($1 porot dixen dhaddop)',
+'search-section' => '(vibhag $1)',
+'search-suggest' => 'mhonnunk sodi: $1',
+'searchrelated' => 'sombondit',
+'searchall' => 'soglle',
+'showingresultsheader' => "{{PLURAL:$5|'''$3''' hantlem '''$1''' porinam|'''$3''' hantlim '''$1 - $2''' porinam}}, '''$4''' haka",
+'search-nonefound' => 'Tujea sodak mell khata toslem kai porinam nan.',
+'powersearch-field' => 'Hachea khatir sodha',
+
+# Preferences page
+'preferences' => 'Posondeo',
+'mypreferences' => 'Posonti',
+'youremail' => 'Tuzo email potto',
+'yourrealname' => 'Khorem nanv:',
+'prefs-help-email' => 'Email potto sokticho na, pun tum gupitutor visroxi zalear gupitutor punorsthapon korunk email pottechi goroz podta.',
+'prefs-help-email-others' => 'Tujean dusreank tujea vapurpeacho panar vo bhasabhasache panar aslele eke email zodde vorvim tuje xim sompork korunk diunk zata.
+Dusre tuje xim sompork kortat tednam tuzo email potto tankam kollchenam.',
+
+# Groups
+'group-all' => '(soglle)',
+
+# Special:Log/newusers
+'newuserlogpage' => 'Vapurpi rochnnechem sotr',
+
+# Associated actions - in the sentence "You do not have permission to X"
+'action-edit' => 'hem pan bodol',
+
+# Recent changes
+'nchanges' => '$1 {{PLURAL:$1|bodlop|bodlopam}}',
+'recentchanges' => 'Halincho bodol',
+'recentchanges-legend' => 'Hallinch zalleo bodlopancheo poryay',
+'recentchanges-feed-description' => "Wiki'k kel'le halinche bodlopancher hea vhawa vorvim nodor dovor.",
+'recentchanges-label-newpage' => 'Hea sudaran ek novem pan rochlam',
+'recentchanges-label-minor' => 'Ho ek dhaktto sudar',
+'recentchanges-label-bot' => "Hem bodlop eka robotan kel'lem",
+'recentchanges-label-unpatrolled' => 'Hem sudharop azun topasunk nam',
+'rcnote' => "Sokoil {{PLURAL:$1|dilelim nimannim '''1''' bodlopam| '''$1''' bodlopam}} {{PLURAL:$2|xevotchea disan|xevottchim '''$2''' disanim}}, $5, $4 porian.",
+'rcnotefrom' => "Sokoil '''$2''' savn zalelim bodolpam dileant ( '''$1'''meren dakhoileant).",
+'rclistfrom' => '$1 savn suru zatelim novim bodolpam dakhoi',
+'rcshowhideminor' => '$1 dhaktteo sudarnneo',
+'rcshowhidebots' => '$1 robot',
+'rcshowhideliu' => '$1 sotrromb kelele vapuddpi',
+'rcshowhideanons' => '$1 nanv-naslelim vapurpi',
+'rcshowhidepatr' => '$1 topaslele sudharop',
+'rcshowhidemine' => 'Mhojem sudarop $1',
+'rclinks' => "Xevtiche $2 disanim zal'le $1 bodlopam dakhoi<br />$3",
+'diff' => 'frk',
+'hist' => 'iti',
+'hide' => 'Lipoi',
+'show' => 'Dakhoi',
+'minoreditletter' => 'd',
+'newpageletter' => 'N',
+'boteditletter' => 'r',
+'rc-enhanced-expand' => 'Bariksann dakhoi',
+'rc-enhanced-hide' => 'Bariksann lipoi',
+
+# Recent changes linked
+'recentchangeslinked' => 'Sombondit bodolpam',
+'recentchangeslinked-toolbox' => 'Sombondit bodolpam',
+'recentchangeslinked-title' => '"$1" sombondit zalelim bodolpam',
+'recentchangeslinked-summary' => "Hem zaun asa eke panaksun vo eka voraksun, halinch kel'lim bodlopanchi suchi.
+
+[[Special:Watchlist|Tujea sadurvollerint]] aslelim panam 'datt' asat.",
+'recentchangeslinked-page' => 'Panache nanv:',
+'recentchangeslinked-to' => "Dil'em panache bodlek haka zodlelem panank kel'lim bodlopam dakhoi",
+
+# Upload
+'upload' => 'Fail upload kor',
+'uploadlogpage' => 'Uploadachem sotr',
+'filedesc' => 'Sar',
+'uploadedimage' => ' "[[$1]]" upload zalem',
+'watchthisupload' => 'Hea faylar dixtt dovor',
+
+'license' => 'Porvangi',
+'license-header' => 'Porvangi',
+
+# File description page
+'file-anchor-link' => 'Fail',
+'filehist' => 'Failicho itihas',
+'filehist-help' => ' Tea vellar aslelea rupan pollonvk tarikh/vellar koll mar',
+'filehist-revert' => 'Nimanea avruttik porot vor',
+'filehist-current' => 'chalont',
+'filehist-datetime' => 'Tarikh/Vell',
+'filehist-thumb' => 'Lhan-imaz',
+'filehist-thumbtext' => '$1 avrute khatir lhan-imaz',
+'filehist-user' => 'Vaporpi',
+'filehist-dimensions' => 'Akar',
+'filehist-comment' => 'vivek',
+'imagelinks' => 'Failicho vapor',
+'linkstoimage' => '{{PLURAL:$1|Hem pan|$1 Him panam}} hea failik {{PLURAL:$1|zoddtta|zoddttat}}',
+'nolinkstoimage' => 'Hea failik zoddpi panam nant',
+'sharedupload-desc-here' => 'Hi fail $1, hachi ani dusreo projectanim haka uzar korunk zata.
+Hachem [$2 failichem vivron panan] asleli vivron khala dilea:',
+
+# File deletion
+'filedelete-otherreason' => 'Dusrem/aniki karon:',
+
+# Random page
+'randompage' => 'Khoincheim pan',
+
+# Statistics
+'statistics' => 'Ankddevari',
+
+# Miscellaneous special pages
+'nbytes' => '$1 {{PLURAL:$1|byte|bytesi}}',
+'nmembers' => '$1 {{PLURAL:$1|vangddi}}',
+'prefixindex' => 'Panam jenche nanvache survatek asa...',
+'shortpages' => 'Dhaktim panam',
+'longpages' => 'Lamb panam',
+'usercreated' => '$3 hannem $1 disa $2 vaztam rochlelem',
+'newpages' => 'Novim panam',
+'move' => 'Zago bodol',
+'pager-newer-n' => '{{PLURAL:$1|novem 1|novim $1}}',
+'pager-older-n' => '{{PLURAL:$1|adlem 1|adlim $1}}',
+
+# Book sources
+'booksources' => 'Pustokachem mull',
+'booksources-search-legend' => 'Pustokachim mullam sod',
+'booksources-go' => 'Fuddem voch',
+
+# Special:Log
+'speciallogtitlelabel' => 'Vishoi vo vapurpi:',
+'log' => 'Sotram',
+
+# Special:AllPages
+'allpages' => 'Sogllim panam',
+'alphaindexline' => '$1 savn $2',
+'nextpage' => 'Mukklem pan ($1)',
+'prevpage' => "Ad'dlem pan ($1)",
+'allpagesfrom' => 'Hanga thavn suru zatelea panank dakhoi:',
+'allarticles' => 'Sogllim panam',
+'allpagessubmit' => 'Fuddem voch',
+
+# Special:Categories
+'categories' => 'Vorg',
+
+# Special:LinkSearch
+'linksearch-line' => '$1 $2 savn zoddlelem asa',
+
+# Special:ListGroupRights
+'listgrouprights-members' => '(vapuddpeanchi suchi)',
+
+# Email user
+'emailuser' => 'Email dhadd',
+
+# Watchlist
+'watchlist' => 'Sadurachi volleri',
+'mywatchlist' => 'Sadurachi volleri',
+'watchlistfor2' => '$1 hache khatir $2',
+'watch' => 'Sadur rav',
+'watchthispage' => 'Hea panar dixtt dovor',
+'unwatch' => 'Nodor kadd',
+'watchlist-details' => '{{PLURAL:$1|$1 pan tujea sadurvollerint asa|$1 panam tujea sadurvollerint asat}} , ulovpachim panam meznastanam.',
+'wlshowlast' => 'Xevottchim $1 voram $2 dis $3 dakhoi',
+'watchlist-options' => 'Sadurvollericheo poryay',
+
+# Displayed when you click the "watch" button and it is in the process of watching
+'watching' => 'Disht dovortanv...',
+'unwatching' => 'Disht kaddthanv...',
+
+# Delete
+'actioncomplete' => 'Karvai sompurnn',
+'actionfailed' => 'Karvai oiesiesvi',
+'dellogpage' => 'Vogllaoneche sotr',
+'deleteotherreason' => 'Dusrem/aniki karon:',
+
+# Rollback
+'rollbacklink' => 'kovoll',
+
+# Protect
+'protectlogpage' => 'Surokxitechem sotr',
+'protectedarticle' => 'rakhlelem "[[$1]]"',
+'protect-otherreason' => 'Dusrem/aniki karon:',
+'protect-otherreason-op' => 'Dusrem karon',
+
+# Undelete
+'undeletelink' => 'polloi/adlea zagear hadd',
+'undeleteviewlink' => 'polloi',
+
+# Namespace form on various pages
+'namespace' => 'Nanv-tholl',
+'invert' => 'Nonddni urfattoi',
+'blanknamespace' => '(Mukhel)',
+
+# Contributions
+'contributions' => '{{GENDER:$1|Vapuddpi}} borovpam',
+'contributions-title' => '$1 hea vapuddpean kelelim borovpam',
+'mycontris' => 'Borovpam',
+'contribsub2' => '{{GENDER:$3|$1}} hacheo ($2)',
+'uctop' => '(atachem)',
+'month' => 'Mhoinea savn (ani adichem):',
+'year' => 'Hea vorsa savn (ani adichem):',
+
+'sp-contributions-newbies' => 'Fokot novea khateachim borovpam dakhoi',
+'sp-contributions-blocklog' => 'addavnniache sotr',
+'sp-contributions-uploads' => 'upload',
+'sp-contributions-logs' => 'sotr',
+'sp-contributions-talk' => 'bhasabhas',
+'sp-contributions-search' => 'Borovpam sod',
+'sp-contributions-username' => 'Antorzall namo vo vapuddpeachem nanv:',
+'sp-contributions-toponly' => 'Fokot halincheo uzollnneo dakhoi',
+'sp-contributions-submit' => 'Sod',
+
+# What links here
+'whatlinkshere' => 'Hanga kitem zoddta',
+'whatlinkshere-title' => '"$1" haka zoddlelim panam',
+'whatlinkshere-page' => 'Pan:',
+'linkshere' => "Sokoilim panam '''[[:$1]]''' ak zoddtat:",
+'nolinkshere' => "Khoincheim pan '''[[:$1]]''' ak zoddna.",
+'isredirect' => 'Porot dixen pan dhadd',
+'istemplate' => 'Durasth-somaves',
+'isimage' => 'failichem zoddop',
+'whatlinkshere-prev' => '{{PLURAL:$1|adlem|adlem $1}}',
+'whatlinkshere-next' => '{{PLURAL:$1|fuddlem|fuddlim $1}}',
+'whatlinkshere-links' => '← zoddpam',
+'whatlinkshere-hideredirs' => '$1 porot dixen',
+'whatlinkshere-hidetrans' => '$1 durasth-somaves',
+'whatlinkshere-hidelinks' => '$1 zoddpam',
+'whatlinkshere-hideimages' => 'Failinchim zoddpam $1',
+'whatlinkshere-filters' => 'Challnio',
+
+# Block/unblock
+'ipboptions' => '2 voram:2 hours,1 dis:1 day,3 dis:3 days,1 satollo:1 week,2 satolle:2 weeks,1 mhoino:1 month,3 mhoine:3 months,6 mhoine:6 months,1 voros:1 year,sasnnank:infinite',
+'ipbotherreason' => 'Dusrem/aniki karon:',
+'ipblocklist' => 'Addhailele vapuddpi',
+'blocklink' => 'addavnnni',
+'unblocklink' => 'Addavnni kadd',
+'change-blocklink' => 'Addavnnni bodol',
+'contribslink' => 'borovp',
+'blocklogpage' => 'addavnnechem sotr',
+'blocklogentry' => '[[$1]] addailelem $2 asun vellacho ont: $3',
+'block-log-flags-nocreate' => 'Khatem rochop opatr kelam',
+
+# Move page
+'move-watch' => 'Hea panar disht dovor',
+'movelogpage' => 'Sotr bodol',
+'movereason' => 'Karonn:',
+'revertmove' => 'porti',
+
+# Export
+'export' => 'Panank niryat kor',
+
+# Namespace 8 related
+'allmessagesname' => 'Nanv',
+'allmessagesdefault' => 'Falta sondex mozkur',
+
+# Thumbnails
+'thumbnail-more' => 'vaddoi',
+'thumbnail_error' => 'Lhan-imaz toiar kortana chuk zali. Karonn: $1',
+
+# Tooltip help for the actions
+'tooltip-pt-userpage' => 'Tujem vapuddpachem pan',
+'tooltip-pt-mytalk' => 'Tumchem ulovpachem pan',
+'tooltip-pt-preferences' => 'Tumcheo avddi',
+'tooltip-pt-watchlist' => 'Bodlachea dekhrekh korpachea panachi volleri',
+'tooltip-pt-mycontris' => 'Tujea borovpanchi suchi',
+'tooltip-pt-login' => 'Tumkam sotrrombh korunk protsavan asa; em soktichem nhoi',
+'tooltip-pt-logout' => 'Sotracho xevott',
+'tooltip-ca-talk' => 'Mozkurachea vixoiavoir bhasabhas',
+'tooltip-ca-edit' => 'Tumchean hem pan sudarunk zata. Upkar korun sambhallche adim zholok butanv vapor',
+'tooltip-ca-addsection' => 'Novo vibhag suru kor',
+'tooltip-ca-viewsource' => 'Hem pan rakhun dovorlam.
+Tujean tachem mull pollonv ieta',
+'tooltip-ca-history' => 'Hea panacheo adleo uzollnneo',
+'tooltip-ca-protect' => 'Hem pan rakh',
+'tooltip-ca-delete' => 'Hem pan vogllai',
+'tooltip-ca-move' => 'Hem pan fuddem vhor',
+'tooltip-ca-watch' => 'Hem pan tujea sadurvollerint zodd',
+'tooltip-ca-unwatch' => 'Hem pan tumchea sadurtaievelean kadd',
+'tooltip-search' => '{{SITENAME}}  sod',
+'tooltip-search-go' => 'Hea just nanvachem pan asa zalear tea panar voch',
+'tooltip-search-fulltext' => 'Hea utarea khatir pana sod',
+'tooltip-p-logo' => 'Mukhel panak bhett di',
+'tooltip-n-mainpage' => 'Mukhel panak bhett di',
+'tooltip-n-mainpage-description' => 'Mukhel Panak bhett di',
+'tooltip-n-portal' => 'Hea prokolpa vixim, tumchean kitem korum ieta, khoim kitem sodchem',
+'tooltip-n-currentevents' => 'Chalu ghoddneachea fattbhuichi mahiti sod',
+'tooltip-n-recentchanges' => 'Wikint halinch bodol keleleachi volleri',
+'tooltip-n-randompage' => 'Khoincheim ek pan uktem kor',
+'tooltip-n-help' => 'sodpachem tholl',
+'tooltip-t-whatlinkshere' => 'Hanga zoddlelea sogllea wiki pananchi volleri',
+'tooltip-t-recentchangeslinked' => 'Hea panak zoddlelea panachim halinchim bodolpam',
+'tooltip-feed-atom' => 'Hea panak Atom purovnni',
+'tooltip-t-contributions' => 'Hea vapuddpeachea borovpanchi suchi',
+'tooltip-t-emailuser' => 'Hea vapuddpeak email patthoi',
+'tooltip-t-upload' => 'Faili upload kor',
+'tooltip-t-specialpages' => 'Kherit pananchi volleri',
+'tooltip-t-print' => 'Hea panachem chhapunk ietelem rupantor',
+'tooltip-t-permalink' => 'Hea panache uzollnnek togpi zodd',
+'tooltip-ca-nstab-main' => 'Suchi pan polloi',
+'tooltip-ca-nstab-user' => 'Vapuddpeachem pan polloi',
+'tooltip-ca-nstab-special' => 'Hem ek kherit pan, tujeam hem pan sudarunk zaina',
+'tooltip-ca-nstab-project' => 'Project-achem pan polloi',
+'tooltip-ca-nstab-image' => 'Failichem pan polloi',
+'tooltip-ca-nstab-template' => 'Saacho polloi',
+'tooltip-ca-nstab-category' => 'Vorgachem pan polloi',
+'tooltip-minoredit' => 'Haka ek kirkoll sudharop mhunn khunnay',
+'tooltip-save' => 'Tujim bodolpam sambhall',
+'tooltip-preview' => 'Bodlopanchi zholok polloi, upkar hem samballche adim vapor!',
+'tooltip-diff' => 'Tumi hea mozkurant kelelo bodol dakhoiat',
+'tooltip-compareselectedversions' => 'Hea panacheo don nivoddleleo uzollneo modem forok polloi',
+'tooltip-watch' => 'Hem pan tujea xadurvollerint zodd',
+'tooltip-rollback' => '"Kovllop" hea panachea xevttachea borovpa kodde kea kollant portota.',
+'tooltip-undo' => '"Rodd\' kor" sudharop portita ani sudharopak Zholok ritin ukodta. Tem saran karon zoddunk dita.',
+'tooltip-summary' => 'Mottvo sar ghal',
+
+# Browsing diffs
+'previousdiff' => '←  Adlo sudar',
+'nextdiff' => 'Fuddlem bodlop →',
+
+# Media information
+'file-info-size' => '$1 × $2 pixelam, failicho akar: $3, MIME prokar: $4',
+'file-nohires' => 'Odhik bariksai na.',
+'svg-long-desc' => 'SVG fail, nanvak $1 × $2 pixeli, failcho akar: $3',
+'show-big-image' => 'Akhi bariksai',
+
+# Bad image list
+'bad_image_list' => 'Akar oso asa:
+
+Fokot suchicheo vostu (*-chean suru zateleo) dheanant ghevcheo
+Tech vollir, uprantli zoddnni opvad dhorpant ieta,mhonnche zoim pana failichea ek vollint asunk xoktat.',
+
+# Metadata
+'metadata' => 'Metadata',
+'metadata-help' => "Hea failin anikui mahiti asa, hi fail korunk uzar kel'le digital camera vo scanner sun tem aila zait.
+Zori tor hi failik bodol'lam zalear kai mahiti bodololem failik sarkem mell khaina zait.",
+'metadata-fields' => 'Metadata chovkott konsollttoch, hea sondexant metadata molleantlea murtipanachea dakhovnnent aspav astolo.
+* make
+* model
+* datetimeoriginal
+* exposuretime
+* fnumber
+* isospeedratings
+* focallength
+* artist
+* copyright
+* imagedescription
+* gpslatitude
+* gpslongitude
+* gpsaltitude',
+
+# External editor support
+'edit-externally' => 'Hea failik bhaili program uzar korun bodol.',
+'edit-externally-help' => '(Odhik mahite khatir [//www.mediawiki.org/wiki/Manual:External_editors setup instructions] polloi)',
+
+# 'all' in various places, this might be different for inflected languages
+'watchlistall2' => 'soglle',
+'namespacesall' => 'sogllem',
+'monthsall' => 'sogllem',
+
+# Watchlist editing tools
+'watchlisttools-view' => 'Sombondhi bodlopam polloi',
+'watchlisttools-edit' => 'Sadurvolleri polloi ani sudar',
+'watchlisttools-raw' => 'Sadurvollerichi mull-an bodol kor',
+
+# Core parser functions
+'duplicate-defaultsort' => "'''Chotrai:''' Falta anukraman mukhel ''$2'' rodd korta adhlem falta anukraman mukhel ''$1'', haka.",
+
+# Special:SpecialPages
+'specialpages' => 'Khaxelim Panam',
+
+# External image whitelist
+'external_image_whitelist' => " #Hi voll asa toxich dovor<pre>
+#Khala sodpache sache (''regular expressions'') ghal (fokot // modem voita poi tem bhag)
+#Hanche borobor bhaile zodlele murt comparar kel'le zatele
+#Mell khatat tim murt koxeo distele, na zalear fokot mortek ek zodd distele
+#Jeo voll #-an suru zatele tem vivek mhunn manlele zatele
+#Hanga vhodle and dhakte okxora modem forok podona
+
+#Soglle sodpache sache hea volla voir ghal. Hi voll asa toxich dovor</pre>",
+
+# Special:Tags
+'tag-filter' => '[[Special:Tags|Dospi]] challni:',
+
+);
index 770bd89..32f816e 100644 (file)
@@ -13,6 +13,7 @@
  * @author Jetlag
  * @author Mnemonic kek
  * @author Urhixidur
+ * @author Xiaomingyan
  */
 
 $messages = array(
@@ -1809,7 +1810,7 @@ Please visit [//www.mediawiki.org/wiki/Localisation MediaWiki Localisation] and
 'file-info-size' => '$1 × $2像素,文件大小:$3,MIME類型:$4',
 'file-nohires' => '無做得提供嘅還較高分辨率。',
 'svg-long-desc' => 'SVG文件,尺寸:$1×$2像素,文件大細:$3',
-'show-big-image' => '完分辨率',
+'show-big-image' => '完分辨率',
 
 # Special:NewFiles
 'newimages' => 'Sîn-kien thù-chhiong ke va̍k-lòng',
index 6ead023..a617a02 100644 (file)
@@ -820,7 +820,7 @@ $2',
 'gotaccount' => 'כבר נרשמתם? $1.',
 'gotaccountlink' => 'כניסה לחשבון',
 'userlogin-resetlink' => 'שכחת את פרטי הכניסה?',
-'userlogin-resetpassword-link' => '×\90×\99פ×\95ס ×\94ס×\99ס×\9e×\94',
+'userlogin-resetpassword-link' => 'ש×\9b×\97ת ×\90ת ×\94ס×\99ס×\9e×\94?',
 'helplogin-url' => 'Help:כניסה לחשבון',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|עזרה בכניסה לחשבון]]',
 'userlogin-loggedin' => 'אתם כבר מחוברים לחשבון {{GENDER:$1|$1}}.
@@ -898,8 +898,8 @@ $2',
 'acct_creation_throttle_hit' => 'מבקרים באתר זה דרך כתובת ה־IP שלכם כבר יצרו {{PLURAL:$1|חשבון אחד|$1 חשבונות}} ביום האחרון. זהו המקסימום המותר בתקופה זו.
 לפיכך, מבקרים דרך כתובת ה־IP הזו לא יכולים ליצור חשבונות נוספים ברגע זה.',
 'emailauthenticated' => 'כתובת הדוא"ל שלך אומתה ב־$3, $2.',
-'emailnotauthenticated' => '×\9bת×\95×\91ת ×\94×\93×\95×\90\9c ×©×\9c×\9b×\9d ×¢×\93×\99×\99×\9f ×\9c×\90 ×\90×\95שרה.
\9c×\90 ×\99×\99ש×\9c×\97 ×\90×\9c×\99×\9b×\9d ×\93×\95×\90\9c ×¢×\91×\95ר ×\90×£ ×\90×\97ת ×\9e×\94×\90פשר×\95×\99ות הבאות.',
+'emailnotauthenticated' => '×\9bת×\95×\91ת ×\94×\93×\95×\90\9c ×©×\9c×\9b×\9d ×¢×\93×\99×\99×\9f ×\9c×\90 ×\90×\95×\9eתה.
\9c×\90 ×\99×\99ש×\9c×\97 ×\90×\9c×\99×\9b×\9d ×\93×\95×\90\9c ×¢×\91×\95ר ×\90×£ ×\90×\97ת ×\9e×\94ת×\9b×\95× ות הבאות.',
 'noemailprefs' => 'אנא ציינו כתובת דוא"ל בהעדפות שלכם כדי שתכונות אלה יעבדו.',
 'emailconfirmlink' => 'אישור כתובת הדוא"ל שלך',
 'invalidemailaddress' => 'כתובת הדוא"ל אינה מתקבלת כיוון שנראה שהיא בפורמט לא נכון.
@@ -2249,7 +2249,7 @@ $1',
 כל שורה מכילה קישור לשתי ההפניות הראשונות, וכן את היעד של ההפניה השנייה, שהיא לרוב היעד ה"אמיתי" של ההפניה, שההפניה הראשונה אמורה להצביע אליו.
 פריטים <del>מחוקים</del> כבר תוקנו.',
 'double-redirect-fixed-move' => '[[$1]] הועבר.
\9bעת ×\96×\90ת הפניה לדף [[$2]].',
\9bעת ×\96×\95 הפניה לדף [[$2]].',
 'double-redirect-fixed-maintenance' => 'תיקון הפניה כפולה מ[[$1]] ל[[$2]].',
 'double-redirect-fixer' => 'מתקן הפניות',
 
index 8b76026..54f931c 100644 (file)
@@ -377,7 +377,7 @@ $messages = array(
 'newwindow' => '(opnast í nýjum glugga)',
 'cancel' => 'Hætta við',
 'moredotdotdot' => 'Meira...',
-'morenotlisted' => 'fleiri ekki skráð...',
+'morenotlisted' => 'Þessi listi er ekki tæmandi.',
 'mypage' => 'Síða',
 'mytalk' => 'Spjall',
 'anontalk' => 'Spjallsíða þessa vistfangs.',
@@ -480,7 +480,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Um {{SITENAME}}',
 'aboutpage' => 'Project:Um verkefnið',
-'copyright' => 'Efni má nota samkvæmt $1.',
+'copyright' => 'Efni má nota samkvæmt $1 nema kemur fram annars.',
 'copyrightpage' => '{{ns:project}}:Höfundarréttur',
 'currentevents' => 'Potturinn',
 'currentevents-url' => 'Project:Potturinn',
@@ -562,6 +562,7 @@ Sjá [[Special:Version|útgáfusíðuna]].',
 # General errors
 'error' => 'Villa',
 'databaseerror' => 'Gagnagrunnsvilla',
+'databaseerror-error' => 'Villa: $1',
 'laggedslavemode' => 'Viðvörun: Síðan inniheldur ekki nýjustu uppfærslur.',
 'readonly' => 'Gagnagrunnur læstur',
 'enterlockreason' => 'Gefðu fram ástæðu fyrir læsingunni, og einnig áætlun
@@ -650,6 +651,7 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'yourname' => 'Notandanafn:',
 'userlogin-yourname' => 'Notandanafn',
 'userlogin-yourname-ph' => 'Skrifaðu inn notendanafnið þitt',
+'createacct-another-username-ph' => 'Skrifaðu inn notendanafnið',
 'yourpassword' => 'Lykilorð:',
 'userlogin-yourpassword' => 'Lykilorð',
 'userlogin-yourpassword-ph' => 'Skrifaðu niður lykilorðið þitt',
@@ -682,10 +684,15 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'userlogin-resetpassword-link' => 'Endursetja lykilorð',
 'helplogin-url' => 'Help:Innskráning',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hjálp við innskráningu]]',
+'userlogin-loggedin' => 'Þú ert búin(n) að skrá þig inn sem {{GENDER:$1|$1}}.
+Notaðu eyðablaðið fyrir neðan til að skrá þig inn sem annar notandi.',
+'userlogin-createanother' => 'Stofna annan aðgang',
 'createacct-join' => 'Sláðu inn þínar upplýsingar fyrir neðan.',
+'createacct-another-join' => 'Skrifaðu upplýsingar um nýja aðganginn fyrir neðan.',
 'createacct-emailrequired' => 'Netfang',
 'createacct-emailoptional' => 'Netfang (valfrjálst)',
 'createacct-email-ph' => 'Skrifaðu niður netfangið þitt',
+'createacct-another-email-ph' => 'Skrifaðu netfang',
 'createaccountmail' => 'Nota handahófsvalið bráðabirgðalykilorð og senda það á netfangið sem er tilgreint hér fyrir neðan',
 'createacct-realname' => 'Raunverulegt nafn (valfrjálst)',
 'createaccountreason' => 'Ástæða:',
@@ -694,6 +701,7 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'createacct-captcha' => 'Öryggis athugun',
 'createacct-imgcaptcha-ph' => 'Sláðu inn textann að ofan',
 'createacct-submit' => 'Búa til aðganginn',
+'createacct-another-submit' => 'Stofna annan aðgang',
 'createacct-benefit-heading' => '{{SITENAME}} er skrifuð af fólki eins og þér.',
 'createacct-benefit-body1' => '{{PLURAL:$1|breyting|breytingar}}',
 'createacct-benefit-body2' => '{{PLURAL:$1|síða|síður}}',
@@ -763,10 +771,11 @@ Gjörðu svo vel og settu inn netfang á gildu formi eða tæmdu reitinn.',
 Þú getur hunsað þessi skilaboð, ef villa hefur átt sér stað.',
 'usernamehasherror' => 'Notendanöfn mega ekki innihalda kassa (#)',
 'login-throttled' => 'Þér hefur mistekist að skrá þig inn undir þessu notendanafni of oft.
-Vinsamlegast reynið aftur síðar.',
+Vinsamlegast bíðið $1 áður en þú reynir aftur.',
 'login-abort-generic' => 'Innskráningin misheppnaðist - hætt var við hana.',
 'loginlanguagelabel' => 'Tungumál: $1',
 'suspicious-userlogout' => 'Beiðni um útskráningu hafnað því hún var líklegast send frá biluðum vafra eða vefseli sem hefur vistað vefsíðuna í flýtiminni.',
+'createacct-another-realname-tip' => 'Alvöru nafn er valfrjálst. Ef þú kýst að gefa það upp, verður það notað til að gefa þér heiður af verkum þínum.',
 
 # Email sending
 'php-mail-error-unknown' => 'Óþekkt villa í PHP mail() aðgerð.',
@@ -783,7 +792,7 @@ Til að klára að skrá þig inn, verður þú að endurstilla lykilorðið hé
 'newpassword' => 'Nýja lykilorðið',
 'retypenew' => 'Endurtaktu nýja lykilorðið:',
 'resetpass_submit' => 'Skrifaðu aðgangsorðið og skráðu þig inn',
-'changepassword-success' => 'Aðgangsorðinu þínu hefur verið breytt! Skráir þig inn...',
+'changepassword-success' => 'Það tókst að breyta lykilorðinu þínu!',
 'resetpass_forbidden' => 'Ekki er hægt að breyta lykilorðum',
 'resetpass-no-info' => 'Þú verður að vera skráð(ur) inn til að hafa aðgang að þessari síðu.',
 'resetpass-submit-loggedin' => 'Breyta lykilorði',
@@ -838,6 +847,18 @@ Tímabundið lykilorð: $2',
 'changeemail-submit' => 'Breyta netfangi',
 'changeemail-cancel' => 'Hætta við',
 
+# Special:ResetTokens
+'resettokens' => 'Endurstilla lykla',
+'resettokens-text' => 'Hér getur þú endurstillt lykla sem veita þér aðgang að ákveðnum persónuupplýsingum um aðganginn þinn.
+
+Þú átt að gera það ef þú ert búin(n) að deila þeim með einhverjum öðrum óviljandi eða ef búið er að brjóta inn í aðganginn þinn.',
+'resettokens-no-tokens' => 'Það eru engir lyklar að endurstilla.',
+'resettokens-legend' => 'Endurstilla lykla',
+'resettokens-tokens' => 'Lyklar:',
+'resettokens-token-label' => '$1 (núverandi gildi: $2)',
+'resettokens-done' => 'Lyklarnir hafa verið endurstilltir.',
+'resettokens-resetbutton' => 'Endurstilla valda lykla',
+
 # Edit page toolbar
 'bold_sample' => 'Feitletraður texti',
 'bold_tip' => 'Feitletraður texti',
@@ -1017,7 +1038,7 @@ Verndunarskrá síðunnar er gefin fyrir neðan til tilvísunar.",
 'nocreate-loggedin' => 'Þú hefur ekki leyfi til að skapa nýjar síður.',
 'sectioneditnotsupported-title' => 'Hlutabreyting er ekki virk',
 'sectioneditnotsupported-text' => 'Hlutabreyting er ekki virk á þessari síðu.',
-'permissionserrors' => 'Leyfisvillur',
+'permissionserrors' => 'Leyfisvilla',
 'permissionserrorstext' => 'Þú hefur ekki leyfi til að gera þetta, af eftirfarandi {{PLURAL:$1|ástæðu|ástæðum}}:',
 'permissionserrorstext-withaction' => 'Þú hefur ekki réttindi til að $2, af eftirfarandi {{PLURAL:$1|ástæðu|ástæðum}}:',
 'recreate-moveddeleted-warn' => "'''Viðvörun: Þú ert að endurskapa síðu sem áður hefur verið eytt.'''
@@ -1076,6 +1097,7 @@ Hluti sniðsins verður ekki með.",
 'undo-failure' => 'Breytinguna var ekki hægt að taka tilbaka vegna breytinga í millitíðinni.',
 'undo-norev' => 'Ekki var hægt að taka breytinguna aftr því að hún er ekki til eða henni var eytt.',
 'undo-summary' => 'Tek aftur breytingu $1 frá [[Special:Contributions/$2|$2]] ([[User talk:$2|spjall]])',
+'undo-summary-username-hidden' => 'Afturkalla breytingu $1 eftir faldan notanda',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Ekki hægt að búa til aðgang',
@@ -1104,7 +1126,7 @@ Skýringartexti: (nú) = skoðanamunur á núverandi útgáfu,
 'history-fieldset-title' => 'Skoða breytingaskrá',
 'history-show-deleted' => 'Eingöngu eyddar breytingar',
 'histfirst' => 'elstu',
-'histlast' => 'yngstu',
+'histlast' => 'nýjustu',
 'historysize' => '({{PLURAL:$1|1 bæti|$1 bæti}})',
 'historyempty' => '(tóm)',
 
@@ -1168,15 +1190,15 @@ Frekari upplýsingar eru í [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGE
 * Óviðeigandi persónulegar upplýsingar
 *: ''heimilisfang, símanúmer, kennitala, osfrv.''",
 'revdelete-legend' => 'Setja sjáanlegar hamlanir',
-'revdelete-hide-text' => 'Fela breytingatexta',
+'revdelete-hide-text' => 'Breytingatexti',
 'revdelete-hide-image' => 'Fela efni skráar',
 'revdelete-hide-name' => 'Fela aðgerð og mark',
-'revdelete-hide-comment' => 'Fela breytingarágrip',
-'revdelete-hide-user' => 'Fela notandanafn/vistfang',
+'revdelete-hide-comment' => 'Breytingarágrip',
+'revdelete-hide-user' => 'Notandanafn/vistfang',
 'revdelete-hide-restricted' => 'Dylja gögn frá stjórnendum og öðrum',
 'revdelete-radio-same' => '(ekki breyta)',
-'revdelete-radio-set' => '',
-'revdelete-radio-unset' => 'Nei',
+'revdelete-radio-set' => 'Sjáanlegt',
+'revdelete-radio-unset' => 'Falið',
 'revdelete-suppress' => 'Dylja gögn frá stjórnendum og öðrum',
 'revdelete-unsuppress' => 'Fjarlægja takmarkanir á endurvöktum breytingum',
 'revdelete-log' => 'Ástæða:',
@@ -1258,6 +1280,7 @@ Athugaðu að með því að nota flakktenglana er þessi dálkur endurstilltur.
 'compareselectedversions' => 'Bera saman valdar útgáfur',
 'showhideselectedversions' => 'Sýna/fela valdar breytingar',
 'editundo' => 'Taka aftur þessa breytingu',
+'diff-empty' => '(Enginn munur)',
 'diff-multi' => '({{PLURAL:$1|Ein millibreyting ekki sýnd|$1 millibreytingar ekki sýndar}} frá {{PLURAL:$2|notanda|$2 notendum}}.)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Ein millibreyting ekki sýnd|$1 millibreytingar ekki sýndar}} frá fleiri en {{PLURAL:$2|einum notanda|$2 notendum}}.)',
 'difference-missing-revision' => '{{PLURAL:$2|Ein útgáfa|$2 útgáfur}} samanburðarins ($1) {{PLURAL:$2|fannst|fundust}} ekki.
@@ -1359,7 +1382,7 @@ Athugaðu að skrár þeirra yfir {{SITENAME}}-efni kunna að vera úreltar.',
 'prefs-rendering' => 'Útlit',
 'saveprefs' => 'Vista',
 'resetprefs' => 'Endurstilla valmöguleika',
-'restoreprefs' => 'Endurheimta allar stillingar',
+'restoreprefs' => 'Endurstilla allar sjálfgefnar stillingar (í öllum hlutum)',
 'prefs-editing' => 'Breytingarflipinn',
 'rows' => 'Raðir',
 'columns' => 'Dálkar',
@@ -1415,10 +1438,10 @@ Ekki er hægt að taka þessa breytingu til baka.',
 'badsig' => 'Ógild hrá undirskrift. Athugaðu HTML-kóða.',
 'badsiglength' => 'Undirskriftin er of löng.
 Hún þarf að vera færri en $1 {{PLURAL:$1|stafur|stafir}}.',
-'yourgender' => 'Kyn:',
-'gender-unknown' => 'Ã\93skilgreint',
-'gender-male' => 'Karl',
-'gender-female' => 'Kona',
+'yourgender' => 'Hvernig vilt þú helst lýsa þér?',
+'gender-unknown' => 'Ã\89g vil heldur ekki gefa upp',
+'gender-male' => 'Hann breytir wikisíðum',
+'gender-female' => 'Hún breytir wikisíðum',
 'prefs-help-gender' => 'Valfrjálst: notað til að aðgreina kynin í meldingum hugbúnaðarins. Þessar upplýsingar verða aðgengilegar öllum.',
 'email' => 'Tölvupóstur',
 'prefs-help-realname' => 'Alvöru nafn er valfrjálst.
@@ -1432,7 +1455,9 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'prefs-signature' => 'Undirskrift',
 'prefs-dateformat' => 'Dagasnið',
 'prefs-timeoffset' => 'Tímamismunur',
-'prefs-advancedediting' => 'Háþróaðir möguleikar',
+'prefs-advancedediting' => 'Almennir valkostir',
+'prefs-editor' => 'Ritsjóri',
+'prefs-preview' => 'Forskoðun',
 'prefs-advancedrc' => 'Háþróaðir möguleikar',
 'prefs-advancedrendering' => 'Háþróaðir möguleikar',
 'prefs-advancedsearchoptions' => 'Háþróaðir möguleikar',
@@ -1440,7 +1465,9 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'prefs-displayrc' => 'Útlitsmöguleikar',
 'prefs-displaysearchoptions' => 'Útlitsmöguleikar',
 'prefs-displaywatchlist' => 'Útlitsmöguleikar',
+'prefs-tokenwatchlist' => 'Lykill',
 'prefs-diffs' => 'Breytingar',
+'prefs-help-prefershttps' => 'Þessi stilling tekur gildi í næsta skiptið sem þú skráir inn.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'Netfang virðist vera virkt.',
@@ -1464,9 +1491,11 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'userrights-no-interwiki' => 'Þú hefur ekki leyfi til að breyta notandaréttindum á öðrum wiki-síðum.',
 'userrights-nodatabase' => 'Gagnagrunnurinn $1 er ekki til eða ekki staðbundinn.',
 'userrights-nologin' => 'Þú verður að [[Special:UserLogin|innskrá]] þig á möppudýraaðgang til að geta útdeilt notandaréttindum.',
-'userrights-notallowed' => 'Þinn aðgangur hefur ekki réttindi til að útdeila notandaréttindum.',
+'userrights-notallowed' => 'Þú hefur ekki réttindi til að útdeila eða draga til baka notandaréttini.',
 'userrights-changeable-col' => 'Hópar sem þú getur breytt',
 'userrights-unchangeable-col' => 'Hópar sem þú getur ekki breytt',
+'userrights-conflict' => 'Árekstur í að breyta notandaréttindum! Vinsamlegast skoðaðu aftur og staðfestu breytingar þínar.',
+'userrights-removed-self' => 'Þér hefur tekist að fjarlægja þín eigin réttindi. Vegna þess mátt þú ekki lengur skoða þessa síðu.',
 
 # Groups
 'group' => 'Hópur:',
@@ -1532,13 +1561,19 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'right-proxyunbannable' => 'Sneiða hjá sjálfvirkum proxy-bönnum',
 'right-unblockself' => 'Afbanna sjálfan sig',
 'right-protect' => 'Breyta verndunarstigi og breyta vernduðum síðum',
-'right-editprotected' => 'Breyta verndaðar síður (án keðjuverndunar)',
+'right-editprotected' => 'Breyta síðum vernduðum sem „{{int:protect-level-sysop}}“',
+'right-editsemiprotected' => 'Breyta síðum vernduðum sem „{{int:protect-level-autoconfirmed}}“',
 'right-editinterface' => 'Breyta notandaviðmótinu',
 'right-editusercssjs' => 'Breyta CSS- og JS-skrám annarra',
 'right-editusercss' => 'Breyta CSS-skrám annarra',
 'right-edituserjs' => 'Breyta JS-skrám annarra',
 'right-editmyusercss' => 'Breyta þinni eigin CSS-notandaskrá',
 'right-editmyuserjs' => 'Breyta þinni eigin JavaScript-notandaskrá',
+'right-viewmywatchlist' => 'Skoða þinn eigin vaktlista',
+'right-editmywatchlist' => 'Breyta þínum eigin vaktlista. Athugið að nokkrar aðgerðir bæta enn við síður án þessa réttindis.',
+'right-viewmyprivateinfo' => 'Skoða þínar eigin persónuupplýsingar (t.d. netfang, alvörunafn)',
+'right-editmyprivateinfo' => 'Breyta þínum eigin persónuupplýsingum (t.d. netfangi, alvörunafni)',
+'right-editmyoptions' => 'Breyta þínum eigin stillingum',
 'right-rollback' => 'Taka snögglega aftur breytingar síðasta notanda sem breytti síðunni',
 'right-markbotedits' => 'Merkja endurtektar breytingar sem vélmennabreytingar',
 'right-noratelimit' => 'Sneiða hjá takmörkunum',
@@ -1590,8 +1625,8 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'action-block' => 'Banna notandanum að gera breytingar',
 'action-protect' => 'breyta verndunarstigum fyrir þessa síðu',
 'action-rollback' => 'Taka snögglega aftur breytingar síðasta notanda sem breytti ákveðinni síðu',
-'action-import' => 'Flytja inn þessa skrá frá öðrum wiki',
-'action-importupload' => 'Flytja inn þessa síðu frá skráar upphali',
+'action-import' => 'flytja inn síður frá öðrum wiki',
+'action-importupload' => 'flytja inn síður frá skráarupphali',
 'action-patrol' => 'Merkja breytingar annara sem yfirfarnar',
 'action-autopatrol' => 'Merkja eigin breytingu sem yfirfarna',
 'action-unwatchedpages' => 'Skoða lista yfir óvaktaðar síður',
@@ -1600,12 +1635,19 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'action-userrights-interwiki' => 'breyta notandaréttindum annarra notenda á öðrum wiki-verkefnum',
 'action-siteadmin' => 'læsa eða opna gagnagrunninn',
 'action-sendemail' => 'senda tölvupósta',
+'action-editmywatchlist' => 'breyta vaktlistanum þínum',
+'action-viewmywatchlist' => 'skoða vaktlistann þinn',
+'action-viewmyprivateinfo' => 'skoða persónuupplýsingar þínar',
+'action-editmyprivateinfo' => 'breyta persónuupplýsingum þínum',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|breyting|breytingar}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|síðan síðustu heimsókn}}',
+'enhancedrc-history' => 'breytingaskrá',
 'recentchanges' => 'Nýlegar breytingar',
 'recentchanges-legend' => 'Stillingar nýlegra breytinga',
 'recentchanges-summary' => 'Hér geturðu fylgst með nýjustu breytingunum.',
+'recentchanges-noresult' => 'Engar breytingar í uppgefna tímabilinu sem passa við þessa mælikvarða.',
 'recentchanges-feed-description' => 'Hér er hægt að fylgjast með nýlegum breytingum á {{SITENAME}}.',
 'recentchanges-label-newpage' => 'Þessi breyting skapaði nýja síðu',
 'recentchanges-label-minor' => 'Þetta er minniháttar breyting',
@@ -1633,7 +1675,7 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'rc_categories_any' => 'Alla',
 'rc-change-size-new' => '$1 {{PLURAL:$1|bæt|bæti}} eftir breytingu',
 'newsectionsummary' => 'Nýr hluti: /* $1 */',
-'rc-enhanced-expand' => 'Sýna upplýsingar (þarfnast JavaScript)',
+'rc-enhanced-expand' => 'Sýna upplýsingar',
 'rc-enhanced-hide' => 'Fela ítarefni',
 'rc-old-title' => 'Upphaflega búin til undir nafninu "$1"',
 
@@ -1653,8 +1695,7 @@ Síður á [[Special:Watchlist|vaktlistanum þínum]] eru '''feitletraðar'''.",
 'reuploaddesc' => 'Aftur á innhlaðningarformið.',
 'upload-tryagain' => 'Sendu breytta myndlýsingu',
 'uploadnologin' => 'Óinnskráð(ur)',
-'uploadnologintext' => 'Þú verður að vera [[Special:UserLogin|skráð(ur) inn]]
-til að hlaða inn skrám.',
+'uploadnologintext' => 'Þú verður $1 til að hala upp skrár.',
 'upload_directory_missing' => 'Mappa upphlaða ($1) er týnd og vefþjónninn gat ekki búið hana til.',
 'upload_directory_read_only' => 'Mistókst að skrifa í möppu upphlaða ($1) á vefþjóni.',
 'uploaderror' => 'Villa í innhlaðningu',
@@ -1893,8 +1934,7 @@ Athugaðu hvort síðan sé aðgengileg, bíddu í smástund og reyndu aftur.
 'upload_source_file' => '(skrá á tölvunni þinni)',
 
 # Special:ListFiles
-'listfiles-summary' => 'Þessi kerfissíða sýnir allar upphlaðnar skrár.
-Þegar hún er síuð ákveðnu notendanafni birtast eingöngu myndir frá honum.',
+'listfiles-summary' => 'Þessi kerfissíða sýnir allar upphlaðnar skrár.',
 'listfiles_search_for' => 'Leita að miðilsnafni:',
 'imgfile' => 'skrá',
 'listfiles' => 'Skráalisti',
@@ -1905,6 +1945,10 @@ Athugaðu hvort síðan sé aðgengileg, bíddu í smástund og reyndu aftur.
 'listfiles_size' => 'Stærð (bæti)',
 'listfiles_description' => 'Lýsing',
 'listfiles_count' => 'Útgáfur',
+'listfiles-show-all' => 'Taka með gamlar útgáfur af myndum',
+'listfiles-latestversion' => 'Núverandi útgáfa',
+'listfiles-latestversion-yes' => 'Já',
+'listfiles-latestversion-no' => 'Nei',
 
 # File description page
 'file-anchor-link' => 'Skrá',
@@ -2001,6 +2045,13 @@ Leitarstrengurinn á að vera á þessu formi: efnistag/myndasnið, t.d. <code>i
 'randompage' => 'Handahófsvalin grein',
 'randompage-nopages' => 'Það eru engar síður í {{PLURAL:$2|nafnrýminu|nafnrýmunum}}: $1.',
 
+# Random page in category
+'randomincategory' => 'Handhófsvalin síða í flokki',
+'randomincategory-invalidcategory' => '„$1“ er ekki gilt flokkarheiti',
+'randomincategory-nopages' => 'Það eru engar síður í flokkinum [[:Category:$1|$1]].',
+'randomincategory-selectcategory' => 'Fá handhófsvalda síðu úr flokkinum: $1 $2.',
+'randomincategory-selectcategory-submit' => 'Fara',
+
 # Random redirect
 'randomredirect' => 'Handahófsvalin tilvísun',
 'randomredirect-nopages' => 'Það eru engar tilvísanir í nafnrýminu „$1“.',
@@ -2026,6 +2077,10 @@ Leitarstrengurinn á að vera á þessu formi: efnistag/myndasnið, t.d. <code>i
 'statistics-users-active-desc' => 'Notendur sem hafa framkvæmt aðgerð {{PLURAL:$1|síðastliðin dag|síðastliðna $1 daga}}',
 'statistics-mostpopular' => 'Mest skoðuðu síður',
 
+'pageswithprop' => 'Síður með eiginleika',
+'pageswithprop-legend' => 'Síður með síðueiginleika',
+'pageswithprop-text' => 'Á þessari síðu er listi yfir síður sem hafa ákveðna síðueiginleika.',
+'pageswithprop-prop' => 'Heiti eiginleika:',
 'pageswithprop-submit' => 'Áfram',
 
 'doubleredirects' => 'Tvöfaldar tilvísanir',
@@ -2085,6 +2140,7 @@ Hún er tilvísun á [[$2]].',
 'mostrevisions' => 'Síður eftir fjölda breytinga',
 'prefixindex' => 'Allar síður með forskeyti',
 'prefixindex-namespace' => 'Allar síður með forskeyti ($1 nafnrými)',
+'prefixindex-strip' => 'Fjarlægja forskeyti í listanum',
 'shortpages' => 'Stuttar síður',
 'longpages' => 'Langar síður',
 'deadendpages' => 'Botnlangar',
@@ -2100,6 +2156,7 @@ Hún er tilvísun á [[$2]].',
 'listusers' => 'Notendalisti',
 'listusers-editsonly' => 'Sýna eingöngu notendur með breytingar',
 'listusers-creationsort' => 'Raða eftir stofndegi',
+'listusers-desc' => 'Raða í lækkandi röð',
 'usereditcount' => '$1 {{PLURAL:$1|breyting|breytingar}}',
 'usercreated' => '{{GENDER:$3|Stofnað|}} $1 $2',
 'newpages' => 'Nýjustu greinar',
@@ -2529,7 +2586,7 @@ $1',
 'contributions' => 'Framlög {{GENDER:$1|notanda}}',
 'contributions-title' => 'Framlög notanda $1',
 'mycontris' => 'Framlög',
-'contribsub2' => 'Eftir $1 ($2)',
+'contribsub2' => 'Eftir {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Engar breytingar fundnar sem passa við þessa viðmiðun.',
 'uctop' => '(núverandi)',
 'month' => 'Frá mánuðinum (og fyrr):',
@@ -3035,8 +3092,8 @@ Vinsamlegast reyndu aftur.',
 'spam_reverting' => 'Tek aftur síðustu breytingu sem inniheldur ekki tengil á $1',
 'spam_blanking' => 'Allar útgáfur innihéldu tengla á $1, tæmi síðuna',
 'spam_deleting' => 'Allar útgáfur innihéldu tengla á $1, eyði síðunni',
-'simpleantispam-label' => 'Kæfuvörn.
-Ekki fylla þetta út!',
+'simpleantispam-label' => "Kæfuvörn.
+'''EKKI''' fylla þetta út!",
 
 # Info page
 'pageinfo-title' => 'Upplýsingar um $1',
@@ -3051,12 +3108,12 @@ Ekki fylla þetta út!',
 'pageinfo-article-id' => 'Einkennisnúmer síðunnar',
 'pageinfo-language' => 'Tungumál síðunnar',
 'pageinfo-robot-policy' => 'Leitarvélastaða',
-'pageinfo-robot-index' => 'Skráanleg',
-'pageinfo-robot-noindex' => 'Óskráanleg',
+'pageinfo-robot-index' => 'Heimilað',
+'pageinfo-robot-noindex' => 'Ekki heimilað',
 'pageinfo-views' => 'Fjöldi innlita',
 'pageinfo-watchers' => 'Fjöldi notenda, sem vakta síðuna',
 'pageinfo-few-watchers' => 'Vöktuð af færri en $1 {{PLURAL:$1|notanda|notendum}}',
-'pageinfo-redirects-name' => 'Tilvísanir til þessarar síðu',
+'pageinfo-redirects-name' => 'Fjöldi tilvísana til þessarar síðu',
 'pageinfo-subpages-name' => 'Undirsíður þessarar síðu',
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|tilvísun|tilvísanir}}; $3 {{PLURAL:$3|ekki tilvísun|ekki tilvísanir}})',
 'pageinfo-firstuser' => 'Stofnandi síðunnar',
@@ -3362,6 +3419,7 @@ Ef skránni hefur verið breytt, kann að vera að einhverjar upplýsingar eigi
 'exif-disclaimer' => 'Fyrirvari',
 'exif-contentwarning' => 'Viðvörun innihalds myndar',
 'exif-giffilecomment' => 'GIF athugasemd',
+'exif-intellectualgenre' => 'Tegund hlutar',
 'exif-scenecode' => 'IPTC kóði myndefnis',
 'exif-event' => 'Lýsir viðburðinum',
 'exif-organisationinimage' => 'Lýsir félaginu',
@@ -3804,7 +3862,10 @@ MediaWiki er útgefin í þeirri von að hann sé gagnlegur, en ÁN ALLRAR ÁBYR
 'tags-tag' => 'Nafn tags',
 'tags-display-header' => 'Útlit í breytingarskrá',
 'tags-description-header' => 'Tæmandi merkingarlýsing',
+'tags-active-header' => 'Virkt?',
 'tags-hitcount-header' => 'Merktar breytingar',
+'tags-active-yes' => 'Já',
+'tags-active-no' => 'Nei',
 'tags-edit' => 'breyta',
 'tags-hitcount' => '$1 {{PLURAL:$1|breyting|breytingar}}',
 
@@ -3825,6 +3886,7 @@ MediaWiki er útgefin í þeirri von að hann sé gagnlegur, en ÁN ALLRAR ÁBYR
 'dberr-problems' => 'Því miður!Tæknilegir örðugleikar eru á þessari síðu.',
 'dberr-again' => 'Reyndu að bíða í nokkrar mínútur og endurhladdu síðan síðuna.',
 'dberr-info' => '(Mistókst að hafa samband við gagnaþjón: $1)',
+'dberr-info-hidden' => '(Mistókst að hafa samband við gagnaþjón)',
 'dberr-usegoogle' => 'Þú getur notað Google til að leita á meðan.',
 'dberr-outofdate' => 'Athugaðu að afrit þeirra gætu verið úreld.',
 'dberr-cachederror' => 'Þetta er afritað eintak af umbeðinni síðu og gæti verið úreld.',
@@ -3960,4 +4022,8 @@ Ef ekki, þá getur þú notað einfalt eyðublað hér fyrir neðan. Athugasemd
 # Image rotation
 'rotate-comment' => 'Myndinni var snúið um $1 {{PLURAL:$1|gráðu|gráður}} réttsælis',
 
+# Limit report
+'limitreport-walltime' => 'Rauntímanotkun',
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|sekúnda|sekúndur}}',
+
 );
index 658d105..b633869 100644 (file)
@@ -746,7 +746,7 @@ Non dimenticare di personalizzare le [[Special:Preferences|preferenze di {{SITEN
 'gotaccount' => 'Hai già un accesso? $1.',
 'gotaccountlink' => 'Entra',
 'userlogin-resetlink' => 'Hai dimenticato i tuoi dati di accesso?',
-'userlogin-resetpassword-link' => 'Reimposta la tua password',
+'userlogin-resetpassword-link' => 'Hai dimenticato la password?',
 'helplogin-url' => 'Help:Login',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Aiuto con il login]]',
 'userlogin-loggedin' => 'Sei già connesso come {{GENDER:$1|$1}}.
index 849cf8e..364e212 100644 (file)
@@ -829,7 +829,7 @@ $2',
 'gotaccount' => 'アカウントを既に持っている場合、$1。',
 'gotaccountlink' => 'ログインしてください',
 'userlogin-resetlink' => 'ログイン情報をお忘れですか?',
-'userlogin-resetpassword-link' => 'パスワードを再設定',
+'userlogin-resetpassword-link' => 'パスワードをお忘れですか?',
 'helplogin-url' => 'Help:ログイン',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|ログインのヘルプ]]',
 'userlogin-loggedin' => '{{GENDER:$1|$1}} として既にログインしています。
@@ -908,9 +908,9 @@ Cookieを有効にしていることを確認して、このページを再読
 'mailerror' => 'メールを送信する際にエラーが発生しました: $1',
 'acct_creation_throttle_hit' => 'あなたと同じ IP アドレスでこのウィキに訪れた人が、最近 24 時間で {{PLURAL:$1|$1 アカウント}}を作成しており、これはこの期間で作成が許可されている最大数です。
 そのため、現在この IP アドレスではアカウントをこれ以上作成できません。',
-'emailauthenticated' => 'メールアドレスは$2 $3に認証済みです。',
-'emailnotauthenticated' => 'メールアドレスが認証されていません。
-認証されるまで、以下のいかなる機能でもメールは送信されません。',
+'emailauthenticated' => 'メールアドレスは$2 $3に確認済みです。',
+'emailnotauthenticated' => 'メールアドレスが確認されていません。
+確認されるまで、以下のいかなる機能でもメールは送信されません。',
 'noemailprefs' => 'これらの機能を有効にするには、個人設定でメールアドレスを登録してください。',
 'emailconfirmlink' => 'あなたのメールアドレスを確認',
 'invalidemailaddress' => '入力されたメールアドレスが正しい形式に従っていないため、受け付けられません。
index 18e8ba8..95187da 100644 (file)
@@ -810,7 +810,7 @@ $2',
 'gotaccount' => '계정이 이미 있다면, $1.',
 'gotaccountlink' => '로그인하세요',
 'userlogin-resetlink' => '사용자 이름이나 비밀번호를 잊으셨나요?',
-'userlogin-resetpassword-link' => 'ë\82´ ë¹\84ë°\80ë²\88í\98¸ ì\9e¬ì\84¤ì \95',
+'userlogin-resetpassword-link' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ì\9e\8aì\9c¼ì\85¨ë\82\98ì\9a\94?',
 'helplogin-url' => 'Help:로그인',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|로그인에 관한 도움말]]',
 'userlogin-loggedin' => '이미 $1로 로그인되어 있습니다. 아래의 양식으로 다른 계정으로 로그인하세요.',
@@ -887,7 +887,7 @@ $2',
 'mailerror' => '메일 보내기 오류: $1',
 'acct_creation_throttle_hit' => '당신의 IP 주소를 이용한 방문자가 이전에 이미 {{PLURAL:$1|계정 $1개}}를 만들어, 계정 만들기 한도를 초과하였습니다.
 따라서 지금은 이 IP 주소로는 더 이상 계정을 만들 수 없습니다.',
-'emailauthenticated' => '이메일 주소는 $2 $3에 인증되었습니다.',
+'emailauthenticated' => '이메일 주소는 $2 에 $3 에서 인증되었습니다.',
 'emailnotauthenticated' => '이메일 주소를 인증하지 않았습니다.
 이메일 확인 절차를 거치지 않으면 다음 이메일 기능을 사용할 수 없습니다.',
 'noemailprefs' => '이 기능을 사용하기 위해서는 사용자 환경 설정에서 이메일 주소를 설정해야 합니다.',
@@ -2768,7 +2768,7 @@ $1',
 # Contributions
 'contributions' => '{{GENDER:$1|사용자}} 기여',
 'contributions-title' => '$1 사용자의 기여 목록',
-'mycontris' => '기여 목록',
+'mycontris' => '기여',
 'contribsub2' => '{{GENDER:$3|$1}}($2)의 기여',
 'nocontribs' => '지정한 조건과 일치하는 바뀜을 찾을 수 없습니다.',
 'uctop' => '(최신)',
@@ -3190,7 +3190,7 @@ $2',
 'tooltip-pt-anontalk' => '현재 사용하는 IP 주소에 대한 토론 문서',
 'tooltip-pt-preferences' => '사용자 환경 설정',
 'tooltip-pt-watchlist' => '주시문서에 대한 바뀜 목록',
-'tooltip-pt-mycontris' => '내 기여 목록',
+'tooltip-pt-mycontris' => '내 기여 목록',
 'tooltip-pt-login' => '꼭 로그인해야 하는 것은 아니지만, 로그인을 권장합니다.',
 'tooltip-pt-anonlogin' => '꼭 필요한 것은 아니지만, 로그인을 하면 편리한 점이 많습니다.',
 'tooltip-pt-logout' => '로그아웃',
index 36a19a0..d7a4bbc 100644 (file)
@@ -898,7 +898,7 @@ Titulus: '''({{int:cur}})''' = dissimilis ab emendatione novissima,
 'searchprofile-articles-tooltip' => 'Quaerere in $1',
 'searchprofile-project-tooltip' => 'Quaerere in $1',
 'searchprofile-images-tooltip' => 'Fasciculos quaerere',
-'searchprofile-everything-tooltip' => 'Omnia perscrutari (etiam paginae disputatoriae)',
+'searchprofile-everything-tooltip' => 'Omnia perscrutari (etiam paginae disputationis)',
 'searchprofile-advanced-tooltip' => 'In spatiis nominalibus accommotis quaerere',
 'search-result-size' => '$1 ({{PLURAL:$2|1 verbum|$2 verba}})',
 'search-result-score' => 'Gravitas: $1%',
index 5952fe5..d0c9d65 100644 (file)
@@ -333,6 +333,7 @@ $messages = array(
 
 # General errors
 'error' => 'Йоҥылыш',
+'databaseerror-error' => 'Йоҥылыш: $1',
 'missing-article' => 'Тыгай текст дене возымо лаштык базыште муалтын огыл, "$1" $2.
 
 Кунам тый тоштемше кылвер почеш шӧрымӧ вашталтымаш лаштыкыш (але эртымгорно лаштыкыш) куснет, тыге лийын кертеш.
@@ -355,9 +356,13 @@ $messages = array(
 'virus-unknownscanner' => 'палыдыме антивирус:',
 
 # Login and logout pages
+'welcomeuser' => 'Пагален ӱжына, $1!',
 'yourname' => 'Пайдаланышын лӱмжӧ:',
 'yourpassword' => 'Шолыпмут:',
+'createacct-yourpassword-ph' => 'Шолыпмутым пурто',
 'yourpasswordagain' => 'Шолыпмутым угыч пуртымаш:',
+'createacct-yourpasswordagain' => 'Шолыпмутым пеҥгыдемде',
+'createacct-yourpasswordagain-ph' => 'Шолыпмутым угыч пурто',
 'remembermypassword' => 'Тиде компьютерыште мыйым шарнаш (эн шуко $1 {{PLURAL:$1|кечылан|кечылан}})',
 'yourdomainname' => 'Тендан домен:',
 'login' => 'Шке денет палымым ыште',
@@ -372,7 +377,9 @@ $messages = array(
 'gotaccount' => "Тый регистрацийым эртенат? '''$1'''.",
 'gotaccountlink' => 'Шке денет палымым ыште',
 'userlogin-resetlink' => 'Лӱмдам але шолыпмутдам монденда?',
+'userlogin-resetpassword-link' => 'Шолыпмутым монденат?',
 'createaccountmail' => 'Кӱчык жаплан чокым ыштыме шолыпмутым мылам e-mail дене колташ',
+'createacct-benefit-heading' => '{{SITENAME}} тендан гаяк еҥ-влак дене ыштен шындалтын.',
 'nosuchuser' => '"$1" лӱман пайдаланыше уке.
 Пайдаланышын лӱмыштӧ йӱкпале-влакын кугытшо тӱрыс лийшаш.
 Лӱмым чын возымым терге але [[Special:UserLogin/signup|регистрацийым эрте]].',
index 1b1b2db..1cf0f51 100644 (file)
@@ -811,7 +811,7 @@ $2',
 'gotaccount' => "Веќе имате корисничка сметка? '''$1'''.",
 'gotaccountlink' => 'Најавете се',
 'userlogin-resetlink' => 'Си ги заборавивте податоците за најава?',
-'userlogin-resetpassword-link' => 'Смени Ð»Ð¾Ð·Ð¸Ð½ÐºÐ°',
+'userlogin-resetpassword-link' => 'Ð\88а Ð·Ð°Ð±Ð¾Ñ\80авивÑ\82е Ð»Ð¾Ð·Ð¸Ð½ÐºÐ°Ñ\82а?',
 'helplogin-url' => 'Help:Најава',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помош со најавата]]',
 'userlogin-loggedin' => 'Веќе сте најавени како {{GENDER:$1|$1}}.
@@ -887,7 +887,7 @@ $2',
 'mailerror' => 'Грешка при испраќање на е-поштата: $1',
 'acct_creation_throttle_hit' => 'Корисници на ова вики користејќи ја вашата IP-адреса создале {{PLURAL:$1|1 корисничка сметка|$1 кориснички сметки}} во последниве денови, при што е достигнат максималниот број на кориснички сметки предвиден и овозможен за овој период.
 Како резултат на ова, посетителите кои ја користат оваа IP-адреса во моментов нема да можат да создаваат нови сметки.',
-'emailauthenticated' => 'Ð\92аÑ\88аÑ\82а Ðµ-поÑ\88Ñ\82енÑ\81ка Ð°Ð´Ñ\80еÑ\81а Ðµ Ð¿Ð¾Ñ\82вÑ\80дена Ð½Ð° $2 Ð²Ð¾ $3 Ñ\87.',
+'emailauthenticated' => 'Вашата е-пошта адреса е потврдена на $2 во $3 ч.',
 'emailnotauthenticated' => 'Вашата е-поштенска адреса сè уште не е потврдена.
 Нема да биде испратена е-пошта во ниту еден од следниве случаи.',
 'noemailprefs' => 'Наведете е-поштенска адреса за да функционираат следниве својства.',
index 04734d6..a06e144 100644 (file)
@@ -856,8 +856,8 @@ $3-н тодорхойлсон шалтгаан нь ''$2''",
 (сүүлчийн) = өмнөх засвартай харьцуулах, Б = бага зэргийн засвар',
 'history-fieldset-title' => 'Түүх сөхөе',
 'history-show-deleted' => 'Зөвхөн устгагдсаныг',
-'histfirst' => 'Эхний',
-'histlast' => 'Сүүлийн',
+'histfirst' => 'хамгийн эхэнд',
+'histlast' => 'хамгийн шинэ',
 'historysize' => '($1 байт)',
 'historyempty' => '(хоосон байна)',
 
@@ -1649,6 +1649,7 @@ URL нь зөв болон сайт ажиллагаатай байгаа эсэ
 'listfiles_search_for' => 'Зургийн нэрээр хайх:',
 'imgfile' => 'файл',
 'listfiles' => 'Файлын жагсаалт',
+'listfiles_thumb' => 'товч агуулга',
 'listfiles_date' => 'Огноо',
 'listfiles_name' => 'Нэр',
 'listfiles_user' => 'Хэрэглэгч',
@@ -2243,7 +2244,7 @@ $1',
 'mycontris' => 'Оруулсан хувь нэмэр',
 'contribsub2' => 'Хэрэглэгч: $1 ($2)',
 'nocontribs' => 'Энэ шалгуурт тохирох өөрчилсөн зүйлүүд олдсонгүй.',
-'uctop' => 'ï¼\88дÑ\8dÑ\8dд)',
+'uctop' => 'ï¼\88одооÑ\85)',
 'month' => 'Дараах сараас (өмнөх засварууд нь ч орно):',
 'year' => 'Дараах жилээс (өмнөх засварууд нь ч орно):',
 
index bdd48cd..ee5e336 100644 (file)
@@ -496,7 +496,7 @@ Che a dësmentia pa ëd cambié ij [[Special:Preferences|sò gust për {{SITENAM
 'gotaccount' => 'Ha-lo già un sò cont? $1.',
 'gotaccountlink' => "Ch'a rintra ant ël sistema",
 'userlogin-resetlink' => "A l'ha dësmentià ij sò detaj për intré ant ël sistema?",
-'userlogin-resetpassword-link' => 'Riamposté la ciav',
+'userlogin-resetpassword-link' => 'Ciav dësmentià?',
 'helplogin-url' => 'Help:Conession',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Agiut con la conession]]',
 'userlogin-loggedin' => "A l'é già rintrà an ël sistema tanme {{GENDER:$1|$1}}.
@@ -569,8 +569,8 @@ Anans che qualsëssìa àutr messagi ëd pòsta a ven-a mandà a 's cont-sì, a
 'mailerror' => 'Eror ën mandand via un mëssagi ëd pòsta eletrònica: $1',
 'acct_creation_throttle_hit' => "Dij visitador ëd costa wiki, an dovrand soa adrëssa IP a l'han creà {{PLURAL:$1|1 cont|$1 cont}} ant l'ùltim di, che a l'é tut lòn che as peul fesse ant cost temp.
 Ëd conseguensa, ij visitador che a deuvro costa adrëssa IP a peulo pì nen fé dij cont al moment.",
-'emailauthenticated' => "Soa adrëssa ëd pòsta eletrònica a l'é stàita autenticà ël $2 a $3.",
-'emailnotauthenticated' => "Soa adrëssa ëd pòsta eletrònica a l'é pa ancó stàita autenticà.
+'emailauthenticated' => "Soa adrëssa ëd pòsta eletrònica a l'é stàita confirmà ël $2 a $3.",
+'emailnotauthenticated' => "Soa adrëssa ëd pòsta eletrònica a l'é pa ancó stàita confirmà.
 Për qualsëssìa ëd coste funsion a sarà mandà gnun mëssagi.",
 'noemailprefs' => "Che a specìfica n'adrëssa ëd pòsta eletrònica se a veul dovré coste funsion-sì.",
 'emailconfirmlink' => 'Che a conferma soa adrëssa ëd pòsta eletrònica',
index fbc2ded..5b26b1b 100644 (file)
@@ -1302,15 +1302,15 @@ Outros administradores da {{SITENAME}} continuarão a poder aceder ao conteúdo
 * Informação pessoal imprópria
 *: ''endereços de domicílio e números de telefone, números da segurança social, etc''",
 'revdelete-legend' => 'Definir restrições de visibilidade',
-'revdelete-hide-text' => 'Ocultar texto da edição',
+'revdelete-hide-text' => 'Revisão do texto',
 'revdelete-hide-image' => 'Ocultar conteúdo do ficheiro',
 'revdelete-hide-name' => 'Ocultar operação e destino',
-'revdelete-hide-comment' => 'Ocultar resumo da edição',
-'revdelete-hide-user' => 'Ocultar nome de utilizador/IP',
+'revdelete-hide-comment' => 'Resumo da edição',
+'revdelete-hide-user' => 'Nome de utilizador/endereço de IP',
 'revdelete-hide-restricted' => 'Ocultar dados dos administradores e de todos os outros',
 'revdelete-radio-same' => '(manter)',
-'revdelete-radio-set' => 'Sim',
-'revdelete-radio-unset' => 'o',
+'revdelete-radio-set' => 'Visível',
+'revdelete-radio-unset' => 'Escondido',
 'revdelete-suppress' => 'Ocultar dados dos administradores e de todos os outros',
 'revdelete-unsuppress' => 'Remover restrições das revisões restauradas',
 'revdelete-log' => 'Motivo:',
@@ -1687,6 +1687,8 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'right-edituserjs' => 'Editar os ficheiros JS de outros utilizadores',
 'right-editmyusercss' => 'Editar os seus próprios ficheiros CSS de utilizador',
 'right-editmyuserjs' => 'Editar os seus próprios ficheiros JavaScript de utilizador',
+'right-viewmywatchlist' => 'Ver sua própria lista de páginas vigiadas',
+'right-editmywatchlist' => 'Editar sua própria lista de páginas vigiadas. Observe que algumas ações seguirão adicionando páginas, mesmo sem este direito.',
 'right-viewmyprivateinfo' => 'Ver os seus próprios dados privados (ex.: endereço de e-mail, nome real)',
 'right-editmyprivateinfo' => 'Editar os seus próprios dados privados (ex.: endereço de e-mail, nome real)',
 'right-editmyoptions' => 'Editar as suas próprias preferências',
@@ -1751,6 +1753,10 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'action-userrights-interwiki' => 'editar privilégios de utilizadores de outras wikis',
 'action-siteadmin' => 'bloquear ou desbloquear a base de dados',
 'action-sendemail' => 'enviar e-mails',
+'action-editmywatchlist' => 'Editar sua lista de páginas vigiadas',
+'action-viewmywatchlist' => 'Ver sua lista de páginas vigiadas',
+'action-viewmyprivateinfo' => 'Ver sua informação privada',
+'action-editmyprivateinfo' => 'Editar sua informação privada',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|alteração|alterações}}',
@@ -2171,6 +2177,7 @@ Talvez queira editar a descrição na [$2 página original de descrição do fic
 
 # Random page in category
 'randomincategory-nopages' => 'Não há páginas na categoria [[:Category:$1|$1]].',
+'randomincategory-selectcategory' => 'Obter página aleatória da categoria: $1 $2',
 'randomincategory-selectcategory-submit' => 'Ir',
 
 # Random redirect
@@ -2279,6 +2286,7 @@ Agora redirecciona para [[$2]].',
 'listusers' => 'Utilizadores',
 'listusers-editsonly' => 'Mostrar apenas utilizadores com edições',
 'listusers-creationsort' => 'Ordenar por data de criação',
+'listusers-desc' => 'Ordenar de forma decrescente',
 'usereditcount' => '$1 {{PLURAL:$1|edição|edições}}',
 'usercreated' => '{{GENDER:$3|Criado|Criada}} em $1 às $2',
 'newpages' => 'Páginas recentes',
@@ -4005,7 +4013,7 @@ Em conjunto com este programa deve ter recebido [{{SERVER}}{{SCRIPTPATH}}/COPYIN
 # Special:Redirect
 'redirect' => 'Redirecionar pelo ID do ficheiro, utilizador ou revisão',
 'redirect-legend' => 'Redirecionar para um ficheiro ou página',
-'redirect-summary' => 'Esta página especial redireciona a um ficheiro (dado o nome do ficheiro), a uma página (dado um ID de revisão) ou a uma página de utilizador (dado o ID do utilizador).',
+'redirect-summary' => 'Esta página especial redireciona a um ficheiro (dado o nome do ficheiro), a uma página (dado um ID de revisão) ou a uma página de utilizador (dado o ID do utilizador). Utilização: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] ou [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Ir',
 'redirect-lookup' => 'Pesquisa:',
 'redirect-value' => 'Valor:',
@@ -4228,6 +4236,7 @@ Caso contrário, pode facilmente usar o formulário abaixo. O seu comentário se
 'rotate-comment' => 'Imagem rodada em $1 {{PLURAL:$1|grau|graus}} no sentido dos ponteiros do relógio',
 
 # Limit report
+'limitreport-title' => 'Dados de perfis do analisador:',
 'limitreport-cputime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
 'limitreport-walltime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
 'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
index cb7d38a..14a9d3b 100644 (file)
@@ -482,7 +482,8 @@ This can also appear in the credits page if the credits feature is enabled,for e
 'qbmyoptions' => 'Heading in the Cologne Blue skin user menu containing links to user (talk) page, preferences, watchlist, etc.
 {{Identical|My pages}}',
 'qbspecialpages' => '{{Identical|Special page}}',
-'faq' => "FAQ is short for ''frequently asked questions''.",
+'faq' => "FAQ is short for ''frequently asked questions''.
+{{Identical|FAQ}}",
 'faqpage' => '{{doc-important|Do not translate <code>Project:</code> part.}}
 "FAQ" is short for "frequently asked questions".
 
@@ -1341,7 +1342,8 @@ The link points to the local [[Special:PasswordReset]].
 
 See example: [[Special:UserLogin]]
 
-userlogin-resetpassword-link may have to be shorter than the old {{msg-mw|userlogin-resetlink}}',
+userlogin-resetpassword-link may have to be shorter than the old {{msg-mw|userlogin-resetlink}}.
+{{Identical|Forgot your password}}',
 'helplogin-url' => '{{doc-important|Do not translate the namespace name <code>Help</code>.}}
 Used as name of the page that provides information about logging into the wiki.
 
@@ -1521,7 +1523,7 @@ Parameters:
 * $3 - time',
 'emailnotauthenticated' => 'Message in [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.
 
-It appears after saving your email address but before it has been authenticated.',
+It appears after saving your email address but before you confirm it.',
 'noemailprefs' => 'Message appearing in the "Email options" section of the "User profile" page in [[Special:Preferences|Preferences]], when no user email address has been entered.',
 'emailconfirmlink' => 'Link to [[Special:ConfirmEmail]].
 
@@ -3012,7 +3014,8 @@ See also:
 * {{msg-mw|prefs-help-email-others|help}}
 * {{msg-mw|prefs-changeemail|link title}}',
 'prefs-email' => 'Used as section name in [[Special:Preferences]].',
-'prefs-rendering' => 'Title of tab in [[Special:Preferences]].',
+'prefs-rendering' => 'Title of tab in [[Special:Preferences]].
+{{Identical|Appearance}}',
 'saveprefs' => 'Button for saving changes in the preferences page.
 
 See also:
@@ -9354,7 +9357,8 @@ See: http://www.awaresystems.be/imaging/tiff/tifftags/ycbcrpositioning.html
 'exif-dc-publisher' => 'One or more publisher of resource.
 {{Identical|Publisher}}',
 'exif-dc-relation' => "Something related to this image. Often a list of URL's to related images.",
-'exif-dc-rights' => 'Copyright information about the image/media given in informal language.',
+'exif-dc-rights' => 'Copyright information about the image/media given in informal language.
+{{Identical|Right}}',
 'exif-dc-source' => 'Source of the image. This is another image that this image is based on. This does not refer to the person who provided the image.',
 'exif-dc-type' => 'Type or genre of image/media. This might be something like painting or photograph.',
 
index fb422e1..e9d7005 100644 (file)
@@ -93,6 +93,7 @@
  * @author Александр Сигачёв
  * @author Гусейн
  * @author ОйЛ
+ * @author Сай
  * @author Умар
  * @author Чаховіч Уладзіслаў
  * @author לערי ריינהארט
@@ -895,7 +896,7 @@ $2',
 'gotaccount' => "Вы уже зарегистрированы? '''$1'''.",
 'gotaccountlink' => 'Представьтесь',
 'userlogin-resetlink' => 'Забыли данные для входа?',
-'userlogin-resetpassword-link' => 'Сброс пароля',
+'userlogin-resetpassword-link' => 'Сбросить ваш пароль?',
 'helplogin-url' => 'Help:Представление системе',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощь со входом в систему]]',
 'userlogin-loggedin' => 'Вы уже вошли как {{GENDER:$1|$1}}.
@@ -968,8 +969,9 @@ $2',
 'mailerror' => 'Ошибка при отправке почты: $1',
 'acct_creation_throttle_hit' => 'За сутки с вашего IP-адреса {{PLURAL:$1|была создана $1 учётная запись участника|было создано $1 учётных записей участников|было создано $1 учётных записей участников}}, что является пределом для данного отрезка времени.
 Таким образом, пользователи, обладающие данным IP-адресом, в данный момент больше не могут создавать новых учётных записей.',
-'emailauthenticated' => 'Ваш почтовый адрес подтверждён $2 в $3.',
-'emailnotauthenticated' => 'Ваш адрес электронной почты ещё не был подтверждён, функции вики-движка по работе с эл. почтой отключены.',
+'emailauthenticated' => 'Ваш адрес электронной почты подтверждён $2 в $3.',
+'emailnotauthenticated' => 'Ваш адрес электронной почты ещё не был подтверждён.
+Письма не будут отправляться ни для одной из следующий функций.',
 'noemailprefs' => 'Адрес электронной почты не был указан, функции вики-движка по работе с эл. почтой отключены.',
 'emailconfirmlink' => 'Подтвердить ваш адрес электронной почты',
 'invalidemailaddress' => 'Адрес электронной почты не может быть принят, так как он не соответствует формату.
@@ -1866,7 +1868,7 @@ $1",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|изменение|изменения|изменений}}',
-'enhancedrc-since-last-visit' => '$1 с последнего посещения',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|с последнего посещения}}',
 'enhancedrc-history' => 'история',
 'recentchanges' => 'Свежие правки',
 'recentchanges-legend' => 'Настройки свежих правок',
index 18b6e2f..31eb83a 100644 (file)
@@ -574,7 +574,7 @@ Poizvedba: $2',
 'actionthrottledtext' => 'Kot ukrep proti smetju, je število izvajanj tega dejanja v časovnem obdobju omejeno, in vi ste ta limit presegli.
 Prosimo, poskusite znova čez nekaj minut.',
 'protectedpagetext' => 'Ta stran je bila zaklenjena za preprečitev urejanja ali drugih dejanj.',
-'viewsourcetext' => 'Lahko si ogledate in kopirate vsebino te strani:',
+'viewsourcetext' => 'Vsebino te strani si lahko ogledate in kopirate:',
 'viewyourtext' => "Lahko si ogledate in kopirate vsebino '''vaših urejanj''' te strani:",
 'protectedinterface' => 'Prikazana stran vsebuje besedilo vmesnika programja na tem wikiju in je zaradi preprečevanja zlorab zaščitena.
 
@@ -647,10 +647,10 @@ Ne pozabite si prilagoditi vaših [[Special:Preferences|nastavitev {{GRAMMAR:rod
 'gotaccount' => 'Račun že imate? $1.',
 'gotaccountlink' => 'Prijavite se',
 'userlogin-resetlink' => 'Ste pozabili svoje prijavne podatke?',
-'userlogin-resetpassword-link' => 'Ponastavite svoje geslo',
+'userlogin-resetpassword-link' => 'Ste pozabili svoje geslo?',
 'helplogin-url' => 'Help:Prijava',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoč pri prijavi]]',
-'userlogin-loggedin' => 'Ste že prijavljeni kot {{GENDER:$1|$1}}.
+'userlogin-loggedin' => 'Prijavljeni ste že kot {{GENDER:$1|$1}}.
 Uporabite spodnji obrazec, da se prijavite kot drug uporabnik.',
 'userlogin-createanother' => 'Ustvari drug račun',
 'createacct-join' => 'Spodaj vnesite svoje informacije.',
@@ -777,8 +777,8 @@ Morda ste že uspešno spremenili geslo ali pa ste zahtevali novo začasno geslo
 
 # Special:PasswordReset
 'passwordreset' => 'Ponastavitev gesla',
-'passwordreset-text-one' => 'Izpolnite obrazec, da ponastavite svoje geslo.',
-'passwordreset-text-many' => 'Izpolnite {{PLURAL:$1|polje|enega od polj}}, da ponastavite svoje geslo.',
+'passwordreset-text-one' => 'Da ponastavite svoje geslo, izpolnite naslednji obrazec.',
+'passwordreset-text-many' => '{{PLURAL:$1|Da ponastavite svoje geslo, izpolnite eno od polj.}}',
 'passwordreset-legend' => 'Ponastavitev gesla',
 'passwordreset-disabled' => 'Ponastavljanje gesla je na tem wikiju onemogočeno.',
 'passwordreset-emaildisabled' => 'Na tem wikiju so možnosti e-pošte onemogočene.',
@@ -879,17 +879,17 @@ To storite, če ste po nesreči žetone z nekom delili, ali če je bil vaš rač
 'summary-preview' => 'Predogled povzetka',
 'subject-preview' => 'Predogled zadeve/naslova:',
 'blockedtitle' => 'Uporabnik je blokiran',
-'blockedtext' => "'''Urejanje z vašim uporabniškim imenom oziroma IP-naslovom je bilo onemogočeno.'''
+'blockedtext' => "'''Urejanje z vašim uporabniškim imenom oziroma IP-naslovom je onemogočeno.'''
 
 Blokiral vas je $1.
-Podan razlog je ''$2''.
+Podani razlog je ''$2''.
 
-* Začetek blokade: $8
-* Potek blokade: $6
-* Namen blokade: $7
+* začetek blokade: $8
+* potek blokade: $6
+* blokirani uporabnik: $7
 
-O blokiranju se lahko pogovorite z $1 ali katerim drugim [[{{MediaWiki:Grouppage-sysop}}|administratorjem]].
-Vedite, da lahko ukaz »Pošlji uporabniku e-pismo« uporabite le, če ste v [[Special:Preferences|nastavitvah]] vpisali in potrdili svoj elektronski naslov ter le-ta ni bil blokiran.
+O blokiranju se lahko pogovorite z uporabnikom/-co $1 ali katerim drugim [[{{MediaWiki:Grouppage-sysop}}|administratorjem]].
+Vedite, da lahko ukaz »Pošlji uporabniku e-pismo« uporabite le, če ste v [[Special:Preferences|nastavitvah]] vpisali in potrdili svoj elektronski naslov in ta ni blokiran.
 Vaš IP-naslov je $3, številka blokade pa #$5.
 Prosimo, vključite ju v vse morebitne poizvedbe.",
 'autoblockedtext' => "Vaš IP-naslov je bil samodejno blokiran, saj je bil uporabljen s strani drugega uporabnika, ki ga je blokiral $1.
@@ -1080,9 +1080,9 @@ Naslednji argumenti so bili izpuščeni.",
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Računa ni moč ustvariti',
-'cantcreateaccount-text' => "Registracija novega uporabnika iz tega IP-naslova ('''$1''') je bila blokirana s strani [[User:$3|$3]].
+'cantcreateaccount-text' => "Registracije z IP-naslova ('''$1''') je administrator(ka) [[User:$3|$3]] blokiral(a).
 
-Razlog, ki ga je podal $3, je ''$2''.",
+Razlog, ki ga je $3 podal(a), je ''$2''.",
 
 # History pages
 'viewpagelogs' => 'Poglej dnevniške zapise o strani',
@@ -2722,7 +2722,7 @@ Za sklicevanje so tule navedeni vnosi v dnevniku blokiranja:',
 'blocklog-showsuppresslog' => 'Ta uporabnik je že bil blokiran in skrit.
 Dnevnik skrivanja je na voljo spodaj:',
 'blocklogentry' => '[[$1]] blokiran s časom poteka blokade $2 $3',
-'reblock-logentry' => 'spremenil nastavitve blokade za [[$1]] z iztekom dne $2 ob $3',
+'reblock-logentry' => 'je spremenil(a) nastavitve blokade za [[$1]] na čas $2 $3',
 'blocklogtext' => 'Prikazan je dnevnik blokiranja in deblokiranja uporabnikov. Samodejno blokirani IP-naslovi niso navedeni. Trenutno veljavna blokiranja so navedena na [[Special:BlockList|seznamu blokad]].',
 'unblocklogentry' => 'je deblokiral(-a) »$1«',
 'block-log-flags-anononly' => 'samo za brezimne uporabnike',
index 4250633..afc6b4c 100644 (file)
@@ -800,8 +800,7 @@ $2',
 'myprivateinfoprotected' => 'Немате дозволу за мењање ваших личних информација.',
 'mypreferencesprotected' => 'Немате дозволу за мењање ваших подешавања.',
 'ns-specialprotected' => 'Посебне странице се не могу уређивати.',
-'titleprotected' => "Овај наслов је {{GENDER:$1|заштитио корисник|заштитила корисница|заштитио корисник}} [[User:$1|$1]].
-Наведени разлог: ''$2''.",
+'titleprotected' => "Овај назив је [[User:$1|$1]] заштитио од прављења. Разлог: ''$2''.",
 'filereadonlyerror' => 'Не могу да изменим датотеку „$1“ јер је ризница „$2“ у режиму за читање.
 
 Администратор који ју је закључао понудио је следеће објашњење: „$3“.',
@@ -1436,7 +1435,7 @@ $1",
 'mergehistory-reason' => 'Разлог:',
 
 # Merge log
-'mergelog' => 'Ð\94невник спајања',
+'mergelog' => 'Ð\98Ñ\81Ñ\82оÑ\80иÑ\98а спајања',
 'pagemerge-logentry' => 'страница [[$1]] је спојена у [[$2]] (све до измене $3)',
 'revertmerge' => 'растави',
 'mergelogpagetext' => 'Испод се налази списак скорашњих спајања историја страница.',
@@ -2587,7 +2586,7 @@ $UNWATCHURL
 'deletepage' => 'Обриши страницу',
 'confirm' => 'Потврди',
 'excontent' => 'садржај је био: „$1“',
-'excontentauthor' => 'садржај је био: „$1“ (једину измену {{GENDER:|направио је|направила је|направио је}} [[Special:Contributions/$2|$2]])',
+'excontentauthor' => 'садржај је био: „$1“ (а једини уредник је био „[[Special:Contributions/$2|$2]]“)',
 'exbeforeblank' => 'садржај пре брисања је био: „$1“',
 'exblank' => 'страница је била празна',
 'delete-confirm' => 'Брисање странице „$1“',
@@ -2599,7 +2598,7 @@ $UNWATCHURL
 'actionfailed' => 'Радња није успела',
 'deletedtext' => "Страница „$1“ је обрисана.
 Погледајте ''$2'' за више детаља.",
-'dellogpage' => 'Ð\94невник брисања',
+'dellogpage' => 'Ð\98Ñ\81Ñ\82оÑ\80иÑ\98а брисања',
 'dellogpagetext' => 'Испод је списак последњих брисања.',
 'deletionlog' => 'дневник брисања',
 'reverted' => 'Враћено на ранију измену',
@@ -2720,8 +2719,8 @@ $UNWATCHURL
 'undeleteextrahelp' => "Да бисте вратили целу историју странице, оставите све кућице неозначене и кликните на дугме '''''{{int:undeletebtn}}'''''.
 Ако желите да вратите одређене измене, означите их и кликните на '''''{{int:undeletebtn}}'''''.",
 'undeleterevisions' => '$1 {{PLURAL:$1|измена је архивирана|измене су архивиране|измена је архивирано}}',
-'undeletehistory' => 'Ако вратите страницу, све измене ће бити враћене њеној историји.
\90ко Ñ\98е Ñ\83 Ð¼ÐµÑ\92Ñ\83вÑ\80еменÑ\83 Ð½Ð°Ð¿Ñ\80авÑ\99ена Ð½Ð¾Ð²Ð° Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81 Ð¸Ñ\81Ñ\82им Ð½Ð°Ð·Ð¸Ð²Ð¾Ð¼, Ð²Ñ\80аÑ\9bене Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\9bе Ñ\81е Ð¿Ð¾Ñ\98авиÑ\82и Ñ\83 Ñ\80аниÑ\98ом историји.',
+'undeletehistory' => 'Ако вратите страницу, све ревизије ће бити враћене њеној историји.
\90ко Ñ\98е Ñ\83 Ð¼ÐµÑ\92Ñ\83вÑ\80еменÑ\83 Ð½Ð°Ð¿Ñ\80авÑ\99ена Ð½Ð¾Ð²Ð° Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81 Ð¸Ñ\81Ñ\82им Ð½Ð°Ð·Ð¸Ð²Ð¾Ð¼, Ð²Ñ\80аÑ\9bене Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\9bе Ñ\81е Ð¿Ð¾Ñ\98авиÑ\82и Ñ\83 Ñ\9aеноÑ\98 Ñ\80аниÑ\98оÑ\98 историји.',
 'undeleterevdel' => 'Враћање неће бити извршено ако је резултат тога делимично брисање последње измене.
 У таквим случајевима морате искључити или открити најновије обрисане измене.',
 'undeletehistorynoadmin' => 'Ова страница је обрисана.
@@ -4104,7 +4103,7 @@ $5
 'autosumm-blank' => 'Потпуно обрисана страница',
 'autosumm-replace' => 'Замена садржаја странице са „$1“',
 'autoredircomment' => 'Преусмерење на [[$1]]',
-'autosumm-new' => 'Ð\9dапÑ\80авÑ\99ена Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81а: â\80\9e$1â\80\9c',
+'autosumm-new' => 'Ð\9dова Ñ\81Ñ\82Ñ\80аниÑ\86а: $1',
 
 # Size units
 'size-bytes' => '$1 B',
@@ -4312,7 +4311,10 @@ $5
 'tags-tag' => 'Назив ознаке',
 'tags-display-header' => 'Изглед на списковима измена',
 'tags-description-header' => 'Опис значења',
+'tags-active-header' => 'Активна?',
 'tags-hitcount-header' => 'Означене измене',
+'tags-active-yes' => 'Да',
+'tags-active-no' => 'Не',
 'tags-edit' => 'уреди',
 'tags-hitcount' => '$1 {{PLURAL:$1|измена|измене|измена}}',
 
index 07cfdaf..6845eb0 100644 (file)
@@ -701,8 +701,7 @@ $2',
 'customcssprotected' => 'Nemate dozvolu da menjate ovu CSS stranicu jer sadrži lične postavke drugog korisnika.',
 'customjsprotected' => 'Nemate dozvolu da menjate ovu stranicu javaskripta jer sadrži lične postavke drugog korisnika.',
 'ns-specialprotected' => 'Posebne stranice se ne mogu uređivati.',
-'titleprotected' => "Ovaj naslov je {{GENDER:$1|zaštitio korisnik|zaštitila korisnica|zaštitio korisnik}} [[User:$1|$1]].
-Navedeni razlog: ''$2''.",
+'titleprotected' => "Ovaj naziv je [[User:$1|$1]] zaštitio od pravljenja. Razlog: ''$2''.",
 'filereadonlyerror' => 'Ne mogu da izmenim datoteku „$1“ jer je riznica „$2“ u režimu za čitanje.
 
 Administrator koji ju je zaključao ponudio je sledeće objašnjenje: „$3“.',
@@ -1321,7 +1320,7 @@ Korišćenje navigacionih veza će poništiti ovu kolonu.',
 'mergehistory-reason' => 'Razlog:',
 
 # Merge log
-'mergelog' => 'Dnevnik spajanja',
+'mergelog' => 'Istorija spajanja',
 'pagemerge-logentry' => 'stranica [[$1]] je spojena u [[$2]] (sve do izmene $3)',
 'revertmerge' => 'rastavi',
 'mergelogpagetext' => 'Ispod se nalazi spisak skorašnjih spajanja istorija stranica.',
@@ -2289,7 +2288,7 @@ Pogledajte i [[Special:WantedCategories|tražene kategorije]].',
 'linksearch-ok' => 'Pretraži',
 'linksearch-text' => 'Mogu se koristiti džokeri poput „*.wikipedia.org“.<br />
 Potreban je najviši domen, kao „*.org“.<br />
-Podržani protokoli: <code>$1</code> (zadaje http:// ako ne navedete protokol).',
+{{PLURAL:$2|Podržan protokol|Podržani protokoli}}: <code>$1</code> (zadaje http:// ako ne navedete protokol).',
 'linksearch-line' => '$1 veza u $2',
 'linksearch-error' => 'Džokeri se mogu pojaviti samo na početku adrese.',
 
@@ -2446,7 +2445,7 @@ Podrška i dalja pomoć:
 'deletepage' => 'Obriši stranicu',
 'confirm' => 'Potvrdi',
 'excontent' => 'sadržaj je bio: „$1“',
-'excontentauthor' => 'sadržaj je bio: „$1“ (jedinu izmenu {{GENDER:|napravio je|napravila je|napravio je}} [[Special:Contributions/$2|$2]])',
+'excontentauthor' => 'sadržaj je bio: „$1“ (a jedini urednik je bio „[[Special:Contributions/$2|$2]]“)',
 'exbeforeblank' => 'sadržaj pre brisanja je bio: „$1“',
 'exblank' => 'stranica je bila prazna',
 'delete-confirm' => 'Brisanje stranice „$1“',
@@ -2458,7 +2457,7 @@ Potvrdite svoju nameru, da razumete posledice i da ovo radite u skladu s [[{{Med
 'actionfailed' => 'Radnja nije uspela',
 'deletedtext' => "Stranica „$1“ je obrisana.
 Pogledajte ''$2'' za više detalja.",
-'dellogpage' => 'Dnevnik brisanja',
+'dellogpage' => 'Istorija brisanja',
 'dellogpagetext' => 'Ispod je spisak poslednjih brisanja.',
 'deletionlog' => 'dnevnik brisanja',
 'reverted' => 'Vraćeno na raniju izmenu',
@@ -2579,8 +2578,8 @@ Arhiva se povremeno čisti od ovakvih stranica.',
 'undeleteextrahelp' => "Da biste vratili celu istoriju stranice, ostavite sve kućice neoznačene i kliknite na dugme '''''{{int:undeletebtn}}'''''.
 Ako želite da vratite određene izmene, označite ih i kliknite na '''''{{int:undeletebtn}}'''''.",
 'undeleterevisions' => '$1 {{PLURAL:$1|izmena je arhivirana|izmene su arhivirane|izmena je arhivirano}}',
-'undeletehistory' => 'Ako vratite stranicu, sve izmene će biti vraćene njenoj istoriji.
-Ako je u međuvremenu napravljena nova stranica s istim nazivom, vraćene izmene će se pojaviti u ranijom istoriji.',
+'undeletehistory' => 'Ako vratite stranicu, sve revizije će biti vraćene njenoj istoriji.
+Ako je u međuvremenu napravljena nova stranica s istim nazivom, vraćene izmene će se pojaviti u njenoj ranijoj istoriji.',
 'undeleterevdel' => 'Vraćanje neće biti izvršeno ako je rezultat toga delimično brisanje poslednje izmene.
 U takvim slučajevima morate isključiti ili otkriti najnovije obrisane izmene.',
 'undeletehistorynoadmin' => 'Ova stranica je obrisana.
@@ -3198,6 +3197,7 @@ Ovo je verovatno izazvano vezom do spoljašnjeg sajta koji se nalazi na crnoj li
 'pageinfo-magic-words' => '{{PLURAL:$1|Magična reč|Magične reči}} ($1)',
 'pageinfo-hidden-categories' => '{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}} ($1)',
 'pageinfo-templates' => '{{PLURAL:$1|Uključeni šablon|Uključeni šabloni}} ($1)',
+'pageinfo-transclusions' => '{{PLURAL:$1|Stranica|Stranice}} uključene u ($1)',
 'pageinfo-toolboxlink' => 'Podaci o stranici',
 'pageinfo-redirectsto' => 'Preusmerava na',
 'pageinfo-redirectsto-info' => 'podaci',
@@ -3944,7 +3944,7 @@ Potvrdite da stvarno želite da napravite stranicu.",
 'autosumm-blank' => 'Potpuno obrisana stranica',
 'autosumm-replace' => 'Zamena sadržaja stranice sa „$1“',
 'autoredircomment' => 'Preusmerenje na [[$1]]',
-'autosumm-new' => 'Napravljena stranica sa: „$1“',
+'autosumm-new' => 'Nova stranica: $1',
 
 # Size units
 'size-bytes' => '$1 B',
@@ -4151,7 +4151,10 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'tags-tag' => 'Naziv oznake',
 'tags-display-header' => 'Izgled na spiskovima izmena',
 'tags-description-header' => 'Opis značenja',
+'tags-active-header' => 'Aktivna?',
 'tags-hitcount-header' => 'Označene izmene',
+'tags-active-yes' => 'Da',
+'tags-active-no' => 'Ne',
 'tags-edit' => 'uredi',
 'tags-hitcount' => '$1 {{PLURAL:$1|izmena|izmene|izmena}}',
 
@@ -4199,7 +4202,7 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'logentry-delete-delete' => '$1 je {{GENDER:$2|obrisao|obrisala}} stranicu $3',
 'logentry-delete-restore' => '$1 je {{GENDER:$2|vratio|vratila}} stranicu $3',
 'logentry-delete-event' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|događaja|$5 daogađaja}} u dnevniku na $3: $4',
-'logentry-delete-revision' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|izmene|$5 izmjena}} na stranici $3: $4',
+'logentry-delete-revision' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|izmene|$5 izmena}} na stranici $3: $4',
 'logentry-delete-event-legacy' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost događaja u dnevniku na $3',
 'logentry-delete-revision-legacy' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost izmena na stranici $3',
 'logentry-suppress-delete' => '$1 je {{GENDER:$2|potisnuo|potisnula}} stranicu $3',
@@ -4219,7 +4222,7 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'logentry-move-move-noredirect' => '$1 je {{GENDER:$2|premestio|premestila}} stranicu $3 na $4 bez ostavljanja preusmerenja',
 'logentry-move-move_redir' => '$1 je {{GENDER:$2|premestio|premestila}} stranicu $3 na $4 preko preusmerenja',
 'logentry-move-move_redir-noredirect' => '$1 je {{GENDER:|premestio|premestila}} stranicu $3 na $4 preko preusmerenja bez ostavljanja preusmerenja',
-'logentry-patrol-patrol' => '$1 je {{GENDER:$2|osznačio|označila}} izenu $4 stranice $3 kao patroliranu',
+'logentry-patrol-patrol' => '$1 je {{GENDER:$2|označio|označila}} izmenu $4 stranice $3 kao patroliranu',
 'logentry-patrol-patrol-auto' => '$1 je automatski {{GENDER:$2|označio|označila}} izmenu $4 stranice $3 kao pregledanu',
 'logentry-newusers-newusers' => '$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog',
 'logentry-newusers-create' => '$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog',
index 84db9db..8497694 100644 (file)
@@ -1216,7 +1216,7 @@ Anledningen till blockeringen var "$2".',
 'next' => 'nästa',
 'last' => 'föregående',
 'page_first' => 'första',
-'page_last' => 'senaste',
+'page_last' => 'sista',
 'histlegend' => "Val av diff: markera i klickrutorna för att jämföra versioner och tryck enter eller knappen längst ner.<br />
 Förklaring: (nuvarande) = skillnad mot nuvarande version; (föregående) = skillnad mot föregående version; '''m''' = mindre ändring.",
 'history-fieldset-title' => 'Bläddra i historiken',
index 532db2c..af34836 100644 (file)
@@ -147,31 +147,31 @@ $messages = array(
 # User preference toggles
 'tog-underline' => 'లంకె క్రీగీత:',
 'tog-justify' => 'పేరాలను ఇరు పక్కలా సమానంగా సర్దు',
-'tog-hideminor' => 'à°\87à°\9fà±\80వలి à°®à°¾à°°à±\8dà°ªà±\81లలà±\8b à°\9aà°¿à°¨à±\8dà°¨ à°®à°¾à°°à±\8dà°ªà±\81లనà±\81 à°¦à°¾à°\9aà°¿à°ªà±\86à°\9fà±\8dà°\9fà±\81',
-'tog-hidepatrolled' => 'à°\87à°\9fà±\80వలి à°®à°¾à°°à±\8dà°ªà±\81లలà±\8b à°¨à°¿à°\98à°¾ à°\89à°¨à±\8dà°¨ à°®à°¾à°°à±\8dà°ªà±\81లనà±\81 à°¦à°¾à°\9aà°¿à°ªà±\86à°\9fà±\8dà°\9fà±\81',
-'tog-newpageshidepatrolled' => 'à°\95à±\8aà°¤à±\8dà°¤ à°ªà±\87à°\9cà±\80à°² à°\9cాబితా à°¨à±\81à°\82à°\9aà°¿ à°¨à°¿à°\98à°¾ à°\89à°¨à±\8dà°¨ à°ªà±\87à°\9cà±\80లనà±\81 à°¦à°¾à°\9aà°¿à°ªà±\86à°\9fà±\8dà°\9fà±\81',
+'tog-hideminor' => 'ఇటీవలి మార్పులలో చిన్న మార్పులను దాచు',
+'tog-hidepatrolled' => 'ఇటీవలి మార్పులలో నిఘా ఉన్న మార్పులను దాచు',
+'tog-newpageshidepatrolled' => 'కొత్త పేజీల జాబితా నుంచి నిఘా ఉన్న పేజీలను దాచు',
 'tog-extendwatchlist' => 'కేవలం ఇటీవలి మార్పులే కాక, మార్పులన్నీ చూపించటానికి నా వీక్షణా జాబితాను పెద్దది చేయి',
 'tog-usenewrc' => 'ఇటీవలి మార్పులు మరియు విక్షణ జాబితాలలో మార్పులను పేజీ వారిగా చూపించు (జావాస్క్రిప్టు అవసరం)',
-'tog-numberheadings' => 'à°¶à±\80à°°à±\8dà°·à°¿à°\95à°²à°\95à±\81 à°\86à°\9fà±\8bమాà°\9fà°¿à°\95à±\8dâ\80\8cà°\97à°¾ à°µà°°à±\81à°¸ à°¸à°\82à°\96à±\8dయలà±\81 à°ªà±\86à°\9fà±\8dà°\9fు',
+'tog-numberheadings' => 'à°¶à±\80à°°à±\8dà°·à°¿à°\95à°²à°\95à±\81 à°\85à°ªà±\8dà°°à°®à±\87à°¯à°\82à°\97à°¾ à°µà°°à±\81à°¸ à°¸à°\82à°\96à±\8dయలà±\81 à°\9aà±\87à°°à±\8dà°\9aు',
 'tog-showtoolbar' => 'దిద్దుబాట్లు చేసేటప్పుడు, అందుకు సహాయపడే పరికరాలపెట్టెను చూపించు (జావాస్క్రిప్టు)',
 'tog-editondblclick' => 'డబుల్‌ క్లిక్కు చేసినప్పుడు పేజీని మార్చు (జావాస్క్రిప్టు)',
-'tog-editsection' => '[మారà±\8dà°\9aà±\81] à°²à°¿à°\82à°\95à±\81 à°¦à±\8dవారా à°µà°¿à°­à°¾à°\97à°\82 à°®à°¾à°°à±\8dà°ªà±\81 à°\95ావాలి',
+'tog-editsection' => '[మారà±\8dà°\9aà±\81] à°²à°¿à°\82à°\95à±\81 à°¦à±\8dవారా à°µà°¿à°­à°¾à°\97à°\82 à°®à°¾à°°à±\8dà°ªà±\81 à°\9aà±\87తనà°\82',
 'tog-editsectiononrightclick' => 'విభాగం పేరు మీద కుడి క్లిక్కుతో విభాగం మార్పు కావాలి (జావాస్క్రిప్టు)',
 'tog-showtoc' => 'విషయసూచిక చూపించు (3 కంటే ఎక్కువ శీర్షికలున్న పేజీలకు)',
 'tog-rememberpassword' => 'ఈ విహారిణిలో నా ప్రవేశాన్ని గుర్తుంచుకో (గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజుల}}కి)',
 'tog-watchcreations' => 'నేను సృష్టించే పేజీలను మరియు దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు',
 'tog-watchdefault' => 'నేను మార్చే పేజీలను మరియు దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు',
-'tog-watchmoves' => 'నేను తరలించిన పేజీలను దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు',
-'tog-watchdeletion' => 'నేను తొలగించిన పేజీలను దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు',
+'tog-watchmoves' => 'à°¨à±\87à°¨à±\81 à°¤à°°à°²à°¿à°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 à°®à°°à°¿à°¯à±\81 à°¦à°¸à±\8dà°¤à±\8dరాలనà±\81 à°¨à°¾ à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\9aà±\87à°°à±\8dà°\9aà±\81',
+'tog-watchdeletion' => 'à°¨à±\87à°¨à±\81 à°¤à±\8aà°²à°\97à°¿à°\82à°\9aà°¿à°¨ à°ªà±\87à°\9cà±\80లనà±\81 à°®à°°à°¿à°¯à±\81 à°¦à°¸à±\8dà°¤à±\8dరాలనà±\81 à°¨à°¾ à°µà±\80à°\95à±\8dà°·à°£ à°\9cాబితాà°\95à±\81 à°\9aà±\87à°°à±\8dà°\9aà±\81',
 'tog-minordefault' => 'ప్రత్యేకంగా తెలుపనంతవరకూ నా మార్పులను చిన్న మార్పులుగా గుర్తించు',
-'tog-previewontop' => 'à°µà±\8dయాసà°\82 à°®à°¾à°°à±\8dà°ªà±\81à°² à°¤à°°à±\81వాత à°\8eలావà±\81à°\82à°\9fà±\81à°\82à°¦à±\8b à°®à°¾à°°à±\8dà°ªà±\81à°²â\80\8c à°¬à°¾à°\95à±\8dà°¸à±\81à°\95à±\81 పైన చూపు',
-'tog-previewonfirst' => 'దిదà±\8dదిబాà°\9fà±\8dà°²à±\81 à°\9aà±\87సిన à°µà±\8dయాసానà±\8dని à°­à°¦à±\8dరపరిà°\9aà±\87 à°®à±\81à°\82à°¦à±\81 à°\8eలా à°µà±\81à°\82à°\9fà±\81à°\82à°¦à±\8b à°\92à°\95సారి చూపించు',
-'tog-nocache' => 'విహారిణిలో పుటల కాషింగుని అచేతనంచేయి',
-'tog-enotifwatchlistpages' => 'నా à°µà±\80à°\95à±\8dషణాà°\9cాబితా à°²à±\8bని à°ªà±\87à°\9cà±\80 à°²à±\87దా à°¦à°¸à±\8dà°¤à±\8dà°°à°\82 à°®à°¾à°°à°¿à°¨à°ªà±\81à°¡à±\81 à°¨à°¾à°\95à±\81 à°\88-à°®à±\86యిలà±\81 à°ªà°\82పిà°\82à°\9aà±\81',
-'tog-enotifusertalkpages' => 'నా à°\9aà°°à±\8dà°\9aà°¾ à°ªà±\87à°\9cà±\80à°²à±\8b à°®à°¾à°°à±\8dà°ªà±\81à°²à±\81 à°\9cà°°à°¿à°\97ినపà±\81à°¡à±\81 à°¨à°¾à°\95à±\81 à°\88-à°®à±\86యిలà±\81 à°ªà°\82పిà°\82à°\9aà±\81',
-'tog-enotifminoredits' => 'à°ªà±\87à°\9cà±\80à°²à±\81 à°®à°°à°¿à°¯à±\81 à°¦à°¸à±\8dà°¤à±\8dరాలà°\95à±\81 à°\9cà°°à°¿à°\97à±\87 à°\9aà°¿à°¨à±\8dà°¨ à°®à°¾à°°à±\8dà°ªà±\81à°²à°\95à±\81 à°\95à±\82à°¡à°¾ à°¨à°¾à°\95à±\81 à°\88-à°®à±\86యిలà±\81à°¨à±\81 à°ªà°\82పిà°\82à°\9aà±\81',
+'tog-previewontop' => 'à°µà±\8dయాసà°\82 à°®à°¾à°°à±\8dà°ªà±\81à°² à°®à±\81à°¨à±\81à°\9aà±\82à°ªà±\81 à°¸à°µà°°à°¿à°\82à°\9aà±\81 à°ªà±\86à°\9fà±\8dà°\9fà±\86 పైన చూపు',
+'tog-previewonfirst' => 'à°®à±\8aà°¦à°\9fà°¿  à°¦à°¿à°¦à±\8dà°¦à±\81బాà°\9fà±\81 à°\9aà±\87సినపà±\81à°¡à±\81 à°µà±\8dయాసపà±\81 à°®à±\81à°¨à±\81à°\9aà±\82à°ªà±\81 చూపించు',
+'tog-nocache' => 'విహారిణిలో పుటల  స్థానికనకలును(కాషింగు) అచేతనం',
+'tog-enotifwatchlistpages' => 'నా వీక్షణాజాబితా లోని పేజీ లేదా దస్త్రం మారినపుడు నాకు ఈ-మెయిలు పంపు',
+'tog-enotifusertalkpages' => 'నా చర్చా పేజీలో మార్పులు జరిగినపుడు నాకు ఈ-మెయిలు పంపు',
+'tog-enotifminoredits' => 'పేజీలు మరియు దస్త్రాలకు జరిగే చిన్న మార్పులకు కూడా నాకు ఈ-మెయిలును పంపు',
 'tog-enotifrevealaddr' => 'గమనింపు మెయిళ్ళలో నా ఈ-మెయిలు చిరునామాను చూపించు',
-'tog-shownumberswatching' => 'à°µà±\80à°\95à±\8dà°·à°\95à±\81à°² à°¸à°\82à°\96à±\8dయనà±\81 à°\9aà±\82పిà°\82à°\9aà±\81',
+'tog-shownumberswatching' => 'వీక్షకుల సంఖ్యను చూపు',
 'tog-oldsig' => 'ప్రస్తుత సంతకం:',
 'tog-fancysig' => 'సంతకాన్ని వికీపాఠ్యంగా తీసుకో (ఆటోమెటిక్‌ లింకు లేకుండా)',
 'tog-uselivepreview' => 'రాస్తున్నదానిని ఎప్పటికప్పుడు సరిచూడండి (జావాస్క్రిప్టు) (పరీక్షాదశలో ఉంది)',
@@ -358,7 +358,7 @@ $messages = array(
 'articlepage' => 'విషయపు పేజీని చూడండి',
 'talk' => 'చర్చ',
 'views' => 'చూపులు',
-'toolbox' => 'పనిముట్ల పెట్టె',
+'toolbox' => 'పనిముట్ల',
 'userpage' => 'వాడుకరి పేజీని చూడండి',
 'projectpage' => 'ప్రాజెక్టు పేజీని చూడు',
 'imagepage' => 'ఫైలు పేజీని చూడండి',
@@ -367,7 +367,7 @@ $messages = array(
 'viewhelppage' => 'సహాయం పేజీని చూడు',
 'categorypage' => 'వర్గం పేజీని చూడు',
 'viewtalkpage' => 'చర్చను చూడు',
-'otherlanguages' => 'à°\87తర à°­à°¾à°·à°²à°²à±\8a',
+'otherlanguages' => 'à°\87తర à°­à°¾à°·à°²à°²à±\8b',
 'redirectedfrom' => '($1 నుండి మళ్ళించబడింది)',
 'redirectpagesub' => 'దారిమార్పు పుట',
 'lastmodifiedat' => 'ఈ పేజీకి $2, $1న చివరి మార్పు జరిగినది.',
@@ -388,7 +388,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} గురించి',
 'aboutpage' => 'Project:గురించి',
-'copyright' => 'విషయ సంగ్రహం $1  కి లోబడి లభ్యం.',
+'copyright' => 'విషయం $1 కి లోబడి లభ్యం, వేరుగా పేర్కొంటే తప్ప.',
 'copyrightpage' => '{{ns:project}}:ప్రచురణ హక్కులు',
 'currentevents' => 'ఇప్పటి ముచ్చట్లు',
 'currentevents-url' => 'Project:ఇప్పటి ముచ్చట్లు',
@@ -890,7 +890,7 @@ $2
 'nocreate-loggedin' => 'కొత్త పేజీలను సృష్టించేందుకు మీకు అనుమతి లేదు.',
 'sectioneditnotsupported-title' => 'విభాగపు దిద్దిబాట్లకి తొడ్పాటు లేదు',
 'sectioneditnotsupported-text' => 'ఈ పేజీలో విభాగాల దిద్దుబాటుకి తోడ్పాటు లేదు.',
-'permissionserrors' => 'à°\85à°¨à±\81మతà±\81à°² à°¤à°ªà±\8dపిదాలà±\81',
+'permissionserrors' => 'à°\85à°¨à±\81మతి à°²à±\8bà°ªà°\82',
 'permissionserrorstext' => 'కింద పేర్కొన్న {{PLURAL:$1|కారణం|కారణాల}} మూలంగా, ఆ పని చెయ్యడానికి మీకు అనుమతిలేదు:',
 'permissionserrorstext-withaction' => 'ఈ క్రింది {{PLURAL:$1|కారణం|కారణాల}} వల్ల, మీకు $2 అనుమతి లేదు:',
 'recreate-moveddeleted-warn' => "'''హెచ్చరిక: ఇంతకు మునుపు ఒకసారి తొలగించిన పేజీని మళ్లీ సృష్టిద్దామని మీరు ప్రయత్నిస్తున్నారు.'''
@@ -1031,7 +1031,7 @@ $3 చెప్పిన కారణం: ''$2''",
 * అనుచితమైన వ్యక్తిగత సమాచారం
 * "ఇంటి చిరునామాలు, టెలిఫోను నంబర్లు, సోషల్ సెక్యూరిటీ నంబర్లు, వగైరాలు"',
 'revdelete-legend' => 'సందర్శక నిబంధనలు అమర్చు',
-'revdelete-hide-text' => 'à°\95à±\82à°°à±\8dà°ªà±\81 à°ªà°¾à° à±\8dయానà±\8dని à°¦à°¾à°\9aà±\81',
+'revdelete-hide-text' => 'à°\95à±\82à°°à±\8dà°ªà±\81 à°ªà°¾à° à±\8dà°¯à°\82',
 'revdelete-hide-image' => 'ఫైలులోని విషయాన్ని దాచు',
 'revdelete-hide-name' => 'చర్యను, లక్ష్యాన్నీ దాచు',
 'revdelete-hide-comment' => 'దిద్దుబాటు వ్యాఖ్యను దాచు',
@@ -1481,7 +1481,7 @@ $1",
 'rc_categories_any' => 'ఏదయినా',
 'rc-change-size-new' => 'మార్పు తర్వాత $1 {{PLURAL:$1|బైటు|బైట్లు}}',
 'newsectionsummary' => '/* $1 */ కొత్త విభాగం',
-'rc-enhanced-expand' => 'వివరాలని à°\9aà±\82పిà°\82à°\9aà±\81 (à°\9cావాసà±\8dà°\95à±\8dà°°à°¿à°ªà±\8dà°\9fà±\8d à°\85వసరà°\82)',
+'rc-enhanced-expand' => 'వివరాలనà±\81 à°\9aà±\82పిà°\82à°\9aà±\81',
 'rc-enhanced-hide' => 'వివరాలను దాచు',
 'rc-old-title' => 'మొదట "$1"గా సృష్టించారు',
 
@@ -2279,9 +2279,9 @@ $1',
 'contributions' => '{{GENDER:$1|వాడుకరి}} రచనలు',
 'contributions-title' => '$1 యొక్క మార్పులు-చేర్పులు',
 'mycontris' => 'మార్పులు చేర్పులు',
-'contribsub2' => '$1 ($2) కొరకు',
+'contribsub2' => '{{GENDER:$3|$1}} ($2) కొరకు',
 'nocontribs' => 'ఈ విధమైన మార్పులేమీ దొరకలేదు.',
-'uctop' => '(à°ªà±\88ది)',
+'uctop' => '(à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤)',
 'month' => 'ఈ నెల నుండి (అంతకు ముందువి):',
 'year' => 'ఈ సంవత్సరం నుండి (అంతకు ముందువి):',
 
index c0c2bef..98de2bb 100644 (file)
@@ -808,7 +808,7 @@ Tarayıcınızın önbelleğini temizleyinceye kadar bazı sayfalarda, oturumunu
 'gotaccount' => 'Zaten bir hesabınız var mı? $1.',
 'gotaccountlink' => 'Oturum açın',
 'userlogin-resetlink' => 'Giriş bilgilerinizi mi unuttunuz?',
-'userlogin-resetpassword-link' => 'Parolanızı sıfırlayın',
+'userlogin-resetpassword-link' => 'Parolanızı mı unuttunuz?',
 'helplogin-url' => 'Help:Oturum açma',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Oturum açma konusunda yardım alın]]',
 'createacct-join' => 'Aşağıya bilgilerinizi girin.',
index a135875..f426d59 100644 (file)
@@ -821,7 +821,7 @@ $1',
 'gotaccount' => "Ви вже зареєстровані? '''$1'''.",
 'gotaccountlink' => 'Увійдіть',
 'userlogin-resetlink' => 'Забули дані, потрібні для входу?',
-'userlogin-resetpassword-link' => 'СкинÑ\83Ñ\82и Ð¿Ð°Ñ\80олÑ\8c',
+'userlogin-resetpassword-link' => 'Ð\97абÑ\83ли Ð¿Ð°Ñ\80олÑ\8c?',
 'helplogin-url' => 'Help:Вхід до системи',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Допомога в реєстрації]]',
 'userlogin-loggedin' => 'Ви вже увійшли як {{GENDER:$1|$1}}.
@@ -900,8 +900,8 @@ $1',
 'mailerror' => 'Помилка при відправці пошти: $1',
 'acct_creation_throttle_hit' => 'Відвідувачі з вашої IP-адреси вже створили $1 {{PLURAL:$1|обліковий запис|облікових записи|облікових записів}} за останню добу, що є максимумом для цього відрізка часу.
 Таким чином, користувачі з цієї IP-адреси не можуть на цей момент створювати нових облікових записів.',
-'emailauthenticated' => 'Ð\90дÑ\80еÑ\81Ñ\83 Ð²Ð°Ñ\88оÑ\97 ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ð¿Ñ\96дÑ\82веÑ\80джено $2 Ð¾ $3.',
-'emailnotauthenticated' => 'Адресу вашої електронної пошти <strong>ще не підтверджено</strong>, функції вікі-двигуна роботи з ел. поштою відключені.',
+'emailauthenticated' => 'Ð\92аÑ\88Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ð±Ñ\83ло Ð¿Ñ\96дÑ\82веÑ\80джено Ð½Ð°  $2  Ð¾  $3.',
+'emailnotauthenticated' => 'Адресу вашої електронної пошти ще не підтверджено. Жодна лист не буде надіслано для будь-якої з наступних функцій.',
 'noemailprefs' => 'Адресу електронної пошти не вказано, функції вікі роботи з ел. поштою відключені.',
 'emailconfirmlink' => 'Підтвердити адресу вашої електронної пошти',
 'invalidemailaddress' => 'Уведена адреса не може бути прийнята, бо вона не відповідає формату адрес електронної пошти.
@@ -1343,15 +1343,15 @@ $3 зазначив таку причину: ''$2''",
 * Непотрібна особиста інформація
 *: ''домашні адреси, номери телефонів, номер паспорта тощо.''",
 'revdelete-legend' => 'Установити обмеження',
-'revdelete-hide-text' => 'Ð\9fÑ\80иÑ\85ований Ñ\82екÑ\81Ñ\82 Ñ\86Ñ\96Ñ\94Ñ\97 Ð²ÐµÑ\80Ñ\81Ñ\96Ñ\97 Ñ\81Ñ\82оÑ\80Ñ\96нки',
+'revdelete-hide-text' => 'ТекÑ\81Ñ\82 Ð²Ð¸Ð¿Ñ\80авленÑ\8c',
 'revdelete-hide-image' => 'Приховати вміст файлу',
 'revdelete-hide-name' => "Приховати дію та її об'єкт",
-'revdelete-hide-comment' => 'Ð\9fÑ\80иÑ\85оваÑ\82и ÐºÐ¾Ð¼ÐµÐ½Ñ\82аÑ\80',
-'revdelete-hide-user' => "Ð\9fÑ\80иÑ\85оваÑ\82и Ñ\96м'Ñ\8f Ð°Ð²Ñ\82оÑ\80а",
+'revdelete-hide-comment' => 'Ð\9fÑ\96дÑ\81Ñ\83мок Ð·Ð¼Ñ\96н',
+'revdelete-hide-user' => "Ð\86м'Ñ\8f Ð°Ð²Ñ\82оÑ\80а/IP Ð°Ð´Ñ\80еÑ\81а",
 'revdelete-hide-restricted' => 'Приховати дані також і від адміністраторів',
 'revdelete-radio-same' => '(не змінювати)',
-'revdelete-radio-set' => 'Так',
-'revdelete-radio-unset' => 'Ð\9dÑ\96',
+'revdelete-radio-set' => 'Ð\92идимий',
+'revdelete-radio-unset' => 'Ð\9fÑ\80иÑ\85ований',
 'revdelete-suppress' => 'Приховувати дані також і від адміністраторів',
 'revdelete-unsuppress' => 'Зняти обмеження з відновлених версій',
 'revdelete-log' => 'Причина:',
index 2a1fca9..1d6e405 100644 (file)
@@ -777,7 +777,7 @@ Hãy nhớ thay đổi [[Special:Preferences|tùy chọn cá nhân {{SITENAME}}]
 'gotaccount' => "Đã mở tài khoản rồi? '''$1'''.",
 'gotaccountlink' => 'Đăng nhập',
 'userlogin-resetlink' => 'Quên mất thông tin đăng nhập?',
-'userlogin-resetpassword-link' => 'Đặt lại mật khẩu của bạn',
+'userlogin-resetpassword-link' => 'Quên mật khẩu?',
 'helplogin-url' => 'Help:Đăng nhập',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Trợ giúp đăng nhập]]',
 'userlogin-loggedin' => 'Bạn đã đăng nhập với tên {{GENDER:$1}}$1.
@@ -1285,7 +1285,7 @@ Các quản lý khác ở {{SITENAME}} vẫn có thể truy nhập vào nội du
 'revdelete-hide-restricted' => 'Ẩn giấu thông tin khỏi các Quản lý lẫn thành viên khác',
 'revdelete-radio-same' => '(không đổi)',
 'revdelete-radio-set' => 'Có',
-'revdelete-radio-unset' => 'Không',
+'revdelete-radio-unset' => 'Ẩn',
 'revdelete-suppress' => 'Che dữ liệu đối với bảo quản viên cũng như các thành viên khác',
 'revdelete-unsuppress' => 'Bỏ các hạn chế trên các phiên bản được phục hồi',
 'revdelete-log' => 'Lý do:',
index 69a4b97..3ca4da0 100644 (file)
@@ -12,6 +12,7 @@
  * @author O
  * @author Reedy
  * @author Wu-chinese.com
+ * @author Xiaomingyan
  * @author Yfdyh000
  * @author 十弌
  */
@@ -1342,7 +1343,7 @@ $1",
 
 # Book sources
 'booksources' => '书源',
-'booksources-search-legend' => '搜索网络书源',
+'booksources-search-legend' => '搜索图书来源',
 'booksources-go' => '去',
 
 # Special:Log
index 1910ca5..4c40e02 100644 (file)
@@ -1174,15 +1174,15 @@ $2
 * אויפדעקונג פון פריוואטקייט אינפארמאציע
 * ''היים אדרעסן, טעלעפאן נומערן, אדער סאשעל סעקיורעטי, א.א.וו.:'''",
 'revdelete-legend' => 'שטעלט ווייזונג באגרענעצונגען',
-'revdelete-hide-text' => '×\91×\90×\94×\90×\9c×\98 ×\90×\99× ×\94×\90×\9c×\98 ×¤×\95×\9f ×\95×\95ערס×\99×¢',
+'revdelete-hide-text' => '×\95×\95ערס×\99×¢ ×\98עקס×\98',
 'revdelete-hide-image' => 'באהאלט טעקע אינהאלט',
 'revdelete-hide-name' => 'באהאלט אקציע און ציל',
-'revdelete-hide-comment' => '×\91×\90×\94×\90×\9c×\98 ×¢× ×\93ער×\9f ×\94ער×\94',
-'revdelete-hide-user' => "×\91×\90Ö·×\94×\90Ö·×\9c×\98×\9f ×¨×¢×\93×\90ַק×\98×\90ר'ס ×\91×\90× ×\99צער-× ×\90×\9e×¢×\9f/IP-×\90Ö·×\93רעס",
+'revdelete-hide-comment' => 'רע×\93×\90ק×\98×\99ר×\95× ×\92 ×¨×¢×\96×\95×\9e×¢',
+'revdelete-hide-user' => "רעדאַקטאר'ס באניצער-נאמען/IP-אַדרעס",
 'revdelete-hide-restricted' => 'באהאלט אינפארמאציע אויך פון אדמיניסטראטורן פונקט ווי פשוטע באנוצער',
 'revdelete-radio-same' => '(נישט ענדערן)',
-'revdelete-radio-set' => '×\99×\90',
-'revdelete-radio-unset' => '× ×\99×\99ן',
+'revdelete-radio-set' => '×\96×¢×\91×\90ר',
+'revdelete-radio-unset' => 'פֿ×\90ַר×\91×\90ָר×\92ן',
 'revdelete-suppress' => 'באַהאַלטן אינפֿארמאַציע פון אַדמיניסטראַטארן ווי אויך אנדערע',
 'revdelete-unsuppress' => 'טוה אפ באגרענעצונגן אין גענדערטע רעוויזיעס',
 'revdelete-log' => 'אורזאַך:',
@@ -1481,6 +1481,8 @@ $1",
 'userrights-notallowed' => 'איר האט נישט קיין ערלויבניש צוצולייגן אדער אוועקנעמען באַניצער רעכטן.',
 'userrights-changeable-col' => 'גרופעס איר קענט ענדערן',
 'userrights-unchangeable-col' => 'גרופעס איר קענט נישט ענדערן',
+'userrights-conflict' => 'קאנפֿליקט פון באניצער־רעכטן ענדערונגען! זייט אזוי גוט רעצענזירן און באשטעטיקן אײַערע ענדערונגען.',
+'userrights-removed-self' => 'איר האט דערפאלגרייך אראפגענומען אייערע אייגענע רעכטע. אזוי קענט איר מער נישט דערגרייכן דעם בלאט.',
 
 # Groups
 'group' => 'גרופע:',
index cf98e7a..9b557f5 100644 (file)
@@ -728,10 +728,9 @@ $1',
 'no-null-revision' => '无法创建对"$1"页面新的空白修订',
 'badtitle' => '错误标题',
 'badtitletext' => '所请求页面的标题是无效的、不存在,跨语言或跨wiki链接的标题错误。它可能包含一个或更多的不能用于标题的字符。',
-'perfcached' => '下列数据已缓存,但可能已过时。最高{{PLURAL:$1|一个结果|$1个结果}}在缓存中可用。',
-'perfcachedts' => '下列数据已缓存,最后更新于$1。缓存中最多可有{{PLURAL:$4|1个结果|$4个结果}}。',
-'querypage-no-updates' => '当前禁止对此页面进行更新。
-此处的数据将不能被立即刷新。',
+'perfcached' => '以下是缓存的数据,可能不是最新的数据。缓存中最多有{{PLURAL:$1|$1条结果}}。',
+'perfcachedts' => '以下是缓存的数据,最后更新于$1。缓存中最多有{{PLURAL:$4|$4条结果}}。',
+'querypage-no-updates' => '该页面的更新目前停用。这里的数据不会马上刷新。',
 'wrong_wfQuery_params' => '错误的参数被传递到 wfQuery()<br />
 函数:$1<br />
 查询:$2',
@@ -754,7 +753,7 @@ $2',
 'customjsprotected' => '您没有权限编辑此JavaScript页面,因为它包含另一位用户的个人设置。',
 'mycustomcssprotected' => '您没有权限编辑这个 CSS 页面。',
 'mycustomjsprotected' => '您没有权限编辑这个 JavaScript 页面。',
-'myprivateinfoprotected' => '您没有权限来编辑您的私人信息。',
+'myprivateinfoprotected' => '你没有权限编辑你的私人信息。',
 'mypreferencesprotected' => '您没有权限来编辑您的个人设置。',
 'ns-specialprotected' => '特殊页面不可编辑。',
 'titleprotected' => '此标题已被[[User:$1|$1]]保护以防止创建。理由是“$2”。',
@@ -813,9 +812,8 @@ $2',
 'userlogin-resetpassword-link' => '重置你的密码',
 'helplogin-url' => 'Help:登录',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|登录帮助]]',
-'userlogin-loggedin' => '您已经作为{{GENDER:$1|$1}}登录。
-使用以下表单以作为另一账户登录。',
-'userlogin-createanother' => '创建另一个帐户',
+'userlogin-loggedin' => '你已经以{{GENDER:$1|$1}}的身份登录。使用下面的表格以其他用户的身份登录。',
+'userlogin-createanother' => '创建另一个账户',
 'createacct-join' => '请在下面输入你的信息。',
 'createacct-another-join' => '在下方输入新帐户信息。',
 'createacct-emailrequired' => '电子邮件地址',
@@ -826,11 +824,11 @@ $2',
 'createacct-realname' => '真实姓名 (可选)',
 'createaccountreason' => '原因:',
 'createacct-reason' => '原因',
-'createacct-reason-ph' => '为ä»\80ä¹\88æ\82¨è¦\81å\88\9b建å\8f¦ä¸\80个å¸\90户',
+'createacct-reason-ph' => '你为ä»\80ä¹\88è¦\81å\88\9b建å\8f¦ä¸\80个账户',
 'createacct-captcha' => '安全检查',
 'createacct-imgcaptcha-ph' => '请输入上图中的文字',
 'createacct-submit' => '创建您的账户',
-'createacct-another-submit' => '创建另一个户',
+'createacct-another-submit' => '创建另一个户',
 'createacct-benefit-heading' => '{{SITENAME}}是由像您这样的人建立的。',
 'createacct-benefit-body1' => '{{PLURAL:$1|编辑}}',
 'createacct-benefit-body2' => '{{PLURAL:$1|页面}}',
@@ -948,8 +946,8 @@ $2
 
 # Special:ChangeEmail
 'changeemail' => '更改电子邮件地址',
-'changeemail-header' => '更改帐户的电子邮件地址',
-'changeemail-text' => 'å¡«å\86\99此表å\8d\95å\8f¯ä»¥æ\9b´æ\94¹æ\82¨ç\9a\84ç\94µå­\90é\82®ä»¶å\9c°å\9d\80ã\80\82æ\82¨å°\86é\9c\80è¦\81è¾\93å\85¥æ\82¨ç\9a\84å¯\86ç \81以确认此更改。',
+'changeemail-header' => '更改账户电子邮件地址',
+'changeemail-text' => 'å®\8cæ\88\90该表格以æ\9b´æ\94¹ä½ ç\9a\84ç\94µå­\90é\82®ä»¶å\9c°å\9d\80ã\80\82ä½ é\9c\80è¦\81è¾\93å\85¥ä½ ç\9a\84å¯\86ç \81以确认该更改。',
 'changeemail-no-info' => '
 您必须登录以直接访问本页。',
 'changeemail-oldemail' => '当前电子邮件地址:',
@@ -1145,10 +1143,10 @@ $2
 'content-failed-to-parse' => '未能将 $2 内容转换为 $1:$3',
 'invalid-content-data' => '无效的内容数据',
 'content-not-allowed-here' => '[[$2]]页面上不允许“$1”内容',
-'editwarning-warning' => '离开这个页面会令您遗失之前的所有更改。若您已经登入,您可在您参数设置的“编辑”节中关闭此警告。',
+'editwarning-warning' => '离开本页面可能导致你失去任何你已经作出的更改。如果你处于登录状态,你可以在你的设置的“编辑”部分停用该警告。',
 
 # Content models
-'content-model-wikitext' => 'wiki语法',
+'content-model-wikitext' => 'wiki文本',
 'content-model-text' => '纯文本',
 'content-model-javascript' => 'JavaScript',
 'content-model-css' => 'CSS',
@@ -1401,7 +1399,7 @@ $1",
 'powersearch' => '高级搜索',
 'powersearch-legend' => '高级搜索',
 'powersearch-ns' => '在以下的名字空间中搜索:',
-'powersearch-redir' => '列出重定向',
+'powersearch-redir' => '列出重定向',
 'powersearch-field' => '搜索',
 'powersearch-togglelabel' => '选择:',
 'powersearch-toggleall' => '全选',
@@ -1492,7 +1490,7 @@ $1",
 'yourrealname' => '真实姓名:',
 'yourlanguage' => '语言:',
 'yourvariant' => '内容语言变种:',
-'prefs-help-variant' => '您希望用于显示本站内容的语种或拼写语系。',
+'prefs-help-variant' => '你希望用于显示该wiki的内容页面的语言变种或正字法。',
 'yournick' => '新签名:',
 'prefs-help-signature' => '讨论页面上的评论应该使用“<nowiki>~~~~</nowiki>”签名,它会自动转换为你的签名及时间戳。',
 'badsig' => '错误的原始签名。请检查HTML标签。',
@@ -1602,7 +1600,7 @@ $1",
 'right-bot' => '被视为自动过程',
 'right-nominornewtalk' => '不使小编辑在讨论页面引发新信息提示',
 'right-apihighlimits' => '在API查询中使用更高的限制',
-'right-writeapi' => '使用书写API',
+'right-writeapi' => '使用写入API',
 'right-delete' => '删除页面',
 'right-bigdelete' => '删除有大型历史的页面',
 'right-deletelogentry' => '删除和恢复特定的日志项目',
@@ -1626,12 +1624,12 @@ $1",
 'right-editusercssjs' => '编辑其他用户的CSS和JavaScript文件',
 'right-editusercss' => '编辑其他用户的CSS文件',
 'right-edituserjs' => '编辑其他用户的JavaScript文件',
-'right-editmyusercss' => '编辑您自己的用户 CSS 文件',
-'right-editmyuserjs' => '编辑您自己的用户 JavaScript 文件',
+'right-editmyusercss' => '编辑你自己的用户CSS文件',
+'right-editmyuserjs' => '编辑你自己的用户JavaScript文件',
 'right-viewmywatchlist' => '查看你的监视列表',
 'right-editmywatchlist' => '编辑您的监视列表。请注意即使没有这种权利,某些操作仍将添加页面。',
-'right-viewmyprivateinfo' => '查看您自己的私人数据 (如电子邮件地址、 真实姓名)',
-'right-editmyprivateinfo' => '编辑您自己的私人数据 (如电子邮件地址、 真实姓名)',
+'right-viewmyprivateinfo' => '查看你自己的私人数据(如电子邮件地址、真实姓名)',
+'right-editmyprivateinfo' => '编辑你自己的私人数据(如电子邮件地址、真实姓名)',
 'right-editmyoptions' => '编辑您的个人设置',
 'right-rollback' => '快速回退最后编辑特定页面的用户的编辑',
 'right-markbotedits' => '标记回退编辑为机器人编辑',
@@ -1673,7 +1671,7 @@ $1",
 'action-reupload' => '覆盖本文件',
 'action-reupload-shared' => '覆盖共享文件库的本文件',
 'action-upload_by_url' => '从URL上传本文件',
-'action-writeapi' => '使用书写API',
+'action-writeapi' => '使用写入API',
 'action-delete' => '删除本页',
 'action-deleterevision' => '删除本版本',
 'action-deletedhistory' => '查看本页面被删除的历史',
@@ -1697,7 +1695,7 @@ $1",
 'action-editmywatchlist' => '编辑你的监视列表',
 'action-viewmywatchlist' => '查看你的监视列表',
 'action-viewmyprivateinfo' => '查看您的私人信息',
-'action-editmyprivateinfo' => '编辑的私人信息',
+'action-editmyprivateinfo' => '编辑的私人信息',
 
 # Recent changes
 'nchanges' => '$1个更改',
@@ -1970,7 +1968,7 @@ $1',
 'upload_source_file' => '(您计算机上的一个文件)',
 
 # Special:ListFiles
-'listfiles-summary' => '本特殊页面示所有上传的文件。',
+'listfiles-summary' => '本特殊页面示所有上传的文件。',
 'listfiles_search_for' => '按媒体名称搜索:',
 'imgfile' => '文件',
 'listfiles' => '文件列表',
@@ -2070,7 +2068,7 @@ $1',
 
 # Unused templates
 'unusedtemplates' => '未使用模板',
-'unusedtemplatestext' => '此页é\9d¢å\88\97å\87º{{ns:template}}å\90\8då­\97空é\97´ä¸\8bæ\89\80æ\9c\89æ\9cªè¢«å\85¶å®\83页é\9d¢ä½¿ç\94¨ç\9a\84页é\9d¢ã\80\82请å\9c¨å\88 é\99¤è¿\99äº\9b模æ\9d¿å\89\8dæ£\80æ\9f¥å\85¶å®\83é\93¾å\85¥è¯¥æ¨¡æ\9d¿ç\9a\84页é\9d¢。',
+'unusedtemplatestext' => 'æ\9c¬é¡µé\9d¢å\88\97å\87º{{ns:template}}å\90\8då­\97空é\97´ä¸­æ\89\80æ\9c\89æ\9cªå\8c\85å\90«äº\8eå\85¶å®\83页é\9d¢ç\9a\84页é\9d¢ã\80\82请记å¾\97å\9c¨å\88 é\99¤è¿\99äº\9b模æ\9d¿å\89\8dæ£\80æ\9f¥å\85¶ä»\96é\93¾è\87³å®\83们ç\9a\84é\93¾æ\8e¥。',
 'unusedtemplateswlh' => '其它链接',
 
 # Random page
@@ -2078,11 +2076,11 @@ $1',
 'randompage-nopages' => '在以下{{PLURAL:$2|名字空间}}中没有页面:$1。',
 
 # Random page in category
-'randomincategory' => '分类中随机页面',
+'randomincategory' => '分类中随机页面',
 'randomincategory-invalidcategory' => '“$1”不是一个有效的分类名称。',
 'randomincategory-nopages' => '[[:Category:$1]]中没有页面。',
-'randomincategory-selectcategory' => '从分类获取随机页面:$1 $2。',
-'randomincategory-selectcategory-submit' => 'æ\98¾ç¤º',
+'randomincategory-selectcategory' => '获取随机页面从分类:$1 $2。',
+'randomincategory-selectcategory-submit' => 'æ\8f\90交',
 
 # Random redirect
 'randomredirect' => '随机重定向',
@@ -2103,37 +2101,37 @@ $1',
 'statistics-edits-average' => '每页平均编辑数',
 'statistics-views-total' => '查看总数',
 'statistics-views-total-desc' => '不存在页面和特殊页面的查看数未计入',
-'statistics-views-peredit' => '每编辑查看数',
+'statistics-views-peredit' => '每编辑查看数',
 'statistics-users' => '注册[[Special:ListUsers|用户]]',
 'statistics-users-active' => '活跃用户',
 'statistics-users-active-desc' => '在前$1天中操作过的用户',
-'statistics-mostpopular' => 'æµ\8fè§\88æ\9c\80å¤\9aç\9a\84页面',
+'statistics-mostpopular' => 'æ\9c\80å¤\9aæ\9f¥ç\9c\8b页面',
 
 'pageswithprop' => '有页面属性的页面',
 'pageswithprop-legend' => '有页面属性的页面',
-'pageswithprop-text' => '此页é\9d¢å\88\97å\87ºäº\86使ç\94¨ç\89¹å®\9a页é\9d¢å±\9eæ\80§ç\9a\84页é\9d¢å\90\8då\8d\95。',
+'pageswithprop-text' => 'æ\9c¬é¡µé\9d¢å\88\97å\87ºä½¿ç\94¨ç\89¹å®\9a页é\9d¢å±\9eæ\80§ç\9a\84页é\9d¢。',
 'pageswithprop-prop' => '属性名称:',
 'pageswithprop-submit' => '提交',
 'pageswithprop-prophidden-long' => '长文本属性值已隐藏($1)',
 'pageswithprop-prophidden-binary' => '已隐藏二进制属性值($1)',
 
 'doubleredirects' => '双重重定向',
-'doubleredirectstext' => '本页面列出重定向至其他重定向页的页面。每行含有第一及第二重定向的链接和第二重定向的目标(通常是第一重定向应该指向的“真实”目标页面)。<del>带删除线的</del>条目已被解决。',
+'doubleredirectstext' => '本页面列出重定向至其他重定向页面的页面。每行含有第一及第二重定向的链接和第二重定向的目标(这通常是第一重定向应该指向的“实际”目标页面)。<del>带删除线的</del>条目已经被解决。',
 'double-redirect-fixed-move' => '[[$1]]已被移动。它现在重定向至[[$2]]。',
 'double-redirect-fixed-maintenance' => '修复双重重定向自[[$1]]至[[$2]]。',
-'double-redirect-fixer' => '重定向修复器',
+'double-redirect-fixer' => '重定向修复器',
 
 'brokenredirects' => '受损重定向',
-'brokenredirectstext' => '以下的重定向页面指向的是不存在的页面:',
+'brokenredirectstext' => '以下重定向链接至不存在的页面:',
 'brokenredirects-edit' => '编辑',
 'brokenredirects-delete' => '删除',
 
 'withoutinterwiki' => '无语言链接页面',
-'withoutinterwiki-summary' => '以下的页面是未有语言链接到其它语言版本。',
+'withoutinterwiki-summary' => '以下页面没有链接至其它语言版本。',
 'withoutinterwiki-legend' => '前缀',
 'withoutinterwiki-submit' => '显示',
 
-'fewestrevisions' => '版本最少页面',
+'fewestrevisions' => '有最少版本的页面',
 
 # Miscellaneous special pages
 'nbytes' => '$1字节',
@@ -2147,7 +2145,7 @@ $1',
 'ntransclusions' => '用于$1个页面中',
 'specialpage-empty' => '无该报告的结果。',
 'lonelypages' => '孤立页面',
-'lonelypagestext' => '以下页面尚未被{{SITENAME}}中的其它页面链接或被之包含。',
+'lonelypagestext' => '以下页面没有被{{SITENAME}}的其它页面链接或包含。',
 'uncategorizedpages' => '未归类页面',
 'uncategorizedcategories' => '未归类分类',
 'uncategorizedimages' => '未归类文件',
@@ -2159,30 +2157,30 @@ $1',
 'wantedpages' => '需要的页面',
 'wantedpages-badtitle' => '在结果组上的无效标题:$1',
 'wantedfiles' => '需要的文件',
-'wantedfiletext-cat' => 'ä¸\8bå\88\97被使ç\94¨ç\9a\84æ\96\87件并ä¸\8då­\98å\9c¨ã\80\82å·²å\88\97å\87ºå\8f¯è\83½å­\98å\9c¨å¤\96é\83¨åª\92ä½\93åº\93中ç\9a\84æ\96\87件ã\80\82ä»»ä½\95此类误æ\8a¥å°\86被<del>å\89\94é\99¤</del>ã\80\82æ­¤å¤\96ï¼\8c[[:$1]]å\88\97å\87ºå\88\97å\87ºäº\86åµ\8cå\85¥ä¸\8då­\98å\9c¨æ\96\87件ç\9a\84页é\9d¢。',
-'wantedfiletext-nocat' => 'ä¸\8bå\88\97被使ç\94¨ç\9a\84æ\96\87件并ä¸\8då­\98å\9c¨ã\80\82å·²å\88\97å\87ºå\8f¯è\83½å­\98å\9c¨å¤\96é\83¨åª\92ä½\93åº\93中ç\9a\84æ\96\87件ã\80\82ä»»ä½\95此类误æ\8a¥å°\86被<del>å\89\94é\99¤</del>。',
+'wantedfiletext-cat' => '以ä¸\8bæ\96\87件被使ç\94¨ï¼\8cä½\86并ä¸\8då­\98å\9c¨ã\80\82æ\9d¥è\87ªå¤\96é\83¨åº\93ç\9a\84æ\96\87件å\8d³ä½¿å­\98å\9c¨ä¹\9få\8f¯è\83½è¢«å\88\97å\87ºã\80\82ä»»ä½\95è¿\99类误æ\8a¥ä¼\9aç\94¨<del>å\88 é\99¤çº¿</del>æ \87è®°ã\80\82å\8f¦å¤\96ï¼\8cæ\8f\92å\85¥ä¸\8då­\98å\9c¨ç\9a\84æ\96\87件ç\9a\84页é\9d¢å\88\97äº\8e[[:$1]]。',
+'wantedfiletext-nocat' => '以ä¸\8bæ\96\87件被使ç\94¨ï¼\8cä½\86并ä¸\8då­\98å\9c¨ã\80\82æ\9d¥è\87ªå¤\96é\83¨åº\93ç\9a\84æ\96\87件å\8d³ä½¿å­\98å\9c¨ä¹\9få\8f¯è\83½è¢«å\88\97å\87ºã\80\82ä»»ä½\95è¿\99类误æ\8a¥ä¼\9aç\94¨<del>å\88 é\99¤çº¿</del>æ \87è®°。',
 'wantedtemplates' => '需要的模板',
 'mostlinked' => '最多链接页面',
 'mostlinkedcategories' => '最多链接分类',
 'mostlinkedtemplates' => '最多链接模板',
-'mostcategories' => 'æ\9c\80å¤\9aå\88\86ç±»页面',
+'mostcategories' => 'æ\9c\89æ\9c\80å¤\9aå\88\86ç±»ç\9a\84页面',
 'mostimages' => '最多链接文件',
-'mostinterwikis' => 'æ\9c\80å¤\9a跨语è¨\80é\93¾æ\8e¥页面',
-'mostrevisions' => 'æ\9c\80å¤\9aç\89\88æ\9c¬页面',
+'mostinterwikis' => 'æ\9c\89æ\9c\80å¤\9aè·¨wikiç\9a\84页面',
+'mostrevisions' => 'æ\9c\89æ\9c\80å¤\9aç\89\88æ\9c¬ç\9a\84页面',
 'prefixindex' => '所有有前缀的页面',
 'prefixindex-namespace' => '所有有前缀的页面($1名字空间)',
-'prefixindex-strip' => '在列表中省略前缀',
+'prefixindex-strip' => '在列表中除去前缀',
 'shortpages' => '短页面',
 'longpages' => '长页面',
 'deadendpages' => '断链页面',
-'deadendpagestext' => '以下页面没有链接到{{SITENAME}}中的其它页面。',
+'deadendpagestext' => '以下页面没有链接至{{SITENAME}}的其它页面。',
 'protectedpages' => '受保护页面',
 'protectedpages-indef' => '仅无限期保护',
 'protectedpages-cascade' => '仅连锁保护',
 'protectedpagestext' => '以下页面受到保护,不能移移或编辑',
 'protectedpagesempty' => '在这些参数下没有页面正在保护。',
 'protectedtitles' => '受保护标题',
-'protectedtitlestext' => '以下的页面已经被保护以防止创建',
+'protectedtitlestext' => '以下标题受到保护,不能创建',
 'protectedtitlesempty' => '在这些参数之下并无标题正在保护。',
 'listusers' => '用户列表',
 'listusers-editsonly' => '只显示有编辑的用户',
@@ -2195,9 +2193,8 @@ $1',
 'ancientpages' => '最老页面',
 'move' => '移动',
 'movethispage' => '移动本页',
-'unusedimagestext' => '下列文件已存在,但并未插入任何页面。
-请注意其它网站可能会直接通过URL链接此文件,因此下面列出的文件依然有可能被使用。',
-'unusedcategoriestext' => '虽然没有被其它页面或者分类所采用,但列表中的分类页依然存在。',
+'unusedimagestext' => '以下文件实际存在,但并没有插入任何页面。请注意,其他网站可能会使用直接URL链接某个文件,因此它即使被实际使用也可能在这里列出。',
+'unusedcategoriestext' => '以下分类页面实际存在,即使没有其它页面或分类利用它们。',
 'notargettitle' => '无目标',
 'notargettext' => '您还没有指定一个目标页面或用户以进行此项操作。',
 'nopagetitle' => '无目标页面',
@@ -2208,11 +2205,11 @@ $1',
 'querypage-disabled' => '本特殊页面因性能问题而停用。',
 
 # Book sources
-'booksources' => '网络书源',
+'booksources' => '图书来源',
 'booksources-search-legend' => '搜索图书来源',
 'booksources-isbn' => 'ISBN:',
 'booksources-go' => '提交',
-'booksources-text' => '以ä¸\8bæ\98¯ä¸\80äº\9bç½\91ç»\9c书åº\97ç\9a\84é\93¾æ\8e¥å\88\97表ï¼\8cå\85¶ä¸­å\8f¯è\83½æ\9c\89æ\82¨è¦\81æ\89¾ç\9a\84书ç±\8d的更多信息:',
+'booksources-text' => 'ä¸\8bé\9d¢æ\98¯é\94\80å\94®æ\96°ä¹¦å\92\8cäº\8cæ\89\8b书ç\9a\84å\85¶ä»\96ç½\91ç«\99ç\9a\84é\93¾æ\8e¥ç\9a\84å\88\97表ï¼\8cä¹\9få\8f¯è\83½æ\9c\89å\85³äº\8eä½ æ­£å\9c¨å¯»æ\89¾ç\9a\84å\9b¾ä¹¦的更多信息:',
 'booksources-invalid-isbn' => '提供的ISBN号码并不正确,请检查原始复制来源号码是否有误。',
 
 # Special:Log
@@ -2230,18 +2227,18 @@ $1',
 'alphaindexline' => '$1到$2',
 'nextpage' => '下一页($1)',
 'prevpage' => '上一页($1)',
-'allpagesfrom' => '显示从此处开始的页面:',
-'allpagesto' => '显示从此处结束的页面:',
+'allpagesfrom' => '显示页面开始于:',
+'allpagesto' => '显示页面结束于:',
 'allarticles' => '所有页面',
 'allinnamespace' => '所有页面($1名字空间)',
 'allnotinnamespace' => '所有页面(非$1名字空间)',
 'allpagesprev' => '前',
 'allpagesnext' => '后',
 'allpagessubmit' => '提交',
-'allpagesprefix' => '显示具有此前缀(名字空间)的页面:',
+'allpagesprefix' => '显示有该前缀的页面:',
 'allpagesbadtitle' => '给定的页面标题是非法的,或者具有一个内部语言或内部 wiki 的前缀。它可能包含一个或更多的不能用于标题的字符。',
 'allpages-bad-ns' => '在{{SITENAME}}中没有一个叫做"$1"的名字空间。',
-'allpages-hide-redirects' => '隐藏重定向',
+'allpages-hide-redirects' => '隐藏重定向',
 
 # SpecialCachedPage
 'cachedspecial-viewing-cached-ttl' => '你正在查看本页面至少$1前的缓存版本。',
@@ -2250,10 +2247,8 @@ $1',
 
 # Special:Categories
 'categories' => '分类',
-'categoriespagetext' => '以下的{{PLURAL:$1|分类}}中包含了页面或媒体。
-[[Special:UnusedCategories|未用分类]]不会在这里列示。
-请同时参阅[[Special:WantedCategories|需要的分类]]。',
-'categoriesfrom' => '显示由此项起之分类:',
+'categoriespagetext' => '以下{{PLURAL:$1|分类包含}}页面或媒体文件。[[Special:UnusedCategories|未使用分类]]不显示在这里。另请见[[Special:WantedCategories|需要的分类]]。',
+'categoriesfrom' => '显示分类开始于:',
 'special-categories-sort-count' => '按数量排列',
 'special-categories-sort-abc' => '按字母排列',
 
@@ -2289,8 +2284,7 @@ $1',
 
 # Special:ListGroupRights
 'listgrouprights' => '用户组权限',
-'listgrouprights-summary' => '以下面是一个在这个维基中所定义出来的用户权限列表,以及它们的访问权。
-更多有关个别权限的细节可以在[[{{MediaWiki:Listgrouprights-helppage}}|这里]]找到。',
+'listgrouprights-summary' => '以下是在本wiki定义的用户组及它们的相关访问权限的列表。可能有关于单个权限的[[{{MediaWiki:Listgrouprights-helppage}}|附加信息]]。',
 'listgrouprights-key' => '说明:
 * <span class="listgrouprights-granted">被授予的权限</span>
 * <span class="listgrouprights-revoked">被取消的权限</span>',
@@ -3127,14 +3121,14 @@ $2',
 'pageinfo-length' => '页面长度(字节)',
 'pageinfo-article-id' => '页面ID',
 'pageinfo-language' => '页面内容语言',
-'pageinfo-robot-policy' => '机器人索引',
+'pageinfo-robot-policy' => '爬虫索引',
 'pageinfo-robot-index' => '允许',
 'pageinfo-robot-noindex' => '不允许',
 'pageinfo-views' => '查看数',
 'pageinfo-watchers' => '页面监视者数',
 'pageinfo-few-watchers' => '少于$1个监视者',
-'pageinfo-redirects-name' => '重定向到此页的数量',
-'pageinfo-subpages-name' => '本页子页面数',
+'pageinfo-redirects-name' => '至该页面的重定向数',
+'pageinfo-subpages-name' => '该页面的子页面数',
 'pageinfo-subpages-value' => '$1($2个重定向页,$3个非重定向页)',
 'pageinfo-firstuser' => '页面创建者',
 'pageinfo-firsttime' => '页面创建日期',
@@ -3210,7 +3204,7 @@ $1',
 'svg-long-desc' => 'SVG文件,尺寸为$1 × $2像素,文件大小:$3',
 'svg-long-desc-animated' => '动画SVG文件,尺寸为$1 × $2像素,文件大小:$3',
 'svg-long-error' => '无效的SVG文件:$1',
-'show-big-image' => '完分辨率',
+'show-big-image' => '完分辨率',
 'show-big-image-preview' => '本预览的尺寸:$1。',
 'show-big-image-other' => '其他{{PLURAL:$2|分辨率}}:$1。',
 'show-big-image-size' => '$1×$2像素',
@@ -3938,7 +3932,7 @@ MediaWiki发表时预期有用,但对此'''无任何保证''',亦无隐含
 'specialpages-group-changes' => '最近更改与日志',
 'specialpages-group-media' => '媒体文件报告与上传',
 'specialpages-group-users' => '用户与权限',
-'specialpages-group-highuse' => '高使用页面',
+'specialpages-group-highuse' => '高使用页面',
 'specialpages-group-pages' => '页面列表',
 'specialpages-group-pagetools' => '页面工具',
 'specialpages-group-wiki' => '数据与工具',
index 4da841d..0ce7c4b 100644 (file)
@@ -2196,7 +2196,7 @@ $1',
 'allpagesprev' => '前',
 'allpagesnext' => '後',
 'allpagessubmit' => '提交',
-'allpagesprefix' => '顯示具有此前綴(名字空間)的頁面:',
+'allpagesprefix' => '顯示有此前綴的頁面:',
 'allpagesbadtitle' => '給定的頁面標題是非法的,或者具有一個內部語言或內部 wiki 的前綴。它可能包含一個或更多的不能用於標題的字元。',
 'allpages-bad-ns' => '在{{SITENAME}}中沒有一個叫做「$1」的名字空間。',
 'allpages-hide-redirects' => '隱藏重定向頁',
index ba7c3cf..2300694 100644 (file)
@@ -94,7 +94,7 @@ class UploadStashCleanup extends Maintenance {
 
                // Delete all the corresponding thumbnails...
                $dir = $tempRepo->getZonePath( 'thumb' );
-               $iterator = $tempRepo->getBackend()->getFileList( array( 'dir' => $dir ) );
+               $iterator = $tempRepo->getBackend()->getFileList( array( 'dir' => $dir, 'adviseStat' => 1 ) );
                $this->output( "Deleting old thumbnails...\n" );
                $i = 0;
                foreach ( $iterator as $file ) {
@@ -120,7 +120,9 @@ class UploadStashCleanup extends Maintenance {
                $i = 0;
                foreach ( $iterator as $file ) {
                        // Absolute sanity check for stashed files and file segments
-                       if ( !preg_match( '#(^\d{14}!|\.\d+\.\w+\.\d+$)#', basename( $file ) ) ) {
+                       $base = basename( $file );
+                       // @TODO: why are there thumbnails stored in here?
+                       if ( !preg_match( '#(^\d{14}!|\.\d+\.\w+\.\d+$|-\w{12}\.\w{6}\.\d+\.)#', $base ) ) {
                                $this->output( "Skipped non-stash $file\n" );
                                continue;
                        }
index 12458ee..e6e0f65 100644 (file)
@@ -21,6 +21,7 @@
                "../../resources/mediawiki.action/mediawiki.action.view.postEdit.js",
                "../../resources/mediawiki.page/mediawiki.page.startup.js",
                "../../resources/mediawiki.api",
-               "../../resources/jquery/jquery.localize.js"
+               "../../resources/jquery/jquery.localize.js",
+               "../../resources/jquery/jquery.spinner.js"
        ]
 }
index 7910ec8..4bb8369 100644 (file)
@@ -5,6 +5,7 @@
 
 /**
  * @method ajax
+ * @static
  * @source <http://api.jquery.com/jQuery.ajax/>
  * @return {jqXHR}
  */
index 378217f..5c93964 100644 (file)
@@ -132,6 +132,8 @@ class UpdateMediaWiki extends Maintenance {
                        wfCountDown( 5 );
                }
 
+               $time1 = new MWTimestamp();
+
                $shared = $this->hasOption( 'doshared' );
 
                $updates = array( 'core', 'extensions' );
@@ -164,8 +166,10 @@ class UpdateMediaWiki extends Maintenance {
                if ( !$this->hasOption( 'nopurge' ) ) {
                        $updater->purgeCache();
                }
+               $time2 = new MWTimestamp();
 
                $this->output( "\nDone.\n" );
+               $this->output( "\nThe job took ". $time2->diff( $time1 )->format( "%i:%S" ). "\n" );
        }
 
        function afterFinalSetup() {
index 463dec8..9eb5227 100644 (file)
@@ -1043,7 +1043,10 @@ return array(
                        'size-gigabytes',
                        'largefileserver',
                ),
-               'dependencies' => array( 'mediawiki.libs.jpegmeta', 'mediawiki.util' ),
+               'dependencies' => array(
+                       'mediawiki.libs.jpegmeta',
+                       'mediawiki.util',
+               ),
        ),
        'mediawiki.special.userlogin' => array(
                'styles' => array(
@@ -1124,7 +1127,6 @@ return array(
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
                'dependencies' => array(
-                       'mediawiki.legacy.wikibits',
                        'jquery.byteLimit',
                ),
                'position' => 'top',
@@ -1145,9 +1147,9 @@ return array(
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
                'dependencies' => array(
+                       'jquery.spinner',
                        'mediawiki.api',
                        'mediawiki.Title',
-                       'mediawiki.legacy.wikibits',
                        'mediawiki.util',
                ),
        ),
index 93e30b9..27dabc6 100644 (file)
@@ -1,7 +1,9 @@
 /**
- * jQuery spinner
+ * jQuery Spinner
  *
  * Simple jQuery plugin to create, inject and remove spinners.
+ *
+ * @class jQuery.plugin.spinner
  */
 ( function ( $ ) {
 
 
        $.extend({
                /**
-                * Creates a spinner element.
+                * Create a spinner element
                 *
                 * The argument is an object with options used to construct the spinner. These can be:
                 *
                 * It is a good practice to keep a reference to the created spinner to be able to remove it later.
-                * Alternatively one can use the id option and removeSpinner() (but make sure to choose an id
+                * Alternatively one can use the id option and #removeSpinner (but make sure to choose an id
                 * that's unlikely to cause conflicts, e.g. with extensions, gadgets or user scripts).
                 *
                 * CSS classes used:
-                *   .mw-spinner for every spinner
-                *   .mw-spinner-small / .mw-spinner-large for size
-                *   .mw-spinner-block / .mw-spinner-inline for display types
+                * - .mw-spinner for every spinner
+                * - .mw-spinner-small / .mw-spinner-large for size
+                * - .mw-spinner-block / .mw-spinner-inline for display types
                 *
-                * @example
                 *   // Create a large spinner reserving all available horizontal space.
                 *   var $spinner = $.createSpinner({ size: 'large', type: 'block' });
                 *   // Insert above page content.
                 *   $( '#mw-content-text' ).prepend( $spinner );
-                * @example
+                *
                 *   // Place a small inline spinner next to the "Save" button
                 *   var $spinner = $.createSpinner({ size: 'small', type: 'inline' });
                 *   // Alternatively, just `$.createSpinner();` as these are the default options.
                 *   $( '#wpSave' ).after( $spinner );
-                * @example
+                *
                 *   // The following two are equivalent:
                 *   $.createSpinner( 'magic' );
                 *   $.createSpinner({ id: 'magic' });
                 *
-                * @param {Object|String} opts [optional] ID string or options:
-                *  - id: If given, spinner will be given an id of "mw-spinner-<id>"
+                * @static
+                * @inheritable
+                * @param {Object|string} [opts] ID string or options:
+                *  - id: If given, spinner will be given an id of "mw-spinner-{id}"
                 *  - size: 'small' (default) or 'large' for a 20-pixel or 32-pixel spinner
                 *  - type: 'inline' (default) or 'block'. Inline creates an inline-block with width and
                 *    height equal to spinner size. Block is a block-level element with width 100%, height
                },
 
                /**
-                * Removes a spinner element.
+                * Remove a spinner element
                 *
-                * @param {String} id [optional] Id of the spinner, as passed to createSpinner.
-                * @return {jQuery} The (now detached) spinner.
+                * @static
+                * @inheritable
+                * @param {string} id Id of the spinner, as passed to #createSpinner
+                * @return {jQuery} The (now detached) spinner element
                 */
                removeSpinner: function ( id ) {
                        return $( '#mw-spinner-' + id ).remove();
        });
 
        /**
-        * Injects a spinner after the elements in the jQuery collection
-        * (as siblings, not children). Collection contents remain unchanged.
+        * Inject a spinner after each element in the collection
+        *
+        * Inserts spinner as siblings, not children, of the target elements.
+        * Collection contents remain unchanged.
         *
-        * @param {Object|String} opts See createSpinner() for description.
+        * @param {Object|string} [opts] See #createSpinner
         * @return {jQuery}
         */
        $.fn.injectSpinner = function ( opts ) {
                return this.after( $.createSpinner( opts ) );
        };
+
+       /**
+        * @class jQuery
+        * @mixins jQuery.plugin.spinner
+        */
+
 }( jQuery ) );
index 545cd07..e9afa4a 100644 (file)
                                        cleanTitle = title.replace( /_/g, ' ' );
                                        link = mw.html.element(
                                                'a', {
-                                                       href: mw.util.wikiGetlink( title ),
+                                                       href: mw.util.getUrl( title ),
                                                        title: cleanTitle
                                                }, cleanTitle
                                        );
index 4a64566..dde5abf 100644 (file)
                /**
                 * Get the URL to this title
                 *
-                * @see mw.util#wikiGetlink
+                * @see mw.util#getUrl
                 * @return {string}
                 */
                getUrl: function () {
-                       return mw.util.wikiGetlink( this.toString() );
+                       return mw.util.getUrl( this.toString() );
                },
 
                /**
index b634917..70b9be9 100644 (file)
                        var page, anchor, url;
 
                        page = nodes[0];
-                       url = mw.util.wikiGetlink( page );
+                       url = mw.util.getUrl( page );
 
                        // [[Some Page]] or [[Namespace:Some Page]]
                        if ( nodes.length === 1 ) {
index 9267a49..1f89792 100644 (file)
@@ -75,11 +75,11 @@ var mw = ( function ( $, undefined ) {
         * @class mw.Map
         *
         * @constructor
-        * @param {boolean} [global=false] Whether to store the values in the global window
-        *  object or a exclusively in the object property 'values'.
+        * @param {Object|boolean} [values] Value-bearing object to map, or boolean
+        *  true to map over the global object. Defaults to an empty object.
         */
-       function Map( global ) {
-               this.values = global === true ? window : {};
+       function Map( values ) {
+               this.values = values === true ? window : ( values || {} );
                return this;
        }
 
index 4334a9f..259f1c8 100644 (file)
                 *     e.g. { action: 'edit' }. Optional.
                 * @return {string} Location for a page with name of `str` or boolean false on error.
                 */
-               wikiGetlink: function ( str, params ) {
+               getUrl: function ( str, params ) {
                        var url = mw.config.get( 'wgArticlePath' ).replace( '$1',
                                util.wikiUrlencode( typeof str === 'string' ? str : mw.config.get( 'wgPageName' ) ) );
                        if ( params && !$.isEmptyObject( params ) ) {
                }
        };
 
+       /**
+        * @method wikiGetlink
+        * @inheritdoc #getUrl
+        * @deprecated since 1.23 Use #getUrl instead.
+        */
+       mw.log.deprecate( util, 'wikiGetlink', util.getUrl, 'Use mw.util.getUrl instead.' );
+
        mw.util = util;
 
 }( mediaWiki, jQuery ) );
index 462fa9c..dc142ca 100644 (file)
@@ -38,15 +38,16 @@ var ProtectionForm = window.ProtectionForm = {
                        check = document.createElement( 'input' );
                        check.id = 'mwProtectUnchained';
                        check.type = 'checkbox';
-                       cell.appendChild( check );
-                       window.addClickHandler( check, function () {
+                       $( check ).click( function () {
                                ProtectionForm.onChainClick();
                        } );
 
-                       cell.appendChild( document.createTextNode( ' ' ) );
                        label = document.createElement( 'label' );
                        label.htmlFor = 'mwProtectUnchained';
                        label.appendChild( document.createTextNode( opts.labelText ) );
+
+                       cell.appendChild( check );
+                       cell.appendChild( document.createTextNode( ' ' ) );
                        cell.appendChild( label );
 
                        check.checked = !this.areAllTypesMatching();
index 580cf25..d639f63 100644 (file)
@@ -2,7 +2,8 @@
 ( function ( mw, $ ) {
 var    licenseSelectorCheck, wgUploadWarningObj, wgUploadLicenseObj, fillDestFilename,
        ajaxUploadDestCheck = mw.config.get( 'wgAjaxUploadDestCheck' ),
-       fileExtensions = mw.config.get( 'wgFileExtensions' );
+       fileExtensions = mw.config.get( 'wgFileExtensions' ),
+       $spinnerDestCheck, $spinnerLicense;
 
 licenseSelectorCheck = window.licenseSelectorCheck = function () {
        var selector = document.getElementById( 'wpLicense' ),
@@ -151,7 +152,7 @@ wgUploadWarningObj = window.wgUploadWarningObj = {
                if ( !ajaxUploadDestCheck || this.nameToCheck === '' ) {
                        return;
                }
-               window.injectSpinner( document.getElementById( 'wpDestFile' ), 'destcheck' );
+               $spinnerDestCheck = $.createSpinner().insertAfter( '#wpDestFile' );
 
                var uploadWarningObj = this;
                ( new mw.Api() ).get( {
@@ -170,7 +171,8 @@ wgUploadWarningObj = window.wgUploadWarningObj = {
        },
 
        processResult: function ( result, fileName ) {
-               window.removeSpinner( 'destcheck' );
+               $spinnerDestCheck.remove();
+               $spinnerDestCheck = undefined;
                this.setWarning( result.html );
                this.responseCache[fileName] = result.html;
        },
@@ -179,7 +181,7 @@ wgUploadWarningObj = window.wgUploadWarningObj = {
                var warningElt = document.getElementById( 'wpDestFile-warning' ),
                        ackElt = document.getElementsByName( 'wpDestFileWarningAck' );
 
-               this.setInnerHTML(warningElt, warning);
+               this.setInnerHTML( warningElt, warning );
 
                // Set a value in the form indicating that the warning is acknowledged and
                // doesn't need to be redisplayed post-upload
@@ -314,7 +316,8 @@ wgUploadLicenseObj = window.wgUploadLicenseObj = {
                                return;
                        }
                }
-               window.injectSpinner( document.getElementById( 'wpLicense' ), 'license' );
+
+               $spinnerLicense = $.createSpinner().insertAfter( '#wpLicense' );
 
                title = document.getElementById( 'wpDestFile' ).value;
                if ( !title ) {
@@ -333,7 +336,8 @@ wgUploadLicenseObj = window.wgUploadLicenseObj = {
        },
 
        processResult: function ( result, license ) {
-               window.removeSpinner( 'license' );
+               $spinnerLicense.remove();
+               $spinnerLicense = undefined;
                this.responseCache[license] = result.parse.text['*'];
                this.showPreview( this.responseCache[license] );
        },
index 65db555..d28ca0a 100644 (file)
@@ -89,17 +89,17 @@ mw.log.deprecate( win, 'ie6_bugs', false, msg );
 msg = 'Use jQuery instead';
 
 // Ignored dummy values
-mw.log.deprecate( win, 'doneOnloadHook', undefined );
-mw.log.deprecate( win, 'onloadFuncts', [] );
-mw.log.deprecate( win, 'runOnloadHook', $.noop );
-mw.log.deprecate( win, 'changeText', $.noop );
-mw.log.deprecate( win, 'killEvt', $.noop );
-mw.log.deprecate( win, 'addHandler', $.noop );
-mw.log.deprecate( win, 'hookEvent', $.noop );
-mw.log.deprecate( win, 'addClickHandler', $.noop );
-mw.log.deprecate( win, 'removeHandler', $.noop );
-mw.log.deprecate( win, 'getElementsByClassName', function () { return []; } );
-mw.log.deprecate( win, 'getInnerText', function () { return ''; } );
+mw.log.deprecate( win, 'doneOnloadHook', undefined, msg );
+mw.log.deprecate( win, 'onloadFuncts', [], msg );
+mw.log.deprecate( win, 'runOnloadHook', $.noop, msg );
+mw.log.deprecate( win, 'changeText', $.noop, msg );
+mw.log.deprecate( win, 'killEvt', $.noop, msg );
+mw.log.deprecate( win, 'addHandler', $.noop, msg );
+mw.log.deprecate( win, 'hookEvent', $.noop, msg );
+mw.log.deprecate( win, 'addClickHandler', $.noop, msg );
+mw.log.deprecate( win, 'removeHandler', $.noop, msg );
+mw.log.deprecate( win, 'getElementsByClassName', function () { return []; }, msg );
+mw.log.deprecate( win, 'getInnerText', function () { return ''; }, msg );
 
 // Run a function after the window onload event is fired
 mw.log.deprecate( win, 'addOnloadHook', function ( hookFunct ) {
@@ -110,7 +110,7 @@ mw.log.deprecate( win, 'addOnloadHook', function ( hookFunct ) {
                // run immediately instead of queueing.
                hookFunct();
        }
-} );
+}, msg );
 
 $( win ).on( 'load', function () {
        var i, functs;
@@ -198,7 +198,7 @@ mw.log.deprecate( win, 'tooltipAccessKeyPrefix', 'alt-', msg );
 mw.log.deprecate( win, 'tooltipAccessKeyRegexp', /\[(alt-)?(.)\]$/, msg );
 mw.log.deprecate( win, 'updateTooltipAccessKeys', mw.util.updateTooltipAccessKeys, msg );
 mw.log.deprecate( win, 'addPortletLink', mw.util.addPortletLink, msg );
-mw.log.deprecate( win, 'appendCSS', mw.util.addCSS );
+mw.log.deprecate( win, 'appendCSS', mw.util.addCSS, msg );
 
 /**
  * Wikipage import methods
index 13312bb..d4d9f43 100644 (file)
@@ -14922,6 +14922,64 @@ language=sr
 </p>
 !!end
 
+!! test
+Don't break link parsing if language converter markup is in the caption.
+!! options
+language=sr variant=sr-ec
+!! input
+[[Main Page|-{R|main page}-]]
+!! result
+<p><a href="/wiki/Main_Page" title="Маин Паге">main page</a>
+</p>
+!! end
+
+# This test is currently broken in the PHP parser (bug 52661)
+!! test
+Don't break image parsing if language converter markup is in the caption.
+!! options
+language=sr
+disabled
+!! input
+[[File:Foobar.jpg|-{R|caption}-]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="caption"><img alt="caption" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+# This test is currently broken in the PHP parser (bug 52661)
+!! test
+Don't break list handling if language converter markup is in the item.
+!! options
+language=zh variant=zh-cn
+disabled
+!! input
+;-{zh-cn:AAA;zh-tw:BBB}-
+!! result
+<dl><dt>AAA
+</dt></dl>
+
+!! end
+
+# This test is currently broken in the PHP parser (bug 52661)
+!! test
+Don't break table handling if language converter markup is in the cell.
+!! options
+language=sr variant=sr-ec
+disabled
+!! input
+{|
+|-
+| -{R|B}-
+|}
+!! result
+<table>
+
+<tr>
+<td> B
+</td></tr></table>
+
+!! end
+
 !! test
 Bug 529: Uncovered bullet
 !! input
index 8c849bc..6ce78b5 100644 (file)
@@ -537,6 +537,12 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                global $wgDBprefix;
 
                $tables = $db->listTables( $wgDBprefix, __METHOD__ );
+
+               if ( $db->getType() === 'mysql' ) {
+                       # bug 43571: cannot clone VIEWs under MySQL
+                       $views = $db->listViews( $wgDBprefix, __METHOD__ );
+                       $tables = array_diff( $tables, $views );
+               }
                $tables = array_map( array( __CLASS__, 'unprefixTable' ), $tables );
 
                // Don't duplicate test tables from the previous fataled run
diff --git a/tests/phpunit/includes/CdbTest.php b/tests/phpunit/includes/CdbTest.php
deleted file mode 100644 (file)
index 4832ada..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * Test the CDB reader/writer
- * @covers CdbWriter_PHP
- * @covers CdbWriter_DBA
- */
-class CdbTest extends MediaWikiTestCase {
-
-       protected function setUp() {
-               parent::setUp();
-               if ( !CdbReader::haveExtension() ) {
-                       $this->markTestSkipped( 'Native CDB support is not available' );
-               }
-       }
-
-       /**
-        * @group medium
-        */
-       public function testCdb() {
-               $dir = wfTempDir();
-               if ( !is_writable( $dir ) ) {
-                       $this->markTestSkipped( "Temp dir isn't writable" );
-               }
-
-               $phpcdbfile = $this->getNewTempFile();
-               $dbacdbfile = $this->getNewTempFile();
-
-               $w1 = new CdbWriter_PHP( $phpcdbfile );
-               $w2 = new CdbWriter_DBA( $dbacdbfile );
-
-               $data = array();
-               for ( $i = 0; $i < 1000; $i++ ) {
-                       $key = $this->randomString();
-                       $value = $this->randomString();
-                       $w1->set( $key, $value );
-                       $w2->set( $key, $value );
-
-                       if ( !isset( $data[$key] ) ) {
-                               $data[$key] = $value;
-                       }
-               }
-
-               $w1->close();
-               $w2->close();
-
-               $this->assertEquals(
-                       md5_file( $phpcdbfile ),
-                       md5_file( $dbacdbfile ),
-                       'same hash'
-               );
-
-               $r1 = new CdbReader_PHP( $phpcdbfile );
-               $r2 = new CdbReader_DBA( $dbacdbfile );
-
-               foreach ( $data as $key => $value ) {
-                       if ( $key === '' ) {
-                               // Known bug
-                               continue;
-                       }
-                       $v1 = $r1->get( $key );
-                       $v2 = $r2->get( $key );
-
-                       $v1 = $v1 === false ? '(not found)' : $v1;
-                       $v2 = $v2 === false ? '(not found)' : $v2;
-
-                       # cdbAssert( 'Mismatch', $key, $v1, $v2 );
-                       $this->cdbAssert( "PHP error", $key, $v1, $value );
-                       $this->cdbAssert( "DBA error", $key, $v2, $value );
-               }
-       }
-
-       private function randomString() {
-               $len = mt_rand( 0, 10 );
-               $s = '';
-               for ( $j = 0; $j < $len; $j++ ) {
-                       $s .= chr( mt_rand( 0, 255 ) );
-               }
-
-               return $s;
-       }
-
-       private function cdbAssert( $msg, $key, $v1, $v2 ) {
-               $this->assertEquals(
-                       $v2,
-                       $v1,
-                       $msg . ', k=' . bin2hex( $key )
-               );
-       }
-}
diff --git a/tests/phpunit/includes/ExceptionTest.php b/tests/phpunit/includes/ExceptionTest.php
new file mode 100644 (file)
index 0000000..9e76045
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+/**
+ * Tests for includes/Exception.php.
+ *
+ * @author Antoine Musso
+ * @copyright Copyright © 2013, Antoine Musso
+ * @copyright Copyright © 2013, Wikimedia Foundation Inc.
+ * @file
+ */
+
+class ExceptionTest extends MediaWikiTestCase {
+
+       /**
+        * @expectedException MWException
+        */
+       function testMwexceptionThrowing() {
+               throw new MWException();
+       }
+
+       /**
+        * Verify the exception classes are JSON serializabe.
+        *
+        * @covers MWExceptionHandler::jsonSerializeException
+        * @dataProvider provideExceptionClasses
+        */
+       function testJsonSerializeExceptions( $exception_class ) {
+               $json = MWExceptionHandler::jsonSerializeException(
+                       new $exception_class()
+               );
+               $this->assertNotEquals( false, $json,
+                       "The $exception_class exception should be JSON serializable, got false." );
+       }
+
+       function provideExceptionClasses() {
+               return array(
+                       array( 'Exception' ),
+                       array( 'MWException' ),
+               );
+       }
+
+
+       /**
+        * Lame JSON schema validation.
+        *
+        * @covers MWExceptionHandler::jsonSerializeException
+        *
+        * @param $expectedKeyType String Type expected as returned by gettype()
+        * @param $exClass String An exception class (ie: Exception, MWException)
+        * @param $key String Name of the key to validate in the serialized JSON
+        * @dataProvider provideJsonSerializedKeys
+        */
+       function testJsonserializeexceptionKeys($expectedKeyType, $exClass, $key) {
+
+               # Make sure we log a backtrace:
+               $this->setMwGlobals( array( 'wgLogExceptionBacktrace' => true ) );
+
+               $json = json_decode(
+                       MWExceptionHandler::jsonSerializeException( new $exClass())
+               );
+               $this->assertObjectHasAttribute( $key, $json,
+                       "JSON serialized exception is missing key '$key'"
+               );
+               $this->assertInternalType( $expectedKeyType, $json->$key,
+                       "JSON serialized key '$key' has type " . gettype($json->$key)
+                       . " (expected: $expectedKeyType)."
+               );
+       }
+
+       /**
+        * Returns test cases: exception class, key name, gettype()
+        */
+       function provideJsonSerializedKeys() {
+               $testCases = array();
+               foreach( array( 'Exception', 'MWException' ) as $exClass ) {
+                       $exTests = array(
+                               array( 'string',  $exClass,  'id' ),
+                               array( 'string',  $exClass,  'file' ),
+                               array( 'integer', $exClass,  'line' ),
+                               array( 'string',  $exClass,  'message' ),
+                               array( 'null',    $exClass,  'url' ),
+                               # Backtrace only enabled with wgLogExceptionBacktrace = true
+                               array( 'array',   $exClass,  'backtrace' ),
+                       );
+                       $testCases = array_merge($testCases, $exTests);
+               }
+               return $testCases;
+       }
+
+       /**
+        * Given wgLogExceptionBacktrace is true
+        * then serialized exception SHOULD have a backtrace
+        *
+        * @covers MWExceptionHandler::jsonSerializeException
+        */
+       function testJsonserializeexceptionBacktracingEnabled() {
+               $this->setMwGlobals( array( 'wgLogExceptionBacktrace' => true ) );
+               $json = json_decode(
+                       MWExceptionHandler::jsonSerializeException( new Exception() )
+               );
+               $this->assertObjectHasAttribute( 'backtrace', $json );
+       }
+
+       /**
+        * Given wgLogExceptionBacktrace is false
+        * then serialized exception SHOULD NOT have a backtrace
+        *
+        * @covers MWExceptionHandler::jsonSerializeException
+        */
+       function testJsonserializeexceptionBacktracingDisabled() {
+               $this->setMwGlobals( array( 'wgLogExceptionBacktrace' => false ) );
+               $json = json_decode(
+                       MWExceptionHandler::jsonSerializeException( new Exception() )
+               );
+               $this->assertObjectNotHasAttribute( 'backtrace', $json );
+
+       }
+
+}
index 08d6ba8..665fa39 100644 (file)
@@ -34,6 +34,7 @@ class FormOptionsTest extends MediaWikiTestCase {
                $this->object->add( 'string1', 'string one' );
                $this->object->add( 'string2', 'string two' );
                $this->object->add( 'integer', 0 );
+               $this->object->add( 'float', 0.0 );
                $this->object->add( 'intnull', 0, FormOptions::INTNULL );
        }
 
@@ -45,6 +46,9 @@ class FormOptionsTest extends MediaWikiTestCase {
        private function assertGuessInt( $data ) {
                $this->guess( FormOptions::INT, $data );
        }
+       private function assertGuessFloat( $data ) {
+               $this->guess( FormOptions::FLOAT, $data );
+       }
        private function assertGuessString( $data ) {
                $this->guess( FormOptions::STRING, $data );
        }
@@ -71,10 +75,15 @@ class FormOptionsTest extends MediaWikiTestCase {
                $this->assertGuessInt( 5 );
                $this->assertGuessInt( 0x0F );
 
+               $this->assertGuessFloat( 0.0 );
+               $this->assertGuessFloat( 1.5 );
+               $this->assertGuessFloat( 1e3 );
+
                $this->assertGuessString( 'true' );
                $this->assertGuessString( 'false' );
                $this->assertGuessString( '5' );
                $this->assertGuessString( '0' );
+               $this->assertGuessString( '1.5' );
        }
 
        /**
index 41230a1..b06f3d2 100644 (file)
@@ -7,19 +7,13 @@ class WfExpandUrlTest extends MediaWikiTestCase {
         * @dataProvider provideExpandableUrls
         */
        public function testWfExpandUrl( $fullUrl, $shortUrl, $defaultProto, $server, $canServer, $httpsMode, $message ) {
-               // Fake $wgServer and $wgCanonicalServer
+               // Fake $wgServer, $wgCanonicalServer and $wgRequest->getProtocol()
                $this->setMwGlobals( array(
                        'wgServer' => $server,
                        'wgCanonicalServer' => $canServer,
+                       'wgRequest' => new FauxRequest( array(), false, null, $httpsMode ? 'https' : 'http' )
                ) );
 
-               // Fake $_SERVER['HTTPS'] if needed
-               if ( $httpsMode ) {
-                       $_SERVER['HTTPS'] = 'on';
-               } else {
-                       unset( $_SERVER['HTTPS'] );
-               }
-
                $this->assertEquals( $fullUrl, wfExpandUrl( $shortUrl, $defaultProto ), $message );
        }
 
diff --git a/tests/phpunit/includes/HashRingTest.php b/tests/phpunit/includes/HashRingTest.php
deleted file mode 100644 (file)
index 68dfea1..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * @group HashRing
- */
-class HashRingTest extends MediaWikiTestCase {
-       /**
-        * @covers HashRing
-        */
-       public function testHashRing() {
-               $ring = new HashRing( array( 's1' => 1, 's2' => 1, 's3' => 2, 's4' => 2, 's5' => 2, 's6' => 3 ) );
-
-               $locations = array();
-               for ( $i = 0; $i < 20; $i++ ) {
-                       $locations[ "hello$i"] = $ring->getLocation( "hello$i" );
-               }
-               $expectedLocations = array(
-                       "hello0" => "s5",
-                       "hello1" => "s6",
-                       "hello2" => "s2",
-                       "hello3" => "s5",
-                       "hello4" => "s6",
-                       "hello5" => "s4",
-                       "hello6" => "s5",
-                       "hello7" => "s4",
-                       "hello8" => "s5",
-                       "hello9" => "s5",
-                       "hello10" => "s3",
-                       "hello11" => "s6",
-                       "hello12" => "s1",
-                       "hello13" => "s3",
-                       "hello14" => "s3",
-                       "hello15" => "s5",
-                       "hello16" => "s4",
-                       "hello17" => "s6",
-                       "hello18" => "s6",
-                       "hello19" => "s3"
-               );
-
-               $this->assertEquals( $expectedLocations, $locations, 'Items placed at proper locations' );
-
-               $locations = array();
-               for ( $i = 0; $i < 5; $i++ ) {
-                       $locations[ "hello$i"] = $ring->getLocations( "hello$i", 2 );
-               }
-
-               $expectedLocations = array(
-                       "hello0" => array( "s5", "s6" ),
-                       "hello1" => array( "s6", "s4" ),
-                       "hello2" => array( "s2", "s1" ),
-                       "hello3" => array( "s5", "s6" ),
-                       "hello4" => array( "s6", "s4" ),
-               );
-               $this->assertEquals( $expectedLocations, $locations, 'Items placed at proper locations' );
-       }
-}
diff --git a/tests/phpunit/includes/IPTest.php b/tests/phpunit/includes/IPTest.php
deleted file mode 100644 (file)
index c074eea..0000000
+++ /dev/null
@@ -1,595 +0,0 @@
-<?php
-/**
- * Tests for IP validity functions.
- *
- * Ported from /t/inc/IP.t by avar.
- *
- * @group IP
- * @todo Test methods in this call should be split into a method and a
- * dataprovider.
- */
-
-class IPTest extends MediaWikiTestCase {
-       /**
-        *  not sure it should be tested with boolean false. hashar 20100924
-        * @covers IP::isIPAddress
-        */
-       public function testisIPAddress() {
-               $this->assertFalse( IP::isIPAddress( false ), 'Boolean false is not an IP' );
-               $this->assertFalse( IP::isIPAddress( true ), 'Boolean true is not an IP' );
-               $this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
-               $this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
-               $this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
-               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1' ), 'IPv6 with a double :: occurrence' );
-               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::' ), 'IPv6 with a double :: occurrence, last at end' );
-               $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1' ), 'IPv6 with a double :: occurrence, firt at beginning' );
-               $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
-               $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
-               $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
-               $this->assertFalse( IP::isIPAddress( 'fc:100:300' ), 'IPv6 with only 3 words' );
-
-               $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
-               $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
-               $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
-               $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
-               $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
-
-               $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
-                       '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
-               foreach ( $validIPs as $ip ) {
-                       $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
-               }
-       }
-
-       /**
-        * @covers IP::isIPv6
-        */
-       public function testisIPv6() {
-               $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
-               $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
-               $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
-               $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
-
-               $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
-               $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
-               $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
-               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
-               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
-               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
-
-               $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
-               $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
-
-               $this->assertFalse( IP::isIPv6( ':::' ) );
-               $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
-
-               $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
-               $this->assertTrue( IP::isIPv6( '::0' ) );
-               $this->assertTrue( IP::isIPv6( '::fc' ) );
-               $this->assertTrue( IP::isIPv6( '::fc:100' ) );
-               $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
-               $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
-               $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
-               $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
-               $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
-
-               $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
-               $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
-
-               $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
-               $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
-               $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
-
-               $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
-               $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
-               $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
-               $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
-               $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
-               $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
-               $this->assertTrue( IP::isIPv6( '2001::df' ), 'IPv6 with "::" and 2 words' );
-               $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
-               $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
-
-               $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
-               $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
-
-               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
-       }
-
-       /**
-        * @covers IP::isIPv4
-        */
-       public function testisIPv4() {
-               $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
-               $this->assertFalse( IP::isIPv4( true ), 'Boolean true is not an IP' );
-               $this->assertFalse( IP::isIPv4( "" ), 'Empty string is not an IP' );
-               $this->assertFalse( IP::isIPv4( 'abc' ) );
-               $this->assertFalse( IP::isIPv4( ':' ) );
-               $this->assertFalse( IP::isIPv4( '124.24.52' ), 'IPv4 not enough quads' );
-               $this->assertFalse( IP::isIPv4( '24.324.52.13' ), 'IPv4 out of range' );
-               $this->assertFalse( IP::isIPv4( '.24.52.13' ), 'IPv4 starts with period' );
-
-               $this->assertTrue( IP::isIPv4( '124.24.52.13' ) );
-               $this->assertTrue( IP::isIPv4( '1.24.52.13' ) );
-               $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
-       }
-
-       /**
-        * @covers IP::isValid
-        */
-       public function testValidIPs() {
-               foreach ( range( 0, 255 ) as $i ) {
-                       $a = sprintf( "%03d", $i );
-                       $b = sprintf( "%02d", $i );
-                       $c = sprintf( "%01d", $i );
-                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
-                               $ip = "$f.$f.$f.$f";
-                               $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv4 address" );
-                       }
-               }
-               foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
-                       $a = sprintf( "%04x", $i );
-                       $b = sprintf( "%03x", $i );
-                       $c = sprintf( "%02x", $i );
-                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
-                               $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
-                               $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv6 address" );
-                       }
-               }
-               // test with some abbreviations
-               $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
-               $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
-               $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
-               $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
-
-               $this->assertTrue( IP::isValid( 'fc:100::' ) );
-               $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
-               $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
-
-               $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
-               $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
-               $this->assertTrue( IP::isValid( '2001::df' ), 'IPv6 with "::" and 2 words' );
-               $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
-               $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
-               $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
-               $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
-
-               $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
-               $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
-       }
-
-       /**
-        * @covers IP::isValid
-        */
-       public function testInvalidIPs() {
-               // Out of range...
-               foreach ( range( 256, 999 ) as $i ) {
-                       $a = sprintf( "%03d", $i );
-                       $b = sprintf( "%02d", $i );
-                       $c = sprintf( "%01d", $i );
-                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
-                               $ip = "$f.$f.$f.$f";
-                               $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
-                       }
-               }
-               foreach ( range( 'g', 'z' ) as $i ) {
-                       $a = sprintf( "%04s", $i );
-                       $b = sprintf( "%03s", $i );
-                       $c = sprintf( "%02s", $i );
-                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
-                               $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
-                               $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv6 address" );
-                       }
-               }
-               // Have CIDR
-               $ipCIDRs = array(
-                       '212.35.31.121/32',
-                       '212.35.31.121/18',
-                       '212.35.31.121/24',
-                       '::ff:d:321:5/96',
-                       'ff::d3:321:5/116',
-                       'c:ff:12:1:ea:d:321:5/120',
-               );
-               foreach ( $ipCIDRs as $i ) {
-                       $this->assertFalse( IP::isValid( $i ),
-                               "$i is an invalid IP address because it is a block" );
-               }
-               // Incomplete/garbage
-               $invalid = array(
-                       'www.xn--var-xla.net',
-                       '216.17.184.G',
-                       '216.17.184.1.',
-                       '216.17.184',
-                       '216.17.184.',
-                       '256.17.184.1'
-               );
-               foreach ( $invalid as $i ) {
-                       $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
-               }
-       }
-
-       /**
-        * @covers IP::isValidBlock
-        */
-       public function testValidBlocks() {
-               $valid = array(
-                       '116.17.184.5/32',
-                       '0.17.184.5/30',
-                       '16.17.184.1/24',
-                       '30.242.52.14/1',
-                       '10.232.52.13/8',
-                       '30.242.52.14/0',
-                       '::e:f:2001/96',
-                       '::c:f:2001/128',
-                       '::10:f:2001/70',
-                       '::fe:f:2001/1',
-                       '::6d:f:2001/8',
-                       '::fe:f:2001/0',
-               );
-               foreach ( $valid as $i ) {
-                       $this->assertTrue( IP::isValidBlock( $i ), "$i is a valid IP block" );
-               }
-       }
-
-       /**
-        * @covers IP::isValidBlock
-        */
-       public function testInvalidBlocks() {
-               $invalid = array(
-                       '116.17.184.5/33',
-                       '0.17.184.5/130',
-                       '16.17.184.1/-1',
-                       '10.232.52.13/*',
-                       '7.232.52.13/ab',
-                       '11.232.52.13/',
-                       '::e:f:2001/129',
-                       '::c:f:2001/228',
-                       '::10:f:2001/-1',
-                       '::6d:f:2001/*',
-                       '::86:f:2001/ab',
-                       '::23:f:2001/',
-               );
-               foreach ( $invalid as $i ) {
-                       $this->assertFalse( IP::isValidBlock( $i ), "$i is not a valid IP block" );
-               }
-       }
-
-       /**
-        * Improve IP::sanitizeIP() code coverage
-        * @todo Most probably incomplete
-        */
-       public function testSanitizeIP() {
-               $this->assertNull( IP::sanitizeIP( '' ) );
-               $this->assertNull( IP::sanitizeIP( ' ' ) );
-       }
-
-       /**
-        * @covers IP::toUnsigned
-        * @dataProvider provideToUnsigned
-        */
-       public function testToUnsigned( $expected, $input ) {
-               $result = IP::toUnsigned( $input );
-               $this->assertTrue( $result === false || is_string( $result ) || is_int( $result ) );
-               $this->assertEquals( $expected, $result );
-       }
-
-       /**
-        * Provider for IP::testToUnsigned()
-        */
-       public static function provideToUnsigned() {
-               return array(
-                       array( 1, '0.0.0.1' ),
-                       array( 16909060, '1.2.3.4' ),
-                       array( 2130706433, '127.0.0.1' ),
-                       array( '2147483648', '128.0.0.0' ),
-                       array( '3735931646', '222.173.202.254' ),
-                       array( pow( 2, 32 ) - 1, '255.255.255.255' ),
-                       array( false, 'IN.VA.LI.D' ),
-                       array( 1, '::1' ),
-                       array( '42540766452641154071740215577757643572', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
-                       array( '42540766452641154071740215577757643572', '2001:db8:85a3::8a2e:0370:7334' ),
-                       array( false, 'IN:VA::LI:D' ),
-                       array( false, ':::1' )
-               );
-       }
-
-       /**
-        * @covers IP::toHex
-        * @dataProvider provideToHex
-        */
-       public function testToHex( $expected, $input ) {
-               $result = IP::toHex( $input );
-               $this->assertTrue( $result === false || is_string( $result ) );
-               $this->assertEquals( $expected, $result );
-       }
-
-       /**
-        * Provider for IP::testToHex()
-        */
-       public static function provideToHex() {
-               return array(
-                       array( '00000001', '0.0.0.1' ),
-                       array( '01020304', '1.2.3.4' ),
-                       array( '7F000001', '127.0.0.1' ),
-                       array( '80000000', '128.0.0.0' ),
-                       array( 'DEADCAFE', '222.173.202.254' ),
-                       array( 'FFFFFFFF', '255.255.255.255' ),
-                       array( false, 'IN.VA.LI.D' ),
-                       array( 'v6-00000000000000000000000000000001', '::1' ),
-                       array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
-                       array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
-                       array( false, 'IN:VA::LI:D' ),
-                       array( false, ':::1' )
-               );
-       }
-
-       /**
-        * @covers IP::isPublic
-        */
-       public function testPrivateIPs() {
-               $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
-               foreach ( $private as $p ) {
-                       $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
-               }
-               $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
-               foreach ( $public as $p ) {
-                       $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
-               }
-       }
-
-       // Private wrapper used to test CIDR Parsing.
-       private function assertFalseCIDR( $CIDR, $msg = '' ) {
-               $ff = array( false, false );
-               $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
-       }
-
-       // Private wrapper to test network shifting using only dot notation
-       private function assertNet( $expected, $CIDR ) {
-               $parse = IP::parseCIDR( $CIDR );
-               $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
-       }
-
-       /**
-        * @covers IP::hexToQuad
-        */
-       public function testHexToQuad() {
-               $this->assertEquals( '0.0.0.1', IP::hexToQuad( '00000001' ) );
-               $this->assertEquals( '255.0.0.0', IP::hexToQuad( 'FF000000' ) );
-               $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
-               $this->assertEquals( '10.188.222.255', IP::hexToQuad( '0ABCDEFF' ) );
-               // hex not left-padded...
-               $this->assertEquals( '0.0.0.0', IP::hexToQuad( '0' ) );
-               $this->assertEquals( '0.0.0.1', IP::hexToQuad( '1' ) );
-               $this->assertEquals( '0.0.0.255', IP::hexToQuad( 'FF' ) );
-               $this->assertEquals( '0.0.255.0', IP::hexToQuad( 'FF00' ) );
-       }
-
-       /**
-        * @covers IP::hexToOctet
-        */
-       public function testHexToOctet() {
-               $this->assertEquals( '0:0:0:0:0:0:0:1',
-                       IP::hexToOctet( '00000000000000000000000000000001' ) );
-               $this->assertEquals( '0:0:0:0:0:0:FF:3',
-                       IP::hexToOctet( '00000000000000000000000000FF0003' ) );
-               $this->assertEquals( '0:0:0:0:0:0:FF00:6',
-                       IP::hexToOctet( '000000000000000000000000FF000006' ) );
-               $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
-                       IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
-               $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
-                       IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
-               // hex not left-padded...
-               $this->assertEquals( '0:0:0:0:0:0:0:0', IP::hexToOctet( '0' ) );
-               $this->assertEquals( '0:0:0:0:0:0:0:1', IP::hexToOctet( '1' ) );
-               $this->assertEquals( '0:0:0:0:0:0:0:FF', IP::hexToOctet( 'FF' ) );
-               $this->assertEquals( '0:0:0:0:0:0:0:FFD0', IP::hexToOctet( 'FFD0' ) );
-               $this->assertEquals( '0:0:0:0:0:0:FA00:0', IP::hexToOctet( 'FA000000' ) );
-               $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
-       }
-
-       /**
-        * IP::parseCIDR() returns an array containing a signed IP address
-        * representing the network mask and the bit mask.
-        * @covers IP::parseCIDR
-        */
-       public function testCIDRParsing() {
-               $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
-               $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
-
-               // Verify if statement
-               $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
-               $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
-               $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
-               $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
-
-               // Check internal logic
-               # 0 mask always result in array(0,0)
-               $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
-               $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
-               $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
-
-               // @todo FIXME: Add more tests.
-
-               # This part test network shifting
-               $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
-               $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
-               $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
-               $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
-               $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
-               $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
-               $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
-               $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
-               $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
-       }
-
-       /**
-        * @covers IP::canonicalize
-        */
-       public function testIPCanonicalizeOnValidIp() {
-               $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
-                       'Canonicalization of a valid IP returns it unchanged' );
-       }
-
-       /**
-        * @covers IP::canonicalize
-        */
-       public function testIPCanonicalizeMappedAddress() {
-               $this->assertEquals(
-                       '192.0.2.152',
-                       IP::canonicalize( '::ffff:192.0.2.152' )
-               );
-               $this->assertEquals(
-                       '192.0.2.152',
-                       IP::canonicalize( '::192.0.2.152' )
-               );
-       }
-
-       /**
-        * Issues there are most probably from IP::toHex() or IP::parseRange()
-        * @covers IP::isInRange
-        * @dataProvider provideIPsAndRanges
-        */
-       public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
-               $this->assertEquals(
-                       $expected,
-                       IP::isInRange( $addr, $range ),
-                       $message
-               );
-       }
-
-       /** Provider for testIPIsInRange() */
-       public static function provideIPsAndRanges() {
-               # Format: (expected boolean, address, range, optional message)
-               return array(
-                       # IPv4
-                       array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
-                       array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
-                       array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
-
-                       array( false, '0.0.0.0', '192.0.2.0/24' ),
-                       array( false, '255.255.255', '192.0.2.0/24' ),
-
-                       # IPv6
-                       array( false, '::1', '2001:DB8::/32' ),
-                       array( false, '::', '2001:DB8::/32' ),
-                       array( false, 'FE80::1', '2001:DB8::/32' ),
-
-                       array( true, '2001:DB8::', '2001:DB8::/32' ),
-                       array( true, '2001:0DB8::', '2001:DB8::/32' ),
-                       array( true, '2001:DB8::1', '2001:DB8::/32' ),
-                       array( true, '2001:0DB8::1', '2001:DB8::/32' ),
-                       array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
-                               '2001:DB8::/32' ),
-
-                       array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
-               );
-       }
-
-       /**
-        * Test for IP::splitHostAndPort().
-        * @dataProvider provideSplitHostAndPort
-        */
-       public function testSplitHostAndPort( $expected, $input, $description ) {
-               $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
-       }
-
-       /**
-        * Provider for IP::splitHostAndPort()
-        */
-       public static function provideSplitHostAndPort() {
-               return array(
-                       array( false, '[', 'Unclosed square bracket' ),
-                       array( false, '[::', 'Unclosed square bracket 2' ),
-                       array( array( '::', false ), '::', 'Bare IPv6 0' ),
-                       array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
-                       array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
-                       array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
-                       array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
-                       array( false, '::x', 'Double colon but no IPv6' ),
-                       array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
-                       array( false, 'x:x', 'Hostname and invalid port' ),
-                       array( array( 'x', false ), 'x', 'Plain hostname' )
-               );
-       }
-
-       /**
-        * Test for IP::combineHostAndPort()
-        * @dataProvider provideCombineHostAndPort
-        */
-       public function testCombineHostAndPort( $expected, $input, $description ) {
-               list( $host, $port, $defaultPort ) = $input;
-               $this->assertEquals(
-                       $expected,
-                       IP::combineHostAndPort( $host, $port, $defaultPort ),
-                       $description );
-       }
-
-       /**
-        * Provider for IP::combineHostAndPort()
-        */
-       public static function provideCombineHostAndPort() {
-               return array(
-                       array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
-                       array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
-                       array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
-                       array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
-               );
-       }
-
-       /**
-        * Test for IP::sanitizeRange()
-        * @dataProvider provideIPCIDRs
-        */
-       public function testSanitizeRange( $input, $expected, $description ) {
-               $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
-       }
-
-       /**
-        * Provider for IP::testSanitizeRange()
-        */
-       public static function provideIPCIDRs() {
-               return array(
-                       array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
-                       array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
-                       array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
-                       array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
-                       array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
-                       array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
-                       array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
-                       array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
-               );
-       }
-
-       /**
-        * Test for IP::prettifyIP()
-        * @dataProvider provideIPsToPrettify
-        */
-       public function testPrettifyIP( $ip, $prettified ) {
-               $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
-       }
-
-       /**
-        * Provider for IP::testPrettifyIP()
-        */
-       public static function provideIPsToPrettify() {
-               return array(
-                       array( '0:0:0:0:0:0:0:0', '::' ),
-                       array( '0:0:0::0:0:0', '::' ),
-                       array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
-                       array( '0:0::f', '::f' ),
-                       array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
-                       array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
-                       array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
-                       array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
-                       array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
-                       array( '0:0:0:0:0:0:0:0/16', '::/16' ),
-                       array( '0:0:0::0:0:0/64', '::/64' ),
-                       array( '0:0::f/52', '::f/52' ),
-                       array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
-                       array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
-                       array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
-                       array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
-                       array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
-               );
-       }
-}
diff --git a/tests/phpunit/includes/StringUtilsTest.php b/tests/phpunit/includes/StringUtilsTest.php
deleted file mode 100644 (file)
index 89759e5..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-class StringUtilsTest extends MediaWikiTestCase {
-
-       /**
-        * This tests StringUtils::isUtf8 whenever we have the mbstring extension
-        * loaded.
-        *
-        * @covers StringUtils::isUtf8
-        * @dataProvider provideStringsForIsUtf8Check
-        */
-       public function testIsUtf8WithMbstring( $expected, $string ) {
-               if ( !function_exists( 'mb_check_encoding' ) ) {
-                       $this->markTestSkipped( 'Test requires the mbstring PHP extension' );
-               }
-               $this->assertEquals( $expected,
-                       StringUtils::isUtf8( $string ),
-                       'Testing string "' . $this->escaped( $string ) . '" with mb_check_encoding'
-               );
-       }
-
-       /**
-        * This tests StringUtils::isUtf8 making sure we use the pure PHP
-        * implementation used as a fallback when mb_check_encoding() is
-        * not available.
-        *
-        * @covers StringUtils::isUtf8
-        * @dataProvider provideStringsForIsUtf8Check
-        */
-       public function testIsUtf8WithPhpFallbackImplementation( $expected, $string ) {
-               $this->assertEquals( $expected,
-                       StringUtils::isUtf8( $string, /** disable mbstring: */true ),
-                       'Testing string "' . $this->escaped( $string ) . '" with pure PHP implementation'
-               );
-       }
-
-       /**
-        * Print high range characters as an hexadecimal
-        */
-       function escaped( $string ) {
-               $escaped = '';
-               $length = strlen( $string );
-               for ( $i = 0; $i < $length; $i++ ) {
-                       $char = $string[$i];
-                       $val = ord( $char );
-                       if ( $val > 127 ) {
-                               $escaped .= '\x' . dechex( $val );
-                       } else {
-                               $escaped .= $char;
-                       }
-               }
-
-               return $escaped;
-       }
-
-       /**
-        * See also "UTF-8 decoder capability and stress test" by
-        * Markus Kuhn:
-        * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
-        */
-       public static function provideStringsForIsUtf8Check() {
-               // Expected return values for StringUtils::isUtf8()
-               $PASS = true;
-               $FAIL = false;
-
-               return array(
-                       'some ASCII' => array( $PASS, 'Some ASCII' ),
-                       'euro sign' => array( $PASS, "Euro sign €" ),
-
-                       'first possible sequence 1 byte' => array( $PASS, "\x00" ),
-                       'first possible sequence 2 bytes' => array( $PASS, "\xc2\x80" ),
-                       'first possible sequence 3 bytes' => array( $PASS, "\xe0\xa0\x80" ),
-                       'first possible sequence 4 bytes' => array( $PASS, "\xf0\x90\x80\x80" ),
-                       'first possible sequence 5 bytes' => array( $FAIL, "\xf8\x88\x80\x80\x80" ),
-                       'first possible sequence 6 bytes' => array( $FAIL, "\xfc\x84\x80\x80\x80\x80" ),
-
-                       'last possible sequence 1 byte' => array( $PASS, "\x7f" ),
-                       'last possible sequence 2 bytes' => array( $PASS, "\xdf\xbf" ),
-                       'last possible sequence 3 bytes' => array( $PASS, "\xef\xbf\xbf" ),
-                       'last possible sequence 4 bytes (U+1FFFFF)' => array( $FAIL, "\xf7\xbf\xbf\xbf" ),
-                       'last possible sequence 5 bytes' => array( $FAIL, "\xfb\xbf\xbf\xbf\xbf" ),
-                       'last possible sequence 6 bytes' => array( $FAIL, "\xfd\xbf\xbf\xbf\xbf\xbf" ),
-
-                       'boundary 1' => array( $PASS, "\xed\x9f\xbf" ),
-                       'boundary 2' => array( $PASS, "\xee\x80\x80" ),
-                       'boundary 3' => array( $PASS, "\xef\xbf\xbd" ),
-                       'boundary 4' => array( $PASS, "\xf2\x80\x80\x80" ),
-                       'boundary 5 (U+FFFFF)' => array( $PASS, "\xf3\xbf\xbf\xbf" ),
-                       'boundary 6 (U+100000)' => array( $PASS, "\xf4\x80\x80\x80" ),
-                       'boundary 7 (U+10FFFF)' => array( $PASS, "\xf4\x8f\xbf\xbf" ),
-                       'boundary 8 (U+110000)' => array( $FAIL, "\xf4\x90\x80\x80" ),
-
-                       'malformed 1' => array( $FAIL, "\x80" ),
-                       'malformed 2' => array( $FAIL, "\xbf" ),
-                       'malformed 3' => array( $FAIL, "\x80\xbf" ),
-                       'malformed 4' => array( $FAIL, "\x80\xbf\x80" ),
-                       'malformed 5' => array( $FAIL, "\x80\xbf\x80\xbf" ),
-                       'malformed 6' => array( $FAIL, "\x80\xbf\x80\xbf\x80" ),
-                       'malformed 7' => array( $FAIL, "\x80\xbf\x80\xbf\x80\xbf" ),
-                       'malformed 8' => array( $FAIL, "\x80\xbf\x80\xbf\x80\xbf\x80" ),
-
-                       'last byte missing 1' => array( $FAIL, "\xc0" ),
-                       'last byte missing 2' => array( $FAIL, "\xe0\x80" ),
-                       'last byte missing 3' => array( $FAIL, "\xf0\x80\x80" ),
-                       'last byte missing 4' => array( $FAIL, "\xf8\x80\x80\x80" ),
-                       'last byte missing 5' => array( $FAIL, "\xfc\x80\x80\x80\x80" ),
-                       'last byte missing 6' => array( $FAIL, "\xdf" ),
-                       'last byte missing 7' => array( $FAIL, "\xef\xbf" ),
-                       'last byte missing 8' => array( $FAIL, "\xf7\xbf\xbf" ),
-                       'last byte missing 9' => array( $FAIL, "\xfb\xbf\xbf\xbf" ),
-                       'last byte missing 10' => array( $FAIL, "\xfd\xbf\xbf\xbf\xbf" ),
-
-                       'extra continuation byte 1' => array( $FAIL, "e\xaf" ),
-                       'extra continuation byte 2' => array( $FAIL, "\xc3\x89\xaf" ),
-                       'extra continuation byte 3' => array( $FAIL, "\xef\xbc\xa5\xaf" ),
-                       'extra continuation byte 4' => array( $FAIL, "\xf0\x9d\x99\xb4\xaf" ),
-
-                       'impossible bytes 1' => array( $FAIL, "\xfe" ),
-                       'impossible bytes 2' => array( $FAIL, "\xff" ),
-                       'impossible bytes 3' => array( $FAIL, "\xfe\xfe\xff\xff" ),
-
-                       'overlong sequences 1' => array( $FAIL, "\xc0\xaf" ),
-                       'overlong sequences 2' => array( $FAIL, "\xc1\xaf" ),
-                       'overlong sequences 3' => array( $FAIL, "\xe0\x80\xaf" ),
-                       'overlong sequences 4' => array( $FAIL, "\xf0\x80\x80\xaf" ),
-                       'overlong sequences 5' => array( $FAIL, "\xf8\x80\x80\x80\xaf" ),
-                       'overlong sequences 6' => array( $FAIL, "\xfc\x80\x80\x80\x80\xaf" ),
-
-                       'maximum overlong sequences 1' => array( $FAIL, "\xc1\xbf" ),
-                       'maximum overlong sequences 2' => array( $FAIL, "\xe0\x9f\xbf" ),
-                       'maximum overlong sequences 3' => array( $FAIL, "\xf0\x8f\xbf\xbf" ),
-                       'maximum overlong sequences 4' => array( $FAIL, "\xf8\x87\xbf\xbf" ),
-                       'maximum overlong sequences 5' => array( $FAIL, "\xfc\x83\xbf\xbf\xbf\xbf" ),
-
-                       'surrogates 1 (U+D799)' => array( $PASS, "\xed\x9f\xbf" ),
-                       'surrogates 2 (U+E000)' => array( $PASS, "\xee\x80\x80" ),
-                       'surrogates 3 (U+D800)' => array( $FAIL, "\xed\xa0\x80" ),
-                       'surrogates 4 (U+DBFF)' => array( $FAIL, "\xed\xaf\xbf" ),
-                       'surrogates 5 (U+DC00)' => array( $FAIL, "\xed\xb0\x80" ),
-                       'surrogates 6 (U+DFFF)' => array( $FAIL, "\xed\xbf\xbf" ),
-                       'surrogates 7 (U+D800 U+DC00)' => array( $FAIL, "\xed\xa0\x80\xed\xb0\x80" ),
-
-                       'noncharacters 1' => array( $PASS, "\xef\xbf\xbe" ),
-                       'noncharacters 2' => array( $PASS, "\xef\xbf\xbf" ),
-               );
-       }
-}
index fb63a56..b0d1726 100644 (file)
@@ -11,11 +11,11 @@ class TemplateCategoriesTest extends MediaWikiLangTestCase {
         * @covers Title::getParentCategories
         */
        public function testTemplateCategories() {
-               $title = Title::newFromText( "Categorized from template" );
-               $page = WikiPage::factory( $title );
                $user = new User();
-               $user->mRights = array( 'createpage', 'edit', 'purge' );
+               $user->mRights = array( 'createpage', 'edit', 'purge', 'delete' );
 
+               $title = Title::newFromText( "Categorized from template" );
+               $page = WikiPage::factory( $title );
                $page->doEditContent(
                        new WikitextContent( '{{Categorising template}}' ),
                        'Create a page with a template',
@@ -25,12 +25,13 @@ class TemplateCategoriesTest extends MediaWikiLangTestCase {
                );
 
                $this->assertEquals(
-                       array()
-                       , $title->getParentCategories()
+                       array(),
+                       $title->getParentCategories(),
+                       'Verify that the category doesn\'t contain the page before the template is created'
                );
 
+               // Create template
                $template = WikiPage::factory( Title::newFromText( 'Template:Categorising template' ) );
-
                $template->doEditContent(
                        new WikitextContent( '[[Category:Solved bugs]]' ),
                        'Add a category through a template',
@@ -45,9 +46,51 @@ class TemplateCategoriesTest extends MediaWikiLangTestCase {
                $jobs->loadParamsAndArgs( null, array( 'quiet' => true ), null );
                $jobs->execute();
 
+               // Make sure page is in the category
                $this->assertEquals(
-                       array( 'Category:Solved_bugs' => $title->getPrefixedText() )
-                       , $title->getParentCategories()
+                       array( 'Category:Solved_bugs' => $title->getPrefixedText() ),
+                       $title->getParentCategories(),
+                       'Verify that the page is in the category after the template is created'
+               );
+
+               // Edit the template
+               $template->doEditContent(
+                       new WikitextContent( '[[Category:Solved bugs 2]]' ),
+                       'Change the category added by the template',
+                       0,
+                       false,
+                       $user
                );
+
+               // Run the job queue
+               JobQueueGroup::destroySingletons();
+               $jobs = new RunJobs;
+               $jobs->loadParamsAndArgs( null, array( 'quiet' => true ), null );
+               $jobs->execute();
+
+               // Make sure page is in the right category
+               $this->assertEquals(
+                       array( 'Category:Solved_bugs_2' => $title->getPrefixedText() ),
+                       $title->getParentCategories(),
+                       'Verify that the page is in the right category after the template is edited'
+               );
+
+               // Now delete the template
+               $error = '';
+               $template->doDeleteArticleReal( 'Delete the template', false, 0, true, $error, $user );
+
+               // Run the job queue
+               JobQueueGroup::destroySingletons();
+               $jobs = new RunJobs;
+               $jobs->loadParamsAndArgs( null, array( 'quiet' => true ), null );
+               $jobs->execute();
+
+               // Make sure the page is no longer in the category
+               $this->assertEquals(
+                       array(),
+                       $title->getParentCategories(),
+                       'Verify that the page is no longer in the category after template deletion'
+               );
+
        }
 }
diff --git a/tests/phpunit/includes/UIDGeneratorTest.php b/tests/phpunit/includes/UIDGeneratorTest.php
deleted file mode 100644 (file)
index 8f78ae5..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-class UIDGeneratorTest extends MediaWikiTestCase {
-
-       /**
-        * @dataProvider provider_testTimestampedUID
-        * @covers UIDGenerator::newTimestampedUID128
-        * @covers UIDGenerator::newTimestampedUID88
-        */
-       public function testTimestampedUID( $method, $digitlen, $bits, $tbits, $hostbits ) {
-               $id = call_user_func( array( 'UIDGenerator', $method ) );
-               $this->assertEquals( true, ctype_digit( $id ), "UID made of digit characters" );
-               $this->assertLessThanOrEqual( $digitlen, strlen( $id ),
-                       "UID has the right number of digits" );
-               $this->assertLessThanOrEqual( $bits, strlen( wfBaseConvert( $id, 10, 2 ) ),
-                       "UID has the right number of bits" );
-
-               $ids = array();
-               for ( $i = 0; $i < 300; $i++ ) {
-                       $ids[] = call_user_func( array( 'UIDGenerator', $method ) );
-               }
-
-               $lastId = array_shift( $ids );
-               if ( $hostbits ) {
-                       $lastHost = substr( wfBaseConvert( $lastId, 10, 2, $bits ), -$hostbits );
-               }
-
-               $this->assertArrayEquals( array_unique( $ids ), $ids, "All generated IDs are unique." );
-
-               foreach ( $ids as $id ) {
-                       $id_bin = wfBaseConvert( $id, 10, 2 );
-                       $lastId_bin = wfBaseConvert( $lastId, 10, 2 );
-
-                       $this->assertGreaterThanOrEqual(
-                               substr( $id_bin, 0, $tbits ),
-                               substr( $lastId_bin, 0, $tbits ),
-                               "New ID timestamp ($id_bin) >= prior one ($lastId_bin)." );
-
-                       if ( $hostbits ) {
-                               $this->assertEquals(
-                                       substr( $id_bin, 0, -$hostbits ),
-                                       substr( $lastId_bin, 0, -$hostbits ),
-                                       "Host ID of ($id_bin) is same as prior one ($lastId_bin)." );
-                       }
-
-                       $lastId = $id;
-               }
-       }
-
-       /**
-        * array( method, length, bits, hostbits )
-        * NOTE: When adding a new method name here please update the covers tags for the tests!
-        */
-       public static function provider_testTimestampedUID() {
-               return array(
-                       array( 'newTimestampedUID128', 39, 128, 46, 48 ),
-                       array( 'newTimestampedUID128', 39, 128, 46, 48 ),
-                       array( 'newTimestampedUID88', 27, 88, 46, 32 ),
-               );
-       }
-
-       /**
-        * @covers UIDGenerator::newUUIDv4
-        */
-       public function testUUIDv4() {
-               for ( $i = 0; $i < 100; $i++ ) {
-                       $id = UIDGenerator::newUUIDv4();
-                       $this->assertEquals( true,
-                               preg_match( '!^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$!', $id ),
-                               "UID $id has the right format" );
-               }
-       }
-
-       /**
-        * @covers UIDGenerator::newRawUUIDv4
-        */
-       public function testRawUUIDv4() {
-               for ( $i = 0; $i < 100; $i++ ) {
-                       $id = UIDGenerator::newRawUUIDv4();
-                       $this->assertEquals( true,
-                               preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
-                               "UID $id has the right format" );
-               }
-       }
-
-       /**
-        * @covers UIDGenerator::newRawUUIDv4
-        */
-       public function testRawUUIDv4QuickRand() {
-               for ( $i = 0; $i < 100; $i++ ) {
-                       $id = UIDGenerator::newRawUUIDv4( UIDGenerator::QUICK_RAND );
-                       $this->assertEquals( true,
-                               preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
-                               "UID $id has the right format" );
-               }
-       }
-
-}
diff --git a/tests/phpunit/includes/ZipDirectoryReaderTest.php b/tests/phpunit/includes/ZipDirectoryReaderTest.php
deleted file mode 100644 (file)
index 2627a41..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-/**
- * @covers ZipDirectoryReader
- * NOTE: this test is more like an integration test than a unit test
- */
-class ZipDirectoryReaderTest extends MediaWikiTestCase {
-       protected $zipDir;
-       protected $entries;
-
-       protected function setUp() {
-               parent::setUp();
-               $this->zipDir = __DIR__ . '/../data/zip';
-       }
-
-       function zipCallback( $entry ) {
-               $this->entries[] = $entry;
-       }
-
-       function readZipAssertError( $file, $error, $assertMessage ) {
-               $this->entries = array();
-               $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", array( $this, 'zipCallback' ) );
-               $this->assertTrue( $status->hasMessage( $error ), $assertMessage );
-       }
-
-       function readZipAssertSuccess( $file, $assertMessage ) {
-               $this->entries = array();
-               $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", array( $this, 'zipCallback' ) );
-               $this->assertTrue( $status->isOK(), $assertMessage );
-       }
-
-       public function testEmpty() {
-               $this->readZipAssertSuccess( 'empty.zip', 'Empty zip' );
-       }
-
-       public function testMultiDisk0() {
-               $this->readZipAssertError( 'split.zip', 'zip-unsupported',
-                       'Split zip error' );
-       }
-
-       public function testNoSignature() {
-               $this->readZipAssertError( 'nosig.zip', 'zip-wrong-format',
-                       'No signature should give "wrong format" error' );
-       }
-
-       public function testSimple() {
-               $this->readZipAssertSuccess( 'class.zip', 'Simple ZIP' );
-               $this->assertEquals( $this->entries, array( array(
-                       'name' => 'Class.class',
-                       'mtime' => '20010115000000',
-                       'size' => 1,
-               ) ) );
-       }
-
-       public function testBadCentralEntrySignature() {
-               $this->readZipAssertError( 'wrong-central-entry-sig.zip', 'zip-bad',
-                       'Bad central entry error' );
-       }
-
-       public function testTrailingBytes() {
-               $this->readZipAssertError( 'trail.zip', 'zip-bad',
-                       'Trailing bytes error' );
-       }
-
-       public function testWrongCDStart() {
-               $this->readZipAssertError( 'wrong-cd-start-disk.zip', 'zip-unsupported',
-                       'Wrong CD start disk error' );
-       }
-
-
-       public function testCentralDirectoryGap() {
-               $this->readZipAssertError( 'cd-gap.zip', 'zip-bad',
-                       'CD gap error' );
-       }
-
-       public function testCentralDirectoryTruncated() {
-               $this->readZipAssertError( 'cd-truncated.zip', 'zip-bad',
-                       'CD truncated error (should hit unpack() overrun)' );
-       }
-
-       public function testLooksLikeZip64() {
-               $this->readZipAssertError( 'looks-like-zip64.zip', 'zip-unsupported',
-                       'A file which looks like ZIP64 but isn\'t, should give error' );
-       }
-}
index 2cf6dca..d075f54 100644 (file)
@@ -8,6 +8,9 @@
  */
 class ApiFormatWddxTest extends ApiFormatTestBase {
 
+       /**
+        * @requires function wddx_deserialize
+        */
        public function testValidSyntax( ) {
                $data = $this->apiRequest( 'wddx', array( 'action' => 'query', 'meta' => 'siteinfo' ) );
 
index 134f856..10a9227 100644 (file)
@@ -31,6 +31,7 @@
  */
 class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
        // From DatabaseBase
+       function __construct() {}
        protected function closeConnection() {}
        protected function doQuery( $sql ) {}
 
@@ -123,4 +124,87 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                return json_decode( '"' . $str . '"' );
        }
 
+       function getMockForViews() {
+               $db = $this->getMockBuilder( 'DatabaseMysql' )
+                       ->disableOriginalConstructor()
+                       ->setMethods( array( 'fetchRow', 'query' ) )
+                       ->getMock();
+
+               $db->expects( $this->any() )
+                       ->method( 'query' )
+                       ->with( $this->anything() )
+                       ->will(
+                               $this->returnValue( null )
+                       );
+
+               $db->expects( $this->any() )
+                       ->method( 'fetchRow' )
+                       ->with( $this->anything() )
+                       ->will( $this->onConsecutiveCalls(
+                               array( 'Tables_in_' => 'view1' ),
+                               array( 'Tables_in_' => 'view2' ),
+                               array( 'Tables_in_' => 'myview' ),
+                               false  # no more rows
+                       ));
+               return $db;
+       }
+       /**
+        * @covers DatabaseMysqlBase::listViews
+        */
+       function testListviews() {
+               $db = $this->getMockForViews();
+
+               // The first call populate an internal cache of views
+               $this->assertEquals( array( 'view1', 'view2', 'myview'),
+                       $db->listViews() );
+               $this->assertEquals( array( 'view1', 'view2', 'myview'),
+                       $db->listViews() );
+
+               // Prefix filtering
+               $this->assertEquals( array( 'view1', 'view2' ),
+                       $db->listViews( 'view' ) );
+               $this->assertEquals( array( 'myview' ),
+                       $db->listViews( 'my' ) );
+               $this->assertEquals( array(),
+                       $db->listViews( 'UNUSED_PREFIX' ) );
+               $this->assertEquals( array( 'view1', 'view2', 'myview'),
+                       $db->listViews( '' ) );
+       }
+
+       /**
+        * @covers DatabaseMysqlBase::isView
+        * @dataProvider provideViewExistanceChecks
+        */
+       function testIsView( $isView, $viewName ) {
+               $db = $this->getMockForViews();
+
+               switch( $isView ) {
+                       case true:
+                               $this->assertTrue( $db->isView( $viewName ),
+                                       "$viewName should be considered a view" );
+                       break;
+
+                       case false:
+                               $this->assertFalse( $db->isView( $viewName ),
+                                       "$viewName has not been defined as a view" );
+                       break;
+               }
+
+       }
+
+       function provideViewExistanceChecks() {
+               return array(
+                       // format: whether it is a view, view name
+                       array( true, 'view1' ),
+                       array( true, 'view2' ),
+                       array( true, 'myview' ),
+
+                       array( false, 'user' ),
+
+                       array( false, 'view10' ),
+                       array( false, 'my' ),
+                       array( false, 'OH_MY_GOD' ),  # they killed kenny!
+               );
+       }
+
 }
index 70ee946..65726eb 100644 (file)
@@ -149,6 +149,14 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                $this->assertEquals( "ALTER TABLE foo ADD COLUMN foo_bar INTEGER DEFAULT 42",
                        $this->replaceVars( "ALTER TABLE foo\nADD COLUMN foo_bar int(10) unsigned DEFAULT 42" )
                );
+
+               $this->assertEquals( "DROP INDEX foo",
+                       $this->replaceVars( "DROP INDEX /*i*/foo ON /*_*/bar" )
+               );
+
+               $this->assertEquals( "DROP INDEX foo -- dropping index",
+                       $this->replaceVars( "DROP INDEX /*i*/foo ON /*_*/bar -- dropping index" )
+               );
        }
 
        /**
diff --git a/tests/phpunit/includes/utils/CdbTest.php b/tests/phpunit/includes/utils/CdbTest.php
new file mode 100644 (file)
index 0000000..487ee1f
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * Test the CDB reader/writer
+ * @covers CdbWriterPHP
+ * @covers CdbWriterDBA
+ */
+class CdbTest extends MediaWikiTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+               if ( !CdbReader::haveExtension() ) {
+                       $this->markTestSkipped( 'Native CDB support is not available' );
+               }
+       }
+
+       /**
+        * @group medium
+        */
+       public function testCdb() {
+               $dir = wfTempDir();
+               if ( !is_writable( $dir ) ) {
+                       $this->markTestSkipped( "Temp dir isn't writable" );
+               }
+
+               $phpcdbfile = $this->getNewTempFile();
+               $dbacdbfile = $this->getNewTempFile();
+
+               $w1 = new CdbWriterPHP( $phpcdbfile );
+               $w2 = new CdbWriterDBA( $dbacdbfile );
+
+               $data = array();
+               for ( $i = 0; $i < 1000; $i++ ) {
+                       $key = $this->randomString();
+                       $value = $this->randomString();
+                       $w1->set( $key, $value );
+                       $w2->set( $key, $value );
+
+                       if ( !isset( $data[$key] ) ) {
+                               $data[$key] = $value;
+                       }
+               }
+
+               $w1->close();
+               $w2->close();
+
+               $this->assertEquals(
+                       md5_file( $phpcdbfile ),
+                       md5_file( $dbacdbfile ),
+                       'same hash'
+               );
+
+               $r1 = new CdbReaderPHP( $phpcdbfile );
+               $r2 = new CdbReaderDBA( $dbacdbfile );
+
+               foreach ( $data as $key => $value ) {
+                       if ( $key === '' ) {
+                               // Known bug
+                               continue;
+                       }
+                       $v1 = $r1->get( $key );
+                       $v2 = $r2->get( $key );
+
+                       $v1 = $v1 === false ? '(not found)' : $v1;
+                       $v2 = $v2 === false ? '(not found)' : $v2;
+
+                       # cdbAssert( 'Mismatch', $key, $v1, $v2 );
+                       $this->cdbAssert( "PHP error", $key, $v1, $value );
+                       $this->cdbAssert( "DBA error", $key, $v2, $value );
+               }
+       }
+
+       private function randomString() {
+               $len = mt_rand( 0, 10 );
+               $s = '';
+               for ( $j = 0; $j < $len; $j++ ) {
+                       $s .= chr( mt_rand( 0, 255 ) );
+               }
+
+               return $s;
+       }
+
+       private function cdbAssert( $msg, $key, $v1, $v2 ) {
+               $this->assertEquals(
+                       $v2,
+                       $v1,
+                       $msg . ', k=' . bin2hex( $key )
+               );
+       }
+}
diff --git a/tests/phpunit/includes/utils/HashRingTest.php b/tests/phpunit/includes/utils/HashRingTest.php
new file mode 100644 (file)
index 0000000..68dfea1
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @group HashRing
+ */
+class HashRingTest extends MediaWikiTestCase {
+       /**
+        * @covers HashRing
+        */
+       public function testHashRing() {
+               $ring = new HashRing( array( 's1' => 1, 's2' => 1, 's3' => 2, 's4' => 2, 's5' => 2, 's6' => 3 ) );
+
+               $locations = array();
+               for ( $i = 0; $i < 20; $i++ ) {
+                       $locations[ "hello$i"] = $ring->getLocation( "hello$i" );
+               }
+               $expectedLocations = array(
+                       "hello0" => "s5",
+                       "hello1" => "s6",
+                       "hello2" => "s2",
+                       "hello3" => "s5",
+                       "hello4" => "s6",
+                       "hello5" => "s4",
+                       "hello6" => "s5",
+                       "hello7" => "s4",
+                       "hello8" => "s5",
+                       "hello9" => "s5",
+                       "hello10" => "s3",
+                       "hello11" => "s6",
+                       "hello12" => "s1",
+                       "hello13" => "s3",
+                       "hello14" => "s3",
+                       "hello15" => "s5",
+                       "hello16" => "s4",
+                       "hello17" => "s6",
+                       "hello18" => "s6",
+                       "hello19" => "s3"
+               );
+
+               $this->assertEquals( $expectedLocations, $locations, 'Items placed at proper locations' );
+
+               $locations = array();
+               for ( $i = 0; $i < 5; $i++ ) {
+                       $locations[ "hello$i"] = $ring->getLocations( "hello$i", 2 );
+               }
+
+               $expectedLocations = array(
+                       "hello0" => array( "s5", "s6" ),
+                       "hello1" => array( "s6", "s4" ),
+                       "hello2" => array( "s2", "s1" ),
+                       "hello3" => array( "s5", "s6" ),
+                       "hello4" => array( "s6", "s4" ),
+               );
+               $this->assertEquals( $expectedLocations, $locations, 'Items placed at proper locations' );
+       }
+}
diff --git a/tests/phpunit/includes/utils/IPTest.php b/tests/phpunit/includes/utils/IPTest.php
new file mode 100644 (file)
index 0000000..24b077a
--- /dev/null
@@ -0,0 +1,610 @@
+<?php
+/**
+ * Tests for IP validity functions.
+ *
+ * Ported from /t/inc/IP.t by avar.
+ *
+ * @group IP
+ * @todo Test methods in this call should be split into a method and a
+ * dataprovider.
+ */
+
+class IPTest extends MediaWikiTestCase {
+       /**
+        *  not sure it should be tested with boolean false. hashar 20100924
+        * @covers IP::isIPAddress
+        */
+       public function testisIPAddress() {
+               $this->assertFalse( IP::isIPAddress( false ), 'Boolean false is not an IP' );
+               $this->assertFalse( IP::isIPAddress( true ), 'Boolean true is not an IP' );
+               $this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
+               $this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
+               $this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
+               $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1' ), 'IPv6 with a double :: occurrence' );
+               $this->assertFalse(
+                       IP::isIPAddress( '2001:0DB8::A:1::' ),
+                       'IPv6 with a double :: occurrence, last at end'
+               );
+               $this->assertFalse(
+                       IP::isIPAddress( '::2001:0DB8::5:1' ),
+                       'IPv6 with a double :: occurrence, firt at beginning'
+               );
+               $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
+               $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
+               $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
+               $this->assertFalse( IP::isIPAddress( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+               $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
+               $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
+               $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
+               $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
+               $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
+
+               $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
+                       '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
+               foreach ( $validIPs as $ip ) {
+                       $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
+               }
+       }
+
+       /**
+        * @covers IP::isIPv6
+        */
+       public function testisIPv6() {
+               $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
+               $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
+               $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
+               $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+               $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
+               $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
+               $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
+               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
+               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
+               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
+
+               $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
+               $this->assertFalse(
+                       IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ),
+                       'IPv6 with 9 words ending with "::"'
+               );
+
+               $this->assertFalse( IP::isIPv6( ':::' ) );
+               $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
+
+               $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
+               $this->assertTrue( IP::isIPv6( '::0' ) );
+               $this->assertTrue( IP::isIPv6( '::fc' ) );
+               $this->assertTrue( IP::isIPv6( '::fc:100' ) );
+               $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
+               $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
+               $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
+               $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
+               $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
+
+               $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
+               $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
+
+               $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
+               $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
+               $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
+
+               $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
+               $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
+               $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
+               $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
+               $this->assertTrue( IP::isIPv6( '2001::df' ), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
+
+               $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
+               $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
+
+               $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
+       }
+
+       /**
+        * @covers IP::isIPv4
+        */
+       public function testisIPv4() {
+               $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
+               $this->assertFalse( IP::isIPv4( true ), 'Boolean true is not an IP' );
+               $this->assertFalse( IP::isIPv4( "" ), 'Empty string is not an IP' );
+               $this->assertFalse( IP::isIPv4( 'abc' ) );
+               $this->assertFalse( IP::isIPv4( ':' ) );
+               $this->assertFalse( IP::isIPv4( '124.24.52' ), 'IPv4 not enough quads' );
+               $this->assertFalse( IP::isIPv4( '24.324.52.13' ), 'IPv4 out of range' );
+               $this->assertFalse( IP::isIPv4( '.24.52.13' ), 'IPv4 starts with period' );
+
+               $this->assertTrue( IP::isIPv4( '124.24.52.13' ) );
+               $this->assertTrue( IP::isIPv4( '1.24.52.13' ) );
+               $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
+       }
+
+       /**
+        * @covers IP::isValid
+        */
+       public function testValidIPs() {
+               foreach ( range( 0, 255 ) as $i ) {
+                       $a = sprintf( "%03d", $i );
+                       $b = sprintf( "%02d", $i );
+                       $c = sprintf( "%01d", $i );
+                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+                               $ip = "$f.$f.$f.$f";
+                               $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv4 address" );
+                       }
+               }
+               foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
+                       $a = sprintf( "%04x", $i );
+                       $b = sprintf( "%03x", $i );
+                       $c = sprintf( "%02x", $i );
+                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+                               $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
+                               $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv6 address" );
+                       }
+               }
+               // test with some abbreviations
+               $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
+               $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
+               $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
+               $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+               $this->assertTrue( IP::isValid( 'fc:100::' ) );
+               $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
+               $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
+
+               $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
+               $this->assertTrue( IP::isValid( '2001::df' ), 'IPv6 with "::" and 2 words' );
+               $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
+               $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
+               $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
+
+               $this->assertFalse(
+                       IP::isValid( 'fc:100:a:d:1:e:ac:0::' ),
+                       'IPv6 with 8 words ending with "::"'
+               );
+               $this->assertFalse(
+                       IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ),
+                       'IPv6 with 9 words ending with "::"'
+               );
+       }
+
+       /**
+        * @covers IP::isValid
+        */
+       public function testInvalidIPs() {
+               // Out of range...
+               foreach ( range( 256, 999 ) as $i ) {
+                       $a = sprintf( "%03d", $i );
+                       $b = sprintf( "%02d", $i );
+                       $c = sprintf( "%01d", $i );
+                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+                               $ip = "$f.$f.$f.$f";
+                               $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
+                       }
+               }
+               foreach ( range( 'g', 'z' ) as $i ) {
+                       $a = sprintf( "%04s", $i );
+                       $b = sprintf( "%03s", $i );
+                       $c = sprintf( "%02s", $i );
+                       foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+                               $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
+                               $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv6 address" );
+                       }
+               }
+               // Have CIDR
+               $ipCIDRs = array(
+                       '212.35.31.121/32',
+                       '212.35.31.121/18',
+                       '212.35.31.121/24',
+                       '::ff:d:321:5/96',
+                       'ff::d3:321:5/116',
+                       'c:ff:12:1:ea:d:321:5/120',
+               );
+               foreach ( $ipCIDRs as $i ) {
+                       $this->assertFalse( IP::isValid( $i ),
+                               "$i is an invalid IP address because it is a block" );
+               }
+               // Incomplete/garbage
+               $invalid = array(
+                       'www.xn--var-xla.net',
+                       '216.17.184.G',
+                       '216.17.184.1.',
+                       '216.17.184',
+                       '216.17.184.',
+                       '256.17.184.1'
+               );
+               foreach ( $invalid as $i ) {
+                       $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
+               }
+       }
+
+       /**
+        * @covers IP::isValidBlock
+        */
+       public function testValidBlocks() {
+               $valid = array(
+                       '116.17.184.5/32',
+                       '0.17.184.5/30',
+                       '16.17.184.1/24',
+                       '30.242.52.14/1',
+                       '10.232.52.13/8',
+                       '30.242.52.14/0',
+                       '::e:f:2001/96',
+                       '::c:f:2001/128',
+                       '::10:f:2001/70',
+                       '::fe:f:2001/1',
+                       '::6d:f:2001/8',
+                       '::fe:f:2001/0',
+               );
+               foreach ( $valid as $i ) {
+                       $this->assertTrue( IP::isValidBlock( $i ), "$i is a valid IP block" );
+               }
+       }
+
+       /**
+        * @covers IP::isValidBlock
+        */
+       public function testInvalidBlocks() {
+               $invalid = array(
+                       '116.17.184.5/33',
+                       '0.17.184.5/130',
+                       '16.17.184.1/-1',
+                       '10.232.52.13/*',
+                       '7.232.52.13/ab',
+                       '11.232.52.13/',
+                       '::e:f:2001/129',
+                       '::c:f:2001/228',
+                       '::10:f:2001/-1',
+                       '::6d:f:2001/*',
+                       '::86:f:2001/ab',
+                       '::23:f:2001/',
+               );
+               foreach ( $invalid as $i ) {
+                       $this->assertFalse( IP::isValidBlock( $i ), "$i is not a valid IP block" );
+               }
+       }
+
+       /**
+        * Improve IP::sanitizeIP() code coverage
+        * @todo Most probably incomplete
+        */
+       public function testSanitizeIP() {
+               $this->assertNull( IP::sanitizeIP( '' ) );
+               $this->assertNull( IP::sanitizeIP( ' ' ) );
+       }
+
+       /**
+        * @covers IP::toUnsigned
+        * @dataProvider provideToUnsigned
+        */
+       public function testToUnsigned( $expected, $input ) {
+               $result = IP::toUnsigned( $input );
+               $this->assertTrue( $result === false || is_string( $result ) || is_int( $result ) );
+               $this->assertEquals( $expected, $result );
+       }
+
+       /**
+        * Provider for IP::testToUnsigned()
+        */
+       public static function provideToUnsigned() {
+               return array(
+                       array( 1, '0.0.0.1' ),
+                       array( 16909060, '1.2.3.4' ),
+                       array( 2130706433, '127.0.0.1' ),
+                       array( '2147483648', '128.0.0.0' ),
+                       array( '3735931646', '222.173.202.254' ),
+                       array( pow( 2, 32 ) - 1, '255.255.255.255' ),
+                       array( false, 'IN.VA.LI.D' ),
+                       array( 1, '::1' ),
+                       array( '42540766452641154071740215577757643572', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
+                       array( '42540766452641154071740215577757643572', '2001:db8:85a3::8a2e:0370:7334' ),
+                       array( false, 'IN:VA::LI:D' ),
+                       array( false, ':::1' )
+               );
+       }
+
+       /**
+        * @covers IP::toHex
+        * @dataProvider provideToHex
+        */
+       public function testToHex( $expected, $input ) {
+               $result = IP::toHex( $input );
+               $this->assertTrue( $result === false || is_string( $result ) );
+               $this->assertEquals( $expected, $result );
+       }
+
+       /**
+        * Provider for IP::testToHex()
+        */
+       public static function provideToHex() {
+               return array(
+                       array( '00000001', '0.0.0.1' ),
+                       array( '01020304', '1.2.3.4' ),
+                       array( '7F000001', '127.0.0.1' ),
+                       array( '80000000', '128.0.0.0' ),
+                       array( 'DEADCAFE', '222.173.202.254' ),
+                       array( 'FFFFFFFF', '255.255.255.255' ),
+                       array( false, 'IN.VA.LI.D' ),
+                       array( 'v6-00000000000000000000000000000001', '::1' ),
+                       array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
+                       array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
+                       array( false, 'IN:VA::LI:D' ),
+                       array( false, ':::1' )
+               );
+       }
+
+       /**
+        * @covers IP::isPublic
+        */
+       public function testPrivateIPs() {
+               $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
+               foreach ( $private as $p ) {
+                       $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
+               }
+               $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
+               foreach ( $public as $p ) {
+                       $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
+               }
+       }
+
+       // Private wrapper used to test CIDR Parsing.
+       private function assertFalseCIDR( $CIDR, $msg = '' ) {
+               $ff = array( false, false );
+               $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
+       }
+
+       // Private wrapper to test network shifting using only dot notation
+       private function assertNet( $expected, $CIDR ) {
+               $parse = IP::parseCIDR( $CIDR );
+               $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
+       }
+
+       /**
+        * @covers IP::hexToQuad
+        */
+       public function testHexToQuad() {
+               $this->assertEquals( '0.0.0.1', IP::hexToQuad( '00000001' ) );
+               $this->assertEquals( '255.0.0.0', IP::hexToQuad( 'FF000000' ) );
+               $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
+               $this->assertEquals( '10.188.222.255', IP::hexToQuad( '0ABCDEFF' ) );
+               // hex not left-padded...
+               $this->assertEquals( '0.0.0.0', IP::hexToQuad( '0' ) );
+               $this->assertEquals( '0.0.0.1', IP::hexToQuad( '1' ) );
+               $this->assertEquals( '0.0.0.255', IP::hexToQuad( 'FF' ) );
+               $this->assertEquals( '0.0.255.0', IP::hexToQuad( 'FF00' ) );
+       }
+
+       /**
+        * @covers IP::hexToOctet
+        */
+       public function testHexToOctet() {
+               $this->assertEquals( '0:0:0:0:0:0:0:1',
+                       IP::hexToOctet( '00000000000000000000000000000001' ) );
+               $this->assertEquals( '0:0:0:0:0:0:FF:3',
+                       IP::hexToOctet( '00000000000000000000000000FF0003' ) );
+               $this->assertEquals( '0:0:0:0:0:0:FF00:6',
+                       IP::hexToOctet( '000000000000000000000000FF000006' ) );
+               $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
+                       IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
+               $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
+                       IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
+               // hex not left-padded...
+               $this->assertEquals( '0:0:0:0:0:0:0:0', IP::hexToOctet( '0' ) );
+               $this->assertEquals( '0:0:0:0:0:0:0:1', IP::hexToOctet( '1' ) );
+               $this->assertEquals( '0:0:0:0:0:0:0:FF', IP::hexToOctet( 'FF' ) );
+               $this->assertEquals( '0:0:0:0:0:0:0:FFD0', IP::hexToOctet( 'FFD0' ) );
+               $this->assertEquals( '0:0:0:0:0:0:FA00:0', IP::hexToOctet( 'FA000000' ) );
+               $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
+       }
+
+       /**
+        * IP::parseCIDR() returns an array containing a signed IP address
+        * representing the network mask and the bit mask.
+        * @covers IP::parseCIDR
+        */
+       public function testCIDRParsing() {
+               $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
+               $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
+
+               // Verify if statement
+               $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
+               $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
+               $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
+               $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
+
+               // Check internal logic
+               # 0 mask always result in array(0,0)
+               $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
+               $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
+               $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
+
+               // @todo FIXME: Add more tests.
+
+               # This part test network shifting
+               $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
+               $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
+               $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
+               $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
+               $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
+               $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
+               $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
+               $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
+               $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
+       }
+
+       /**
+        * @covers IP::canonicalize
+        */
+       public function testIPCanonicalizeOnValidIp() {
+               $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
+                       'Canonicalization of a valid IP returns it unchanged' );
+       }
+
+       /**
+        * @covers IP::canonicalize
+        */
+       public function testIPCanonicalizeMappedAddress() {
+               $this->assertEquals(
+                       '192.0.2.152',
+                       IP::canonicalize( '::ffff:192.0.2.152' )
+               );
+               $this->assertEquals(
+                       '192.0.2.152',
+                       IP::canonicalize( '::192.0.2.152' )
+               );
+       }
+
+       /**
+        * Issues there are most probably from IP::toHex() or IP::parseRange()
+        * @covers IP::isInRange
+        * @dataProvider provideIPsAndRanges
+        */
+       public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
+               $this->assertEquals(
+                       $expected,
+                       IP::isInRange( $addr, $range ),
+                       $message
+               );
+       }
+
+       /** Provider for testIPIsInRange() */
+       public static function provideIPsAndRanges() {
+               # Format: (expected boolean, address, range, optional message)
+               return array(
+                       # IPv4
+                       array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
+                       array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
+                       array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
+
+                       array( false, '0.0.0.0', '192.0.2.0/24' ),
+                       array( false, '255.255.255', '192.0.2.0/24' ),
+
+                       # IPv6
+                       array( false, '::1', '2001:DB8::/32' ),
+                       array( false, '::', '2001:DB8::/32' ),
+                       array( false, 'FE80::1', '2001:DB8::/32' ),
+
+                       array( true, '2001:DB8::', '2001:DB8::/32' ),
+                       array( true, '2001:0DB8::', '2001:DB8::/32' ),
+                       array( true, '2001:DB8::1', '2001:DB8::/32' ),
+                       array( true, '2001:0DB8::1', '2001:DB8::/32' ),
+                       array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
+                               '2001:DB8::/32' ),
+
+                       array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
+               );
+       }
+
+       /**
+        * Test for IP::splitHostAndPort().
+        * @dataProvider provideSplitHostAndPort
+        */
+       public function testSplitHostAndPort( $expected, $input, $description ) {
+               $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
+       }
+
+       /**
+        * Provider for IP::splitHostAndPort()
+        */
+       public static function provideSplitHostAndPort() {
+               return array(
+                       array( false, '[', 'Unclosed square bracket' ),
+                       array( false, '[::', 'Unclosed square bracket 2' ),
+                       array( array( '::', false ), '::', 'Bare IPv6 0' ),
+                       array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
+                       array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
+                       array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
+                       array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
+                       array( false, '::x', 'Double colon but no IPv6' ),
+                       array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
+                       array( false, 'x:x', 'Hostname and invalid port' ),
+                       array( array( 'x', false ), 'x', 'Plain hostname' )
+               );
+       }
+
+       /**
+        * Test for IP::combineHostAndPort()
+        * @dataProvider provideCombineHostAndPort
+        */
+       public function testCombineHostAndPort( $expected, $input, $description ) {
+               list( $host, $port, $defaultPort ) = $input;
+               $this->assertEquals(
+                       $expected,
+                       IP::combineHostAndPort( $host, $port, $defaultPort ),
+                       $description );
+       }
+
+       /**
+        * Provider for IP::combineHostAndPort()
+        */
+       public static function provideCombineHostAndPort() {
+               return array(
+                       array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
+                       array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
+                       array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
+                       array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
+               );
+       }
+
+       /**
+        * Test for IP::sanitizeRange()
+        * @dataProvider provideIPCIDRs
+        */
+       public function testSanitizeRange( $input, $expected, $description ) {
+               $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
+       }
+
+       /**
+        * Provider for IP::testSanitizeRange()
+        */
+       public static function provideIPCIDRs() {
+               return array(
+                       array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
+                       array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
+                       array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
+                       array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
+                       array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
+                       array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
+                       array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
+                       array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
+               );
+       }
+
+       /**
+        * Test for IP::prettifyIP()
+        * @dataProvider provideIPsToPrettify
+        */
+       public function testPrettifyIP( $ip, $prettified ) {
+               $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
+       }
+
+       /**
+        * Provider for IP::testPrettifyIP()
+        */
+       public static function provideIPsToPrettify() {
+               return array(
+                       array( '0:0:0:0:0:0:0:0', '::' ),
+                       array( '0:0:0::0:0:0', '::' ),
+                       array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
+                       array( '0:0::f', '::f' ),
+                       array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
+                       array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
+                       array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
+                       array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
+                       array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
+                       array( '0:0:0:0:0:0:0:0/16', '::/16' ),
+                       array( '0:0:0::0:0:0/64', '::/64' ),
+                       array( '0:0::f/52', '::f/52' ),
+                       array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
+                       array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
+                       array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
+                       array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
+                       array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
+               );
+       }
+}
diff --git a/tests/phpunit/includes/utils/StringUtilsTest.php b/tests/phpunit/includes/utils/StringUtilsTest.php
new file mode 100644 (file)
index 0000000..89759e5
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+
+class StringUtilsTest extends MediaWikiTestCase {
+
+       /**
+        * This tests StringUtils::isUtf8 whenever we have the mbstring extension
+        * loaded.
+        *
+        * @covers StringUtils::isUtf8
+        * @dataProvider provideStringsForIsUtf8Check
+        */
+       public function testIsUtf8WithMbstring( $expected, $string ) {
+               if ( !function_exists( 'mb_check_encoding' ) ) {
+                       $this->markTestSkipped( 'Test requires the mbstring PHP extension' );
+               }
+               $this->assertEquals( $expected,
+                       StringUtils::isUtf8( $string ),
+                       'Testing string "' . $this->escaped( $string ) . '" with mb_check_encoding'
+               );
+       }
+
+       /**
+        * This tests StringUtils::isUtf8 making sure we use the pure PHP
+        * implementation used as a fallback when mb_check_encoding() is
+        * not available.
+        *
+        * @covers StringUtils::isUtf8
+        * @dataProvider provideStringsForIsUtf8Check
+        */
+       public function testIsUtf8WithPhpFallbackImplementation( $expected, $string ) {
+               $this->assertEquals( $expected,
+                       StringUtils::isUtf8( $string, /** disable mbstring: */true ),
+                       'Testing string "' . $this->escaped( $string ) . '" with pure PHP implementation'
+               );
+       }
+
+       /**
+        * Print high range characters as an hexadecimal
+        */
+       function escaped( $string ) {
+               $escaped = '';
+               $length = strlen( $string );
+               for ( $i = 0; $i < $length; $i++ ) {
+                       $char = $string[$i];
+                       $val = ord( $char );
+                       if ( $val > 127 ) {
+                               $escaped .= '\x' . dechex( $val );
+                       } else {
+                               $escaped .= $char;
+                       }
+               }
+
+               return $escaped;
+       }
+
+       /**
+        * See also "UTF-8 decoder capability and stress test" by
+        * Markus Kuhn:
+        * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+        */
+       public static function provideStringsForIsUtf8Check() {
+               // Expected return values for StringUtils::isUtf8()
+               $PASS = true;
+               $FAIL = false;
+
+               return array(
+                       'some ASCII' => array( $PASS, 'Some ASCII' ),
+                       'euro sign' => array( $PASS, "Euro sign €" ),
+
+                       'first possible sequence 1 byte' => array( $PASS, "\x00" ),
+                       'first possible sequence 2 bytes' => array( $PASS, "\xc2\x80" ),
+                       'first possible sequence 3 bytes' => array( $PASS, "\xe0\xa0\x80" ),
+                       'first possible sequence 4 bytes' => array( $PASS, "\xf0\x90\x80\x80" ),
+                       'first possible sequence 5 bytes' => array( $FAIL, "\xf8\x88\x80\x80\x80" ),
+                       'first possible sequence 6 bytes' => array( $FAIL, "\xfc\x84\x80\x80\x80\x80" ),
+
+                       'last possible sequence 1 byte' => array( $PASS, "\x7f" ),
+                       'last possible sequence 2 bytes' => array( $PASS, "\xdf\xbf" ),
+                       'last possible sequence 3 bytes' => array( $PASS, "\xef\xbf\xbf" ),
+                       'last possible sequence 4 bytes (U+1FFFFF)' => array( $FAIL, "\xf7\xbf\xbf\xbf" ),
+                       'last possible sequence 5 bytes' => array( $FAIL, "\xfb\xbf\xbf\xbf\xbf" ),
+                       'last possible sequence 6 bytes' => array( $FAIL, "\xfd\xbf\xbf\xbf\xbf\xbf" ),
+
+                       'boundary 1' => array( $PASS, "\xed\x9f\xbf" ),
+                       'boundary 2' => array( $PASS, "\xee\x80\x80" ),
+                       'boundary 3' => array( $PASS, "\xef\xbf\xbd" ),
+                       'boundary 4' => array( $PASS, "\xf2\x80\x80\x80" ),
+                       'boundary 5 (U+FFFFF)' => array( $PASS, "\xf3\xbf\xbf\xbf" ),
+                       'boundary 6 (U+100000)' => array( $PASS, "\xf4\x80\x80\x80" ),
+                       'boundary 7 (U+10FFFF)' => array( $PASS, "\xf4\x8f\xbf\xbf" ),
+                       'boundary 8 (U+110000)' => array( $FAIL, "\xf4\x90\x80\x80" ),
+
+                       'malformed 1' => array( $FAIL, "\x80" ),
+                       'malformed 2' => array( $FAIL, "\xbf" ),
+                       'malformed 3' => array( $FAIL, "\x80\xbf" ),
+                       'malformed 4' => array( $FAIL, "\x80\xbf\x80" ),
+                       'malformed 5' => array( $FAIL, "\x80\xbf\x80\xbf" ),
+                       'malformed 6' => array( $FAIL, "\x80\xbf\x80\xbf\x80" ),
+                       'malformed 7' => array( $FAIL, "\x80\xbf\x80\xbf\x80\xbf" ),
+                       'malformed 8' => array( $FAIL, "\x80\xbf\x80\xbf\x80\xbf\x80" ),
+
+                       'last byte missing 1' => array( $FAIL, "\xc0" ),
+                       'last byte missing 2' => array( $FAIL, "\xe0\x80" ),
+                       'last byte missing 3' => array( $FAIL, "\xf0\x80\x80" ),
+                       'last byte missing 4' => array( $FAIL, "\xf8\x80\x80\x80" ),
+                       'last byte missing 5' => array( $FAIL, "\xfc\x80\x80\x80\x80" ),
+                       'last byte missing 6' => array( $FAIL, "\xdf" ),
+                       'last byte missing 7' => array( $FAIL, "\xef\xbf" ),
+                       'last byte missing 8' => array( $FAIL, "\xf7\xbf\xbf" ),
+                       'last byte missing 9' => array( $FAIL, "\xfb\xbf\xbf\xbf" ),
+                       'last byte missing 10' => array( $FAIL, "\xfd\xbf\xbf\xbf\xbf" ),
+
+                       'extra continuation byte 1' => array( $FAIL, "e\xaf" ),
+                       'extra continuation byte 2' => array( $FAIL, "\xc3\x89\xaf" ),
+                       'extra continuation byte 3' => array( $FAIL, "\xef\xbc\xa5\xaf" ),
+                       'extra continuation byte 4' => array( $FAIL, "\xf0\x9d\x99\xb4\xaf" ),
+
+                       'impossible bytes 1' => array( $FAIL, "\xfe" ),
+                       'impossible bytes 2' => array( $FAIL, "\xff" ),
+                       'impossible bytes 3' => array( $FAIL, "\xfe\xfe\xff\xff" ),
+
+                       'overlong sequences 1' => array( $FAIL, "\xc0\xaf" ),
+                       'overlong sequences 2' => array( $FAIL, "\xc1\xaf" ),
+                       'overlong sequences 3' => array( $FAIL, "\xe0\x80\xaf" ),
+                       'overlong sequences 4' => array( $FAIL, "\xf0\x80\x80\xaf" ),
+                       'overlong sequences 5' => array( $FAIL, "\xf8\x80\x80\x80\xaf" ),
+                       'overlong sequences 6' => array( $FAIL, "\xfc\x80\x80\x80\x80\xaf" ),
+
+                       'maximum overlong sequences 1' => array( $FAIL, "\xc1\xbf" ),
+                       'maximum overlong sequences 2' => array( $FAIL, "\xe0\x9f\xbf" ),
+                       'maximum overlong sequences 3' => array( $FAIL, "\xf0\x8f\xbf\xbf" ),
+                       'maximum overlong sequences 4' => array( $FAIL, "\xf8\x87\xbf\xbf" ),
+                       'maximum overlong sequences 5' => array( $FAIL, "\xfc\x83\xbf\xbf\xbf\xbf" ),
+
+                       'surrogates 1 (U+D799)' => array( $PASS, "\xed\x9f\xbf" ),
+                       'surrogates 2 (U+E000)' => array( $PASS, "\xee\x80\x80" ),
+                       'surrogates 3 (U+D800)' => array( $FAIL, "\xed\xa0\x80" ),
+                       'surrogates 4 (U+DBFF)' => array( $FAIL, "\xed\xaf\xbf" ),
+                       'surrogates 5 (U+DC00)' => array( $FAIL, "\xed\xb0\x80" ),
+                       'surrogates 6 (U+DFFF)' => array( $FAIL, "\xed\xbf\xbf" ),
+                       'surrogates 7 (U+D800 U+DC00)' => array( $FAIL, "\xed\xa0\x80\xed\xb0\x80" ),
+
+                       'noncharacters 1' => array( $PASS, "\xef\xbf\xbe" ),
+                       'noncharacters 2' => array( $PASS, "\xef\xbf\xbf" ),
+               );
+       }
+}
diff --git a/tests/phpunit/includes/utils/UIDGeneratorTest.php b/tests/phpunit/includes/utils/UIDGeneratorTest.php
new file mode 100644 (file)
index 0000000..8f78ae5
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+
+class UIDGeneratorTest extends MediaWikiTestCase {
+
+       /**
+        * @dataProvider provider_testTimestampedUID
+        * @covers UIDGenerator::newTimestampedUID128
+        * @covers UIDGenerator::newTimestampedUID88
+        */
+       public function testTimestampedUID( $method, $digitlen, $bits, $tbits, $hostbits ) {
+               $id = call_user_func( array( 'UIDGenerator', $method ) );
+               $this->assertEquals( true, ctype_digit( $id ), "UID made of digit characters" );
+               $this->assertLessThanOrEqual( $digitlen, strlen( $id ),
+                       "UID has the right number of digits" );
+               $this->assertLessThanOrEqual( $bits, strlen( wfBaseConvert( $id, 10, 2 ) ),
+                       "UID has the right number of bits" );
+
+               $ids = array();
+               for ( $i = 0; $i < 300; $i++ ) {
+                       $ids[] = call_user_func( array( 'UIDGenerator', $method ) );
+               }
+
+               $lastId = array_shift( $ids );
+               if ( $hostbits ) {
+                       $lastHost = substr( wfBaseConvert( $lastId, 10, 2, $bits ), -$hostbits );
+               }
+
+               $this->assertArrayEquals( array_unique( $ids ), $ids, "All generated IDs are unique." );
+
+               foreach ( $ids as $id ) {
+                       $id_bin = wfBaseConvert( $id, 10, 2 );
+                       $lastId_bin = wfBaseConvert( $lastId, 10, 2 );
+
+                       $this->assertGreaterThanOrEqual(
+                               substr( $id_bin, 0, $tbits ),
+                               substr( $lastId_bin, 0, $tbits ),
+                               "New ID timestamp ($id_bin) >= prior one ($lastId_bin)." );
+
+                       if ( $hostbits ) {
+                               $this->assertEquals(
+                                       substr( $id_bin, 0, -$hostbits ),
+                                       substr( $lastId_bin, 0, -$hostbits ),
+                                       "Host ID of ($id_bin) is same as prior one ($lastId_bin)." );
+                       }
+
+                       $lastId = $id;
+               }
+       }
+
+       /**
+        * array( method, length, bits, hostbits )
+        * NOTE: When adding a new method name here please update the covers tags for the tests!
+        */
+       public static function provider_testTimestampedUID() {
+               return array(
+                       array( 'newTimestampedUID128', 39, 128, 46, 48 ),
+                       array( 'newTimestampedUID128', 39, 128, 46, 48 ),
+                       array( 'newTimestampedUID88', 27, 88, 46, 32 ),
+               );
+       }
+
+       /**
+        * @covers UIDGenerator::newUUIDv4
+        */
+       public function testUUIDv4() {
+               for ( $i = 0; $i < 100; $i++ ) {
+                       $id = UIDGenerator::newUUIDv4();
+                       $this->assertEquals( true,
+                               preg_match( '!^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$!', $id ),
+                               "UID $id has the right format" );
+               }
+       }
+
+       /**
+        * @covers UIDGenerator::newRawUUIDv4
+        */
+       public function testRawUUIDv4() {
+               for ( $i = 0; $i < 100; $i++ ) {
+                       $id = UIDGenerator::newRawUUIDv4();
+                       $this->assertEquals( true,
+                               preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
+                               "UID $id has the right format" );
+               }
+       }
+
+       /**
+        * @covers UIDGenerator::newRawUUIDv4
+        */
+       public function testRawUUIDv4QuickRand() {
+               for ( $i = 0; $i < 100; $i++ ) {
+                       $id = UIDGenerator::newRawUUIDv4( UIDGenerator::QUICK_RAND );
+                       $this->assertEquals( true,
+                               preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
+                               "UID $id has the right format" );
+               }
+       }
+
+}
diff --git a/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php b/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php
new file mode 100644 (file)
index 0000000..f0203d4
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @covers ZipDirectoryReader
+ * NOTE: this test is more like an integration test than a unit test
+ */
+class ZipDirectoryReaderTest extends MediaWikiTestCase {
+       protected $zipDir;
+       protected $entries;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->zipDir = __DIR__ . '/../../data/zip';
+       }
+
+       function zipCallback( $entry ) {
+               $this->entries[] = $entry;
+       }
+
+       function readZipAssertError( $file, $error, $assertMessage ) {
+               $this->entries = array();
+               $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", array( $this, 'zipCallback' ) );
+               $this->assertTrue( $status->hasMessage( $error ), $assertMessage );
+       }
+
+       function readZipAssertSuccess( $file, $assertMessage ) {
+               $this->entries = array();
+               $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", array( $this, 'zipCallback' ) );
+               $this->assertTrue( $status->isOK(), $assertMessage );
+       }
+
+       public function testEmpty() {
+               $this->readZipAssertSuccess( 'empty.zip', 'Empty zip' );
+       }
+
+       public function testMultiDisk0() {
+               $this->readZipAssertError( 'split.zip', 'zip-unsupported',
+                       'Split zip error' );
+       }
+
+       public function testNoSignature() {
+               $this->readZipAssertError( 'nosig.zip', 'zip-wrong-format',
+                       'No signature should give "wrong format" error' );
+       }
+
+       public function testSimple() {
+               $this->readZipAssertSuccess( 'class.zip', 'Simple ZIP' );
+               $this->assertEquals( $this->entries, array( array(
+                       'name' => 'Class.class',
+                       'mtime' => '20010115000000',
+                       'size' => 1,
+               ) ) );
+       }
+
+       public function testBadCentralEntrySignature() {
+               $this->readZipAssertError( 'wrong-central-entry-sig.zip', 'zip-bad',
+                       'Bad central entry error' );
+       }
+
+       public function testTrailingBytes() {
+               $this->readZipAssertError( 'trail.zip', 'zip-bad',
+                       'Trailing bytes error' );
+       }
+
+       public function testWrongCDStart() {
+               $this->readZipAssertError( 'wrong-cd-start-disk.zip', 'zip-unsupported',
+                       'Wrong CD start disk error' );
+       }
+
+
+       public function testCentralDirectoryGap() {
+               $this->readZipAssertError( 'cd-gap.zip', 'zip-bad',
+                       'CD gap error' );
+       }
+
+       public function testCentralDirectoryTruncated() {
+               $this->readZipAssertError( 'cd-truncated.zip', 'zip-bad',
+                       'CD truncated error (should hit unpack() overrun)' );
+       }
+
+       public function testLooksLikeZip64() {
+               $this->readZipAssertError( 'looks-like-zip64.zip', 'zip-unsupported',
+                       'A file which looks like ZIP64 but isn\'t, should give error' );
+       }
+}
index f5ff1d9..733368d 100644 (file)
@@ -1,6 +1,6 @@
 <?php
-class AutoLoaderTest extends MediaWikiTestCase {
 
+class AutoLoaderTest extends MediaWikiTestCase {
        protected function setUp() {
                global $wgAutoloadLocalClasses, $wgAutoloadClasses;
 
@@ -13,7 +13,7 @@ class AutoLoaderTest extends MediaWikiTestCase {
                        'TestAutoloadedSerializedClass' => __DIR__ . '/../data/autoloader/TestAutoloadedSerializedClass.php',
                );
                $this->setMwGlobals( 'wgAutoloadLocalClasses', $this->testLocalClasses + $wgAutoloadLocalClasses );
-               InstrumentedAutoLoader::resetAutoloadLocalClassesLower();
+               AutoLoader::resetAutoloadLocalClassesLower();
 
                $this->testExtensionClasses = array(
                        'TestAutoloadedClass' => __DIR__ . '/../data/autoloader/TestAutoloadedClass.php',
@@ -93,12 +93,3 @@ class AutoLoaderTest extends MediaWikiTestCase {
                        "unserialize() can load classes case-insensitively.");
        }
 }
-
-/**
- * Cheater to poke protected members
- */
-class InstrumentedAutoLoader extends AutoLoader {
-       static function resetAutoloadLocalClassesLower() {
-               self::$autoloadLocalClassesLower = null;
-       }
-}
index 13c0efc..f0bb0fc 100644 (file)
                mw.config.set( 'wgArticlePath', '/wiki/$1' );
 
                title = new mw.Title( 'Foobar' );
-               assert.equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, toString passing to wikiGetlink' );
+               assert.equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, getUrl uses mw.util.getUrl' );
 
                title = new mw.Title( 'John Doe', 3 );
                assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
index e0e823d..be362e2 100644 (file)
@@ -585,7 +585,7 @@ QUnit.test( 'HTML', 26, function ( assert ) {
 
        assert.htmlEqual(
                formatParse( 'jquerymsg-italics-with-link' ),
-               'An <i>italicized <a title="link" href="' + mw.html.escape( mw.util.wikiGetlink( 'link' ) ) + '">wiki-link</i>',
+               'An <i>italicized <a title="link" href="' + mw.html.escape( mw.util.getUrl( 'link' ) ) + '">wiki-link</i>',
                'Italics with link inside in parse mode'
        );
 
@@ -625,7 +625,7 @@ QUnit.test( 'HTML', 26, function ( assert ) {
        mw.messages.set( 'jquerymsg-script-link-msg', '<script>[[Foo|bar]]</script>' );
        assert.htmlEqual(
                formatParse( 'jquerymsg-script-link-msg' ),
-               '&lt;script&gt;<a title="Foo" href="' + mw.html.escape( mw.util.wikiGetlink( 'Foo' ) ) + '">bar</a>&lt;/script&gt;',
+               '&lt;script&gt;<a title="Foo" href="' + mw.html.escape( mw.util.getUrl( 'Foo' ) ) + '">bar</a>&lt;/script&gt;',
                'Script tag text is escaped because that element is not allowed, but link inside is still HTML'
        );
 
index 502b55b..594ae25 100644 (file)
@@ -43,7 +43,7 @@
                assert.strictEqual( window.mw, window.mediaWiki, 'mw alias to mediaWiki' );
        } );
 
-       QUnit.test( 'mw.Map', 27, function ( assert ) {
+       QUnit.test( 'mw.Map', 28, function ( assert ) {
                var arry, conf, funky, globalConf, nummy, someValues;
 
                conf = new mw.Map();
                        'lorem': 'ipsum'
                }, 'Map.get returns multiple values correctly as an object' );
 
+               assert.deepEqual( conf, new mw.Map( conf.values ), 'new mw.Map maps over existing values-bearing object' );
+
                assert.deepEqual( conf.get( ['foo', 'notExist'] ), {
                        'foo': 'bar',
                        'notExist': null
                );
                assert.htmlEqual(
                        mw.message( 'mediawiki-italics-with-link' ).parse(),
-                       'An <i>italicized <a title="link" href="' + mw.util.wikiGetlink( 'link' ) + '">wiki-link</i>',
+                       'An <i>italicized <a title="link" href="' + mw.util.getUrl( 'link' ) + '">wiki-link</i>',
                        'Italics with link inside in parse mode'
                );
 
index 08adb93..2be8044 100644 (file)
                assert.equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
        } );
 
-       QUnit.test( 'wikiGetlink', 4, function ( assert ) {
+       QUnit.test( 'getUrl', 4, function ( assert ) {
                // Not part of startUp module
                mw.config.set( 'wgArticlePath', '/wiki/$1' );
                mw.config.set( 'wgPageName', 'Foobar' );
 
-               var href = mw.util.wikiGetlink( 'Sandbox' );
+               var href = mw.util.getUrl( 'Sandbox' );
                assert.equal( href, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
 
-               href = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
+               href = mw.util.getUrl( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
                assert.equal( href, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
                        'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
 
-               href = mw.util.wikiGetlink();
+               href = mw.util.getUrl();
                assert.equal( href, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
 
-               href = mw.util.wikiGetlink( 'Sandbox', { action: 'edit' } );
+               href = mw.util.getUrl( 'Sandbox', { action: 'edit' } );
                assert.equal( href, '/wiki/Sandbox?action=edit',
                        'Simple title with query string; Get link for "Sandbox" with action=edit' );
        } );