Merge "Convert doDeleteArticleReal to startAtomic()/endAtomic()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 26 Oct 2015 19:23:52 +0000 (19:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 26 Oct 2015 19:23:52 +0000 (19:23 +0000)
170 files changed:
autoload.php
includes/DefaultSettings.php
includes/Linker.php
includes/MagicWord.php
includes/MovePage.php
includes/PrefixSearch.php
includes/Setup.php
includes/User.php
includes/api/ApiQueryAllCategories.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryAllImages.php
includes/api/ApiQueryAllRevisions.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryRevisionsBase.php
includes/api/ApiQuerySearch.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryStashImageInfo.php
includes/api/ApiQueryUserInfo.php
includes/api/ApiQueryUsers.php
includes/api/ApiUpload.php
includes/api/i18n/ca.json
includes/api/i18n/de.json
includes/api/i18n/id.json [new file with mode: 0644]
includes/api/i18n/ksh.json
includes/api/i18n/lb.json
includes/api/i18n/lt.json
includes/api/i18n/ru.json
includes/api/i18n/zh-hans.json
includes/clientpool/RedisConnectionPool.php
includes/db/Database.php
includes/db/DatabaseError.php
includes/exception/ErrorPageError.php
includes/filebackend/FileBackendStore.php
includes/installer/Installer.php
includes/installer/LocalSettingsGenerator.php
includes/installer/WebInstaller.php
includes/installer/WebInstallerPage.php
includes/installer/i18n/be-tarask.json
includes/installer/i18n/ca.json
includes/installer/i18n/cs.json
includes/installer/i18n/de.json
includes/installer/i18n/el.json
includes/installer/i18n/en.json
includes/installer/i18n/es.json
includes/installer/i18n/id.json
includes/installer/i18n/ja.json
includes/installer/i18n/ksh.json
includes/installer/i18n/lt.json
includes/jobqueue/jobs/HTMLCacheUpdateJob.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/libs/ObjectFactory.php
includes/libs/composer/ComposerInstalled.php [new file with mode: 0644]
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/MemcachedBagOStuff.php [new file with mode: 0644]
includes/libs/objectcache/MemcachedClient.php [new file with mode: 0644]
includes/libs/objectcache/MemcachedPhpBagOStuff.php [new file with mode: 0644]
includes/libs/objectcache/MultiWriteBagOStuff.php [new file with mode: 0644]
includes/media/TransformationalImageHandler.php
includes/objectcache/MemcachedBagOStuff.php [deleted file]
includes/objectcache/MemcachedClient.php [deleted file]
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/MemcachedPhpBagOStuff.php [deleted file]
includes/objectcache/MultiWriteBagOStuff.php [deleted file]
includes/objectcache/ObjectCache.php
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/parser/CacheTime.php
includes/parser/DateFormatter.php
includes/parser/Parser.php
includes/parser/Preprocessor.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/poolcounter/PoolWorkArticleView.php
includes/resourceloader/ResourceLoader.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialPrefixindex.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialVersion.php
includes/upload/UploadStash.php
languages/i18n/ar.json
languages/i18n/ast.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bgn.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/cs.json
languages/i18n/cu.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gsw.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hsb.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/lb.json
languages/i18n/lrc.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mr.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/olo.json
languages/i18n/pl.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/sco.json
languages/i18n/sd.json
languages/i18n/sgs.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/uz.json
languages/i18n/war.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
mw-config/config.js
package.json
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.special/mediawiki.special.css
resources/src/mediawiki.special/mediawiki.special.upload.js
resources/src/mediawiki.special/mediawiki.special.userlogin.common.css
resources/src/mediawiki.ui/components/buttons.less
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.less
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.notification.css
resources/src/mediawiki/mediawiki.notification.js
tests/phpunit/includes/libs/ObjectFactoryTest.php
tests/phpunit/includes/libs/objectcache/BagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/objectcache/ReplicatedBagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php [new file with mode: 0644]
tests/phpunit/includes/objectcache/BagOStuffTest.php [deleted file]
tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php [deleted file]
tests/phpunit/includes/objectcache/ReplicatedBagOStuffTest.php [deleted file]
tests/phpunit/includes/objectcache/WANObjectCacheTest.php [deleted file]

index 8720f33..731bdaa 100644 (file)
@@ -242,6 +242,7 @@ $wgAutoloadLocalClasses = array(
        'CompareParserCache' => __DIR__ . '/maintenance/compareParserCache.php',
        'CompareParsers' => __DIR__ . '/maintenance/compareParsers.php',
        'ComposerHookHandler' => __DIR__ . '/includes/composer/ComposerHookHandler.php',
+       'ComposerInstalled' => __DIR__ . '/includes/libs/composer/ComposerInstalled.php',
        'ComposerJson' => __DIR__ . '/includes/libs/composer/ComposerJson.php',
        'ComposerLock' => __DIR__ . '/includes/libs/composer/ComposerLock.php',
        'ComposerPackageModifier' => __DIR__ . '/includes/composer/ComposerPackageModifier.php',
@@ -720,7 +721,7 @@ $wgAutoloadLocalClasses = array(
        'MWExceptionHandler' => __DIR__ . '/includes/exception/MWExceptionHandler.php',
        'MWHookException' => __DIR__ . '/includes/Hooks.php',
        'MWHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
-       'MWMemcached' => __DIR__ . '/includes/objectcache/MemcachedClient.php',
+       'MWMemcached' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
        'MWMessagePack' => __DIR__ . '/includes/libs/MWMessagePack.php',
        'MWNamespace' => __DIR__ . '/includes/MWNamespace.php',
        'MWOldPassword' => __DIR__ . '/includes/password/MWOldPassword.php',
@@ -778,11 +779,11 @@ $wgAutoloadLocalClasses = array(
        'MediaWiki\\Widget\\NamespaceInputWidget' => __DIR__ . '/includes/widget/NamespaceInputWidget.php',
        'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php',
        'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php',
-       'MemCachedClientforWiki' => __DIR__ . '/includes/objectcache/MemcachedClient.php',
+       'MemCachedClientforWiki' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
        'MemcLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MemcLockManager.php',
-       'MemcachedBagOStuff' => __DIR__ . '/includes/objectcache/MemcachedBagOStuff.php',
+       'MemcachedBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedBagOStuff.php',
        'MemcachedPeclBagOStuff' => __DIR__ . '/includes/objectcache/MemcachedPeclBagOStuff.php',
-       'MemcachedPhpBagOStuff' => __DIR__ . '/includes/objectcache/MemcachedPhpBagOStuff.php',
+       'MemcachedPhpBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedPhpBagOStuff.php',
        'MemoizedCallable' => __DIR__ . '/includes/libs/MemoizedCallable.php',
        'MemoryFileBackend' => __DIR__ . '/includes/filebackend/MemoryFileBackend.php',
        'MergeHistoryPager' => __DIR__ . '/includes/specials/SpecialMergeHistory.php',
@@ -816,7 +817,7 @@ $wgAutoloadLocalClasses = array(
        'MssqlUpdater' => __DIR__ . '/includes/installer/MssqlUpdater.php',
        'MultiConfig' => __DIR__ . '/includes/config/MultiConfig.php',
        'MultiHttpClient' => __DIR__ . '/includes/libs/MultiHttpClient.php',
-       'MultiWriteBagOStuff' => __DIR__ . '/includes/objectcache/MultiWriteBagOStuff.php',
+       'MultiWriteBagOStuff' => __DIR__ . '/includes/libs/objectcache/MultiWriteBagOStuff.php',
        'MutableConfig' => __DIR__ . '/includes/config/MutableConfig.php',
        'MutableContext' => __DIR__ . '/includes/context/MutableContext.php',
        'MwSql' => __DIR__ . '/maintenance/sql.php',
index 71fe83d..910c121 100644 (file)
@@ -2212,7 +2212,7 @@ $wgObjectCaches = array(
 
        CACHE_ANYTHING => array( 'factory' => 'ObjectCache::newAnything' ),
        CACHE_ACCEL => array( 'factory' => 'ObjectCache::newAccelerator' ),
-       CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached', 'loggroup' => 'memcached' ),
+       CACHE_MEMCACHED => array( 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ),
 
        'db-replicated' => array(
                'class'       => 'ReplicatedBagOStuff',
@@ -5202,80 +5202,85 @@ $wgApplyIpBlocksToXff = false;
  * @par Example:
  * To set a generic maximum of 4 hits in 60 seconds:
  * @code
- * $wgRateLimits = array( 4, 60 );
+ *     $wgRateLimits = array( 4, 60 );
  * @endcode
  *
- * You could also limit per action and then type of users. See the inline
- * code for a template to use.
- *
- * This option set is experimental and likely to change.
+ * @par Example:
+ * You could also limit per action and then type of users.
+ * @code
+ *     $wgRateLimits = array(
+ *         'edit' => array(
+ *             'anon' => array( x, y ), // any and all anonymous edits (aggregate)
+ *             'user' => array( x, y ), // each logged-in user
+ *             'newbie' => array( x, y ), // each new autoconfirmed accounts; overrides 'user'
+ *             'ip' => array( x, y ), // each anon and recent account
+ *             'subnet' => array( x, y ), // ... within a /24 subnet in IPv4 or /64 in IPv6
+ *         )
+ *     )
+ * @endcode
  *
- * @warning Requires memcached.
+ * @warning Requires that $wgMainCacheType is set to something persistent
  */
 $wgRateLimits = array(
+       // Page edits
        'edit' => array(
-               'anon' => null, // for any and all anonymous edits (aggregate)
-               'user' => null, // for each logged-in user
-               'newbie' => null, // for each recent (autoconfirmed) account; overrides 'user'
-               'ip' => null, // for each anon and recent account
-               'subnet' => null, // ... within a /24 subnet in IPv4 or /64 in IPv6
+               'ip' => array( 8, 60 ),
+               'newbie' => array( 8, 60 ),
        ),
+       // Page moves
+       'move' => array(
+               'newbie' => array( 2, 120 ),
+               'user' => array( 8, 60 ),
+       ),
+       // File uploads
        'upload' => array(
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+               'ip' => array( 8, 60 ),
+               'newbie' => array( 8, 60 ),
        ),
-       'move' => array(
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+       // Page rollbacks
+       'rollback' => array(
+               'user' => array( 10, 60 ),
+               'newbie' => array( 5, 120 )
        ),
-       'mailpassword' => array( // triggering password resets emails
-               'anon' => null,
+       // Triggering password resets emails
+       'mailpassword' => array(
+               'ip' => array( 5, 3600 ),
        ),
-       'emailuser' => array( // emailing other users using MediaWiki
-               'user' => null,
+       // Emailing other users using MediaWiki
+       'emailuser' => array(
+               'ip' => array( 5, 86400 ),
+               'newbie' => array( 5, 86400 ),
+               'user' => array( 20, 86400 ),
        ),
-       'linkpurge' => array( // purges of link tables
-               'anon' => null,
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+       // Purging pages
+       'purge' => array(
+               'ip' => array( 30, 60 ),
+               'user' => array( 30, 60 ),
        ),
-       'renderfile' => array( // files rendered via thumb.php or thumb_handler.php
-               'anon' => null,
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+       // Purges of link tables
+       'linkpurge' => array(
+               'ip' => array( 30, 60 ),
+               'user' => array( 30, 60 ),
        ),
-       'renderfile-nonstandard' => array( // same as above but for non-standard thumbnails
-               'anon' => null,
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+       // Files rendered via thumb.php or thumb_handler.php
+       'renderfile' => array(
+               'ip' => array( 700, 30 ),
+               'user' => array( 700, 30 ),
        ),
-       'stashedit' => array( // stashing edits into cache before save
-               'anon' => null,
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+       // Same as above but for non-standard thumbnails
+       'renderfile-nonstandard' => array(
+               'ip' => array( 70, 30 ),
+               'user' => array( 70, 30 ),
        ),
-       'changetag' => array( // adding or removing change tags
-               'user' => null,
-               'newbie' => null,
+       // Stashing edits into cache before save
+       'stashedit' => array(
+               'ip' => array( 30, 60 ),
+               'newbie' => array( 30, 60 ),
        ),
-       'purge' => array( // purging pages
-               'anon' => null,
-               'user' => null,
-               'newbie' => null,
-               'ip' => null,
-               'subnet' => null,
+       // Adding or removing change tags
+       'changetag' => array(
+               'ip' => array( 8, 60 ),
+               'newbie' => array( 8, 60 ),
        ),
 );
 
@@ -5507,7 +5512,7 @@ $wgTrxProfilerLimits = array(
        'JobRunner' => array(
                'readQueryTime' => 30,
                'writeQueryTime' => 5,
-               'maxAffected' => 500
+               'maxAffected' => 1000
        ),
        // Command-line scripts
        'Maintenance' => array(
index e821004..842d276 100644 (file)
@@ -1469,7 +1469,7 @@ class Linker {
                                                if ( $target->getText() == '' && !$target->isExternal()
                                                        && !$local && $title
                                                ) {
-                                                       $newTarget = clone ( $title );
+                                                       $newTarget = clone $title;
                                                        $newTarget->setFragment( '#' . $target->getFragment() );
                                                        $target = $newTarget;
                                                }
index 2c7ba91..424735e 100644 (file)
@@ -941,6 +941,7 @@ class MagicWordArray {
         *
         * @param string $text
         *
+        * @throws Exception
         * @return array
         */
        public function matchAndRemove( &$text ) {
@@ -951,13 +952,22 @@ class MagicWordArray {
                                continue;
                        }
                        $matches = array();
-                       if ( preg_match_all( $regex, $text, $matches, PREG_SET_ORDER ) ) {
+                       $matched = preg_match_all( $regex, $text, $matches, PREG_SET_ORDER );
+                       if ( $matched === false ) {
+                               throw new Exception( __METHOD__ . ': preg_match_all returned false' );
+                       }
+                       if ( $matched ) {
                                foreach ( $matches as $m ) {
                                        list( $name, $param ) = $this->parseMatch( $m );
                                        $found[$name] = $param;
                                }
                        }
-                       $text = preg_replace( $regex, '', $text );
+                       $replaced = preg_replace( $regex, '', $text );
+                       if ( $replaced !== null ) {
+                               $text = $replaced;
+                       } else {
+                               throw new Exception( __METHOD__ . ': preg_replace returned null' );
+                       }
                }
                return $found;
        }
index 2cd9698..0f9374a 100644 (file)
@@ -247,7 +247,7 @@ class MovePage {
                        RepoGroup::singleton()->clearCache( $this->newTitle ); # clear false negative cache
                }
 
-               $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
+               $dbw->startAtomic( __METHOD__ );
                $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE );
                $protected = $this->oldTitle->isProtected();
 
@@ -369,12 +369,13 @@ class MovePage {
                        WatchedItem::duplicateEntries( $this->oldTitle, $this->newTitle );
                }
 
-               $dbw->commit( __METHOD__ );
+               $dbw->endAtomic( __METHOD__ );
+
+               $params = array( $this->oldTitle, $this->newTitle, $user, $pageid, $redirid, $reason );
+               $dbw->onTransactionIdle( function () use ( $params ) {
+                       Hooks::run( 'TitleMoveComplete', $params );
+               } );
 
-               Hooks::run(
-                       'TitleMoveComplete',
-                       array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason )
-               );
                return Status::newGood();
        }
 
index 430b4b8..f36635b 100644 (file)
@@ -258,12 +258,18 @@ abstract class PrefixSearch {
                return $array;
        }
 
+       /**
+        * Get a redirect's destination from a title
+        * @param Title $title A title to redirect. It may not redirect or even exist
+        * @return null|string If title exists and redirects, get the destination's prefixed name
+        */
        private function getRedirectTarget( $title ) {
                $page = WikiPage::factory( $title );
                if ( !$page->exists() ) {
                        return null;
                }
-               return $page->getRedirectTarget()->getPrefixedText();
+               $redir = $page->getRedirectTarget();
+               return $redir ? $redir->getPrefixedText() : null;
        }
 
        /**
index fbfef1f..0b6b018 100644 (file)
@@ -689,7 +689,7 @@ $wgTitle = null;
 Profiler::instance()->scopedProfileOut( $ps_globals );
 $ps_extensions = Profiler::instance()->scopedProfileIn( $fname . '-extensions' );
 
-// Extension setup functions for extensions other than skins
+// Extension setup functions
 // Entries should be added to this variable during the inclusion
 // of the extension file. This allows the extension to perform
 // any necessary initialisation in the fully initialised environment
index 6e52a1d..e1c9e35 100644 (file)
@@ -185,21 +185,8 @@ class User implements IDBAccessObject {
        public $mName;
        /** @var string */
        public $mRealName;
-
-       /**
-        * These fields were marked "@private", but were defined as public to
-        * maintain compatibility with PHP4 code since PHP4 didn't support access
-        * restrictions. AuthManager makes password handling pluggable, meaning
-        * these fields don't make sense anymore. If this broke something, see
-        * T89459 for the context of the change.
-        * @deprecated These are mostly unused, but kept for now to raise errors on attempted access.
-        */
-       // @{
+       /** @var Password|null */
        private $mPassword = null;
-       private $mNewpassword;
-       private $mNewpassTime;
-       private $mPasswordExpires;
-       // @}
 
        /** @var string */
        public $mEmail;
@@ -4097,13 +4084,13 @@ class User implements IDBAccessObject {
                        __METHOD__
                );
                try {
-                       $mNewpassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
+                       $newPassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
                } catch ( PasswordError $e ) {
                        wfDebug( 'Invalid password hash found in database.' );
-                       $mNewpassword = PasswordFactory::newInvalidPassword();
+                       $newPassword = PasswordFactory::newInvalidPassword();
                }
 
-               if ( $mNewpassword->equals( $plaintext ) ) {
+               if ( $newPassword->equals( $plaintext ) ) {
                        if ( is_null( $row->user_newpass_time ) ) {
                                return true;
                        }
index 0711c90..30978a1 100644 (file)
@@ -169,11 +169,9 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
                                ),
                        ),
                        'min' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_TYPE => 'integer'
                        ),
                        'max' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_TYPE => 'integer'
                        ),
                        'limit' => array(
index 4f7984e..8cb1119 100644 (file)
@@ -392,7 +392,6 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                        'namespace' => array(
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => 'namespace',
-                               ApiBase::PARAM_DFLT => null,
                        ),
                        'start' => array(
                                ApiBase::PARAM_TYPE => 'timestamp',
index 877423e..699cba8 100644 (file)
@@ -374,7 +374,6 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
                                )
                        ),
                        'mime' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'limit' => array(
index e853cdc..da895e2 100644 (file)
@@ -94,6 +94,9 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
                        }
                }
 
+               $dir = $params['dir'];
+               $this->addTimestampWhereRange( 'rev_timestamp', $dir, $params['start'], $params['end'] );
+
                if ( $this->fld_tags ) {
                        $this->addTables( 'tag_summary' );
                        $this->addJoinConds(
@@ -141,8 +144,6 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
                        }
                }
 
-               $dir = $params['dir'];
-
                if ( $params['continue'] !== null ) {
                        $op = ( $dir == 'newer' ? '>' : '<' );
                        $cont = explode( '|', $params['continue'] );
index b05c75c..7967826 100644 (file)
@@ -827,7 +827,6 @@ class ApiQueryInfo extends ApiQueryBase {
        public function getAllowedParams() {
                return array(
                        'prop' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => array(
                                        'protection',
@@ -851,7 +850,6 @@ class ApiQueryInfo extends ApiQueryBase {
                        ),
                        'token' => array(
                                ApiBase::PARAM_DEPRECATED => true,
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() )
                        ),
index 609efb0..ebc5c2e 100644 (file)
@@ -485,15 +485,12 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-parse',
                        ),
                        'section' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-section',
                        ),
                        'diffto' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-diffto',
                        ),
                        'difftotext' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotext',
                        ),
                        'difftotextpst' => array(
@@ -502,7 +499,6 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        ),
                        'contentformat' => array(
                                ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
                        ),
                );
index 32607a5..637fc4c 100644 (file)
@@ -308,7 +308,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'what' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_TYPE => array(
                                        'title',
                                        'text',
index 1265155..c13b30b 100644 (file)
@@ -540,18 +540,14 @@ class ApiQuerySiteinfo extends ApiQueryBase {
 
        protected function appendInstalledLibraries( $property ) {
                global $IP;
-               $path = "$IP/composer.lock";
+               $path = "$IP/vendor/composer/installed.json";
                if ( !file_exists( $path ) ) {
-                       // Maybe they're using mediawiki/vendor?
-                       $path = "$IP/vendor/composer.lock";
-                       if ( !file_exists( $path ) ) {
-                               return true;
-                       }
+                       return true;
                }
 
                $data = array();
-               $lock = new ComposerLock( $path );
-               foreach ( $lock->getInstalledDependencies() as $name => $info ) {
+               $installed = new ComposerInstalled( $path );
+               foreach ( $installed->getInstalledDependencies() as $name => $info ) {
                        if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
                                // Skip any extensions or skins since they'll be listed
                                // in their proper section
index 3de72bf..0a75961 100644 (file)
@@ -78,12 +78,10 @@ class ApiQueryStashImageInfo extends ApiQueryImageInfo {
                return array(
                        'filekey' => array(
                                ApiBase::PARAM_ISMULTI => true,
-                               ApiBase::PARAM_DFLT => null
                        ),
                        'sessionkey' => array(
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_DEPRECATED => true,
-                               ApiBase::PARAM_DFLT => null
                        ),
                        'prop' => array(
                                ApiBase::PARAM_ISMULTI => true,
index f916537..93c0dd0 100644 (file)
@@ -250,7 +250,6 @@ class ApiQueryUserInfo extends ApiQueryBase {
        public function getAllowedParams() {
                return array(
                        'prop' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => array(
                                        'blockinfo',
index 1d0048c..a826c1b 100644 (file)
@@ -294,7 +294,6 @@ class ApiQueryUsers extends ApiQueryBase {
        public function getAllowedParams() {
                return array(
                        'prop' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => array(
                                        'blockinfo',
index 320649f..5e13e98 100644 (file)
@@ -836,7 +836,6 @@ class ApiUpload extends ApiBase {
                        'url' => null,
                        'filekey' => null,
                        'sessionkey' => array(
-                               ApiBase::PARAM_DFLT => null,
                                ApiBase::PARAM_DEPRECATED => true,
                        ),
                        'stash' => false,
index 4aacd1a..837e3f9 100644 (file)
@@ -5,7 +5,8 @@
                        "Macofe",
                        "Xavier Dengra",
                        "F3RaN",
-                       "Eduardo Martinez"
+                       "Eduardo Martinez",
+                       "Fitoschido"
                ]
        },
        "apihelp-main-param-format": "El format de la sortida.",
        "apihelp-feedrecentchanges-param-tagfilter": "Filtra segons etiqueta.",
        "apihelp-feedrecentchanges-param-target": "Mostra només els canvis de les pàgines enllaçades a aquesta pàgina.",
        "apihelp-feedrecentchanges-example-simple": "Mostra els canvis recents.",
+       "apihelp-help-description": "Mostra l’ajuda dels mòduls especificats.",
        "apihelp-help-example-recursive": "Tota l'ajuda en una sola pàgina.",
        "apihelp-import-param-rootpage": "Importa com a subpàgina d'aquesta pàgina.",
        "apihelp-login-param-name": "Nom d'usuari.",
        "apihelp-login-param-password": "Contrasenya.",
        "apihelp-login-example-login": "Inicia sessió.",
+       "apihelp-options-example-reset": "Reinicialitza totes les preferències.",
        "apihelp-protect-param-cascade": "Activa la protecció en cascada (és a dir, protegeix les plantilles i imatges utilitzades en aquesta pàgina). S'ignora si cap dels nivells de protecció suporta la protecció en cascada.",
        "apihelp-query+pageswithprop-example-generator": "Obtenir informació addicional sobre les 10 primeres pàgines utilitzant <code>__NOTOC__</code>.",
        "apihelp-query+watchlist-paramvalue-prop-title": "Afegeix el títol de la pàgina.",
index f95e12a..157806d 100644 (file)
@@ -13,7 +13,8 @@
                        "Andreasburmeister",
                        "Anomie",
                        "Duder",
-                       "Ljonka"
+                       "Ljonka",
+                       "FriedhelmW"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page/de|Dokumentation]]\n* [[mw:API:FAQ/de|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:API:Errors_and_warnings|API: Fehler und Warnungen]].",
        "apihelp-expandtemplates-param-text": "Zu konvertierender Wikitext.",
        "apihelp-expandtemplates-param-revid": "Versionsnummer, die für die Anzeige von <nowiki>{{REVISIONID}}</nowiki> und ähnlichen Variablen verwendet wird.",
        "apihelp-expandtemplates-paramvalue-prop-wikitext": "Der expandierte Wikitext.",
+       "apihelp-expandtemplates-paramvalue-prop-ttl": "Die maximale Zeit, nach der der Ergebnis-Cache ungültig wird.",
        "apihelp-expandtemplates-paramvalue-prop-parsetree": "Der XML-Parserbaum der Eingabe.",
        "apihelp-expandtemplates-param-includecomments": "Ob HTML-Kommentare in der Ausgabe eingeschlossen werden sollen.",
        "apihelp-expandtemplates-param-generatexml": "XML-Parserbaum erzeugen (ersetzt durch $1prop=parsetree).",
        "apihelp-feedrecentchanges-param-hideliu": "Änderungen von registrierten Benutzern ausblenden.",
        "apihelp-feedrecentchanges-param-hidepatrolled": "Kontrollierte Änderungen ausblenden.",
        "apihelp-feedrecentchanges-param-hidemyself": "Änderungen des aktuellen Benutzers ausblenden.",
+       "apihelp-feedrecentchanges-param-hidecategorization": "Änderungen der Kategorie-Zugehörigkeit verstecken.",
        "apihelp-feedrecentchanges-param-tagfilter": "Nach Markierung filtern.",
        "apihelp-feedrecentchanges-param-target": "Nur Änderungen an Seiten anzeigen, die von dieser Seite verlinkt sind.",
        "apihelp-feedrecentchanges-param-showlinkedto": "Zeige Änderungen an Seiten die von der ausgewählten Seite verlinkt sind.",
        "apihelp-query+allimages-description": "Alle Bilder nacheinander auflisten.",
        "apihelp-query+allimages-param-sort": "Eigenschaft, nach der sortiert werden soll.",
        "apihelp-query+allimages-param-dir": "Aufzählungsrichtung.",
+       "apihelp-query+allimages-param-minsize": "Beschränkt auf Bilder mit mindestens dieser Anzahl an Bytes.",
+       "apihelp-query+allimages-param-maxsize": "Beschränkt auf Bilder mit höchstens dieser Anzahl an Bytes.",
        "apihelp-query+allimages-param-sha1": "SHA1-Hash des Bildes. Überschreibt $1sha1base36.",
        "apihelp-query+allimages-param-sha1base36": "SHA1-Hash des Bildes (Basis 36; verwendet in MediaWiki).",
        "apihelp-query+allimages-param-limit": "Wie viele Gesamtbilder zurückgegeben werden sollen.",
        "apihelp-userrights-param-user": "Benutzername.",
        "apihelp-userrights-param-userid": "Benutzerkennung.",
        "apihelp-watch-example-watch": "Die Seite <kbd>Main Page</kbd> beobachten.",
+       "apihelp-watch-example-unwatch": "Entbeobachtet die Seite <kbd>Main Page</kbd>.",
        "apihelp-format-example-generic": "Das Abfrageergebnis im $1-Format ausgeben.",
        "apihelp-dbg-description": "Daten im PHP-<code>var_export()</code>-Format ausgeben.",
        "apihelp-dbgfm-description": "Daten im PHP-<code>var_export()</code>-Format ausgeben (schöngedruckt in HTML).",
        "api-help-param-deprecated": "Veraltet.",
        "api-help-param-required": "Dieser Parameter ist erforderlich.",
        "api-help-datatypes-header": "Datentypen",
+       "api-help-param-type-limit": "Typ: Ganzzahl oder <kbd>max</kbd>",
+       "api-help-param-type-integer": "Typ: {{PLURAL:$1|1=Ganzzahl|2=Liste von Ganzzahlen}}",
        "api-help-param-list": "{{PLURAL:$1|1=Einer der folgenden Werte|2=Werte (mit <kbd>{{!}}</kbd> trennen)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Muss leer sein|Kann leer sein oder $2}}",
        "api-help-param-limit": "Nicht mehr als $1 erlaubt.",
        "api-help-param-default": "Standard: $1",
        "api-help-param-default-empty": "Standard: <span class=\"apihelp-empty\">(leer)</span>",
        "api-help-param-token": "Ein „$1“-Token abgerufen von [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
+       "api-help-param-token-webui": "Aus Kompatibilitätsgründen wird der in der Weboberfläche verwendete Token ebenfalls akzeptiert.",
+       "api-help-param-disabled-in-miser-mode": "Deaktiviert aufgrund des [[mw:Manual:$wgMiserMode|Miser-Modus]].",
+       "api-help-param-continue": "Falls weitere Ergebnisse verfügbar sind, dies zum Fortfahren verwenden.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(keine Beschreibung)</span>",
        "api-help-examples": "{{PLURAL:$1|Beispiel|Beispiele}}:",
        "api-help-permissions": "{{PLURAL:$1|Berechtigung|Berechtigungen}}:",
diff --git a/includes/api/i18n/id.json b/includes/api/i18n/id.json
new file mode 100644 (file)
index 0000000..223e585
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "WongKentir"
+               ]
+       },
+       "apihelp-createaccount-param-name": "Nama pengguna"
+}
index ea7c9eb..a13ad0a 100644 (file)
        "apihelp-query+revisions+base-paramvalue-prop-tags": "Makkehronge vun dä Väsjohn.",
        "apihelp-query+revisions+base-param-limit": "Wi vill Väsjohne sulle ußjejovve wähde?",
        "apihelp-query+revisions+base-param-section": "Holl blohß der Ennhald vun däm Affschnett met heh dä Nommer.",
+       "apihelp-query+revisions+base-param-difftotextpst": "Donn dä Täx ömsäze wi vör em Affseschere, ih de Ongerscheijde erus jefonge wähde. Jeihd blohß mem Parramehter <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1difftotext</var> zesamme.",
        "apihelp-query+search-description": "Söhk em jannze Täx.",
        "apihelp-query+search-param-search": "Söhk noh alle Övverschreffte vun Sigge udder Ennhallde, woh dä Wäät drop paß. Mer kann heh met besönder Aufjahbe beim Söhke schtälle, jeh nohdämm wadd_em Wikki sing Projramm för et Söhke esu alles kann.",
        "apihelp-query+search-param-namespace": "Söhk blohß en heh dä Appachtemangs.",
index bc072d7..a49c481 100644 (file)
@@ -38,6 +38,7 @@
        "apihelp-feedrecentchanges-param-hideanons": "Ännerunge vun anonyme Benotzer verstoppen.",
        "apihelp-feedrecentchanges-param-hideliu": "Ännerunge vu registréierte Benotzer verstoppen.",
        "apihelp-feedrecentchanges-param-hidemyself": "Ännerunge vum aktuelle Benotzer verstoppen.",
+       "apihelp-feedrecentchanges-param-hidecategorization": "Ännerunge vun der Memberschaft a Kategorie verstoppen.",
        "apihelp-feedrecentchanges-example-simple": "Rezent Ännerunge weisen",
        "apihelp-help-example-main": "Hëllef fir den Haaptmodul.",
        "apihelp-help-example-recursive": "All Hëllef op enger Säit",
        "apihelp-query+watchlist-paramvalue-prop-user": "Setzt de Benotzer derbäi deen d'Ännerung gemaach huet.",
        "apihelp-query+watchlist-paramvalue-prop-comment": "Setzt d'Bemierkung vun der Ännerung derbäi.",
        "apihelp-query+watchlist-paramvalue-prop-timestamp": "Setzt den Zäitstempel vun der Ännerung derbäi.",
+       "apihelp-query+watchlist-paramvalue-type-external": "Extern Ännerungen.",
+       "apihelp-query+watchlist-paramvalue-type-new": "Ugeluecht Säiten.",
        "apihelp-query+watchlistraw-param-show": "Nëmmen Elementer opzielen déi dëse Critèren entspriechen.",
        "apihelp-query+watchlistraw-example-simple": "Säite vum aktuelle Benotzer senger Iwwerwaachungslëscht opzielen",
        "apihelp-revisiondelete-description": "Versioune läschen a restauréieren.",
index 14b1127..5ad108b 100644 (file)
@@ -5,6 +5,7 @@
                ]
        },
        "apihelp-query+alldeletedrevisions-example-user": "Sąrašas paskutinių 50 ištrintų indėlių pagal vartotoją\n<kbd>Pavyzdys</kbd>.",
+       "apihelp-query+allrevisions-param-namespace": "Rodyti puslapius tik šioje vardų srityje.",
        "apihelp-query+backlinks-example-simple": "Rodyti nuorodas <kbd>Pagrindinis puslapis</kbd>.",
        "apihelp-query+watchlist-paramvalue-type-external": "Išoriniai keitimai.",
        "apihelp-query+watchlist-paramvalue-type-new": "Puslapio sukūrimai.",
index 5ce21c5..134ca2c 100644 (file)
        "api-pageset-param-revids": "Список идентификаторов версий для работы.",
        "api-help-title": "Справка MediaWiki API",
        "api-help-main-header": "Главный модуль",
-       "api-help-flag-deprecated": "ЭÑ\82оÑ\82 Ð¼Ð¾Ð´Ñ\83лÑ\8c Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\83Ñ\81Ñ\82аÑ\80евÑ\88им.",
+       "api-help-flag-deprecated": "ЭÑ\82оÑ\82 Ð¼Ð¾Ð´Ñ\83лÑ\8c Ñ\83Ñ\81Ñ\82аÑ\80ел.",
        "api-help-flag-readrights": "Этот модуль требует прав на чтение.",
        "api-help-flag-writerights": "Этот модуль требует права на запись.",
        "api-help-flag-mustbeposted": "Этот модуль принимает только Post-запросы.",
index ffd3c01..84b058a 100644 (file)
        "apihelp-query+allrevisions-param-namespace": "只列出此名字空间的页面。",
        "apihelp-query+allrevisions-param-generatetitles": "当作为生成器使用时,生成标题而不是修订ID。",
        "apihelp-query+allrevisions-example-user": "列出由用户<kbd>Example</kbd>作出的最近50次贡献。",
+       "apihelp-query+allrevisions-example-ns-main": "列举主名字空间中的前50次修订。",
        "apihelp-query+alltransclusions-description": "列出所有嵌入页面(使用&#123;&#123;x&#125;&#125;嵌入的页面),包括不存在的。",
        "apihelp-query+alltransclusions-param-from": "要列举的起始嵌入标题。",
        "apihelp-query+alltransclusions-param-to": "要列举的最终嵌入标题。",
        "apihelp-query+revisions+base-param-section": "只检索此段落数的内容。",
        "apihelp-query+revisions+base-param-diffto": "要比较修订差异的修订ID。使用<kbd>prev</kbd>、<kbd>next</kbd>和<kbd>cur</kbd>分别用于上个、下个和当前修订。",
        "apihelp-query+revisions+base-param-difftotext": "要比较修订差异的文本。只有修订的有限数字内的差异。覆盖<var>$1diffto</var>。如果<var>$1section</var>被设置,只有那个段落将与此文本之间比较差异",
+       "apihelp-query+revisions+base-param-difftotextpst": "在编辑文本前对其执行预保存转换。只当与<var>$1difftotext</var>一起使用时有效。",
        "apihelp-query+revisions+base-param-contentformat": "序列化用于<var>$1difftotext</var>的格式并预估内容输出。",
        "apihelp-query+search-description": "执行一次全文本搜索。",
        "apihelp-query+search-param-search": "搜索所有匹配此值的页面标题或内容。根据wiki的搜索后端工具,您可以使用搜索字符串以调用特殊搜索功能。",
        "apihelp-query+watchlist-paramvalue-type-external": "外部更改。",
        "apihelp-query+watchlist-paramvalue-type-new": "页面创建。",
        "apihelp-query+watchlist-paramvalue-type-log": "日志记录。",
+       "apihelp-query+watchlist-paramvalue-type-categorize": "分类成员组更改。",
        "apihelp-query+watchlist-param-token": "允许访问其他用户监视列表的安全密钥(可通过用户的[[Special:Preferences#mw-prefsection-watchlist|参数设置]]找到)。",
        "apihelp-query+watchlist-example-simple": "在当前用户的监视列表中列出用于最近更改页面的最新修订。",
        "apihelp-query+watchlist-example-props": "在当前用户的监视列表中检索有关用于最近更改页面的最新修订的额外信息。",
index 8a88fab..64db0d6 100644 (file)
@@ -79,11 +79,11 @@ class RedisConnectionPool implements LoggerAwareInterface {
 
        /**
         * @param array $options
-        * @throws MWException
+        * @throws Exception
         */
        protected function __construct( array $options ) {
                if ( !class_exists( 'Redis' ) ) {
-                       throw new MWException( __CLASS__ . ' requires a Redis client library. ' .
+                       throw new Exception( __CLASS__ . ' requires a Redis client library. ' .
                                'See https://www.mediawiki.org/wiki/Redis#Setup' );
                }
                if ( isset( $options['logger'] ) ) {
@@ -102,7 +102,7 @@ class RedisConnectionPool implements LoggerAwareInterface {
                } elseif ( $options['serializer'] === 'none' ) {
                        $this->serializer = Redis::SERIALIZER_NONE;
                } else {
-                       throw new MWException( "Invalid serializer specified." );
+                       throw new InvalidArgumentException( "Invalid serializer specified." );
                }
        }
 
@@ -219,7 +219,9 @@ class RedisConnectionPool implements LoggerAwareInterface {
                        // TCP connection
                        $hostPort = IP::splitHostAndPort( $server );
                        if ( !$server || !$hostPort ) {
-                               throw new MWException( __CLASS__ . ": invalid configured server \"$server\"" );
+                               throw new InvalidArgumentException(
+                                       __CLASS__ . ": invalid configured server \"$server\""
+                               );
                        }
                        list( $host, $port ) = $hostPort;
                        if ( $port === false ) {
index 2f135a4..57c28bb 100644 (file)
@@ -607,7 +607,6 @@ abstract class DatabaseBase implements IDatabase {
        function __construct( array $params ) {
                global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode;
 
-               $this->mTrxAtomicLevels = new SplStack;
                $this->srvCache = ObjectCache::newAccelerator( 'hash' );
 
                $server = $params['host'];
index 6453854..e6c285e 100644 (file)
@@ -455,5 +455,5 @@ class DBUnexpectedError extends DBError {
 /**
  * @ingroup Database
  */
-class DBReadOnlyError extends DBError {
+class DBReadOnlyError extends DBExpectedError {
 }
index 3631a34..6366c74 100644 (file)
@@ -44,7 +44,7 @@ class ErrorPageError extends MWException {
                // passing to the parent constructor. Our overridden report() below
                // makes sure that the page shown to the user is not forced to English.
                if ( $msg instanceof Message ) {
-                       $enMsg = clone( $msg );
+                       $enMsg = clone $msg;
                } else {
                        $enMsg = wfMessage( $msg, $params );
                }
index 9433964..e5ce968 100644 (file)
@@ -1752,7 +1752,7 @@ abstract class FileBackendStore extends FileBackend {
                                $pathNames[$this->fileCacheKey( $path )] = $path;
                        }
                }
-               // Get all cache entries for these container cache keys...
+               // Get all cache entries for these file cache keys...
                $values = $this->memCache->getMulti( array_keys( $pathNames ) );
                foreach ( $values as $cacheKey => $val ) {
                        $path = $pathNames[$cacheKey];
index 064bd6d..ba0e38f 100644 (file)
@@ -164,7 +164,6 @@ abstract class Installer {
                'wgRightsIcon',
                'wgRightsText',
                'wgRightsUrl',
-               'wgMainCacheType',
                'wgEnableEmail',
                'wgEnableUserEmail',
                'wgEnotifUserTalk',
@@ -691,17 +690,6 @@ abstract class Installer {
                return Status::newGood();
        }
 
-       /**
-        * Exports all wg* variables stored by the installer into global scope.
-        */
-       public function exportVars() {
-               foreach ( $this->settings as $name => $value ) {
-                       if ( substr( $name, 0, 2 ) == 'wg' ) {
-                               $GLOBALS[$name] = $value;
-                       }
-               }
-       }
-
        /**
         * Environment check for DB types.
         * @return bool
index 5a2dd1a..b742074 100644 (file)
@@ -62,8 +62,8 @@ class LocalSettingsGenerator {
                                'wgLanguageCode', 'wgEnableEmail', 'wgEnableUserEmail', 'wgDiff3',
                                'wgEnotifUserTalk', 'wgEnotifWatchlist', 'wgEmailAuthentication',
                                'wgDBtype', 'wgSecretKey', 'wgRightsUrl', 'wgSitename', 'wgRightsIcon',
-                               'wgRightsText', 'wgMainCacheType', 'wgEnableUploads',
-                               'wgMainCacheType', '_MemCachedServers', 'wgDBserver', 'wgDBuser',
+                               'wgRightsText', '_MainCacheType', 'wgEnableUploads',
+                               '_MemCachedServers', 'wgDBserver', 'wgDBuser',
                                'wgDBpassword', 'wgUseInstantCommons', 'wgUpgradeKey', 'wgDefaultSkin',
                                'wgMetaNamespace', 'wgLogo',
                        ),
@@ -287,15 +287,15 @@ class LocalSettingsGenerator {
                $serverSetting = "";
                if ( array_key_exists( 'wgServer', $this->values ) && $this->values['wgServer'] !== null ) {
                        $serverSetting = "\n## The protocol and server name to use in fully-qualified URLs\n";
-                       $serverSetting .= "\$wgServer = \"{$this->values['wgServer']}\";\n";
+                       $serverSetting .= "\$wgServer = \"{$this->values['wgServer']}\";";
                }
 
-               switch ( $this->values['wgMainCacheType'] ) {
+               switch ( $this->values['_MainCacheType'] ) {
                        case 'anything':
                        case 'db':
                        case 'memcached':
                        case 'accel':
-                               $cacheType = 'CACHE_' . strtoupper( $this->values['wgMainCacheType'] );
+                               $cacheType = 'CACHE_' . strtoupper( $this->values['_MainCacheType'] );
                                break;
                        case 'none':
                        default:
@@ -334,7 +334,10 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 \$wgScriptPath = \"{$this->values['wgScriptPath']}\";
 ${serverSetting}
 
-## The relative URL path to the logo.  Make sure you change this from the default,
+## The URL path to static resources (images, scripts, etc.)
+\$wgResourceBasePath = \$wgScriptPath;
+
+## The URL path to the logo.  Make sure you change this from the default,
 ## or else you'll overwrite your logo when you upgrade!
 \$wgLogo = \"{$this->values['wgLogo']}\";
 
index 67a4def..9edc25a 100644 (file)
@@ -159,7 +159,6 @@ class WebInstaller extends Installer {
                        $this->settings = $session['settings'] + $this->settings;
                }
 
-               $this->exportVars();
                $this->setupLanguage();
 
                if ( ( $this->getVar( '_InstallDone' ) || $this->getVar( '_UpgradeDone' ) )
index 0d11463..191c752 100644 (file)
@@ -1131,7 +1131,7 @@ class WebInstallerOptions extends WebInstallerPage {
                $caches[] = 'memcached';
 
                // We'll hide/show this on demand when the value changes, see config.js.
-               $cacheval = $this->getVar( 'wgMainCacheType' );
+               $cacheval = $this->getVar( '_MainCacheType' );
                if ( !$cacheval ) {
                        // We need to set a default here; but don't hardcode it
                        // or we lose it every time we reload the page for validation
@@ -1147,7 +1147,7 @@ class WebInstallerOptions extends WebInstallerPage {
                        // For grep: The following messages are used as the item labels:
                        // config-cache-none, config-cache-accel, config-cache-memcached
                        $this->parent->getRadioSet( array(
-                               'var' => 'wgMainCacheType',
+                               'var' => '_MainCacheType',
                                'label' => 'config-cache-options',
                                'itemLabelPrefix' => 'config-cache-',
                                'values' => $caches,
@@ -1285,7 +1285,7 @@ class WebInstallerOptions extends WebInstallerPage {
                $this->parent->setVarsFromRequest( array( '_RightsProfile', '_LicenseCode',
                        'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', 'wgLogo',
                        'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
-                       'wgEmailAuthentication', 'wgMainCacheType', '_MemCachedServers',
+                       'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
                        'wgUseInstantCommons', 'wgDefaultSkin' ) );
 
                $retVal = true;
@@ -1351,7 +1351,7 @@ class WebInstallerOptions extends WebInstallerPage {
                }
                $this->parent->setVar( '_Extensions', $extsToInstall );
 
-               if ( $this->getVar( 'wgMainCacheType' ) == 'memcached' ) {
+               if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
                        $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
                        if ( !$memcServers ) {
                                $this->parent->showError( 'config-memcache-needservers' );
index 95e2ec4..06e38d5 100644 (file)
        "config-db-install-account": "Імя карыстальніка для ўсталяваньня",
        "config-db-username": "Імя карыстальніка базы зьвестак:",
        "config-db-password": "Пароль базы зьвестак:",
-       "config-db-password-empty": "Калі ласка, увядзіце пароль для новага карыстальніка базы зьвестак: $1.\nМагчыма стварыць карыстальніка без паролю, але гэта небясьпечна.",
-       "config-db-username-empty": "Вы мусіце ўвесьці значэньне парамэтру «{{int:config-db-username}}»",
        "config-db-install-username": "Увядзіце імя карыстальніка, якое будзе выкарыстоўвацца для злучэньня з базай зьвестак падчас усталяваньня. Гэта не назва рахунку MediaWiki; гэта імя карыстальніка Вашай базы зьвестак.",
        "config-db-install-password": "Увядзіце пароль, які будзе выкарыстоўвацца для злучэньня з базай зьвестак падчас усталяваньня. Гэта не пароль рахунку MediaWiki; гэта пароль Вашай базы зьвестак.",
        "config-db-install-help": "Увядзіце імя карыстальніка і пароль, якія будуць выкарыстаныя для далучэньня да базы зьвестак падчас працэсу ўсталяваньня.",
        "config-nofile": "Файл «$1» ня знойдзены. Ці быў ён выдалены?",
        "config-extension-link": "Ці ведаеце вы, што вашая вікі падтрымлівае [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions пашырэньні]?\n\nВы можаце праглядзець [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category пашырэньні паводле катэгорыяў].",
        "mainpagetext": "'''MediaWiki пасьпяхова ўсталяваная.'''",
-       "mainpagedocfooter": "Глядзіце [//meta.wikimedia.org/wiki/Help:Contents дапаможнік карыстальніка] для атрыманьня інфармацыі па карыстаньні вікі-праграмамі.\n\n== З чаго пачаць ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Сьпіс парамэтраў канфігурацыі]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Частыя пытаньні MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Рассылка паведамленьняў пра зьяўленьне новых вэрсіяў MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Пераклад MediaWiki на вашую мову]"
+       "mainpagedocfooter": "Глядзіце [//meta.wikimedia.org/wiki/Help:Contents дапаможнік карыстальніка] для атрыманьня інфармацыі па карыстаньні вікі-праграмамі.\n\n== З чаго пачаць ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Сьпіс парамэтраў канфігурацыі]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Частыя пытаньні MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Рассылка паведамленьняў пра зьяўленьне новых вэрсіяў MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Пераклад MediaWiki на вашую мову]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Даведайцеся, як змагацца з спамам у вашай вікі]"
 }
index ee33109..369a23b 100644 (file)
        "config-missing-db-name": "Heu d'introduir un valor per a «{{int:config-db-name}}».",
        "config-missing-db-host": "Heu d'introduir un valor per a «{{int:config-db-host}}».",
        "config-missing-db-server-oracle": "Heu d’introduir un valor per a «{{int:config-db-host-oracle}}».",
+       "config-invalid-db-name": "El nom de la base de dades, «$1», no és vàlid.\nUtilitzeu només lletres de l’ASCII (a-z, A-Z), xifres (0-9), guions baixos (_) i guionets (-).",
+       "config-invalid-db-prefix": "El prefix de la base de dades, «$1», no és vàlid.\nUtilitzeu només lletres de l’ASCII (a-z, A-Z), xifres (0-9), guions baixos (_) i guionets (-).",
        "config-connection-error": "$1.\n\nComproveu el servidor central, el nom d'usuari i la contrasenya i torneu-ho a provar.",
+       "config-invalid-schema": "L’esquema «$1» no és vàlid per al MediaWiki.\nUtilitzeu només lletres de l’ASCII (a-z, A-Z), xifres (0-9), guions baixos (_) i guionets (-).",
        "config-db-sys-create-oracle": "L'instal·lador només accepta emprar un compte SYSDBA per a la creació d'un nou compte.",
        "config-db-sys-user-exists-oracle": "El compte d’usuari «$1» ja existeix. SYSDBA només es pot fer servir per crear comptes nous.",
        "config-postgres-old": "Cal el PostgreSQL $1 o posterior. Teniu el $2.",
        "config-mssql-old": "Cal utilitzar el Microsoft SQL Server $1 o posterior. Teniu la versió $2.",
+       "config-sqlite-name-help": "Trieu un nom per identificar el wiki.\nNo feu servir espais ni guionets.\nAquest nom s’utilitzarà per a denominar el fitxer de les dades de l’SQLite.",
        "config-sqlite-mkdir-error": "S'ha produït un error en crear el directori de dades «$1».\nComproveu la ubicació i torneu-ho a provar.",
        "config-sqlite-dir-unwritable": "No s'ha pogut escriure al directori «$1».\nCanvieu els seus permisos per tal que el servidor web pugui escriure-hi i torneu-ho a provar.",
        "config-sqlite-connection-error": "$1. \n\nComproveu el directori de dades i el nom de la base de dades a continuació i torneu-ho a provar.",
        "config-sqlite-readonly": "El fitxer <code>$1</code> no es pot escriure.",
        "config-sqlite-cant-create-db": "No s'ha pogut crear el fitxer de base de dades <code>$1</code>.",
+       "config-can-upgrade": "Hi ha taules del MediaWiki en aquesta base de dades.\nPer actualitzar-les al MediaWiki $1, feu clic a <strong>Continua</strong>.",
        "config-upgrade-done-no-regenerate": "S'ha completat l'actualització.\n\nJa podeu [$1 començar a utilitzar el wiki].",
        "config-regenerate": "Torna a generar el LocalSettings.php →",
        "config-show-table-status": "La consulta <code>SHOW TABLE STATUS</code> ha fallat!",
        "config-skins-missing": "No s'ha trobat cap tema; MediaWiki utilitzarà el tema per defecte fins que hi instal·leu alguns adequats.",
        "config-skins-must-enable-some": "Heu de triar com a mínim un tema per habilitar.",
        "config-skins-must-enable-default": "Cal habilitar el tema triat per defecte.",
+       "config-install-begin": "En fer clic a «{{int:config-continue}}» s’iniciarà la instal·lació del MediaWiki. Si encara voleu fer canvis, feu clic a «{{int:config-back}}».",
        "config-install-step-done": "fet",
        "config-install-step-failed": "ha fallat",
        "config-install-extensions": "S'estan incloent les extensions",
index abcb333..9ddbfa4 100644 (file)
        "config-db-install-account": "Uživatelský účet pro instalaci",
        "config-db-username": "Databázové uživatelské jméno:",
        "config-db-password": "Databázové heslo:",
-       "config-db-password-empty": "Zadejte heslo pro nového databázového uživatele: $1.\nPřestože může jít zakládat nové uživatele i bez hesel, není to bezpečné.",
-       "config-db-username-empty": "Musíte zadat hodnotu pro „{{int:config-db-username}}“.",
        "config-db-install-username": "Zadejte uživatelské jméno, které se použije pro připojení k databázi v průběhu instalace.\nToto není jméno uživatelského účtu MediaWiki; toto je uživatelské jméno k vaší databázi.",
        "config-db-install-password": "Zadejte heslo, které se použije pro připojení k databázi v průběhu instalace.\nToto není heslo uživatelského účtu MediaWiki; toto je heslo k vaší databázi.",
        "config-db-install-help": "Zadejte uživatelské jméno a heslo, které se použijí pro připojení k databázi v průběhu instalace.",
        "config-nofile": "Soubor „$1“ nelze nalézt. Byl smazán?",
        "config-extension-link": "Věděli jste, že vaše wiki podporuje [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions rozšíření]?\n\nMůžete si prohlédnout [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category seznam rozšíření po kategoriích].",
        "mainpagetext": "'''MediaWiki byla úspěšně nainstalována.'''",
-       "mainpagedocfooter": "[//meta.wikimedia.org/wiki/Help:Contents Uživatelská příručka] vám napoví, jak MediaWiki používat.\n\n== Začínáme ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Nastavení konfigurace]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Často kladené otázky o MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce E-mailová konference oznámení MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Překlad MediaWiki do vašeho jazyka]"
+       "mainpagedocfooter": "[//meta.wikimedia.org/wiki/Help:Contents Uživatelská příručka] vám napoví, jak používat MediaWiki.\n\n== Začínáme ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Nastavení konfigurace]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Často kladené otázky o MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce E-mailová konference oznámení MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Překlad MediaWiki do vašeho jazyka]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Naučte se bojovat se spamem na vaší wiki]"
 }
index 7c68382..c23a268 100644 (file)
        "config-db-install-account": "Benutzerkonto für die Installation",
        "config-db-username": "Name des Datenbankbenutzers:",
        "config-db-password": "Passwort des Datenbankbenutzers:",
-       "config-db-password-empty": "Bitte ein Passwort für den neuen Datenbankbenutzer angeben: $1.\nObwohl es möglich ist, Datenbankbenutzer ohne Passwort anzulegen, so ist dies nicht sicher.",
-       "config-db-username-empty": "Du musst einen Wert für „{{int:config-db-username}}“ eingeben",
        "config-db-install-username": "Den Benutzernamen angeben, der für die Verbindung mit der Datenbank während des Installationsvorgangs genutzt werden soll. Es handelt sich dabei nicht um den Benutzernamen für das MediaWiki-Konto, sondern um den Benutzernamen der vorgesehenen Datenbank.",
        "config-db-install-password": "Das Passwort angeben, das für die Verbindung mit der Datenbank während des Installationsvorgangs genutzt werden soll. Es handelt sich dabei nicht um das Passwort für das MediaWiki-Konto, sondern um das Passwort der vorgesehenen Datenbank.",
        "config-db-install-help": "Benutzername und Passwort, die während des Installationsvorgangs, für die Verbindung mit der Datenbank, genutzt werden sollen, sind nun anzugeben.",
        "config-nofile": "Die Datei „$1“ konnte nicht gefunden werden. Wurde sie gelöscht?",
        "config-extension-link": "Wusstest du, dass dein Wiki die Nutzung von [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions Erweiterungen] unterstützt?\n\nDu kannst [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category Erweiterungen nach Kategorie] durchsuchen.",
        "mainpagetext": "'''MediaWiki wurde erfolgreich installiert.'''",
-       "mainpagedocfooter": "Hilfe zur Benutzung und Konfiguration der Wiki-Software findest du im [//meta.wikimedia.org/wiki/Help:Contents Benutzerhandbuch].\n\n== Starthilfen ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsvariablen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki-FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mailingliste neuer MediaWiki-Versionen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Lokalisiere MediaWiki für deine Sprache]"
+       "mainpagedocfooter": "Hilfe zur Benutzung und Konfiguration der Wiki-Software findest du im [//meta.wikimedia.org/wiki/Help:Contents Benutzerhandbuch].\n\n== Starthilfen ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsvariablen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki-FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mailingliste neuer MediaWiki-Versionen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Lokalisiere MediaWiki für deine Sprache]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]"
 }
index 8109c2e..d58e92f 100644 (file)
        "config-desc": "Το πρόγραμμα εγκατάστασης για το MediaWiki",
        "config-title": "Εγκατάσταση MediaWiki $1",
        "config-information": "Πληροφορίες",
+       "config-localsettings-upgrade": "Ένα  αρχείο <code>LocalSettings.php</code> έχει εντοπιστεί.\nΓια να αναβαθμίσετε αυτή την εγκατάσταση, παρακαλούμε να εισάγετε την τιμή των <code>$wgUpgradeKey</code> στο παρακάτω πλαίσιο.\nΘα το βρείτε στο <code>LocalSettings.php</code>.",
+       "config-localsettings-cli-upgrade": "Ένα αρχείο <code>LocalSettings.php</code> έχει εντοπιστεί.\nΓια να αναβαθμίσετε αυτή την εγκατάσταση, εκτελέστε το <code>update.php</code> αντ' αυτού.",
        "config-localsettings-key": "Κλειδί αναβάθμισης:",
        "config-localsettings-badkey": "Το κλειδί που δώσατε είναι εσφαλμένο.",
        "config-upgrade-key-missing": "Έχει εντοπιστεί μια υπάρχουσα εγκατάσταση του MediaWiki.\nΓια να αναβαθμίσετε αυτήν την εγκατάσταση, παρακαλούμε να βάλετε την ακόλουθη γραμμή στο κάτω μέρος του <code>LocalSettings.php</code> σας:\n\n$1",
+       "config-localsettings-incomplete": "Το υπάρχον <code>LocalSettings.php</code> φαίνεται να είναι ελλιπές.\nΤο $1 μεταβλητή δεν έχει οριστεί.\nΠαρακαλούμε να αλλάξετε  το <code>LocalSettings.php</code> έτσι ώστε αυτή η μεταβλητή έχει οριστεί, και κάντε κλικ στο \"{{int:Config-continue}}\".",
        "config-session-error": "Σφάλμα κατά την εκκίνηση συνεδρίας: $1",
+       "config-session-expired": "Τα δεδομένα συνόδου φαίνεται να έχουν λήξει.\nΣυνεδρίες έχουν ρυθμιστεί για μια διάρκεια ζωής $1.\nΜπορείτε να αυξήσετε αυτό βάζοντας  <code>session.gc_maxlifetime</code> στο php.ini.\nΚάντε επανεκκίνηση της διαδικασίας εγκατάστασης.",
        "config-your-language": "Η γλώσσα σας:",
        "config-your-language-help": "Επιλέξτε μία γλώσσα για τη διαδικασία της εγκατάστασης.",
        "config-wiki-language": "Γλώσσα του wiki:",
        "config-env-hhvm": "Το HHVM $1 είναι εγκατεστημένο.",
        "config-unicode-using-intl": "Χρησιμοποιώντας την [http://pecl.php.net/intl επέκταση intl PECL] για κανονικοποίηση Unicode.",
        "config-unicode-pure-php-warning": "<strong>Προειδοποίηση:</strong> Η [http://pecl.php.net/intl επέκταση intl PECL] δεν είναι διαθέσιμη για να χειριστεί την κανονικοποίηση Unicode, επιστρέφουμε στην αργή αμιγώς PHP εφαρμογή.\nΕάν λειτουργείτε έναν ιστότοπο υψηλής επισκεψιμότητας, θα πρέπει να ρίξετε μια ματιά στην [//www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations κανονικοποίηση Unicode].",
+       "config-memory-bad": "<strong>Προειδοποίηση:</strong> η <code>memory_limit</code> της PHP είναι $1.\nΑυτό είναι πιθανώς πολύ χαμηλό.\n\nΗ εγκατάσταση ενδέχεται να αποτύχει!",
        "config-xcache": "[http://xcache.lighttpd.net/ Το XCache] είναι εγκατεστημένο",
        "config-apc": "Το [http://www.php.net/apc APC] είναι εγκατεστημένο",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp Το WinCache] είναι εγκατεστημένο",
        "config-diff3-bad": "Το GNU diff3 δεν βρέθηκε.",
+       "config-git": "Βρέθηκε η Git έκδοση λογισμικού ελέγχου: <code>$1</code>.",
+       "config-git-bad": "Η Git έκδοση του λογισμικού ελέγχου δε βρέθηκε.",
+       "config-using-server": "Χρησιμοποιώντας το όνομα του διακομιστή \"<nowiki>$1</nowiki>\".",
+       "config-using-uri": "Χρησιμοποιώντας την διεύθυνση URL του διακομιστή \"<nowiki>$1$2</nowiki>\".",
+       "config-brokenlibxml": "Το σύστημά σας έχει ένα συνδυασμό εκδόσεων της PHP και libxml2 που είναι προβληματικές και μπορεί να προκαλέσει κρυμμένα στοιχεία διαφθοράς στο MediaWiki και άλλες εφαρμογές web.\nΑναβαθμίστε σε libxml2 2.7.3 ή μεταγενέστερο ([https://bugs.php.net/bug.php?id=45996 bug κατατεθεί με την PHP]).\nΗ εγκατάσταση ματαιώθηκε.",
        "config-db-type": "Τύπος βάσης δεδομένων:",
        "config-db-host": "Φιλοξενία βάσης δεδομένων:",
        "config-db-host-oracle": "Βάση δεδομένων TNS:",
@@ -61,6 +71,7 @@
        "config-db-install-account": "Λογαριασμός χρήστη για την εγκατάσταση",
        "config-db-username": "Όνομα χρήστη βάσης δεδομένων:",
        "config-db-password": "Κωδικός πρόσβασης βάσης δεδομένων:",
+       "config-db-account-lock": "Χρησιμοποιήστε το ίδιο όνομα χρήστη και κωδικό πρόσβασης στη διάρκεια της κανονικής λειτουργίας",
        "config-db-wiki-account": "Λογαριασμός χρήστη για κανονική λειτουργία",
        "config-db-prefix": "Πρόθεμα πίνακα βάσης δεδομένων:",
        "config-db-charset": "Σύνολο χαρακτήρων βάσης δεδομένων",
        "config-admin-password": "Κωδικός πρόσβασης:",
        "config-admin-password-confirm": "Επανάληψη κωδικού πρόσβασης:",
        "config-admin-name-blank": "Εισαγάγετε όνομα χρήστη διαχειριστή.",
+       "config-admin-name-invalid": "Το συγκεκριμένο όνομα χρήστη  \"<nowiki>$1</nowiki>\" δεν είναι έγκυρο. Δώστε ένα διαφορετικό όνομα χρήστη.",
+       "config-admin-password-blank": "Εισάγετε έναν κωδικό για τον λογαριασμό του διαχειριστή.",
        "config-admin-password-mismatch": "Οι δύο κωδικοί πρόσβασης που εισηγάγατε δεν ταιριάζουν.",
        "config-admin-email": "Διεύθυνση ηλεκτρονικού ταχυδρομείου:",
        "config-admin-error-bademail": "Έχετε εισαγάγει μη έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.",
index 1eb8e03..ac3178f 100644 (file)
        "config-nofile": "File \"$1\" could not be found. Has it been deleted?",
        "config-extension-link": "Did you know that your wiki supports [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensions]?\n\nYou can browse [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions by category] or the [//www.mediawiki.org/wiki/Extension_Matrix Extension Matrix] to see the full list of extensions.",
        "mainpagetext": "<strong>MediaWiki has been successfully installed.</strong>",
-       "mainpagedocfooter": "Consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software.\n\n== Getting started ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]"
+       "mainpagedocfooter": "Consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software.\n\n== Getting started ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]"
 }
index 9f6f22c..bbab9a1 100644 (file)
        "config-nofile": "El archivo \"$1\" no se pudo encontrar. ¿Se ha eliminado?",
        "config-extension-link": "¿Sabías que tu wiki admite [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensiones]?\n\nPuedes navegar por las [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category categorías] o visitar el [//www.mediawiki.org/wiki/Extension_Matrix centro de extensiones] para ver una lista completa.",
        "mainpagetext": "<strong>MediaWiki se ha instalado con éxito.</strong>",
-       "mainpagedocfooter": "Consulta la [//meta.wikimedia.org/wiki/Help:Contents/es guía del usuario] para obtener información sobre el uso del software wiki.\n\n== Primeros pasos ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista de ajustes de configuración]\n* [//www.mediawiki.org/wiki/Manual:FAQ/es Preguntas frecuentes sobre MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista de correo de anuncios de publicación de MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Traducir MediaWiki en tu idioma]"
+       "mainpagedocfooter": "Consulta la [//meta.wikimedia.org/wiki/Help:Contents/es guía del usuario] para obtener información sobre el uso del software wiki.\n\n== Primeros pasos ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista de ajustes de configuración]\n* [//www.mediawiki.org/wiki/Manual:FAQ/es Preguntas frecuentes sobre MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista de correo de anuncios de publicación de MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Traducir MediaWiki en tu idioma]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Aprende cómo combatir el spam en tu wiki]"
 }
index b9d0df5..16c7b3f 100644 (file)
@@ -10,7 +10,8 @@
                        "Seb35",
                        "Arifin.wijaya",
                        "Ilham151096",
-                       "Bennylin"
+                       "Bennylin",
+                       "WongKentir"
                ]
        },
        "config-desc": "Penginstal untuk MediaWiki",
        "config-memcache-badport": "Nomor porta Memcached harus antara $1 dan $2.",
        "config-extensions": "Ekstensi",
        "config-extensions-help": "Ekstensi yang tercantum di atas terdeteksi di direktori <code>./extensions</code>.\n\nEkstensi tersebut mungkin memerlukan konfigurasi tambahan, tetapi Anda dapat mengaktifkannya sekarang.",
-       "config-skins": "Kulit",
-       "config-skins-use-as-default": "Gunakan kulit ini secara baku",
+       "config-skins": "Tampilan",
+       "config-skins-use-as-default": "Gunakan tampilan ini secara baku",
        "config-install-alreadydone": "'''Peringatan:''' Anda tampaknya telah menginstal MediaWiki dan mencoba untuk menginstalnya lagi.\nLanjutkan ke halaman berikutnya.",
        "config-install-begin": "Dengan menekan \"{{int:config-continue}}\", Anda akan memulai instalasi MediaWiki.\nJika Anda masih ingin membuat perubahan, tekan \"{{int:config-back}}\".",
        "config-install-step-done": "selesai",
index 6020bdc..8a11b09 100644 (file)
        "config-nofile": "ファイル「$1」が見つかりませんでした。削除された可能性があります。",
        "config-extension-link": "あなたのウィキは[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions 拡張機能]をサポートしていることをご存知ですか?\n\n[//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category カテゴリ別で拡張機能を見る]か[//www.mediawiki.org/wiki/Extension_Matrix 拡張機能のマトリックス]で拡張機能すべてのリストをご覧になれます。",
        "mainpagetext": "<strong>MediaWiki のインストールに成功しました。</strong>",
-       "mainpagedocfooter": "ウィキソフトウェアの使い方に関する情報は[//meta.wikimedia.org/wiki/Help:Contents 利用者案内]を参照してください。\n\n== はじめましょう ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/ja 設定の一覧]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ja MediaWiki よくある質問と回答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki リリース情報メーリングリスト]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation/ja MediaWiki のあなたの言語へのローカライズ]"
+       "mainpagedocfooter": "ウィキソフトウェアの使い方に関する情報は[//meta.wikimedia.org/wiki/Help:Contents 利用者案内]を参照してください。\n\n== はじめましょう ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/ja 設定の一覧]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ja MediaWiki よくある質問と回答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki リリース情報メーリングリスト]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation/ja MediaWiki のあなたの言語へのローカライズ]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]"
 }
index 0c11be0..fd7dd5c 100644 (file)
        "config-nofile": "De Dattei „$1“ ham_mer nit jefonge. Es di fottjeschmeße?",
        "config-extension-link": "Häs De jewoß, dat et Wiki [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions Zohsazprojramme] hann kann?\n\nDo kanns [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category Zohsazprojramme noh Saachjroppe] söhke udder en de [//www.mediawiki.org/wiki/Extension_Matrix Tabäll met de Zohsazprojramme] kike, öm de kumplätte Leß met de Zohsazprojramme ze krijje.",
        "mainpagetext": "'''MehdijaWikki es jäz enschtalleht.'''",
-       "mainpagedocfooter": "Luur en et (änglesche) [//meta.wikimedia.org/wiki/Help:Contents Handbohch] wann De weße wells wi de Wikki-ẞoffwähr jebruch un bedehnt wähde moß.\n\n== För der Aanfang ==\nDat es och all op Änglesch:\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings En leß met müjjelesche Enschtällonge för et MehdijaWikki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Öff jefrooch övver et Mehdijawikki&nbsp;&hellip;]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce De Meilengleß met Annköndijonge övver neuje Aßjahbe vum MehdijaWikki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Donn MediaWiki op Ding Schprohch aanpaße]\n\n== Un dann ==\nDonn heh di Sigg ömbenänne un/udder jähje en ääschte Aanfangssigg för heh dat Wikki ußtuusche!\n\nAlles Johde!"
+       "mainpagedocfooter": "Luur en et (änglesche) [//meta.wikimedia.org/wiki/Help:Contents Handbohch] wann De weße wells wi de Wikki-ẞoffwähr jebruch un bedehnt wähde moß.\n\n== För der Aanfang ==\nDat es och all op Änglesch:\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings En leß met müjjelesche Enschtällonge för et MehdijaWikki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Öff jefrooch övver et Mehdijawikki&nbsp;&hellip;]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce De Meilengleß met Annköndijonge övver neuje Ußjahbe vum MehdijaWikki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Donn MediaWiki op Ding Schprohch aanpaße]\n\n=== Jrammatek ===\nJeh nohdämm, ovv_et „di {{SITENAME}}“, „dä {{SITENAME}}“ udder „dat {{SITENAME}}“ heiß, moß mer velleijsch en Datteij änndere. Wann „{{SITENAME}}“ med „wikki“ ov „wiki“ ophürt, moß mer nix donn. Bei „dä {{SITENAME}}“ och nit. Söns kütt en di Datteij <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">languages/classes/LanguageKsh.php</code> vör udder henger dä Reihj met „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">No need add neuter wikis having names ending in -wiki.</code>“ en neuje Reihj eren:\n* för „di {{SITENAME}}“ heijß di:\n*: <code>'{{SITENAME}}' => 'f',</code>\n* för „dat {{SITENAME}}“ heijs et:\n*: <code>'{{SITENAME}}' => 'n',</code>\n\n== Un dann ==\nDonn heh di Sigg ömbenänne un/udder jähje en ääschte Aanfangssigg för heh dat Wikki ußtuusche!\n\nAlles Johde!"
 }
index a30aeae..6b732a9 100644 (file)
@@ -73,6 +73,8 @@
        "config-db-schema": "MediaWiki schema:",
        "config-pg-test-error": "Negalima prisijungti prie duomenų bazės <strong>$1</strong>: $2",
        "config-sqlite-dir": "SQLite duomenų katalogas:",
+       "config-oracle-def-ts": "Numatytoji lentelių sritis:",
+       "config-oracle-temp-ts": "Laikina lentelių sritis:",
        "config-type-mysql": "MySQL (arba suderinama)",
        "config-type-mssql": "Microsoft SQL serveris",
        "config-header-mysql": "MySQL nustatymai",
        "config-header-oracle": "Oracle nustatymai",
        "config-header-mssql": "„Microsoft“ SQL serverio nustatymai",
        "config-invalid-db-type": "Neteisingas duomenų bazės tipas",
+       "config-missing-db-host": "Privalote įvesti „{{int:config-db-host}}“ reikšmę.",
+       "config-missing-db-server-oracle": "Privalote įvesti „{{int:config-db-host-oracle}}“ reikšmę.",
        "config-postgres-old": "PostgreSQL $1 ar vėlesnė yra reikalinga. Jūs turite $2.",
+       "config-regenerate": "Pergeneruoti LocalSettings.php →",
+       "config-db-web-account-same": "Naudoti tą pačią paskyrą kaip ir įdiegimui",
+       "config-db-web-create": "Sukurti paskyrą, jeigu jos nėra",
+       "config-mysql-engine": "Saugojimo variklis:",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
+       "config-mysql-charset": "Duomenų bazės simbolių rinkinys:",
        "config-mysql-utf8": "UTF-8",
+       "config-mssql-auth": "Autentifikavimo tipas:",
+       "config-mssql-sqlauth": "SQL Serverio autentifikavimas",
        "config-mssql-windowsauth": "Windows autentifikavimas",
        "config-site-name": "Viki pavadinimas:",
        "config-site-name-blank": "Įveskite svetainės pavadinimą.",
        "config-license-pd": "Viešas domenas",
        "config-email-settings": "El. pašto nustatymai",
        "config-email-watchlist": "Įjungti stebimų pranešimą",
+       "config-email-auth": "Įjungti el. pašto autentifikavimą",
        "config-upload-settings": "Vaizdų ir failų įkėlimai",
        "config-upload-enable": "Įgalinti failų įkėlimus",
        "config-upload-deleted": "Katalogas ištrintiems failams:",
        "config-install-done": "'''Sveikiname!'''\nJūs sėkmingai įdiegėte MediaWiki.\n\nĮdiegimo programa sukūrė <code>LocalSettings.php</code> failą.\nJame yra visos jūsų konfigūracijos.\n\nJums reikės atsisiųsti ir įdėti jį į savo wiki įdiegimo bazę (pačiame kataloge, kaip index.php). Atsisiuntimas turėtų prasidėti automatiškai.\n\nJei atsisiuntimas nebuvo pasiūlytas, arba jį atšaukėte, galite iš naujo atsisiųsti paspaudę žemiau esančią nuorodą:\n\n$3\n\n'''Pastaba:''' Jei jūs to nepadarysite dabar, tada šis sukurtas konfigūracijos failas nebus galimas vėliau, jei išeisite iš įdiegimo be atsisiuntimo.\n\nKai baigsite, jūs galėsite '''[$2 įeiti į savo viki]'''.",
        "config-download-localsettings": "Atsisiųsti <code>LocalSettings.php</code>",
        "config-help": "pagalba",
+       "config-help-tooltip": "spustelėkite išplėtimui",
+       "config-nofile": "Failas \"$1\" nerastas. Ar jis buvo ištrintas?",
        "mainpagetext": "'''MediaWiki sėkmingai įdiegta.'''",
        "mainpagedocfooter": "Informacijos apie wiki programinės įrangos naudojimą, ieškokite [//meta.wikimedia.org/wiki/Help:Contents žinyne].\n\n== Pradžiai ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Konfigūracijos nustatymų sąrašas]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki DUK]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki pranešimai paštu apie naujas versijas]"
 }
index ef9fec0..6b1a1e3 100644 (file)
@@ -103,6 +103,7 @@ class HTMLCacheUpdateJob extends Job {
                // Check $wgUpdateRowsPerQuery for sanity; batch jobs are sized by that already.
                foreach ( array_chunk( $pageIds, $wgUpdateRowsPerQuery ) as $batch ) {
                        $dbw->commit( __METHOD__, 'flush' );
+                       wfWaitForSlaves();
 
                        $dbw->update( 'page',
                                array( 'page_touched' => $dbw->timestamp( $touchTimestamp ) ),
index 1d59b32..3a83cb8 100644 (file)
@@ -111,12 +111,7 @@ class RefreshLinksJob extends Job {
         * @param Title $title
         * @return bool
         */
-       protected function runForTitle( Title $title = null ) {
-               if ( is_null( $title ) ) {
-                       $this->setLastError( "refreshLinks: Invalid title" );
-                       return false;
-               }
-
+       protected function runForTitle( Title $title ) {
                // Wait for the DB of the current/next slave DB handle to catch up to the master.
                // This way, we get the correct page_latest for templates or files that just changed
                // milliseconds ago, having triggered this job to begin with.
index 0b9aa7c..6191612 100644 (file)
@@ -35,13 +35,6 @@ class ObjectFactory {
         * an 'args' key that provides arguments to pass to the
         * constructor/callable.
         *
-        * Object construction using a specification having both 'class' and
-        * 'args' members will call the constructor of the class using
-        * ReflectionClass::newInstanceArgs. The use of ReflectionClass carries
-        * a performance penalty and should not be used to create large numbers of
-        * objects. If this is needed, consider introducing a factory method that
-        * can be called via call_user_func_array() instead.
-        *
         * Values in the arguments collection which are Closure instances will be
         * expanded by invoking them with no arguments before passing the
         * resulting value on to the constructor/callable. This can be used to
@@ -77,8 +70,7 @@ class ObjectFactory {
                        if ( !$args ) {
                                $obj = new $clazz();
                        } else {
-                               $ref = new ReflectionClass( $clazz );
-                               $obj = $ref->newInstanceArgs( $args );
+                               $obj = static::constructClassInstance( $clazz, $args );
                        }
                } elseif ( isset( $spec['factory'] ) ) {
                        $obj = call_user_func_array( $spec['factory'], $args );
@@ -117,4 +109,86 @@ class ObjectFactory {
                        }
                }, $list );
        }
+
+       /**
+        * Construct an instance of the given class using the given arguments.
+        *
+        * PHP's `call_user_func_array()` doesn't work with object construction so
+        * we have to use other measures. Starting with PHP 5.6.0 we could use the
+        * "splat" operator (`...`) to unpack the array into an argument list.
+        * Sadly there is no way to conditionally include a syntax construct like
+        * a new operator in a way that allows older versions of PHP to still
+        * parse the file. Instead, we will try a loop unrolling technique that
+        * works for 0-10 arguments. If we are passed 11 or more arguments we will
+        * take the performance penalty of using
+        * `ReflectionClass::newInstanceArgs()` to construct the desired object.
+        *
+        * @param string $clazz Class name
+        * @param array $args Constructor arguments
+        * @return mixed Constructed instance
+        */
+       public static function constructClassInstance( $clazz, $args ) {
+               // TODO: when PHP min version supported is >=5.6.0 replace this
+               // function body with `return new $clazz( ... $args );`.
+               $obj = null;
+               switch ( count( $args ) ) {
+                       case 0:
+                               $obj = new $clazz();
+                               break;
+                       case 1:
+                               $obj = new $clazz( $args[0] );
+                               break;
+                       case 2:
+                               $obj = new $clazz( $args[0], $args[1] );
+                               break;
+                       case 3:
+                               $obj = new $clazz( $args[0], $args[1], $args[2] );
+                               break;
+                       case 4:
+                               $obj = new $clazz( $args[0], $args[1], $args[2], $args[3] );
+                               break;
+                       case 5:
+                               $obj = new $clazz(
+                                       $args[0], $args[1], $args[2], $args[3], $args[4]
+                               );
+                               break;
+                       case 6:
+                               $obj = new $clazz(
+                                       $args[0], $args[1], $args[2], $args[3], $args[4],
+                                       $args[5]
+                               );
+                               break;
+                       case 7:
+                               $obj = new $clazz(
+                                       $args[0], $args[1], $args[2], $args[3], $args[4],
+                                       $args[5], $args[6]
+                               );
+                               break;
+                       case 8:
+                               $obj = new $clazz(
+                                       $args[0], $args[1], $args[2], $args[3], $args[4],
+                                       $args[5], $args[6], $args[7]
+                               );
+                               break;
+                       case 9:
+                               $obj = new $clazz(
+                                       $args[0], $args[1], $args[2], $args[3], $args[4],
+                                       $args[5], $args[6], $args[7], $args[8]
+                               );
+                               break;
+                       case 10:
+                               $obj = new $clazz(
+                                       $args[0], $args[1], $args[2], $args[3], $args[4],
+                                       $args[5], $args[6], $args[7], $args[8], $args[9]
+                               );
+                               break;
+                       default:
+                               // Fall back to using ReflectionClass and curse the developer
+                               // who decided that 11+ args was a reasonable method
+                               // signature.
+                               $ref = new ReflectionClass( $clazz );
+                               $obj = $ref->newInstanceArgs( $args );
+               }
+               return $obj;
+       }
 }
diff --git a/includes/libs/composer/ComposerInstalled.php b/includes/libs/composer/ComposerInstalled.php
new file mode 100644 (file)
index 0000000..5f87b54
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Reads an installed.json file and provides accessors to get what is
+ * installed
+ *
+ * @since 1.27
+ */
+class ComposerInstalled {
+
+       /**
+        * @param string $location
+        */
+       public function __construct( $location ) {
+               $this->contents = json_decode( file_get_contents( $location ), true );
+       }
+
+       /**
+        * Dependencies currently installed according to installed.json
+        *
+        * @return array
+        */
+       public function getInstalledDependencies() {
+               $deps = array();
+               foreach ( $this->contents as $installed ) {
+                       $deps[$installed['name']] = array(
+                               'version' => ComposerJson::normalizeVersion( $installed['version'] ),
+                               'type' => $installed['type'],
+                               'licenses' => isset( $installed['license'] ) ? $installed['license'] : array(),
+                               'authors' => isset( $installed['authors'] ) ? $installed['authors'] : array(),
+                               'description' => isset( $installed['description'] ) ? $installed['description']: '',
+                       );
+               }
+
+               ksort( $deps );
+               return $deps;
+       }
+}
index c3c357f..ecc5e37 100644 (file)
@@ -627,7 +627,11 @@ abstract class BagOStuff implements LoggerAwareInterface {
         * @return string
         */
        public function makeKeyInternal( $keyspace, $args ) {
-               $key = $keyspace . ':' . implode( ':', $args );
+               $key = $keyspace;
+               foreach ( $args as $arg ) {
+                       $arg = str_replace( ':', '%3A', $arg );
+                       $key = $key . ':' . $arg;
+               }
                return strtr( $key, ' ', '_' );
        }
 
diff --git a/includes/libs/objectcache/MemcachedBagOStuff.php b/includes/libs/objectcache/MemcachedBagOStuff.php
new file mode 100644 (file)
index 0000000..ef6b3c7
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * Base class for memcached clients.
+ *
+ * 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
+ * @ingroup Cache
+ */
+
+/**
+ * Base class for memcached clients.
+ *
+ * @ingroup Cache
+ */
+class MemcachedBagOStuff extends BagOStuff {
+       /** @var MWMemcached|Memcached */
+       protected $client;
+
+       /**
+        * Fill in some defaults for missing keys in $params.
+        *
+        * @param array $params
+        * @return array
+        */
+       protected function applyDefaultParams( $params ) {
+               if ( !isset( $params['compress_threshold'] ) ) {
+                       $params['compress_threshold'] = 1500;
+               }
+               if ( !isset( $params['connect_timeout'] ) ) {
+                       $params['connect_timeout'] = 0.5;
+               }
+               return $params;
+       }
+
+       protected function doGet( $key, $flags = 0 ) {
+               $casToken = null;
+
+               return $this->getWithToken( $key, $casToken, $flags );
+       }
+
+       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+               return $this->client->get( $this->validateKeyEncoding( $key ), $casToken );
+       }
+
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+               return $this->client->set( $this->validateKeyEncoding( $key ), $value,
+                       $this->fixExpiry( $exptime ) );
+       }
+
+       protected function cas( $casToken, $key, $value, $exptime = 0 ) {
+               return $this->client->cas( $casToken, $this->validateKeyEncoding( $key ),
+                       $value, $this->fixExpiry( $exptime ) );
+       }
+
+       public function delete( $key ) {
+               return $this->client->delete( $this->validateKeyEncoding( $key ) );
+       }
+
+       public function add( $key, $value, $exptime = 0 ) {
+               return $this->client->add( $this->validateKeyEncoding( $key ), $value,
+                       $this->fixExpiry( $exptime ) );
+       }
+
+       public function merge( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
+               if ( !is_callable( $callback ) ) {
+                       throw new Exception( "Got invalid callback." );
+               }
+
+               return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
+       }
+
+       /**
+        * Get the underlying client object. This is provided for debugging
+        * purposes.
+        * @return BagOStuff
+        */
+       public function getClient() {
+               return $this->client;
+       }
+
+       /**
+        * Construct a cache key.
+        *
+        * @since 1.27
+        * @param string $keyspace
+        * @param array $args
+        * @return string
+        */
+       public function makeKeyInternal( $keyspace, $args ) {
+               // Memcached keys have a maximum length of 255 characters. From that,
+               // subtract the number of characters we need for the keyspace and for
+               // the separator character needed for each argument.
+               $charsLeft = 255 - strlen( $keyspace ) - count( $args );
+
+               $args = array_map(
+                       function ( $arg ) use ( &$charsLeft ) {
+                               $arg = strtr( $arg, ' ', '_' );
+
+                               // Make sure %, #, and non-ASCII chars are escaped
+                               $arg = preg_replace_callback(
+                                       '/[^\x21-\x22\x24\x26-\x39\x3b-\x7e]+/',
+                                       function ( $m ) {
+                                               return rawurlencode( $m[0] );
+                                       },
+                                       $arg
+                               );
+
+                               // 33 = 32 characters for the MD5 + 1 for the '#' prefix.
+                               if ( $charsLeft > 33 && strlen( $arg ) > $charsLeft ) {
+                                       $arg = '#' . md5( $arg );
+                               }
+
+                               $charsLeft -= strlen( $arg );
+                               return $arg;
+                       },
+                       $args
+               );
+
+               if ( $charsLeft < 0 ) {
+                       return $keyspace . ':##' . md5( implode( ':', $args ) );
+               }
+
+               return $keyspace . ':' . implode( ':', $args );
+       }
+
+       /**
+        * Ensure that a key is safe to use (contains no control characters and no
+        * characters above the ASCII range.)
+        *
+        * @param string $key
+        * @return string
+        * @throws Exception
+        */
+       public function validateKeyEncoding( $key ) {
+               if ( preg_match( '/[^\x21-\x7e]+/', $key ) ) {
+                       throw new Exception( "Key contains invalid characters: $key" );
+               }
+               return $key;
+       }
+
+       /**
+        * TTLs higher than 30 days will be detected as absolute TTLs
+        * (UNIX timestamps), and will result in the cache entry being
+        * discarded immediately because the expiry is in the past.
+        * Clamp expires >30d at 30d, unless they're >=1e9 in which
+        * case they are likely to really be absolute (1e9 = 2011-09-09)
+        * @param int $expiry
+        * @return int
+        */
+       function fixExpiry( $expiry ) {
+               if ( $expiry > 2592000 && $expiry < 1000000000 ) {
+                       $expiry = 2592000;
+               }
+               return (int)$expiry;
+       }
+
+       /**
+        * Send a debug message to the log
+        * @param string $text
+        */
+       protected function debugLog( $text ) {
+               $this->logger->debug( $text );
+       }
+
+       public function modifySimpleRelayEvent( array $event ) {
+               if ( array_key_exists( 'val', $event ) ) {
+                       $event['flg'] = 0; // data is not serialized nor gzipped (for memcached driver)
+               }
+
+               return $event;
+       }
+}
diff --git a/includes/libs/objectcache/MemcachedClient.php b/includes/libs/objectcache/MemcachedClient.php
new file mode 100644 (file)
index 0000000..5010b89
--- /dev/null
@@ -0,0 +1,1276 @@
+<?php
+// @codingStandardsIgnoreFile It's an external lib and it isn't. Let's not bother.
+/**
+ * Memcached client for PHP.
+ *
+ * +---------------------------------------------------------------------------+
+ * | memcached client, PHP                                                     |
+ * +---------------------------------------------------------------------------+
+ * | Copyright (c) 2003 Ryan T. Dean <rtdean@cytherianage.net>                 |
+ * | All rights reserved.                                                      |
+ * |                                                                           |
+ * | Redistribution and use in source and binary forms, with or without        |
+ * | modification, are permitted provided that the following conditions        |
+ * | are met:                                                                  |
+ * |                                                                           |
+ * | 1. Redistributions of source code must retain the above copyright         |
+ * |    notice, this list of conditions and the following disclaimer.          |
+ * | 2. Redistributions in binary form must reproduce the above copyright      |
+ * |    notice, this list of conditions and the following disclaimer in the    |
+ * |    documentation and/or other materials provided with the distribution.   |
+ * |                                                                           |
+ * | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
+ * | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
+ * | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
+ * | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
+ * | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
+ * | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
+ * | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
+ * | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
+ * | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
+ * +---------------------------------------------------------------------------+
+ * | Author: Ryan T. Dean <rtdean@cytherianage.net>                            |
+ * | Heavily influenced by the Perl memcached client by Brad Fitzpatrick.      |
+ * |   Permission granted by Brad Fitzpatrick for relicense of ported Perl     |
+ * |   client logic under 2-clause BSD license.                                |
+ * +---------------------------------------------------------------------------+
+ *
+ * @file
+ * $TCAnet$
+ */
+
+/**
+ * This is the PHP client for memcached - a distributed memory cache daemon.
+ * More information is available at http://www.danga.com/memcached/
+ *
+ * Usage example:
+ *
+ * require_once 'memcached.php';
+ *
+ * $mc = new MWMemcached(array(
+ *              'servers' => array('127.0.0.1:10000',
+ *                                 array('192.0.0.1:10010', 2),
+ *                                 '127.0.0.1:10020'),
+ *              'debug'   => false,
+ *              'compress_threshold' => 10240,
+ *              'persistent' => true));
+ *
+ * $mc->add( 'key', array( 'some', 'array' ) );
+ * $mc->replace( 'key', 'some random string' );
+ * $val = $mc->get( 'key' );
+ *
+ * @author  Ryan T. Dean <rtdean@cytherianage.net>
+ * @version 0.1.2
+ */
+
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+// {{{ requirements
+// }}}
+
+// {{{ class MWMemcached
+/**
+ * memcached client class implemented using (p)fsockopen()
+ *
+ * @author  Ryan T. Dean <rtdean@cytherianage.net>
+ * @ingroup Cache
+ */
+class MWMemcached {
+       // {{{ properties
+       // {{{ public
+
+       // {{{ constants
+       // {{{ flags
+
+       /**
+        * Flag: indicates data is serialized
+        */
+       const SERIALIZED = 1;
+
+       /**
+        * Flag: indicates data is compressed
+        */
+       const COMPRESSED = 2;
+
+       /**
+        * Flag: indicates data is an integer
+        */
+       const INTVAL = 4;
+
+       // }}}
+
+       /**
+        * Minimum savings to store data compressed
+        */
+       const COMPRESSION_SAVINGS = 0.20;
+
+       // }}}
+
+       /**
+        * Command statistics
+        *
+        * @var array
+        * @access public
+        */
+       public $stats;
+
+       // }}}
+       // {{{ private
+
+       /**
+        * Cached Sockets that are connected
+        *
+        * @var array
+        * @access private
+        */
+       public $_cache_sock;
+
+       /**
+        * Current debug status; 0 - none to 9 - profiling
+        *
+        * @var bool
+        * @access private
+        */
+       public $_debug;
+
+       /**
+        * Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
+        *
+        * @var array
+        * @access private
+        */
+       public $_host_dead;
+
+       /**
+        * Is compression available?
+        *
+        * @var bool
+        * @access private
+        */
+       public $_have_zlib;
+
+       /**
+        * Do we want to use compression?
+        *
+        * @var bool
+        * @access private
+        */
+       public $_compress_enable;
+
+       /**
+        * At how many bytes should we compress?
+        *
+        * @var int
+        * @access private
+        */
+       public $_compress_threshold;
+
+       /**
+        * Are we using persistent links?
+        *
+        * @var bool
+        * @access private
+        */
+       public $_persistent;
+
+       /**
+        * If only using one server; contains ip:port to connect to
+        *
+        * @var string
+        * @access private
+        */
+       public $_single_sock;
+
+       /**
+        * Array containing ip:port or array(ip:port, weight)
+        *
+        * @var array
+        * @access private
+        */
+       public $_servers;
+
+       /**
+        * Our bit buckets
+        *
+        * @var array
+        * @access private
+        */
+       public $_buckets;
+
+       /**
+        * Total # of bit buckets we have
+        *
+        * @var int
+        * @access private
+        */
+       public $_bucketcount;
+
+       /**
+        * # of total servers we have
+        *
+        * @var int
+        * @access private
+        */
+       public $_active;
+
+       /**
+        * Stream timeout in seconds. Applies for example to fread()
+        *
+        * @var int
+        * @access private
+        */
+       public $_timeout_seconds;
+
+       /**
+        * Stream timeout in microseconds
+        *
+        * @var int
+        * @access private
+        */
+       public $_timeout_microseconds;
+
+       /**
+        * Connect timeout in seconds
+        */
+       public $_connect_timeout;
+
+       /**
+        * Number of connection attempts for each server
+        */
+       public $_connect_attempts;
+
+       /**
+        * @var LoggerInterface
+        */
+       private $_logger;
+
+       // }}}
+       // }}}
+       // {{{ methods
+       // {{{ public functions
+       // {{{ memcached()
+
+       /**
+        * Memcache initializer
+        *
+        * @param array $args Associative array of settings
+        *
+        * @return mixed
+        */
+       public function __construct( $args ) {
+               $this->set_servers( isset( $args['servers'] ) ? $args['servers'] : array() );
+               $this->_debug = isset( $args['debug'] ) ? $args['debug'] : false;
+               $this->stats = array();
+               $this->_compress_threshold = isset( $args['compress_threshold'] ) ? $args['compress_threshold'] : 0;
+               $this->_persistent = isset( $args['persistent'] ) ? $args['persistent'] : false;
+               $this->_compress_enable = true;
+               $this->_have_zlib = function_exists( 'gzcompress' );
+
+               $this->_cache_sock = array();
+               $this->_host_dead = array();
+
+               $this->_timeout_seconds = 0;
+               $this->_timeout_microseconds = isset( $args['timeout'] ) ? $args['timeout'] : 500000;
+
+               $this->_connect_timeout = isset( $args['connect_timeout'] ) ? $args['connect_timeout'] : 0.1;
+               $this->_connect_attempts = 2;
+
+               $this->_logger = isset( $args['logger'] ) ? $args['logger'] : new NullLogger();
+       }
+
+       // }}}
+       // {{{ add()
+
+       /**
+        * Adds a key/value to the memcache server if one isn't already set with
+        * that key
+        *
+        * @param string $key Key to set with data
+        * @param mixed $val Value to store
+        * @param int $exp (optional) Expiration time. This can be a number of seconds
+        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
+        * longer must be the timestamp of the time at which the mapping should expire. It
+        * is safe to use timestamps in all cases, regardless of expiration
+        * eg: strtotime("+3 hour")
+        *
+        * @return bool
+        */
+       public function add( $key, $val, $exp = 0 ) {
+               return $this->_set( 'add', $key, $val, $exp );
+       }
+
+       // }}}
+       // {{{ decr()
+
+       /**
+        * Decrease a value stored on the memcache server
+        *
+        * @param string $key Key to decrease
+        * @param int $amt (optional) amount to decrease
+        *
+        * @return mixed False on failure, value on success
+        */
+       public function decr( $key, $amt = 1 ) {
+               return $this->_incrdecr( 'decr', $key, $amt );
+       }
+
+       // }}}
+       // {{{ delete()
+
+       /**
+        * Deletes a key from the server, optionally after $time
+        *
+        * @param string $key Key to delete
+        * @param int $time (optional) how long to wait before deleting
+        *
+        * @return bool True on success, false on failure
+        */
+       public function delete( $key, $time = 0 ) {
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               $sock = $this->get_sock( $key );
+               if ( !is_resource( $sock ) ) {
+                       return false;
+               }
+
+               $key = is_array( $key ) ? $key[1] : $key;
+
+               if ( isset( $this->stats['delete'] ) ) {
+                       $this->stats['delete']++;
+               } else {
+                       $this->stats['delete'] = 1;
+               }
+               $cmd = "delete $key $time\r\n";
+               if ( !$this->_fwrite( $sock, $cmd ) ) {
+                       return false;
+               }
+               $res = $this->_fgets( $sock );
+
+               if ( $this->_debug ) {
+                       $this->_debugprint( sprintf( "MemCache: delete %s (%s)\n", $key, $res ) );
+               }
+
+               if ( $res == "DELETED" || $res == "NOT_FOUND" ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * @param string $key
+        * @param int $timeout
+        * @return bool
+        */
+       public function lock( $key, $timeout = 0 ) {
+               /* stub */
+               return true;
+       }
+
+       /**
+        * @param string $key
+        * @return bool
+        */
+       public function unlock( $key ) {
+               /* stub */
+               return true;
+       }
+
+       // }}}
+       // {{{ disconnect_all()
+
+       /**
+        * Disconnects all connected sockets
+        */
+       public function disconnect_all() {
+               foreach ( $this->_cache_sock as $sock ) {
+                       fclose( $sock );
+               }
+
+               $this->_cache_sock = array();
+       }
+
+       // }}}
+       // {{{ enable_compress()
+
+       /**
+        * Enable / Disable compression
+        *
+        * @param bool $enable True to enable, false to disable
+        */
+       public function enable_compress( $enable ) {
+               $this->_compress_enable = $enable;
+       }
+
+       // }}}
+       // {{{ forget_dead_hosts()
+
+       /**
+        * Forget about all of the dead hosts
+        */
+       public function forget_dead_hosts() {
+               $this->_host_dead = array();
+       }
+
+       // }}}
+       // {{{ get()
+
+       /**
+        * Retrieves the value associated with the key from the memcache server
+        *
+        * @param array|string $key key to retrieve
+        * @param float $casToken [optional]
+        *
+        * @return mixed
+        */
+       public function get( $key, &$casToken = null ) {
+
+               if ( $this->_debug ) {
+                       $this->_debugprint( "get($key)\n" );
+               }
+
+               if ( !is_array( $key ) && strval( $key ) === '' ) {
+                       $this->_debugprint( "Skipping key which equals to an empty string" );
+                       return false;
+               }
+
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               $sock = $this->get_sock( $key );
+
+               if ( !is_resource( $sock ) ) {
+                       return false;
+               }
+
+               $key = is_array( $key ) ? $key[1] : $key;
+               if ( isset( $this->stats['get'] ) ) {
+                       $this->stats['get']++;
+               } else {
+                       $this->stats['get'] = 1;
+               }
+
+               $cmd = "gets $key\r\n";
+               if ( !$this->_fwrite( $sock, $cmd ) ) {
+                       return false;
+               }
+
+               $val = array();
+               $this->_load_items( $sock, $val, $casToken );
+
+               if ( $this->_debug ) {
+                       foreach ( $val as $k => $v ) {
+                               $this->_debugprint( sprintf( "MemCache: sock %s got %s\n", serialize( $sock ), $k ) );
+                       }
+               }
+
+               $value = false;
+               if ( isset( $val[$key] ) ) {
+                       $value = $val[$key];
+               }
+               return $value;
+       }
+
+       // }}}
+       // {{{ get_multi()
+
+       /**
+        * Get multiple keys from the server(s)
+        *
+        * @param array $keys Keys to retrieve
+        *
+        * @return array
+        */
+       public function get_multi( $keys ) {
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               if ( isset( $this->stats['get_multi'] ) ) {
+                       $this->stats['get_multi']++;
+               } else {
+                       $this->stats['get_multi'] = 1;
+               }
+               $sock_keys = array();
+               $socks = array();
+               foreach ( $keys as $key ) {
+                       $sock = $this->get_sock( $key );
+                       if ( !is_resource( $sock ) ) {
+                               continue;
+                       }
+                       $key = is_array( $key ) ? $key[1] : $key;
+                       if ( !isset( $sock_keys[$sock] ) ) {
+                               $sock_keys[intval( $sock )] = array();
+                               $socks[] = $sock;
+                       }
+                       $sock_keys[intval( $sock )][] = $key;
+               }
+
+               $gather = array();
+               // Send out the requests
+               foreach ( $socks as $sock ) {
+                       $cmd = 'gets';
+                       foreach ( $sock_keys[intval( $sock )] as $key ) {
+                               $cmd .= ' ' . $key;
+                       }
+                       $cmd .= "\r\n";
+
+                       if ( $this->_fwrite( $sock, $cmd ) ) {
+                               $gather[] = $sock;
+                       }
+               }
+
+               // Parse responses
+               $val = array();
+               foreach ( $gather as $sock ) {
+                       $this->_load_items( $sock, $val, $casToken );
+               }
+
+               if ( $this->_debug ) {
+                       foreach ( $val as $k => $v ) {
+                               $this->_debugprint( sprintf( "MemCache: got %s\n", $k ) );
+                       }
+               }
+
+               return $val;
+       }
+
+       // }}}
+       // {{{ incr()
+
+       /**
+        * Increments $key (optionally) by $amt
+        *
+        * @param string $key Key to increment
+        * @param int $amt (optional) amount to increment
+        *
+        * @return int|null Null if the key does not exist yet (this does NOT
+        * create new mappings if the key does not exist). If the key does
+        * exist, this returns the new value for that key.
+        */
+       public function incr( $key, $amt = 1 ) {
+               return $this->_incrdecr( 'incr', $key, $amt );
+       }
+
+       // }}}
+       // {{{ replace()
+
+       /**
+        * Overwrites an existing value for key; only works if key is already set
+        *
+        * @param string $key Key to set value as
+        * @param mixed $value Value to store
+        * @param int $exp (optional) Expiration time. This can be a number of seconds
+        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
+        * longer must be the timestamp of the time at which the mapping should expire. It
+        * is safe to use timestamps in all cases, regardless of exipration
+        * eg: strtotime("+3 hour")
+        *
+        * @return bool
+        */
+       public function replace( $key, $value, $exp = 0 ) {
+               return $this->_set( 'replace', $key, $value, $exp );
+       }
+
+       // }}}
+       // {{{ run_command()
+
+       /**
+        * Passes through $cmd to the memcache server connected by $sock; returns
+        * output as an array (null array if no output)
+        *
+        * @param Resource $sock Socket to send command on
+        * @param string $cmd Command to run
+        *
+        * @return array Output array
+        */
+       public function run_command( $sock, $cmd ) {
+               if ( !is_resource( $sock ) ) {
+                       return array();
+               }
+
+               if ( !$this->_fwrite( $sock, $cmd ) ) {
+                       return array();
+               }
+
+               $ret = array();
+               while ( true ) {
+                       $res = $this->_fgets( $sock );
+                       $ret[] = $res;
+                       if ( preg_match( '/^END/', $res ) ) {
+                               break;
+                       }
+                       if ( strlen( $res ) == 0 ) {
+                               break;
+                       }
+               }
+               return $ret;
+       }
+
+       // }}}
+       // {{{ set()
+
+       /**
+        * Unconditionally sets a key to a given value in the memcache.  Returns true
+        * if set successfully.
+        *
+        * @param string $key Key to set value as
+        * @param mixed $value Value to set
+        * @param int $exp (optional) Expiration time. This can be a number of seconds
+        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
+        * longer must be the timestamp of the time at which the mapping should expire. It
+        * is safe to use timestamps in all cases, regardless of exipration
+        * eg: strtotime("+3 hour")
+        *
+        * @return bool True on success
+        */
+       public function set( $key, $value, $exp = 0 ) {
+               return $this->_set( 'set', $key, $value, $exp );
+       }
+
+       // }}}
+       // {{{ cas()
+
+       /**
+        * Sets a key to a given value in the memcache if the current value still corresponds
+        * to a known, given value.  Returns true if set successfully.
+        *
+        * @param float $casToken Current known value
+        * @param string $key Key to set value as
+        * @param mixed $value Value to set
+        * @param int $exp (optional) Expiration time. This can be a number of seconds
+        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
+        * longer must be the timestamp of the time at which the mapping should expire. It
+        * is safe to use timestamps in all cases, regardless of exipration
+        * eg: strtotime("+3 hour")
+        *
+        * @return bool True on success
+        */
+       public function cas( $casToken, $key, $value, $exp = 0 ) {
+               return $this->_set( 'cas', $key, $value, $exp, $casToken );
+       }
+
+       // }}}
+       // {{{ set_compress_threshold()
+
+       /**
+        * Sets the compression threshold
+        *
+        * @param int $thresh Threshold to compress if larger than
+        */
+       public function set_compress_threshold( $thresh ) {
+               $this->_compress_threshold = $thresh;
+       }
+
+       // }}}
+       // {{{ set_debug()
+
+       /**
+        * Sets the debug flag
+        *
+        * @param bool $dbg True for debugging, false otherwise
+        *
+        * @see MWMemcached::__construct
+        */
+       public function set_debug( $dbg ) {
+               $this->_debug = $dbg;
+       }
+
+       // }}}
+       // {{{ set_servers()
+
+       /**
+        * Sets the server list to distribute key gets and puts between
+        *
+        * @param array $list Array of servers to connect to
+        *
+        * @see MWMemcached::__construct()
+        */
+       public function set_servers( $list ) {
+               $this->_servers = $list;
+               $this->_active = count( $list );
+               $this->_buckets = null;
+               $this->_bucketcount = 0;
+
+               $this->_single_sock = null;
+               if ( $this->_active == 1 ) {
+                       $this->_single_sock = $this->_servers[0];
+               }
+       }
+
+       /**
+        * Sets the timeout for new connections
+        *
+        * @param int $seconds Number of seconds
+        * @param int $microseconds Number of microseconds
+        */
+       public function set_timeout( $seconds, $microseconds ) {
+               $this->_timeout_seconds = $seconds;
+               $this->_timeout_microseconds = $microseconds;
+       }
+
+       // }}}
+       // }}}
+       // {{{ private methods
+       // {{{ _close_sock()
+
+       /**
+        * Close the specified socket
+        *
+        * @param string $sock Socket to close
+        *
+        * @access private
+        */
+       function _close_sock( $sock ) {
+               $host = array_search( $sock, $this->_cache_sock );
+               fclose( $this->_cache_sock[$host] );
+               unset( $this->_cache_sock[$host] );
+       }
+
+       // }}}
+       // {{{ _connect_sock()
+
+       /**
+        * Connects $sock to $host, timing out after $timeout
+        *
+        * @param int $sock Socket to connect
+        * @param string $host Host:IP to connect to
+        *
+        * @return bool
+        * @access private
+        */
+       function _connect_sock( &$sock, $host ) {
+               list( $ip, $port ) = preg_split( '/:(?=\d)/', $host );
+               $sock = false;
+               $timeout = $this->_connect_timeout;
+               $errno = $errstr = null;
+               for ( $i = 0; !$sock && $i < $this->_connect_attempts; $i++ ) {
+                       MediaWiki\suppressWarnings();
+                       if ( $this->_persistent == 1 ) {
+                               $sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
+                       } else {
+                               $sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
+                       }
+                       MediaWiki\restoreWarnings();
+               }
+               if ( !$sock ) {
+                       $this->_error_log( "Error connecting to $host: $errstr\n" );
+                       $this->_dead_host( $host );
+                       return false;
+               }
+
+               // Initialise timeout
+               stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
+
+               // If the connection was persistent, flush the read buffer in case there
+               // was a previous incomplete request on this connection
+               if ( $this->_persistent ) {
+                       $this->_flush_read_buffer( $sock );
+               }
+               return true;
+       }
+
+       // }}}
+       // {{{ _dead_sock()
+
+       /**
+        * Marks a host as dead until 30-40 seconds in the future
+        *
+        * @param string $sock Socket to mark as dead
+        *
+        * @access private
+        */
+       function _dead_sock( $sock ) {
+               $host = array_search( $sock, $this->_cache_sock );
+               $this->_dead_host( $host );
+       }
+
+       /**
+        * @param string $host
+        */
+       function _dead_host( $host ) {
+               $parts = explode( ':', $host );
+               $ip = $parts[0];
+               $this->_host_dead[$ip] = time() + 30 + intval( rand( 0, 10 ) );
+               $this->_host_dead[$host] = $this->_host_dead[$ip];
+               unset( $this->_cache_sock[$host] );
+       }
+
+       // }}}
+       // {{{ get_sock()
+
+       /**
+        * get_sock
+        *
+        * @param string $key Key to retrieve value for;
+        *
+        * @return Resource|bool Resource on success, false on failure
+        * @access private
+        */
+       function get_sock( $key ) {
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               if ( $this->_single_sock !== null ) {
+                       return $this->sock_to_host( $this->_single_sock );
+               }
+
+               $hv = is_array( $key ) ? intval( $key[0] ) : $this->_hashfunc( $key );
+               if ( $this->_buckets === null ) {
+                       $bu = array();
+                       foreach ( $this->_servers as $v ) {
+                               if ( is_array( $v ) ) {
+                                       for ( $i = 0; $i < $v[1]; $i++ ) {
+                                               $bu[] = $v[0];
+                                       }
+                               } else {
+                                       $bu[] = $v;
+                               }
+                       }
+                       $this->_buckets = $bu;
+                       $this->_bucketcount = count( $bu );
+               }
+
+               $realkey = is_array( $key ) ? $key[1] : $key;
+               for ( $tries = 0; $tries < 20; $tries++ ) {
+                       $host = $this->_buckets[$hv % $this->_bucketcount];
+                       $sock = $this->sock_to_host( $host );
+                       if ( is_resource( $sock ) ) {
+                               return $sock;
+                       }
+                       $hv = $this->_hashfunc( $hv . $realkey );
+               }
+
+               return false;
+       }
+
+       // }}}
+       // {{{ _hashfunc()
+
+       /**
+        * Creates a hash integer based on the $key
+        *
+        * @param string $key Key to hash
+        *
+        * @return int Hash value
+        * @access private
+        */
+       function _hashfunc( $key ) {
+               # Hash function must be in [0,0x7ffffff]
+               # We take the first 31 bits of the MD5 hash, which unlike the hash
+               # function used in a previous version of this client, works
+               return hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
+       }
+
+       // }}}
+       // {{{ _incrdecr()
+
+       /**
+        * Perform increment/decriment on $key
+        *
+        * @param string $cmd Command to perform
+        * @param string|array $key Key to perform it on
+        * @param int $amt Amount to adjust
+        *
+        * @return int New value of $key
+        * @access private
+        */
+       function _incrdecr( $cmd, $key, $amt = 1 ) {
+               if ( !$this->_active ) {
+                       return null;
+               }
+
+               $sock = $this->get_sock( $key );
+               if ( !is_resource( $sock ) ) {
+                       return null;
+               }
+
+               $key = is_array( $key ) ? $key[1] : $key;
+               if ( isset( $this->stats[$cmd] ) ) {
+                       $this->stats[$cmd]++;
+               } else {
+                       $this->stats[$cmd] = 1;
+               }
+               if ( !$this->_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
+                       return null;
+               }
+
+               $line = $this->_fgets( $sock );
+               $match = array();
+               if ( !preg_match( '/^(\d+)/', $line, $match ) ) {
+                       return null;
+               }
+               return $match[1];
+       }
+
+       // }}}
+       // {{{ _load_items()
+
+       /**
+        * Load items into $ret from $sock
+        *
+        * @param Resource $sock Socket to read from
+        * @param array $ret returned values
+        * @param float $casToken [optional]
+        * @return bool True for success, false for failure
+        *
+        * @access private
+        */
+       function _load_items( $sock, &$ret, &$casToken = null ) {
+               $results = array();
+
+               while ( 1 ) {
+                       $decl = $this->_fgets( $sock );
+
+                       if ( $decl === false ) {
+                               /*
+                                * If nothing can be read, something is wrong because we know exactly when
+                                * to stop reading (right after "END") and we return right after that.
+                                */
+                               return false;
+                       } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+) (\d+)$/', $decl, $match ) ) {
+                               /*
+                                * Read all data returned. This can be either one or multiple values.
+                                * Save all that data (in an array) to be processed later: we'll first
+                                * want to continue reading until "END" before doing anything else,
+                                * to make sure that we don't leave our client in a state where it's
+                                * output is not yet fully read.
+                                */
+                               $results[] = array(
+                                       $match[1], // rkey
+                                       $match[2], // flags
+                                       $match[3], // len
+                                       $match[4], // casToken
+                                       $this->_fread( $sock, $match[3] + 2 ), // data
+                               );
+                       } elseif ( $decl == "END" ) {
+                               if ( count( $results ) == 0 ) {
+                                       return false;
+                               }
+
+                               /**
+                                * All data has been read, time to process the data and build
+                                * meaningful return values.
+                                */
+                               foreach ( $results as $vars ) {
+                                       list( $rkey, $flags, $len, $casToken, $data ) = $vars;
+
+                                       if ( $data === false || substr( $data, -2 ) !== "\r\n" ) {
+                                               $this->_handle_error( $sock,
+                                                       'line ending missing from data block from $1' );
+                                               return false;
+                                       }
+                                       $data = substr( $data, 0, -2 );
+                                       $ret[$rkey] = $data;
+
+                                       if ( $this->_have_zlib && $flags & self::COMPRESSED ) {
+                                               $ret[$rkey] = gzuncompress( $ret[$rkey] );
+                                       }
+
+                                       /*
+                                        * This unserialize is the exact reason that we only want to
+                                        * process data after having read until "END" (instead of doing
+                                        * this right away): "unserialize" can trigger outside code:
+                                        * in the event that $ret[$rkey] is a serialized object,
+                                        * unserializing it will trigger __wakeup() if present. If that
+                                        * function attempted to read from memcached (while we did not
+                                        * yet read "END"), these 2 calls would collide.
+                                        */
+                                       if ( $flags & self::SERIALIZED ) {
+                                               $ret[$rkey] = unserialize( $ret[$rkey] );
+                                       } elseif ( $flags & self::INTVAL ) {
+                                               $ret[$rkey] = intval( $ret[$rkey] );
+                                       }
+                               }
+
+                               return true;
+                       } else {
+                               $this->_handle_error( $sock, 'Error parsing response from $1' );
+                               return false;
+                       }
+               }
+       }
+
+       // }}}
+       // {{{ _set()
+
+       /**
+        * Performs the requested storage operation to the memcache server
+        *
+        * @param string $cmd Command to perform
+        * @param string $key Key to act on
+        * @param mixed $val What we need to store
+        * @param int $exp (optional) Expiration time. This can be a number of seconds
+        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
+        * longer must be the timestamp of the time at which the mapping should expire. It
+        * is safe to use timestamps in all cases, regardless of exipration
+        * eg: strtotime("+3 hour")
+        * @param float $casToken [optional]
+        *
+        * @return bool
+        * @access private
+        */
+       function _set( $cmd, $key, $val, $exp, $casToken = null ) {
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               $sock = $this->get_sock( $key );
+               if ( !is_resource( $sock ) ) {
+                       return false;
+               }
+
+               if ( isset( $this->stats[$cmd] ) ) {
+                       $this->stats[$cmd]++;
+               } else {
+                       $this->stats[$cmd] = 1;
+               }
+
+               $flags = 0;
+
+               if ( is_int( $val ) ) {
+                       $flags |= self::INTVAL;
+               } elseif ( !is_scalar( $val ) ) {
+                       $val = serialize( $val );
+                       $flags |= self::SERIALIZED;
+                       if ( $this->_debug ) {
+                               $this->_debugprint( sprintf( "client: serializing data as it is not scalar\n" ) );
+                       }
+               }
+
+               $len = strlen( $val );
+
+               if ( $this->_have_zlib && $this->_compress_enable
+                       && $this->_compress_threshold && $len >= $this->_compress_threshold
+               ) {
+                       $c_val = gzcompress( $val, 9 );
+                       $c_len = strlen( $c_val );
+
+                       if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
+                               if ( $this->_debug ) {
+                                       $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len ) );
+                               }
+                               $val = $c_val;
+                               $len = $c_len;
+                               $flags |= self::COMPRESSED;
+                       }
+               }
+
+               $command = "$cmd $key $flags $exp $len";
+               if ( $casToken ) {
+                       $command .= " $casToken";
+               }
+
+               if ( !$this->_fwrite( $sock, "$command\r\n$val\r\n" ) ) {
+                       return false;
+               }
+
+               $line = $this->_fgets( $sock );
+
+               if ( $this->_debug ) {
+                       $this->_debugprint( sprintf( "%s %s (%s)\n", $cmd, $key, $line ) );
+               }
+               if ( $line == "STORED" ) {
+                       return true;
+               }
+               return false;
+       }
+
+       // }}}
+       // {{{ sock_to_host()
+
+       /**
+        * Returns the socket for the host
+        *
+        * @param string $host Host:IP to get socket for
+        *
+        * @return Resource|bool IO Stream or false
+        * @access private
+        */
+       function sock_to_host( $host ) {
+               if ( isset( $this->_cache_sock[$host] ) ) {
+                       return $this->_cache_sock[$host];
+               }
+
+               $sock = null;
+               $now = time();
+               list( $ip, /* $port */) = explode( ':', $host );
+               if ( isset( $this->_host_dead[$host] ) && $this->_host_dead[$host] > $now ||
+                       isset( $this->_host_dead[$ip] ) && $this->_host_dead[$ip] > $now
+               ) {
+                       return null;
+               }
+
+               if ( !$this->_connect_sock( $sock, $host ) ) {
+                       return null;
+               }
+
+               // Do not buffer writes
+               stream_set_write_buffer( $sock, 0 );
+
+               $this->_cache_sock[$host] = $sock;
+
+               return $this->_cache_sock[$host];
+       }
+
+       /**
+        * @param string $text
+        */
+       function _debugprint( $text ) {
+               $this->_logger->debug( $text );
+       }
+
+       /**
+        * @param string $text
+        */
+       function _error_log( $text ) {
+               $this->_logger->error( "Memcached error: $text" );
+       }
+
+       /**
+        * Write to a stream. If there is an error, mark the socket dead.
+        *
+        * @param Resource $sock The socket
+        * @param string $buf The string to write
+        * @return bool True on success, false on failure
+        */
+       function _fwrite( $sock, $buf ) {
+               $bytesWritten = 0;
+               $bufSize = strlen( $buf );
+               while ( $bytesWritten < $bufSize ) {
+                       $result = fwrite( $sock, $buf );
+                       $data = stream_get_meta_data( $sock );
+                       if ( $data['timed_out'] ) {
+                               $this->_handle_error( $sock, 'timeout writing to $1' );
+                               return false;
+                       }
+                       // Contrary to the documentation, fwrite() returns zero on error in PHP 5.3.
+                       if ( $result === false || $result === 0 ) {
+                               $this->_handle_error( $sock, 'error writing to $1' );
+                               return false;
+                       }
+                       $bytesWritten += $result;
+               }
+
+               return true;
+       }
+
+       /**
+        * Handle an I/O error. Mark the socket dead and log an error.
+        *
+        * @param Resource $sock
+        * @param string $msg
+        */
+       function _handle_error( $sock, $msg ) {
+               $peer = stream_socket_get_name( $sock, true /** remote **/ );
+               if ( strval( $peer ) === '' ) {
+                       $peer = array_search( $sock, $this->_cache_sock );
+                       if ( $peer === false ) {
+                               $peer = '[unknown host]';
+                       }
+               }
+               $msg = str_replace( '$1', $peer, $msg );
+               $this->_error_log( "$msg\n" );
+               $this->_dead_sock( $sock );
+       }
+
+       /**
+        * Read the specified number of bytes from a stream. If there is an error,
+        * mark the socket dead.
+        *
+        * @param Resource $sock The socket
+        * @param int $len The number of bytes to read
+        * @return string|bool The string on success, false on failure.
+        */
+       function _fread( $sock, $len ) {
+               $buf = '';
+               while ( $len > 0 ) {
+                       $result = fread( $sock, $len );
+                       $data = stream_get_meta_data( $sock );
+                       if ( $data['timed_out'] ) {
+                               $this->_handle_error( $sock, 'timeout reading from $1' );
+                               return false;
+                       }
+                       if ( $result === false ) {
+                               $this->_handle_error( $sock, 'error reading buffer from $1' );
+                               return false;
+                       }
+                       if ( $result === '' ) {
+                               // This will happen if the remote end of the socket is shut down
+                               $this->_handle_error( $sock, 'unexpected end of file reading from $1' );
+                               return false;
+                       }
+                       $len -= strlen( $result );
+                       $buf .= $result;
+               }
+               return $buf;
+       }
+
+       /**
+        * Read a line from a stream. If there is an error, mark the socket dead.
+        * The \r\n line ending is stripped from the response.
+        *
+        * @param Resource $sock The socket
+        * @return string|bool The string on success, false on failure
+        */
+       function _fgets( $sock ) {
+               $result = fgets( $sock );
+               // fgets() may return a partial line if there is a select timeout after
+               // a successful recv(), so we have to check for a timeout even if we
+               // got a string response.
+               $data = stream_get_meta_data( $sock );
+               if ( $data['timed_out'] ) {
+                       $this->_handle_error( $sock, 'timeout reading line from $1' );
+                       return false;
+               }
+               if ( $result === false ) {
+                       $this->_handle_error( $sock, 'error reading line from $1' );
+                       return false;
+               }
+               if ( substr( $result, -2 ) === "\r\n" ) {
+                       $result = substr( $result, 0, -2 );
+               } elseif ( substr( $result, -1 ) === "\n" ) {
+                       $result = substr( $result, 0, -1 );
+               } else {
+                       $this->_handle_error( $sock, 'line ending missing in response from $1' );
+                       return false;
+               }
+               return $result;
+       }
+
+       /**
+        * Flush the read buffer of a stream
+        * @param Resource $f
+        */
+       function _flush_read_buffer( $f ) {
+               if ( !is_resource( $f ) ) {
+                       return;
+               }
+               $r = array( $f );
+               $w = null;
+               $e = null;
+               $n = stream_select( $r, $w, $e, 0, 0 );
+               while ( $n == 1 && !feof( $f ) ) {
+                       fread( $f, 1024 );
+                       $r = array( $f );
+                       $w = null;
+                       $e = null;
+                       $n = stream_select( $r, $w, $e, 0, 0 );
+               }
+       }
+
+       // }}}
+       // }}}
+       // }}}
+}
+
+// }}}
+
+class MemCachedClientforWiki extends MWMemcached {
+}
diff --git a/includes/libs/objectcache/MemcachedPhpBagOStuff.php b/includes/libs/objectcache/MemcachedPhpBagOStuff.php
new file mode 100644 (file)
index 0000000..1bb38fa
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Object caching using memcached.
+ *
+ * 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
+ * @ingroup Cache
+ */
+
+/**
+ * A wrapper class for the pure-PHP memcached client, exposing a BagOStuff interface.
+ *
+ * @ingroup Cache
+ */
+class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
+       /**
+        * Available parameters are:
+        *   - servers:             The list of IP:port combinations holding the memcached servers.
+        *   - debug:               Whether to set the debug flag in the underlying client.
+        *   - persistent:          Whether to use a persistent connection
+        *   - compress_threshold:  The minimum size an object must be before it is compressed
+        *   - timeout:             The read timeout in microseconds
+        *   - connect_timeout:     The connect timeout in seconds
+        *
+        * @param array $params
+        */
+       function __construct( $params ) {
+               parent::__construct( $params );
+               $params = $this->applyDefaultParams( $params );
+
+               $this->client = new MWMemcached( $params );
+               $this->client->set_servers( $params['servers'] );
+               $this->client->set_debug( $params['debug'] );
+       }
+
+       public function setDebug( $debug ) {
+               $this->client->set_debug( $debug );
+       }
+
+       public function getMulti( array $keys, $flags = 0 ) {
+               foreach ( $keys as $key ) {
+                       $this->validateKeyEncoding( $key );
+               }
+
+               return $this->client->get_multi( $keys );
+       }
+
+       public function incr( $key, $value = 1 ) {
+               $this->validateKeyEncoding( $key );
+
+               return $this->client->incr( $key, $value );
+       }
+
+       public function decr( $key, $value = 1 ) {
+               $this->validateKeyEncoding( $key );
+
+               return $this->client->decr( $key, $value );
+       }
+}
diff --git a/includes/libs/objectcache/MultiWriteBagOStuff.php b/includes/libs/objectcache/MultiWriteBagOStuff.php
new file mode 100644 (file)
index 0000000..73bdabd
--- /dev/null
@@ -0,0 +1,235 @@
+<?php
+/**
+ * Wrapper for object caching in different caches.
+ *
+ * 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
+ * @ingroup Cache
+ */
+
+/**
+ * A cache class that replicates all writes to multiple child caches. Reads
+ * are implemented by reading from the caches in the order they are given in
+ * the configuration until a cache gives a positive result.
+ *
+ * @ingroup Cache
+ */
+class MultiWriteBagOStuff extends BagOStuff {
+       /** @var BagOStuff[] */
+       protected $caches;
+       /** @var bool Use async secondary writes */
+       protected $asyncWrites = false;
+       /** @var callback|null */
+       protected $asyncHandler;
+
+       /** Idiom for "write to all backends" */
+       const ALL = INF;
+
+       const UPGRADE_TTL = 3600; // TTL when a key is copied to a higher cache tier
+
+       /**
+        * $params include:
+        *   - caches: A numbered array of either ObjectFactory::getObjectFromSpec
+        *      arrays yeilding BagOStuff objects or direct BagOStuff objects.
+        *      If using the former, the 'args' field *must* be set.
+        *      The first cache is the primary one, being the first to
+        *      be read in the fallback chain. Writes happen to all stores
+        *      in the order they are defined. However, lock()/unlock() calls
+        *      only use the primary store.
+        *   - replication: Either 'sync' or 'async'. This controls whether writes
+        *      to secondary stores are deferred when possible. Async writes
+        *      require setting 'asyncCallback'. HHVM register_postsend_function() function.
+        *      Async writes can increase the chance of some race conditions
+        *      or cause keys to expire seconds later than expected. It is
+        *      safe to use for modules when cached values: are immutable,
+        *      invalidation uses logical TTLs, invalidation uses etag/timestamp
+        *      validation against the DB, or merge() is used to handle races.
+        *   - asyncHandler: callable that takes a callback and runs it after the
+        *      current web request ends. In CLI mode, it should run it immediately.
+        * @param array $params
+        * @throws InvalidArgumentException
+        */
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               if ( empty( $params['caches'] ) || !is_array( $params['caches'] ) ) {
+                       throw new InvalidArgumentException(
+                               __METHOD__ . ': "caches" parameter must be an array of caches'
+                       );
+               }
+
+               $this->caches = array();
+               foreach ( $params['caches'] as $cacheInfo ) {
+                       if ( $cacheInfo instanceof BagOStuff ) {
+                               $this->caches[] = $cacheInfo;
+                       } else {
+                               if ( !isset( $cacheInfo['args'] ) ) {
+                                       // B/C for when $cacheInfo was for ObjectCache::newFromParams().
+                                       // Callers intenting this to be for ObjectFactory::getObjectFromSpec
+                                       // should have set "args" per the docs above. Doings so avoids extra
+                                       // (likely harmless) params (factory/class/calls) ending up in "args".
+                                       $cacheInfo['args'] = array( $cacheInfo );
+                               }
+                               $this->caches[] = ObjectFactory::getObjectFromSpec( $cacheInfo );
+                       }
+               }
+
+               $this->asyncHandler = isset( $params['asyncHandler'] )
+                       ? $params['asyncHandler']
+                       : null;
+               $this->asyncWrites = (
+                       isset( $params['replication'] ) &&
+                       $params['replication'] === 'async' &&
+                       is_callable( $this->asyncHandler )
+               );
+       }
+
+       public function setDebug( $debug ) {
+               foreach ( $this->caches as $cache ) {
+                       $cache->setDebug( $debug );
+               }
+       }
+
+       protected function doGet( $key, $flags = 0 ) {
+               if ( ( $flags & self::READ_LATEST ) == self::READ_LATEST ) {
+                       // If the latest write was a delete(), we do NOT want to fallback
+                       // to the other tiers and possibly see the old value. Also, this
+                       // is used by mergeViaLock(), which only needs to hit the primary.
+                       return $this->caches[0]->get( $key, $flags );
+               }
+
+               $misses = 0; // number backends checked
+               $value = false;
+               foreach ( $this->caches as $cache ) {
+                       $value = $cache->get( $key, $flags );
+                       if ( $value !== false ) {
+                               break;
+                       }
+                       ++$misses;
+               }
+
+               if ( $value !== false
+                       && $misses > 0
+                       && ( $flags & self::READ_VERIFIED ) == self::READ_VERIFIED
+               ) {
+                       $this->doWrite( $misses, $this->asyncWrites, 'set', $key, $value, self::UPGRADE_TTL );
+               }
+
+               return $value;
+       }
+
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+               $asyncWrites = ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC )
+                       ? false
+                       : $this->asyncWrites;
+
+               return $this->doWrite( self::ALL, $asyncWrites, 'set', $key, $value, $exptime );
+       }
+
+       public function delete( $key ) {
+               return $this->doWrite( self::ALL, $this->asyncWrites, 'delete', $key );
+       }
+
+       public function add( $key, $value, $exptime = 0 ) {
+               return $this->doWrite( self::ALL, $this->asyncWrites, 'add', $key, $value, $exptime );
+       }
+
+       public function incr( $key, $value = 1 ) {
+               return $this->doWrite( self::ALL, $this->asyncWrites, 'incr', $key, $value );
+       }
+
+       public function decr( $key, $value = 1 ) {
+               return $this->doWrite( self::ALL, $this->asyncWrites, 'decr', $key, $value );
+       }
+
+       public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
+               // Only need to lock the first cache; also avoids deadlocks
+               return $this->caches[0]->lock( $key, $timeout, $expiry, $rclass );
+       }
+
+       public function unlock( $key ) {
+               // Only the first cache is locked
+               return $this->caches[0]->unlock( $key );
+       }
+
+       public function getLastError() {
+               return $this->caches[0]->getLastError();
+       }
+
+       public function clearLastError() {
+               $this->caches[0]->clearLastError();
+       }
+
+       /**
+        * Apply a write method to the first $count backing caches
+        *
+        * @param integer $count
+        * @param bool $asyncWrites
+        * @param string $method
+        * @param mixed ...
+        * @return bool
+        */
+       protected function doWrite( $count, $asyncWrites, $method /*, ... */ ) {
+               $ret = true;
+               $args = array_slice( func_get_args(), 3 );
+
+               foreach ( $this->caches as $i => $cache ) {
+                       if ( $i >= $count ) {
+                               break; // ignore the lower tiers
+                       }
+
+                       if ( $i == 0 || !$asyncWrites ) {
+                               // First store or in sync mode: write now and get result
+                               if ( !call_user_func_array( array( $cache, $method ), $args ) ) {
+                                       $ret = false;
+                               }
+                       } else {
+                               // Secondary write in async mode: do not block this HTTP request
+                               $logger = $this->logger;
+                               call_user_func(
+                                       $this->asyncHandler,
+                                       function () use ( $cache, $method, $args, $logger ) {
+                                               if ( !call_user_func_array( array( $cache, $method ), $args ) ) {
+                                                       $logger->warning( "Async $method op failed" );
+                                               }
+                                       }
+                               );
+                       }
+               }
+
+               return $ret;
+       }
+
+       /**
+        * Delete objects expiring before a certain date.
+        *
+        * Succeed if any of the child caches succeed.
+        * @param string $date
+        * @param bool|callable $progressCallback
+        * @return bool
+        */
+       public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) {
+               $ret = false;
+               foreach ( $this->caches as $cache ) {
+                       if ( $cache->deleteObjectsExpiringBefore( $date, $progressCallback ) ) {
+                               $ret = true;
+                       }
+               }
+
+               return $ret;
+       }
+}
index 15753a9..3558149 100644 (file)
@@ -505,30 +505,31 @@ abstract class TransformationalImageHandler extends ImageHandler {
         * Retrieve the version of the installed ImageMagick
         * You can use PHPs version_compare() to use this value
         * Value is cached for one hour.
-        * @return string Representing the IM version.
+        * @return string|bool Representing the IM version; false on error
         */
        protected function getMagickVersion() {
-               global $wgMemc;
-
-               $cache = $wgMemc->get( "imagemagick-version" );
-               if ( !$cache ) {
-                       global $wgImageMagickConvertCommand;
-                       $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . ' -version';
-                       wfDebug( __METHOD__ . ": Running convert -version\n" );
-                       $retval = '';
-                       $return = wfShellExec( $cmd, $retval );
-                       $x = preg_match( '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', $return, $matches );
-                       if ( $x != 1 ) {
-                               wfDebug( __METHOD__ . ": ImageMagick version check failed\n" );
-
-                               return null;
+               return ObjectCache::newAccelerator( CACHE_NONE )->getWithSetCallback(
+                       "imagemagick-version",
+                       3600,
+                       function () {
+                               global $wgImageMagickConvertCommand;
+
+                               $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . ' -version';
+                               wfDebug( __METHOD__ . ": Running convert -version\n" );
+                               $retval = '';
+                               $return = wfShellExec( $cmd, $retval );
+                               $x = preg_match(
+                                       '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', $return, $matches
+                               );
+                               if ( $x != 1 ) {
+                                       wfDebug( __METHOD__ . ": ImageMagick version check failed\n" );
+
+                                       return false;
+                               }
+
+                               return $matches[1];
                        }
-                       $wgMemc->set( "imagemagick-version", $matches[1], 3600 );
-
-                       return $matches[1];
-               }
-
-               return $cache;
+               );
        }
 
        /**
diff --git a/includes/objectcache/MemcachedBagOStuff.php b/includes/objectcache/MemcachedBagOStuff.php
deleted file mode 100644 (file)
index 95f5c8d..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-/**
- * Base class for memcached clients.
- *
- * 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
- * @ingroup Cache
- */
-
-/**
- * Base class for memcached clients.
- *
- * @ingroup Cache
- */
-class MemcachedBagOStuff extends BagOStuff {
-       /** @var MWMemcached|Memcached */
-       protected $client;
-
-       /**
-        * Fill in the defaults for any parameters missing from $params, using the
-        * backwards-compatible global variables
-        * @param array $params
-        * @return array
-        */
-       protected function applyDefaultParams( $params ) {
-               if ( !isset( $params['servers'] ) ) {
-                       $params['servers'] = $GLOBALS['wgMemCachedServers'];
-               }
-               if ( !isset( $params['debug'] ) ) {
-                       $params['debug'] = $GLOBALS['wgMemCachedDebug'];
-               }
-               if ( !isset( $params['persistent'] ) ) {
-                       $params['persistent'] = $GLOBALS['wgMemCachedPersistent'];
-               }
-               if ( !isset( $params['compress_threshold'] ) ) {
-                       $params['compress_threshold'] = 1500;
-               }
-               if ( !isset( $params['timeout'] ) ) {
-                       $params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
-               }
-               if ( !isset( $params['connect_timeout'] ) ) {
-                       $params['connect_timeout'] = 0.5;
-               }
-               return $params;
-       }
-
-       protected function doGet( $key, $flags = 0 ) {
-               $casToken = null;
-
-               return $this->getWithToken( $key, $casToken, $flags );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
-               return $this->client->get( $this->encodeKey( $key ), $casToken );
-       }
-
-       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
-               return $this->client->set( $this->encodeKey( $key ), $value,
-                       $this->fixExpiry( $exptime ) );
-       }
-
-       protected function cas( $casToken, $key, $value, $exptime = 0 ) {
-               return $this->client->cas( $casToken, $this->encodeKey( $key ),
-                       $value, $this->fixExpiry( $exptime ) );
-       }
-
-       public function delete( $key ) {
-               return $this->client->delete( $this->encodeKey( $key ) );
-       }
-
-       public function add( $key, $value, $exptime = 0 ) {
-               return $this->client->add( $this->encodeKey( $key ), $value,
-                       $this->fixExpiry( $exptime ) );
-       }
-
-       public function merge( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
-               if ( !is_callable( $callback ) ) {
-                       throw new Exception( "Got invalid callback." );
-               }
-
-               return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
-       }
-
-       /**
-        * Get the underlying client object. This is provided for debugging
-        * purposes.
-        * @return BagOStuff
-        */
-       public function getClient() {
-               return $this->client;
-       }
-
-       /**
-        * Encode a key for use on the wire inside the memcached protocol.
-        *
-        * We encode spaces and line breaks to avoid protocol errors. We encode
-        * the other control characters for compatibility with libmemcached
-        * verify_key. We leave other punctuation alone, to maximise backwards
-        * compatibility.
-        * @param string $key
-        * @return string
-        */
-       public function encodeKey( $key ) {
-               return preg_replace_callback( '/[\x00-\x20\x25\x7f]+/',
-                       array( $this, 'encodeKeyCallback' ), $key );
-       }
-
-       /**
-        * @param array $m
-        * @return string
-        */
-       protected function encodeKeyCallback( $m ) {
-               return rawurlencode( $m[0] );
-       }
-
-       /**
-        * TTLs higher than 30 days will be detected as absolute TTLs
-        * (UNIX timestamps), and will result in the cache entry being
-        * discarded immediately because the expiry is in the past.
-        * Clamp expires >30d at 30d, unless they're >=1e9 in which
-        * case they are likely to really be absolute (1e9 = 2011-09-09)
-        * @param int $expiry
-        * @return int
-        */
-       function fixExpiry( $expiry ) {
-               if ( $expiry > 2592000 && $expiry < 1000000000 ) {
-                       $expiry = 2592000;
-               }
-               return (int)$expiry;
-       }
-
-       /**
-        * Decode a key encoded with encodeKey(). This is provided as a convenience
-        * function for debugging.
-        *
-        * @param string $key
-        *
-        * @return string
-        */
-       public function decodeKey( $key ) {
-               // matches %00-%20, %25, %7F (=decoded alternatives for those encoded in encodeKey)
-               return preg_replace_callback( '/%([0-1][0-9]|20|25|7F)/i', function ( $match ) {
-                       return urldecode( $match[0] );
-               }, $key );
-       }
-
-       /**
-        * Send a debug message to the log
-        * @param string $text
-        */
-       protected function debugLog( $text ) {
-               $this->logger->debug( $text );
-       }
-
-       public function modifySimpleRelayEvent( array $event ) {
-               if ( array_key_exists( 'val', $event ) ) {
-                       $event['flg'] = 0; // data is not serialized nor gzipped (for memcached driver)
-               }
-
-               return $event;
-       }
-}
diff --git a/includes/objectcache/MemcachedClient.php b/includes/objectcache/MemcachedClient.php
deleted file mode 100644 (file)
index 5010b89..0000000
+++ /dev/null
@@ -1,1276 +0,0 @@
-<?php
-// @codingStandardsIgnoreFile It's an external lib and it isn't. Let's not bother.
-/**
- * Memcached client for PHP.
- *
- * +---------------------------------------------------------------------------+
- * | memcached client, PHP                                                     |
- * +---------------------------------------------------------------------------+
- * | Copyright (c) 2003 Ryan T. Dean <rtdean@cytherianage.net>                 |
- * | All rights reserved.                                                      |
- * |                                                                           |
- * | Redistribution and use in source and binary forms, with or without        |
- * | modification, are permitted provided that the following conditions        |
- * | are met:                                                                  |
- * |                                                                           |
- * | 1. Redistributions of source code must retain the above copyright         |
- * |    notice, this list of conditions and the following disclaimer.          |
- * | 2. Redistributions in binary form must reproduce the above copyright      |
- * |    notice, this list of conditions and the following disclaimer in the    |
- * |    documentation and/or other materials provided with the distribution.   |
- * |                                                                           |
- * | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
- * | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
- * | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
- * | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
- * | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
- * | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
- * | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
- * | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
- * | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
- * | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
- * +---------------------------------------------------------------------------+
- * | Author: Ryan T. Dean <rtdean@cytherianage.net>                            |
- * | Heavily influenced by the Perl memcached client by Brad Fitzpatrick.      |
- * |   Permission granted by Brad Fitzpatrick for relicense of ported Perl     |
- * |   client logic under 2-clause BSD license.                                |
- * +---------------------------------------------------------------------------+
- *
- * @file
- * $TCAnet$
- */
-
-/**
- * This is the PHP client for memcached - a distributed memory cache daemon.
- * More information is available at http://www.danga.com/memcached/
- *
- * Usage example:
- *
- * require_once 'memcached.php';
- *
- * $mc = new MWMemcached(array(
- *              'servers' => array('127.0.0.1:10000',
- *                                 array('192.0.0.1:10010', 2),
- *                                 '127.0.0.1:10020'),
- *              'debug'   => false,
- *              'compress_threshold' => 10240,
- *              'persistent' => true));
- *
- * $mc->add( 'key', array( 'some', 'array' ) );
- * $mc->replace( 'key', 'some random string' );
- * $val = $mc->get( 'key' );
- *
- * @author  Ryan T. Dean <rtdean@cytherianage.net>
- * @version 0.1.2
- */
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
-
-// {{{ requirements
-// }}}
-
-// {{{ class MWMemcached
-/**
- * memcached client class implemented using (p)fsockopen()
- *
- * @author  Ryan T. Dean <rtdean@cytherianage.net>
- * @ingroup Cache
- */
-class MWMemcached {
-       // {{{ properties
-       // {{{ public
-
-       // {{{ constants
-       // {{{ flags
-
-       /**
-        * Flag: indicates data is serialized
-        */
-       const SERIALIZED = 1;
-
-       /**
-        * Flag: indicates data is compressed
-        */
-       const COMPRESSED = 2;
-
-       /**
-        * Flag: indicates data is an integer
-        */
-       const INTVAL = 4;
-
-       // }}}
-
-       /**
-        * Minimum savings to store data compressed
-        */
-       const COMPRESSION_SAVINGS = 0.20;
-
-       // }}}
-
-       /**
-        * Command statistics
-        *
-        * @var array
-        * @access public
-        */
-       public $stats;
-
-       // }}}
-       // {{{ private
-
-       /**
-        * Cached Sockets that are connected
-        *
-        * @var array
-        * @access private
-        */
-       public $_cache_sock;
-
-       /**
-        * Current debug status; 0 - none to 9 - profiling
-        *
-        * @var bool
-        * @access private
-        */
-       public $_debug;
-
-       /**
-        * Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
-        *
-        * @var array
-        * @access private
-        */
-       public $_host_dead;
-
-       /**
-        * Is compression available?
-        *
-        * @var bool
-        * @access private
-        */
-       public $_have_zlib;
-
-       /**
-        * Do we want to use compression?
-        *
-        * @var bool
-        * @access private
-        */
-       public $_compress_enable;
-
-       /**
-        * At how many bytes should we compress?
-        *
-        * @var int
-        * @access private
-        */
-       public $_compress_threshold;
-
-       /**
-        * Are we using persistent links?
-        *
-        * @var bool
-        * @access private
-        */
-       public $_persistent;
-
-       /**
-        * If only using one server; contains ip:port to connect to
-        *
-        * @var string
-        * @access private
-        */
-       public $_single_sock;
-
-       /**
-        * Array containing ip:port or array(ip:port, weight)
-        *
-        * @var array
-        * @access private
-        */
-       public $_servers;
-
-       /**
-        * Our bit buckets
-        *
-        * @var array
-        * @access private
-        */
-       public $_buckets;
-
-       /**
-        * Total # of bit buckets we have
-        *
-        * @var int
-        * @access private
-        */
-       public $_bucketcount;
-
-       /**
-        * # of total servers we have
-        *
-        * @var int
-        * @access private
-        */
-       public $_active;
-
-       /**
-        * Stream timeout in seconds. Applies for example to fread()
-        *
-        * @var int
-        * @access private
-        */
-       public $_timeout_seconds;
-
-       /**
-        * Stream timeout in microseconds
-        *
-        * @var int
-        * @access private
-        */
-       public $_timeout_microseconds;
-
-       /**
-        * Connect timeout in seconds
-        */
-       public $_connect_timeout;
-
-       /**
-        * Number of connection attempts for each server
-        */
-       public $_connect_attempts;
-
-       /**
-        * @var LoggerInterface
-        */
-       private $_logger;
-
-       // }}}
-       // }}}
-       // {{{ methods
-       // {{{ public functions
-       // {{{ memcached()
-
-       /**
-        * Memcache initializer
-        *
-        * @param array $args Associative array of settings
-        *
-        * @return mixed
-        */
-       public function __construct( $args ) {
-               $this->set_servers( isset( $args['servers'] ) ? $args['servers'] : array() );
-               $this->_debug = isset( $args['debug'] ) ? $args['debug'] : false;
-               $this->stats = array();
-               $this->_compress_threshold = isset( $args['compress_threshold'] ) ? $args['compress_threshold'] : 0;
-               $this->_persistent = isset( $args['persistent'] ) ? $args['persistent'] : false;
-               $this->_compress_enable = true;
-               $this->_have_zlib = function_exists( 'gzcompress' );
-
-               $this->_cache_sock = array();
-               $this->_host_dead = array();
-
-               $this->_timeout_seconds = 0;
-               $this->_timeout_microseconds = isset( $args['timeout'] ) ? $args['timeout'] : 500000;
-
-               $this->_connect_timeout = isset( $args['connect_timeout'] ) ? $args['connect_timeout'] : 0.1;
-               $this->_connect_attempts = 2;
-
-               $this->_logger = isset( $args['logger'] ) ? $args['logger'] : new NullLogger();
-       }
-
-       // }}}
-       // {{{ add()
-
-       /**
-        * Adds a key/value to the memcache server if one isn't already set with
-        * that key
-        *
-        * @param string $key Key to set with data
-        * @param mixed $val Value to store
-        * @param int $exp (optional) Expiration time. This can be a number of seconds
-        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
-        * longer must be the timestamp of the time at which the mapping should expire. It
-        * is safe to use timestamps in all cases, regardless of expiration
-        * eg: strtotime("+3 hour")
-        *
-        * @return bool
-        */
-       public function add( $key, $val, $exp = 0 ) {
-               return $this->_set( 'add', $key, $val, $exp );
-       }
-
-       // }}}
-       // {{{ decr()
-
-       /**
-        * Decrease a value stored on the memcache server
-        *
-        * @param string $key Key to decrease
-        * @param int $amt (optional) amount to decrease
-        *
-        * @return mixed False on failure, value on success
-        */
-       public function decr( $key, $amt = 1 ) {
-               return $this->_incrdecr( 'decr', $key, $amt );
-       }
-
-       // }}}
-       // {{{ delete()
-
-       /**
-        * Deletes a key from the server, optionally after $time
-        *
-        * @param string $key Key to delete
-        * @param int $time (optional) how long to wait before deleting
-        *
-        * @return bool True on success, false on failure
-        */
-       public function delete( $key, $time = 0 ) {
-               if ( !$this->_active ) {
-                       return false;
-               }
-
-               $sock = $this->get_sock( $key );
-               if ( !is_resource( $sock ) ) {
-                       return false;
-               }
-
-               $key = is_array( $key ) ? $key[1] : $key;
-
-               if ( isset( $this->stats['delete'] ) ) {
-                       $this->stats['delete']++;
-               } else {
-                       $this->stats['delete'] = 1;
-               }
-               $cmd = "delete $key $time\r\n";
-               if ( !$this->_fwrite( $sock, $cmd ) ) {
-                       return false;
-               }
-               $res = $this->_fgets( $sock );
-
-               if ( $this->_debug ) {
-                       $this->_debugprint( sprintf( "MemCache: delete %s (%s)\n", $key, $res ) );
-               }
-
-               if ( $res == "DELETED" || $res == "NOT_FOUND" ) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * @param string $key
-        * @param int $timeout
-        * @return bool
-        */
-       public function lock( $key, $timeout = 0 ) {
-               /* stub */
-               return true;
-       }
-
-       /**
-        * @param string $key
-        * @return bool
-        */
-       public function unlock( $key ) {
-               /* stub */
-               return true;
-       }
-
-       // }}}
-       // {{{ disconnect_all()
-
-       /**
-        * Disconnects all connected sockets
-        */
-       public function disconnect_all() {
-               foreach ( $this->_cache_sock as $sock ) {
-                       fclose( $sock );
-               }
-
-               $this->_cache_sock = array();
-       }
-
-       // }}}
-       // {{{ enable_compress()
-
-       /**
-        * Enable / Disable compression
-        *
-        * @param bool $enable True to enable, false to disable
-        */
-       public function enable_compress( $enable ) {
-               $this->_compress_enable = $enable;
-       }
-
-       // }}}
-       // {{{ forget_dead_hosts()
-
-       /**
-        * Forget about all of the dead hosts
-        */
-       public function forget_dead_hosts() {
-               $this->_host_dead = array();
-       }
-
-       // }}}
-       // {{{ get()
-
-       /**
-        * Retrieves the value associated with the key from the memcache server
-        *
-        * @param array|string $key key to retrieve
-        * @param float $casToken [optional]
-        *
-        * @return mixed
-        */
-       public function get( $key, &$casToken = null ) {
-
-               if ( $this->_debug ) {
-                       $this->_debugprint( "get($key)\n" );
-               }
-
-               if ( !is_array( $key ) && strval( $key ) === '' ) {
-                       $this->_debugprint( "Skipping key which equals to an empty string" );
-                       return false;
-               }
-
-               if ( !$this->_active ) {
-                       return false;
-               }
-
-               $sock = $this->get_sock( $key );
-
-               if ( !is_resource( $sock ) ) {
-                       return false;
-               }
-
-               $key = is_array( $key ) ? $key[1] : $key;
-               if ( isset( $this->stats['get'] ) ) {
-                       $this->stats['get']++;
-               } else {
-                       $this->stats['get'] = 1;
-               }
-
-               $cmd = "gets $key\r\n";
-               if ( !$this->_fwrite( $sock, $cmd ) ) {
-                       return false;
-               }
-
-               $val = array();
-               $this->_load_items( $sock, $val, $casToken );
-
-               if ( $this->_debug ) {
-                       foreach ( $val as $k => $v ) {
-                               $this->_debugprint( sprintf( "MemCache: sock %s got %s\n", serialize( $sock ), $k ) );
-                       }
-               }
-
-               $value = false;
-               if ( isset( $val[$key] ) ) {
-                       $value = $val[$key];
-               }
-               return $value;
-       }
-
-       // }}}
-       // {{{ get_multi()
-
-       /**
-        * Get multiple keys from the server(s)
-        *
-        * @param array $keys Keys to retrieve
-        *
-        * @return array
-        */
-       public function get_multi( $keys ) {
-               if ( !$this->_active ) {
-                       return false;
-               }
-
-               if ( isset( $this->stats['get_multi'] ) ) {
-                       $this->stats['get_multi']++;
-               } else {
-                       $this->stats['get_multi'] = 1;
-               }
-               $sock_keys = array();
-               $socks = array();
-               foreach ( $keys as $key ) {
-                       $sock = $this->get_sock( $key );
-                       if ( !is_resource( $sock ) ) {
-                               continue;
-                       }
-                       $key = is_array( $key ) ? $key[1] : $key;
-                       if ( !isset( $sock_keys[$sock] ) ) {
-                               $sock_keys[intval( $sock )] = array();
-                               $socks[] = $sock;
-                       }
-                       $sock_keys[intval( $sock )][] = $key;
-               }
-
-               $gather = array();
-               // Send out the requests
-               foreach ( $socks as $sock ) {
-                       $cmd = 'gets';
-                       foreach ( $sock_keys[intval( $sock )] as $key ) {
-                               $cmd .= ' ' . $key;
-                       }
-                       $cmd .= "\r\n";
-
-                       if ( $this->_fwrite( $sock, $cmd ) ) {
-                               $gather[] = $sock;
-                       }
-               }
-
-               // Parse responses
-               $val = array();
-               foreach ( $gather as $sock ) {
-                       $this->_load_items( $sock, $val, $casToken );
-               }
-
-               if ( $this->_debug ) {
-                       foreach ( $val as $k => $v ) {
-                               $this->_debugprint( sprintf( "MemCache: got %s\n", $k ) );
-                       }
-               }
-
-               return $val;
-       }
-
-       // }}}
-       // {{{ incr()
-
-       /**
-        * Increments $key (optionally) by $amt
-        *
-        * @param string $key Key to increment
-        * @param int $amt (optional) amount to increment
-        *
-        * @return int|null Null if the key does not exist yet (this does NOT
-        * create new mappings if the key does not exist). If the key does
-        * exist, this returns the new value for that key.
-        */
-       public function incr( $key, $amt = 1 ) {
-               return $this->_incrdecr( 'incr', $key, $amt );
-       }
-
-       // }}}
-       // {{{ replace()
-
-       /**
-        * Overwrites an existing value for key; only works if key is already set
-        *
-        * @param string $key Key to set value as
-        * @param mixed $value Value to store
-        * @param int $exp (optional) Expiration time. This can be a number of seconds
-        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
-        * longer must be the timestamp of the time at which the mapping should expire. It
-        * is safe to use timestamps in all cases, regardless of exipration
-        * eg: strtotime("+3 hour")
-        *
-        * @return bool
-        */
-       public function replace( $key, $value, $exp = 0 ) {
-               return $this->_set( 'replace', $key, $value, $exp );
-       }
-
-       // }}}
-       // {{{ run_command()
-
-       /**
-        * Passes through $cmd to the memcache server connected by $sock; returns
-        * output as an array (null array if no output)
-        *
-        * @param Resource $sock Socket to send command on
-        * @param string $cmd Command to run
-        *
-        * @return array Output array
-        */
-       public function run_command( $sock, $cmd ) {
-               if ( !is_resource( $sock ) ) {
-                       return array();
-               }
-
-               if ( !$this->_fwrite( $sock, $cmd ) ) {
-                       return array();
-               }
-
-               $ret = array();
-               while ( true ) {
-                       $res = $this->_fgets( $sock );
-                       $ret[] = $res;
-                       if ( preg_match( '/^END/', $res ) ) {
-                               break;
-                       }
-                       if ( strlen( $res ) == 0 ) {
-                               break;
-                       }
-               }
-               return $ret;
-       }
-
-       // }}}
-       // {{{ set()
-
-       /**
-        * Unconditionally sets a key to a given value in the memcache.  Returns true
-        * if set successfully.
-        *
-        * @param string $key Key to set value as
-        * @param mixed $value Value to set
-        * @param int $exp (optional) Expiration time. This can be a number of seconds
-        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
-        * longer must be the timestamp of the time at which the mapping should expire. It
-        * is safe to use timestamps in all cases, regardless of exipration
-        * eg: strtotime("+3 hour")
-        *
-        * @return bool True on success
-        */
-       public function set( $key, $value, $exp = 0 ) {
-               return $this->_set( 'set', $key, $value, $exp );
-       }
-
-       // }}}
-       // {{{ cas()
-
-       /**
-        * Sets a key to a given value in the memcache if the current value still corresponds
-        * to a known, given value.  Returns true if set successfully.
-        *
-        * @param float $casToken Current known value
-        * @param string $key Key to set value as
-        * @param mixed $value Value to set
-        * @param int $exp (optional) Expiration time. This can be a number of seconds
-        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
-        * longer must be the timestamp of the time at which the mapping should expire. It
-        * is safe to use timestamps in all cases, regardless of exipration
-        * eg: strtotime("+3 hour")
-        *
-        * @return bool True on success
-        */
-       public function cas( $casToken, $key, $value, $exp = 0 ) {
-               return $this->_set( 'cas', $key, $value, $exp, $casToken );
-       }
-
-       // }}}
-       // {{{ set_compress_threshold()
-
-       /**
-        * Sets the compression threshold
-        *
-        * @param int $thresh Threshold to compress if larger than
-        */
-       public function set_compress_threshold( $thresh ) {
-               $this->_compress_threshold = $thresh;
-       }
-
-       // }}}
-       // {{{ set_debug()
-
-       /**
-        * Sets the debug flag
-        *
-        * @param bool $dbg True for debugging, false otherwise
-        *
-        * @see MWMemcached::__construct
-        */
-       public function set_debug( $dbg ) {
-               $this->_debug = $dbg;
-       }
-
-       // }}}
-       // {{{ set_servers()
-
-       /**
-        * Sets the server list to distribute key gets and puts between
-        *
-        * @param array $list Array of servers to connect to
-        *
-        * @see MWMemcached::__construct()
-        */
-       public function set_servers( $list ) {
-               $this->_servers = $list;
-               $this->_active = count( $list );
-               $this->_buckets = null;
-               $this->_bucketcount = 0;
-
-               $this->_single_sock = null;
-               if ( $this->_active == 1 ) {
-                       $this->_single_sock = $this->_servers[0];
-               }
-       }
-
-       /**
-        * Sets the timeout for new connections
-        *
-        * @param int $seconds Number of seconds
-        * @param int $microseconds Number of microseconds
-        */
-       public function set_timeout( $seconds, $microseconds ) {
-               $this->_timeout_seconds = $seconds;
-               $this->_timeout_microseconds = $microseconds;
-       }
-
-       // }}}
-       // }}}
-       // {{{ private methods
-       // {{{ _close_sock()
-
-       /**
-        * Close the specified socket
-        *
-        * @param string $sock Socket to close
-        *
-        * @access private
-        */
-       function _close_sock( $sock ) {
-               $host = array_search( $sock, $this->_cache_sock );
-               fclose( $this->_cache_sock[$host] );
-               unset( $this->_cache_sock[$host] );
-       }
-
-       // }}}
-       // {{{ _connect_sock()
-
-       /**
-        * Connects $sock to $host, timing out after $timeout
-        *
-        * @param int $sock Socket to connect
-        * @param string $host Host:IP to connect to
-        *
-        * @return bool
-        * @access private
-        */
-       function _connect_sock( &$sock, $host ) {
-               list( $ip, $port ) = preg_split( '/:(?=\d)/', $host );
-               $sock = false;
-               $timeout = $this->_connect_timeout;
-               $errno = $errstr = null;
-               for ( $i = 0; !$sock && $i < $this->_connect_attempts; $i++ ) {
-                       MediaWiki\suppressWarnings();
-                       if ( $this->_persistent == 1 ) {
-                               $sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
-                       } else {
-                               $sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
-                       }
-                       MediaWiki\restoreWarnings();
-               }
-               if ( !$sock ) {
-                       $this->_error_log( "Error connecting to $host: $errstr\n" );
-                       $this->_dead_host( $host );
-                       return false;
-               }
-
-               // Initialise timeout
-               stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
-
-               // If the connection was persistent, flush the read buffer in case there
-               // was a previous incomplete request on this connection
-               if ( $this->_persistent ) {
-                       $this->_flush_read_buffer( $sock );
-               }
-               return true;
-       }
-
-       // }}}
-       // {{{ _dead_sock()
-
-       /**
-        * Marks a host as dead until 30-40 seconds in the future
-        *
-        * @param string $sock Socket to mark as dead
-        *
-        * @access private
-        */
-       function _dead_sock( $sock ) {
-               $host = array_search( $sock, $this->_cache_sock );
-               $this->_dead_host( $host );
-       }
-
-       /**
-        * @param string $host
-        */
-       function _dead_host( $host ) {
-               $parts = explode( ':', $host );
-               $ip = $parts[0];
-               $this->_host_dead[$ip] = time() + 30 + intval( rand( 0, 10 ) );
-               $this->_host_dead[$host] = $this->_host_dead[$ip];
-               unset( $this->_cache_sock[$host] );
-       }
-
-       // }}}
-       // {{{ get_sock()
-
-       /**
-        * get_sock
-        *
-        * @param string $key Key to retrieve value for;
-        *
-        * @return Resource|bool Resource on success, false on failure
-        * @access private
-        */
-       function get_sock( $key ) {
-               if ( !$this->_active ) {
-                       return false;
-               }
-
-               if ( $this->_single_sock !== null ) {
-                       return $this->sock_to_host( $this->_single_sock );
-               }
-
-               $hv = is_array( $key ) ? intval( $key[0] ) : $this->_hashfunc( $key );
-               if ( $this->_buckets === null ) {
-                       $bu = array();
-                       foreach ( $this->_servers as $v ) {
-                               if ( is_array( $v ) ) {
-                                       for ( $i = 0; $i < $v[1]; $i++ ) {
-                                               $bu[] = $v[0];
-                                       }
-                               } else {
-                                       $bu[] = $v;
-                               }
-                       }
-                       $this->_buckets = $bu;
-                       $this->_bucketcount = count( $bu );
-               }
-
-               $realkey = is_array( $key ) ? $key[1] : $key;
-               for ( $tries = 0; $tries < 20; $tries++ ) {
-                       $host = $this->_buckets[$hv % $this->_bucketcount];
-                       $sock = $this->sock_to_host( $host );
-                       if ( is_resource( $sock ) ) {
-                               return $sock;
-                       }
-                       $hv = $this->_hashfunc( $hv . $realkey );
-               }
-
-               return false;
-       }
-
-       // }}}
-       // {{{ _hashfunc()
-
-       /**
-        * Creates a hash integer based on the $key
-        *
-        * @param string $key Key to hash
-        *
-        * @return int Hash value
-        * @access private
-        */
-       function _hashfunc( $key ) {
-               # Hash function must be in [0,0x7ffffff]
-               # We take the first 31 bits of the MD5 hash, which unlike the hash
-               # function used in a previous version of this client, works
-               return hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
-       }
-
-       // }}}
-       // {{{ _incrdecr()
-
-       /**
-        * Perform increment/decriment on $key
-        *
-        * @param string $cmd Command to perform
-        * @param string|array $key Key to perform it on
-        * @param int $amt Amount to adjust
-        *
-        * @return int New value of $key
-        * @access private
-        */
-       function _incrdecr( $cmd, $key, $amt = 1 ) {
-               if ( !$this->_active ) {
-                       return null;
-               }
-
-               $sock = $this->get_sock( $key );
-               if ( !is_resource( $sock ) ) {
-                       return null;
-               }
-
-               $key = is_array( $key ) ? $key[1] : $key;
-               if ( isset( $this->stats[$cmd] ) ) {
-                       $this->stats[$cmd]++;
-               } else {
-                       $this->stats[$cmd] = 1;
-               }
-               if ( !$this->_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
-                       return null;
-               }
-
-               $line = $this->_fgets( $sock );
-               $match = array();
-               if ( !preg_match( '/^(\d+)/', $line, $match ) ) {
-                       return null;
-               }
-               return $match[1];
-       }
-
-       // }}}
-       // {{{ _load_items()
-
-       /**
-        * Load items into $ret from $sock
-        *
-        * @param Resource $sock Socket to read from
-        * @param array $ret returned values
-        * @param float $casToken [optional]
-        * @return bool True for success, false for failure
-        *
-        * @access private
-        */
-       function _load_items( $sock, &$ret, &$casToken = null ) {
-               $results = array();
-
-               while ( 1 ) {
-                       $decl = $this->_fgets( $sock );
-
-                       if ( $decl === false ) {
-                               /*
-                                * If nothing can be read, something is wrong because we know exactly when
-                                * to stop reading (right after "END") and we return right after that.
-                                */
-                               return false;
-                       } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+) (\d+)$/', $decl, $match ) ) {
-                               /*
-                                * Read all data returned. This can be either one or multiple values.
-                                * Save all that data (in an array) to be processed later: we'll first
-                                * want to continue reading until "END" before doing anything else,
-                                * to make sure that we don't leave our client in a state where it's
-                                * output is not yet fully read.
-                                */
-                               $results[] = array(
-                                       $match[1], // rkey
-                                       $match[2], // flags
-                                       $match[3], // len
-                                       $match[4], // casToken
-                                       $this->_fread( $sock, $match[3] + 2 ), // data
-                               );
-                       } elseif ( $decl == "END" ) {
-                               if ( count( $results ) == 0 ) {
-                                       return false;
-                               }
-
-                               /**
-                                * All data has been read, time to process the data and build
-                                * meaningful return values.
-                                */
-                               foreach ( $results as $vars ) {
-                                       list( $rkey, $flags, $len, $casToken, $data ) = $vars;
-
-                                       if ( $data === false || substr( $data, -2 ) !== "\r\n" ) {
-                                               $this->_handle_error( $sock,
-                                                       'line ending missing from data block from $1' );
-                                               return false;
-                                       }
-                                       $data = substr( $data, 0, -2 );
-                                       $ret[$rkey] = $data;
-
-                                       if ( $this->_have_zlib && $flags & self::COMPRESSED ) {
-                                               $ret[$rkey] = gzuncompress( $ret[$rkey] );
-                                       }
-
-                                       /*
-                                        * This unserialize is the exact reason that we only want to
-                                        * process data after having read until "END" (instead of doing
-                                        * this right away): "unserialize" can trigger outside code:
-                                        * in the event that $ret[$rkey] is a serialized object,
-                                        * unserializing it will trigger __wakeup() if present. If that
-                                        * function attempted to read from memcached (while we did not
-                                        * yet read "END"), these 2 calls would collide.
-                                        */
-                                       if ( $flags & self::SERIALIZED ) {
-                                               $ret[$rkey] = unserialize( $ret[$rkey] );
-                                       } elseif ( $flags & self::INTVAL ) {
-                                               $ret[$rkey] = intval( $ret[$rkey] );
-                                       }
-                               }
-
-                               return true;
-                       } else {
-                               $this->_handle_error( $sock, 'Error parsing response from $1' );
-                               return false;
-                       }
-               }
-       }
-
-       // }}}
-       // {{{ _set()
-
-       /**
-        * Performs the requested storage operation to the memcache server
-        *
-        * @param string $cmd Command to perform
-        * @param string $key Key to act on
-        * @param mixed $val What we need to store
-        * @param int $exp (optional) Expiration time. This can be a number of seconds
-        * to cache for (up to 30 days inclusive).  Any timespans of 30 days + 1 second or
-        * longer must be the timestamp of the time at which the mapping should expire. It
-        * is safe to use timestamps in all cases, regardless of exipration
-        * eg: strtotime("+3 hour")
-        * @param float $casToken [optional]
-        *
-        * @return bool
-        * @access private
-        */
-       function _set( $cmd, $key, $val, $exp, $casToken = null ) {
-               if ( !$this->_active ) {
-                       return false;
-               }
-
-               $sock = $this->get_sock( $key );
-               if ( !is_resource( $sock ) ) {
-                       return false;
-               }
-
-               if ( isset( $this->stats[$cmd] ) ) {
-                       $this->stats[$cmd]++;
-               } else {
-                       $this->stats[$cmd] = 1;
-               }
-
-               $flags = 0;
-
-               if ( is_int( $val ) ) {
-                       $flags |= self::INTVAL;
-               } elseif ( !is_scalar( $val ) ) {
-                       $val = serialize( $val );
-                       $flags |= self::SERIALIZED;
-                       if ( $this->_debug ) {
-                               $this->_debugprint( sprintf( "client: serializing data as it is not scalar\n" ) );
-                       }
-               }
-
-               $len = strlen( $val );
-
-               if ( $this->_have_zlib && $this->_compress_enable
-                       && $this->_compress_threshold && $len >= $this->_compress_threshold
-               ) {
-                       $c_val = gzcompress( $val, 9 );
-                       $c_len = strlen( $c_val );
-
-                       if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
-                               if ( $this->_debug ) {
-                                       $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len ) );
-                               }
-                               $val = $c_val;
-                               $len = $c_len;
-                               $flags |= self::COMPRESSED;
-                       }
-               }
-
-               $command = "$cmd $key $flags $exp $len";
-               if ( $casToken ) {
-                       $command .= " $casToken";
-               }
-
-               if ( !$this->_fwrite( $sock, "$command\r\n$val\r\n" ) ) {
-                       return false;
-               }
-
-               $line = $this->_fgets( $sock );
-
-               if ( $this->_debug ) {
-                       $this->_debugprint( sprintf( "%s %s (%s)\n", $cmd, $key, $line ) );
-               }
-               if ( $line == "STORED" ) {
-                       return true;
-               }
-               return false;
-       }
-
-       // }}}
-       // {{{ sock_to_host()
-
-       /**
-        * Returns the socket for the host
-        *
-        * @param string $host Host:IP to get socket for
-        *
-        * @return Resource|bool IO Stream or false
-        * @access private
-        */
-       function sock_to_host( $host ) {
-               if ( isset( $this->_cache_sock[$host] ) ) {
-                       return $this->_cache_sock[$host];
-               }
-
-               $sock = null;
-               $now = time();
-               list( $ip, /* $port */) = explode( ':', $host );
-               if ( isset( $this->_host_dead[$host] ) && $this->_host_dead[$host] > $now ||
-                       isset( $this->_host_dead[$ip] ) && $this->_host_dead[$ip] > $now
-               ) {
-                       return null;
-               }
-
-               if ( !$this->_connect_sock( $sock, $host ) ) {
-                       return null;
-               }
-
-               // Do not buffer writes
-               stream_set_write_buffer( $sock, 0 );
-
-               $this->_cache_sock[$host] = $sock;
-
-               return $this->_cache_sock[$host];
-       }
-
-       /**
-        * @param string $text
-        */
-       function _debugprint( $text ) {
-               $this->_logger->debug( $text );
-       }
-
-       /**
-        * @param string $text
-        */
-       function _error_log( $text ) {
-               $this->_logger->error( "Memcached error: $text" );
-       }
-
-       /**
-        * Write to a stream. If there is an error, mark the socket dead.
-        *
-        * @param Resource $sock The socket
-        * @param string $buf The string to write
-        * @return bool True on success, false on failure
-        */
-       function _fwrite( $sock, $buf ) {
-               $bytesWritten = 0;
-               $bufSize = strlen( $buf );
-               while ( $bytesWritten < $bufSize ) {
-                       $result = fwrite( $sock, $buf );
-                       $data = stream_get_meta_data( $sock );
-                       if ( $data['timed_out'] ) {
-                               $this->_handle_error( $sock, 'timeout writing to $1' );
-                               return false;
-                       }
-                       // Contrary to the documentation, fwrite() returns zero on error in PHP 5.3.
-                       if ( $result === false || $result === 0 ) {
-                               $this->_handle_error( $sock, 'error writing to $1' );
-                               return false;
-                       }
-                       $bytesWritten += $result;
-               }
-
-               return true;
-       }
-
-       /**
-        * Handle an I/O error. Mark the socket dead and log an error.
-        *
-        * @param Resource $sock
-        * @param string $msg
-        */
-       function _handle_error( $sock, $msg ) {
-               $peer = stream_socket_get_name( $sock, true /** remote **/ );
-               if ( strval( $peer ) === '' ) {
-                       $peer = array_search( $sock, $this->_cache_sock );
-                       if ( $peer === false ) {
-                               $peer = '[unknown host]';
-                       }
-               }
-               $msg = str_replace( '$1', $peer, $msg );
-               $this->_error_log( "$msg\n" );
-               $this->_dead_sock( $sock );
-       }
-
-       /**
-        * Read the specified number of bytes from a stream. If there is an error,
-        * mark the socket dead.
-        *
-        * @param Resource $sock The socket
-        * @param int $len The number of bytes to read
-        * @return string|bool The string on success, false on failure.
-        */
-       function _fread( $sock, $len ) {
-               $buf = '';
-               while ( $len > 0 ) {
-                       $result = fread( $sock, $len );
-                       $data = stream_get_meta_data( $sock );
-                       if ( $data['timed_out'] ) {
-                               $this->_handle_error( $sock, 'timeout reading from $1' );
-                               return false;
-                       }
-                       if ( $result === false ) {
-                               $this->_handle_error( $sock, 'error reading buffer from $1' );
-                               return false;
-                       }
-                       if ( $result === '' ) {
-                               // This will happen if the remote end of the socket is shut down
-                               $this->_handle_error( $sock, 'unexpected end of file reading from $1' );
-                               return false;
-                       }
-                       $len -= strlen( $result );
-                       $buf .= $result;
-               }
-               return $buf;
-       }
-
-       /**
-        * Read a line from a stream. If there is an error, mark the socket dead.
-        * The \r\n line ending is stripped from the response.
-        *
-        * @param Resource $sock The socket
-        * @return string|bool The string on success, false on failure
-        */
-       function _fgets( $sock ) {
-               $result = fgets( $sock );
-               // fgets() may return a partial line if there is a select timeout after
-               // a successful recv(), so we have to check for a timeout even if we
-               // got a string response.
-               $data = stream_get_meta_data( $sock );
-               if ( $data['timed_out'] ) {
-                       $this->_handle_error( $sock, 'timeout reading line from $1' );
-                       return false;
-               }
-               if ( $result === false ) {
-                       $this->_handle_error( $sock, 'error reading line from $1' );
-                       return false;
-               }
-               if ( substr( $result, -2 ) === "\r\n" ) {
-                       $result = substr( $result, 0, -2 );
-               } elseif ( substr( $result, -1 ) === "\n" ) {
-                       $result = substr( $result, 0, -1 );
-               } else {
-                       $this->_handle_error( $sock, 'line ending missing in response from $1' );
-                       return false;
-               }
-               return $result;
-       }
-
-       /**
-        * Flush the read buffer of a stream
-        * @param Resource $f
-        */
-       function _flush_read_buffer( $f ) {
-               if ( !is_resource( $f ) ) {
-                       return;
-               }
-               $r = array( $f );
-               $w = null;
-               $e = null;
-               $n = stream_select( $r, $w, $e, 0, 0 );
-               while ( $n == 1 && !feof( $f ) ) {
-                       fread( $f, 1024 );
-                       $r = array( $f );
-                       $w = null;
-                       $e = null;
-                       $n = stream_select( $r, $w, $e, 0, 0 );
-               }
-       }
-
-       // }}}
-       // }}}
-       // }}}
-}
-
-// }}}
-
-class MemCachedClientforWiki extends MWMemcached {
-}
index 365236d..b7d1eaf 100644 (file)
@@ -100,13 +100,17 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                                break;
                        case 'igbinary':
                                if ( !Memcached::HAVE_IGBINARY ) {
-                                       throw new MWException( __CLASS__ . ': the igbinary extension is not available ' .
-                                               'but igbinary serialization was requested.' );
+                                       throw new InvalidArgumentException(
+                                               __CLASS__ . ': the igbinary extension is not available ' .
+                                               'but igbinary serialization was requested.'
+                                       );
                                }
                                $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_IGBINARY );
                                break;
                        default:
-                               throw new MWException( __CLASS__ . ': invalid value for serializer parameter' );
+                               throw new InvalidArgumentException(
+                                       __CLASS__ . ': invalid value for serializer parameter'
+                               );
                }
                $servers = array();
                foreach ( $params['servers'] as $host ) {
@@ -117,7 +121,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
        protected function getWithToken( $key, &$casToken, $flags = 0 ) {
                $this->debugLog( "get($key)" );
-               $result = $this->client->get( $this->encodeKey( $key ), null, $casToken );
+               $result = $this->client->get( $this->validateKeyEncoding( $key ), null, $casToken );
                $result = $this->checkResult( $key, $result );
                return $result;
        }
@@ -202,14 +206,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
        public function getMulti( array $keys, $flags = 0 ) {
                $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
-               $callback = array( $this, 'encodeKey' );
-               $encodedResult = $this->client->getMulti( array_map( $callback, $keys ) );
-               $encodedResult = $encodedResult ?: array(); // must be an array
-               $result = array();
-               foreach ( $encodedResult as $key => $value ) {
-                       $key = $this->decodeKey( $key );
-                       $result[$key] = $value;
+               foreach ( $keys as $key ) {
+                       $this->validateKeyEncoding( $key );
                }
+               $result = $this->client->getMulti( $keys ) ?: array();
                return $this->checkResult( false, $result );
        }
 
@@ -219,14 +219,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         * @return bool
         */
        public function setMulti( array $data, $exptime = 0 ) {
-               foreach ( $data as $key => $value ) {
-                       $encKey = $this->encodeKey( $key );
-                       if ( $encKey !== $key ) {
-                               $data[$encKey] = $value;
-                               unset( $data[$key] );
-                       }
-               }
                $this->debugLog( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' );
+               foreach ( array_keys( $data ) as $key ) {
+                       $this->validateKeyEncoding( $key );
+               }
                $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
                return $this->checkResult( false, $result );
        }
diff --git a/includes/objectcache/MemcachedPhpBagOStuff.php b/includes/objectcache/MemcachedPhpBagOStuff.php
deleted file mode 100644 (file)
index 6f0ba58..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-/**
- * Object caching using memcached.
- *
- * 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
- * @ingroup Cache
- */
-
-/**
- * A wrapper class for the pure-PHP memcached client, exposing a BagOStuff interface.
- *
- * @ingroup Cache
- */
-class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
-
-       /**
-        * Constructor.
-        *
-        * Available parameters are:
-        *   - servers:             The list of IP:port combinations holding the memcached servers.
-        *   - debug:               Whether to set the debug flag in the underlying client.
-        *   - persistent:          Whether to use a persistent connection
-        *   - compress_threshold:  The minimum size an object must be before it is compressed
-        *   - timeout:             The read timeout in microseconds
-        *   - connect_timeout:     The connect timeout in seconds
-        *
-        * @param array $params
-        */
-       function __construct( $params ) {
-               parent::__construct( $params );
-               $params = $this->applyDefaultParams( $params );
-
-               $this->client = new MemCachedClientforWiki( $params );
-               $this->client->set_servers( $params['servers'] );
-               $this->client->set_debug( $params['debug'] );
-       }
-
-       /**
-        * @param bool $debug
-        */
-       public function setDebug( $debug ) {
-               $this->client->set_debug( $debug );
-       }
-
-       public function getMulti( array $keys, $flags = 0 ) {
-               $callback = array( $this, 'encodeKey' );
-               $encodedResult = $this->client->get_multi( array_map( $callback, $keys ) );
-               $result = array();
-               foreach ( $encodedResult as $key => $value ) {
-                       $key = $this->decodeKey( $key );
-                       $result[$key] = $value;
-               }
-               return $result;
-       }
-
-       /**
-        * @param string $key
-        * @param int $value
-        * @return mixed
-        */
-       public function incr( $key, $value = 1 ) {
-               return $this->client->incr( $this->encodeKey( $key ), $value );
-       }
-
-       /**
-        * @param string $key
-        * @param int $value
-        * @return mixed
-        */
-       public function decr( $key, $value = 1 ) {
-               return $this->client->decr( $this->encodeKey( $key ), $value );
-       }
-}
diff --git a/includes/objectcache/MultiWriteBagOStuff.php b/includes/objectcache/MultiWriteBagOStuff.php
deleted file mode 100644 (file)
index 73bdabd..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-<?php
-/**
- * Wrapper for object caching in different caches.
- *
- * 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
- * @ingroup Cache
- */
-
-/**
- * A cache class that replicates all writes to multiple child caches. Reads
- * are implemented by reading from the caches in the order they are given in
- * the configuration until a cache gives a positive result.
- *
- * @ingroup Cache
- */
-class MultiWriteBagOStuff extends BagOStuff {
-       /** @var BagOStuff[] */
-       protected $caches;
-       /** @var bool Use async secondary writes */
-       protected $asyncWrites = false;
-       /** @var callback|null */
-       protected $asyncHandler;
-
-       /** Idiom for "write to all backends" */
-       const ALL = INF;
-
-       const UPGRADE_TTL = 3600; // TTL when a key is copied to a higher cache tier
-
-       /**
-        * $params include:
-        *   - caches: A numbered array of either ObjectFactory::getObjectFromSpec
-        *      arrays yeilding BagOStuff objects or direct BagOStuff objects.
-        *      If using the former, the 'args' field *must* be set.
-        *      The first cache is the primary one, being the first to
-        *      be read in the fallback chain. Writes happen to all stores
-        *      in the order they are defined. However, lock()/unlock() calls
-        *      only use the primary store.
-        *   - replication: Either 'sync' or 'async'. This controls whether writes
-        *      to secondary stores are deferred when possible. Async writes
-        *      require setting 'asyncCallback'. HHVM register_postsend_function() function.
-        *      Async writes can increase the chance of some race conditions
-        *      or cause keys to expire seconds later than expected. It is
-        *      safe to use for modules when cached values: are immutable,
-        *      invalidation uses logical TTLs, invalidation uses etag/timestamp
-        *      validation against the DB, or merge() is used to handle races.
-        *   - asyncHandler: callable that takes a callback and runs it after the
-        *      current web request ends. In CLI mode, it should run it immediately.
-        * @param array $params
-        * @throws InvalidArgumentException
-        */
-       public function __construct( $params ) {
-               parent::__construct( $params );
-
-               if ( empty( $params['caches'] ) || !is_array( $params['caches'] ) ) {
-                       throw new InvalidArgumentException(
-                               __METHOD__ . ': "caches" parameter must be an array of caches'
-                       );
-               }
-
-               $this->caches = array();
-               foreach ( $params['caches'] as $cacheInfo ) {
-                       if ( $cacheInfo instanceof BagOStuff ) {
-                               $this->caches[] = $cacheInfo;
-                       } else {
-                               if ( !isset( $cacheInfo['args'] ) ) {
-                                       // B/C for when $cacheInfo was for ObjectCache::newFromParams().
-                                       // Callers intenting this to be for ObjectFactory::getObjectFromSpec
-                                       // should have set "args" per the docs above. Doings so avoids extra
-                                       // (likely harmless) params (factory/class/calls) ending up in "args".
-                                       $cacheInfo['args'] = array( $cacheInfo );
-                               }
-                               $this->caches[] = ObjectFactory::getObjectFromSpec( $cacheInfo );
-                       }
-               }
-
-               $this->asyncHandler = isset( $params['asyncHandler'] )
-                       ? $params['asyncHandler']
-                       : null;
-               $this->asyncWrites = (
-                       isset( $params['replication'] ) &&
-                       $params['replication'] === 'async' &&
-                       is_callable( $this->asyncHandler )
-               );
-       }
-
-       public function setDebug( $debug ) {
-               foreach ( $this->caches as $cache ) {
-                       $cache->setDebug( $debug );
-               }
-       }
-
-       protected function doGet( $key, $flags = 0 ) {
-               if ( ( $flags & self::READ_LATEST ) == self::READ_LATEST ) {
-                       // If the latest write was a delete(), we do NOT want to fallback
-                       // to the other tiers and possibly see the old value. Also, this
-                       // is used by mergeViaLock(), which only needs to hit the primary.
-                       return $this->caches[0]->get( $key, $flags );
-               }
-
-               $misses = 0; // number backends checked
-               $value = false;
-               foreach ( $this->caches as $cache ) {
-                       $value = $cache->get( $key, $flags );
-                       if ( $value !== false ) {
-                               break;
-                       }
-                       ++$misses;
-               }
-
-               if ( $value !== false
-                       && $misses > 0
-                       && ( $flags & self::READ_VERIFIED ) == self::READ_VERIFIED
-               ) {
-                       $this->doWrite( $misses, $this->asyncWrites, 'set', $key, $value, self::UPGRADE_TTL );
-               }
-
-               return $value;
-       }
-
-       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
-               $asyncWrites = ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC )
-                       ? false
-                       : $this->asyncWrites;
-
-               return $this->doWrite( self::ALL, $asyncWrites, 'set', $key, $value, $exptime );
-       }
-
-       public function delete( $key ) {
-               return $this->doWrite( self::ALL, $this->asyncWrites, 'delete', $key );
-       }
-
-       public function add( $key, $value, $exptime = 0 ) {
-               return $this->doWrite( self::ALL, $this->asyncWrites, 'add', $key, $value, $exptime );
-       }
-
-       public function incr( $key, $value = 1 ) {
-               return $this->doWrite( self::ALL, $this->asyncWrites, 'incr', $key, $value );
-       }
-
-       public function decr( $key, $value = 1 ) {
-               return $this->doWrite( self::ALL, $this->asyncWrites, 'decr', $key, $value );
-       }
-
-       public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
-               // Only need to lock the first cache; also avoids deadlocks
-               return $this->caches[0]->lock( $key, $timeout, $expiry, $rclass );
-       }
-
-       public function unlock( $key ) {
-               // Only the first cache is locked
-               return $this->caches[0]->unlock( $key );
-       }
-
-       public function getLastError() {
-               return $this->caches[0]->getLastError();
-       }
-
-       public function clearLastError() {
-               $this->caches[0]->clearLastError();
-       }
-
-       /**
-        * Apply a write method to the first $count backing caches
-        *
-        * @param integer $count
-        * @param bool $asyncWrites
-        * @param string $method
-        * @param mixed ...
-        * @return bool
-        */
-       protected function doWrite( $count, $asyncWrites, $method /*, ... */ ) {
-               $ret = true;
-               $args = array_slice( func_get_args(), 3 );
-
-               foreach ( $this->caches as $i => $cache ) {
-                       if ( $i >= $count ) {
-                               break; // ignore the lower tiers
-                       }
-
-                       if ( $i == 0 || !$asyncWrites ) {
-                               // First store or in sync mode: write now and get result
-                               if ( !call_user_func_array( array( $cache, $method ), $args ) ) {
-                                       $ret = false;
-                               }
-                       } else {
-                               // Secondary write in async mode: do not block this HTTP request
-                               $logger = $this->logger;
-                               call_user_func(
-                                       $this->asyncHandler,
-                                       function () use ( $cache, $method, $args, $logger ) {
-                                               if ( !call_user_func_array( array( $cache, $method ), $args ) ) {
-                                                       $logger->warning( "Async $method op failed" );
-                                               }
-                                       }
-                               );
-                       }
-               }
-
-               return $ret;
-       }
-
-       /**
-        * Delete objects expiring before a certain date.
-        *
-        * Succeed if any of the child caches succeed.
-        * @param string $date
-        * @param bool|callable $progressCallback
-        * @return bool
-        */
-       public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) {
-               $ret = false;
-               foreach ( $this->caches as $cache ) {
-                       if ( $cache->deleteObjectsExpiringBefore( $date, $progressCallback ) ) {
-                               $ret = true;
-                       }
-               }
-
-               return $ret;
-       }
-}
index 151bb06..cb783a7 100644 (file)
@@ -178,8 +178,26 @@ class ObjectCache {
                        return call_user_func( $params['factory'], $params );
                } elseif ( isset( $params['class'] ) ) {
                        $class = $params['class'];
-                       if ( $class === 'MultiWriteBagOStuff' && !isset( $params['asyncHandler'] ) ) {
-                               $params['asyncHandler'] = 'DeferredUpdates::addCallableUpdate';
+                       // Automatically set the 'async' update handler
+                       if ( $class === 'MultiWriteBagOStuff' ) {
+                               $params['asyncHandler'] = isset( $params['asyncHandler'] )
+                                       ? $params['asyncHandler']
+                                       : 'DeferredUpdates::addCallableUpdate';
+                       }
+                       // Do b/c logic for MemcachedBagOStuff
+                       if ( is_subclass_of( $class, 'MemcachedBagOStuff' ) ) {
+                               if ( !isset( $params['servers'] ) ) {
+                                       $params['servers'] = $GLOBALS['wgMemCachedServers'];
+                               }
+                               if ( !isset( $params['debug'] ) ) {
+                                       $params['debug'] = $GLOBALS['wgMemCachedDebug'];
+                               }
+                               if ( !isset( $params['persistent'] ) ) {
+                                       $params['persistent'] = $GLOBALS['wgMemCachedPersistent'];
+                               }
+                               if ( !isset( $params['timeout'] ) ) {
+                                       $params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
+                               }
                        }
                        return new $class( $params );
                } else {
@@ -256,20 +274,6 @@ class ObjectCache {
                return self::newFromId( $id );
        }
 
-       /**
-        * Factory function that creates a memcached client object.
-        *
-        * This always uses the PHP client, since the PECL client has a different
-        * hashing scheme and a different interpretation of the flags bitfield, so
-        * switching between the two clients randomly would be disastrous.
-        *
-        * @param array $params
-        * @return MemcachedPhpBagOStuff
-        */
-       public static function newMemcached( $params ) {
-               return new MemcachedPhpBagOStuff( $params );
-       }
-
        /**
         * Create a new cache object of the specified type.
         *
index 7957e65..3638aed 100644 (file)
@@ -1533,7 +1533,7 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager {
        function __construct( $imagePage ) {
                parent::__construct( $imagePage->getContext() );
                $this->mImagePage = $imagePage;
-               $this->mTitle = clone ( $imagePage->getTitle() );
+               $this->mTitle = clone $imagePage->getTitle();
                $this->mTitle->setFragment( '#filehistory' );
                $this->mImg = null;
                $this->mHist = array();
index 244e469..fe64821 100644 (file)
@@ -1092,7 +1092,9 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Get a ParserOutput for the given ParserOptions and revision ID.
-        * The parser cache will be used if possible.
+        *
+        * The parser cache will be used if possible. Cache misses that result
+        * in parser runs are debounced with PoolCounter.
         *
         * @since 1.19
         * @param ParserOptions $parserOptions ParserOptions to use for the parse operation
index 7acfe38..2451390 100644 (file)
@@ -93,11 +93,6 @@ class CacheTime {
                if ( $this->mCacheExpiry === null || $this->mCacheExpiry > $seconds ) {
                        $this->mCacheExpiry = $seconds;
                }
-
-               // hack: set old-style marker for uncacheable entries.
-               if ( $this->mCacheExpiry !== null && $this->mCacheExpiry <= 0 ) {
-                       $this->mCacheTime = -1;
-               }
        }
 
        /**
index ef295ab..78f7775 100644 (file)
@@ -124,18 +124,23 @@ class DateFormatter {
         *              Defaults to the site content language
         * @return DateFormatter
         */
-       public static function &getInstance( $lang = null ) {
-               global $wgMemc, $wgContLang;
-               static $dateFormatter = false;
+       public static function getInstance( $lang = null ) {
+               global $wgContLang, $wgMainCacheType;
+
                $lang = $lang ? wfGetLangObj( $lang ) : $wgContLang;
-               $key = wfMemcKey( 'dateformatter', $lang->getCode() );
+               $cache = ObjectCache::newAccelerator( $wgMainCacheType );
+
+               static $dateFormatter = false;
                if ( !$dateFormatter ) {
-                       $dateFormatter = $wgMemc->get( $key );
-                       if ( !$dateFormatter ) {
-                               $dateFormatter = new DateFormatter( $lang );
-                               $wgMemc->set( $key, $dateFormatter, 3600 );
-                       }
+                       $dateFormatter = $cache->getWithSetCallback(
+                               $cache->makeKey( 'dateformatter', $lang->getCode() ),
+                               3600,
+                               function () use ( $lang ) {
+                                       return new DateFormatter( $lang );
+                               }
+                       );
                }
+
                return $dateFormatter;
        }
 
index efad151..9060756 100644 (file)
@@ -5763,7 +5763,6 @@ class Parser {
                        throw new MWException( __METHOD__ .
                                " can only be called when actually parsing something" );
                }
-               $this->mOutput->setCacheTime( -1 ); // old style, for compatibility
                $this->mOutput->updateCacheExpiry( 0 ); // new style, for consistency
        }
 
index b32593c..b1e49b2 100644 (file)
  * @ingroup Parser
  */
 
+use MediaWiki\Logger\LoggerFactory;
+
 /**
  * @ingroup Parser
  */
-interface Preprocessor {
+abstract class Preprocessor {
+
+       const CACHE_VERSION = 1;
+
+       /**
+        * Store a document tree in the cache.
+        *
+        * @param string $text
+        * @param int $flags
+        */
+       protected function cacheSetTree( $text, $flags, $tree ) {
+               $config = RequestContext::getMain()->getConfig();
+
+               $length = strlen( $text );
+               $threshold = $config->get( 'PreprocessorCacheThreshold' );
+               if ( $threshold === false || $length < $threshold || $length > 1e6 ) {
+                       return false;
+               }
+
+               $key = wfMemcKey(
+                       // TODO: Once we require PHP 5.5, use static::class instead of
+                       // get_called_class() or get_class( $this ).
+                       defined( 'static::CACHE_PREFIX' ) ? static::CACHE_PREFIX : get_called_class(),
+                       md5( $text ), $flags );
+               $value = sprintf( "%08d", static::CACHE_VERSION ) . $tree;
+
+               $cache = ObjectCache::getInstance( $config->get( 'MainCacheType' ) );
+               $cache->set( $key, $value, 86400 );
+
+               LoggerFactory::getInstance( 'Preprocessor' )
+                       ->info( "Cached preprocessor output (key: $key)" );
+       }
+
        /**
-        * Create a new preprocessor object based on an initialised Parser object
+        * Attempt to load a precomputed document tree for some given wikitext
+        * from the cache.
         *
-        * @param Parser $parser
+        * @param string $text
+        * @param int $flags
+        * @return PPNode_Hash_Tree|bool
         */
-       public function __construct( $parser );
+       protected function cacheGetTree( $text, $flags ) {
+               $config = RequestContext::getMain()->getConfig();
+
+               $length = strlen( $text );
+               $threshold = $config->get( 'PreprocessorCacheThreshold' );
+               if ( $threshold === false || $length < $threshold || $length > 1e6 ) {
+                       return false;
+               }
+
+               $cache = ObjectCache::getInstance( $config->get( 'MainCacheType' ) );
+
+               $key = wfMemcKey(
+                       // TODO: Once we require PHP 5.5, use static::class instead of
+                       // get_called_class() or get_class( $this ).
+                       defined( 'static::CACHE_PREFIX' ) ? static::CACHE_PREFIX : get_called_class(),
+                       md5( $text ), $flags );
+
+               $value = $cache->get( $key );
+               if ( !$value ) {
+                       return false;
+               }
+
+               $version = intval( substr( $value, 0, 8 ) );
+               if ( $version !== static::CACHE_VERSION ) {
+                       return false;
+               }
+
+               LoggerFactory::getInstance( 'Preprocessor' )
+                       ->info( "Loaded preprocessor output from cache (key: $key)" );
+
+               return substr( $value, 8 );
+       }
 
        /**
         * Create a new top-level frame for expansion of a page
         *
         * @return PPFrame
         */
-       public function newFrame();
+       abstract public function newFrame();
 
        /**
         * Create a new custom frame for programmatic use of parameter replacement
@@ -47,7 +115,7 @@ interface Preprocessor {
         *
         * @return PPFrame
         */
-       public function newCustomFrame( $args );
+       abstract public function newCustomFrame( $args );
 
        /**
         * Create a new custom node for programmatic use of parameter replacement
@@ -55,7 +123,7 @@ interface Preprocessor {
         *
         * @param array $values
         */
-       public function newPartNodeArray( $values );
+       abstract public function newPartNodeArray( $values );
 
        /**
         * Preprocess text to a PPNode
@@ -65,7 +133,7 @@ interface Preprocessor {
         *
         * @return PPNode
         */
-       public function preprocessToObj( $text, $flags = 0 );
+       abstract public function preprocessToObj( $text, $flags = 0 );
 }
 
 /**
index c329689..b71b9d2 100644 (file)
@@ -25,7 +25,7 @@
  * @ingroup Parser
  */
 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
-class Preprocessor_DOM implements Preprocessor {
+class Preprocessor_DOM extends Preprocessor {
        // @codingStandardsIgnoreEnd
 
        /**
@@ -35,7 +35,7 @@ class Preprocessor_DOM implements Preprocessor {
 
        public $memoryLimit;
 
-       const CACHE_VERSION = 1;
+       const CACHE_PREFIX = 'preprocess-xml';
 
        public function __construct( $parser ) {
                $this->parser = $parser;
@@ -148,30 +148,11 @@ class Preprocessor_DOM implements Preprocessor {
         * @return PPNode_DOM
         */
        public function preprocessToObj( $text, $flags = 0 ) {
-               global $wgMemc, $wgPreprocessorCacheThreshold;
-
-               $xml = false;
-               $cacheable = ( $wgPreprocessorCacheThreshold !== false
-                       && strlen( $text ) > $wgPreprocessorCacheThreshold );
-               if ( $cacheable ) {
-                       $cacheKey = wfMemcKey( 'preprocess-xml', md5( $text ), $flags );
-                       $cacheValue = $wgMemc->get( $cacheKey );
-                       if ( $cacheValue ) {
-                               $version = substr( $cacheValue, 0, 8 );
-                               if ( intval( $version ) == self::CACHE_VERSION ) {
-                                       $xml = substr( $cacheValue, 8 );
-                                       // From the cache
-                                       wfDebugLog( "Preprocessor", "Loaded preprocessor XML from memcached (key $cacheKey)" );
-                               }
-                       }
-                       if ( $xml === false ) {
-                               $xml = $this->preprocessToXml( $text, $flags );
-                               $cacheValue = sprintf( "%08d", self::CACHE_VERSION ) . $xml;
-                               $wgMemc->set( $cacheKey, $cacheValue, 86400 );
-                               wfDebugLog( "Preprocessor", "Saved preprocessor XML to memcached (key $cacheKey)" );
-                       }
-               } else {
+
+               $xml = $this->cacheGetTree( $text, $flags );
+               if ( $xml === false ) {
                        $xml = $this->preprocessToXml( $text, $flags );
+                       $this->cacheSetTree( $text, $flags, $xml );
                }
 
                // Fail if the number of elements exceeds acceptable limits
@@ -179,8 +160,7 @@ class Preprocessor_DOM implements Preprocessor {
                $this->parser->mGeneratedPPNodeCount += substr_count( $xml, '<' );
                $max = $this->parser->mOptions->getMaxGeneratedPPNodeCount();
                if ( $this->parser->mGeneratedPPNodeCount > $max ) {
-                       if ( $cacheable ) {
-                       }
+                       // if ( $cacheable ) { ... }
                        throw new MWException( __METHOD__ . ': generated node count limit exceeded' );
                }
 
@@ -199,8 +179,7 @@ class Preprocessor_DOM implements Preprocessor {
                        $obj = new PPNode_DOM( $dom->documentElement );
                }
 
-               if ( $cacheable ) {
-               }
+               // if ( $cacheable ) { ... }
 
                if ( !$result ) {
                        throw new MWException( __METHOD__ . ' generated invalid XML' );
index 49fa8a1..54824da 100644 (file)
@@ -28,7 +28,7 @@
  * @ingroup Parser
  */
 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
-class Preprocessor_Hash implements Preprocessor {
+class Preprocessor_Hash extends Preprocessor {
        // @codingStandardsIgnoreEnd
 
        /**
@@ -36,7 +36,7 @@ class Preprocessor_Hash implements Preprocessor {
         */
        public $parser;
 
-       const CACHE_VERSION = 1;
+       const CACHE_PREFIX = 'preprocess-hash';
 
        public function __construct( $parser ) {
                $this->parser = $parser;
@@ -88,6 +88,7 @@ class Preprocessor_Hash implements Preprocessor {
                return $node;
        }
 
+
        /**
         * Preprocess some wikitext and return the document tree.
         * This is the ghost of Parser::replace_variables().
@@ -112,25 +113,9 @@ class Preprocessor_Hash implements Preprocessor {
         * @return PPNode_Hash_Tree
         */
        public function preprocessToObj( $text, $flags = 0 ) {
-               // Check cache.
-               global $wgMemc, $wgPreprocessorCacheThreshold;
-
-               $cacheable = $wgPreprocessorCacheThreshold !== false
-                       && strlen( $text ) > $wgPreprocessorCacheThreshold;
-
-               if ( $cacheable ) {
-                       $cacheKey = wfMemcKey( 'preprocess-hash', md5( $text ), $flags );
-                       $cacheValue = $wgMemc->get( $cacheKey );
-                       if ( $cacheValue ) {
-                               $version = substr( $cacheValue, 0, 8 );
-                               if ( intval( $version ) == self::CACHE_VERSION ) {
-                                       $hash = unserialize( substr( $cacheValue, 8 ) );
-                                       // From the cache
-                                       wfDebugLog( "Preprocessor",
-                                               "Loaded preprocessor hash from memcached (key $cacheKey)" );
-                                       return $hash;
-                               }
-                       }
+               $tree = $this->cacheGetTree( $text, $flags );
+               if ( $tree !== false ) {
+                       return unserialize( $tree );
                }
 
                $rules = array(
@@ -629,13 +614,11 @@ class Preprocessor_Hash implements Preprocessor {
                                                                $lastNode = $node;
                                                        }
                                                        if ( !$node ) {
-                                                               if ( $cacheable ) {
-                                                               }
+                                                               // if ( $cacheable ) { ... }
                                                                throw new MWException( __METHOD__ . ': eqpos not found' );
                                                        }
                                                        if ( $node->name !== 'equals' ) {
-                                                               if ( $cacheable ) {
-                                                               }
+                                                               // if ( $cacheable ) { ... }
                                                                throw new MWException( __METHOD__ . ': eqpos is not equals' );
                                                        }
                                                        $equalsNode = $node;
@@ -732,15 +715,7 @@ class Preprocessor_Hash implements Preprocessor {
                $rootNode->lastChild = $stack->rootAccum->lastNode;
 
                // Cache
-               if ( $cacheable ) {
-                       $cacheValue = sprintf( "%08d", self::CACHE_VERSION ) . serialize( $rootNode );
-
-                       // T111289: Cache values should not exceed 1 Mb, but they do.
-                       if ( strlen( $cacheValue ) <= 1e6 ) {
-                               $wgMemc->set( $cacheKey, $cacheValue, 86400 );
-                               wfDebugLog( "Preprocessor", "Saved preprocessor Hash to memcached (key $cacheKey)" );
-                       }
-               }
+               $this->cacheSetTree( $text, $flags, serialize( $rootNode ) );
 
                return $rootNode;
        }
index d601467..3c60ea9 100644 (file)
@@ -147,6 +147,7 @@ class PoolWorkArticleView extends PoolCounterWork {
                        $logger->info( '{time} {title}', array(
                                'time' => number_format( $time, 2 ),
                                'title' => $this->page->getTitle()->getPrefixedDBkey(),
+                               'trigger' => 'view',
                        ) );
                }
 
index c9c60bc..dfd9a8f 100644 (file)
@@ -322,13 +322,15 @@ class ResourceLoader implements LoggerAwareInterface {
                // Allow multiple modules to be registered in one call
                $registrations = is_array( $name ) ? $name : array( $name => $info );
                foreach ( $registrations as $name => $info ) {
-                       // Disallow duplicate registrations
+                       // Warn on duplicate registrations
                        if ( isset( $this->moduleInfos[$name] ) ) {
                                // A module has already been registered by this name
-                               throw new MWException(
-                                       'ResourceLoader duplicate registration error. ' .
-                                       'Another module has already been registered as ' . $name
-                               );
+                               if ( $this->moduleInfos[$name] === $info ) {
+                                       $this->logger->warning(
+                                               'ResourceLoader duplicate registration warning. ' .
+                                               'Another module has already been registered as ' . $name
+                                       );
+                               }
                        }
 
                        // Check $name for validity
index 4348b14..0c1a941 100644 (file)
@@ -100,7 +100,6 @@ class SpecialAllPages extends IncludableSpecialPage {
         */
        function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) {
                $t = $this->getPageTitle();
-
                $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
                $out .= Xml::openElement(
                        'form',
@@ -148,7 +147,6 @@ class SpecialAllPages extends IncludableSpecialPage {
                $out .= Xml::closeElement( 'fieldset' );
                $out .= Xml::closeElement( 'form' );
                $out .= Xml::closeElement( 'div' );
-
                return $out;
        }
 
@@ -273,14 +271,10 @@ class SpecialAllPages extends IncludableSpecialPage {
                        return;
                }
 
+               $navLinks = array();
                $self = $this->getPageTitle();
 
-               $topLinks = array(
-                       Linker::link( $self, $this->msg( 'allpages' )->escaped() )
-               );
-               $bottomLinks = array();
-
-               # Do we put a previous link ?
+               // Generate a "previous page" link if needed
                if ( $prevTitle ) {
                        $query = array( 'from' => $prevTitle->getText() );
 
@@ -292,16 +286,16 @@ class SpecialAllPages extends IncludableSpecialPage {
                                $query['hideredirects'] = $hideredirects;
                        }
 
-                       $prevLink = Linker::linkKnown(
+                       $navLinks[] = Linker::linkKnown(
                                $self,
                                $this->msg( 'prevpage', $prevTitle->getText() )->escaped(),
                                array(),
                                $query
                        );
-                       $topLinks[] = $prevLink;
-                       $bottomLinks[] = $prevLink;
+
                }
 
+               // Generate a "next page" link if needed
                if ( $n == $this->maxPerPage && $s = $res->fetchObject() ) {
                        # $s is the first link of the next chunk
                        $t = Title::makeTitle( $namespace, $s->page_title );
@@ -315,36 +309,28 @@ class SpecialAllPages extends IncludableSpecialPage {
                                $query['hideredirects'] = $hideredirects;
                        }
 
-                       $nextLink = Linker::linkKnown(
+                       $navLinks[] = Linker::linkKnown(
                                $self,
                                $this->msg( 'nextpage', $t->getText() )->escaped(),
                                array(),
                                $query
                        );
-                       $topLinks[] = $nextLink;
-                       $bottomLinks[] = $nextLink;
                }
 
-               $nsForm = $this->namespaceForm( $namespace, $from, $to, $hideredirects );
-               $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ) .
-                       '<tr>
-                                               <td>' .
-                       $nsForm .
-                       '</td>
-                                               <td class="mw-allpages-nav">' .
-                       $this->getLanguage()->pipeList( $topLinks ) .
-                       '</td></tr></table>';
-
-               $output->addHTML( $out2 . $out );
-
-               if ( count( $bottomLinks ) ) {
-                       $output->addHTML(
-                               Html::element( 'hr' ) .
-                                       Html::rawElement( 'div', array( 'class' => 'mw-allpages-nav' ),
-                                               $this->getLanguage()->pipeList( $bottomLinks )
-                                       )
+               $topOut = $this->namespaceForm( $namespace, $from, $to, $hideredirects );
+
+               if ( count( $navLinks ) ) {
+                       // Add pagination links
+                       $pagination = Html::rawElement( 'div',
+                               array( 'class' => 'mw-allpages-nav' ),
+                               $this->getLanguage()->pipeList( $navLinks )
                        );
+
+                       $topOut .= $pagination;
+                       $out .= Html::element( 'hr' ) . $pagination; // Footer
                }
+
+               $output->addHTML( $topOut . $out );
        }
 
        /**
index bc5dfd0..fbe5ab3 100644 (file)
@@ -201,7 +201,7 @@ class SpecialPrefixindex extends SpecialAllPages {
                                )
                        );
 
-                       # ## @todo FIXME: Side link to previous
+                       // @todo FIXME: Side link to previous
 
                        $n = 0;
                        if ( $res->numRows() > 0 ) {
@@ -239,54 +239,55 @@ class SpecialPrefixindex extends SpecialAllPages {
                        }
                }
 
-               $footer = '';
-               if ( $this->including() ) {
-                       $out2 = '';
-               } else {
-                       $nsForm = $this->namespacePrefixForm( $namespace, $prefix );
-                       $self = $this->getPageTitle();
-                       $out2 = Xml::openElement( 'table', array( 'id' => 'mw-prefixindex-nav-table' ) ) .
-                               '<tr>
-                                       <td>' .
-                               $nsForm .
-                               '</td>
-                               <td id="mw-prefixindex-nav-form" class="mw-prefixindex-nav">';
-
-                       if ( $res && ( $n == $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
-                               $query = array(
-                                       'from' => $s->page_title,
-                                       'prefix' => $prefix,
-                                       'hideredirects' => $this->hideRedirects,
-                                       'stripprefix' => $this->stripPrefix,
-                               );
+               $output = $this->getOutput();
 
-                               if ( $namespace || $prefix == '' ) {
-                                       // Keep the namespace even if it's 0 for empty prefixes.
-                                       // This tells us we're not just a holdover from old links.
-                                       $query['namespace'] = $namespace;
-                               }
+               if ( $this->including() ) {
+                       // We don't show the nav-links and the form when included into other
+                       // pages so let's just finish here.
+                       $output->addHTML( $out );
+                       return;
+               }
 
-                               $nextLink = Linker::linkKnown(
-                                       $self,
-                                       $this->msg( 'nextpage', str_replace( '_', ' ', $s->page_title ) )->escaped(),
-                                       array(),
-                                       $query
-                               );
+               $topOut = $this->namespacePrefixForm( $namespace, $prefix );
 
-                               $out2 .= $nextLink;
+               if ( $res && ( $n == $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
+                       $query = array(
+                               'from' => $s->page_title,
+                               'prefix' => $prefix,
+                               'hideredirects' => $this->hideRedirects,
+                               'stripprefix' => $this->stripPrefix,
+                       );
 
-                               $footer = "\n" . Html::element( 'hr' ) .
-                                       Html::rawElement(
-                                               'div',
-                                               array( 'class' => 'mw-prefixindex-nav' ),
-                                               $nextLink
-                                       );
+                       if ( $namespace || $prefix == '' ) {
+                               // Keep the namespace even if it's 0 for empty prefixes.
+                               // This tells us we're not just a holdover from old links.
+                               $query['namespace'] = $namespace;
                        }
-                       $out2 .= "</td></tr>" .
-                               Xml::closeElement( 'table' );
+
+                       $nextLink = Linker::linkKnown(
+                               $this->getPageTitle(),
+                               $this->msg( 'nextpage', str_replace( '_', ' ', $s->page_title ) )->escaped(),
+                               array(),
+                               $query
+                       );
+
+                       // Link shown at the top of the page below the form
+                       $topOut .= Html::rawElement( 'div',
+                               array( 'class' => 'mw-prefixindex-nav' ),
+                               $nextLink
+                       );
+
+                       // Link shown at the footer
+                       $out .= "\n" . Html::element( 'hr' ) .
+                               Html::rawElement(
+                                       'div',
+                                       array( 'class' => 'mw-prefixindex-nav' ),
+                                       $nextLink
+                               );
+
                }
 
-               $this->getOutput()->addHTML( $out2 . $out . $footer );
+               $output->addHTML( $topOut . $out );
        }
 
        protected function getGroupName() {
index 0da598b..8facb35 100644 (file)
@@ -783,8 +783,10 @@ class LoginForm extends SpecialPage {
                // Give general extensions, such as a captcha, a chance to abort logins
                $abort = self::ABORTED;
                if ( !Hooks::run( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) {
+                       if ( !in_array( $abort, self::$statusCodes, true ) ) {
+                               throw new Exception( 'Invalid status code returned from AbortLogin hook: ' . $abort );
+                       }
                        $this->mAbortLoginErrorMsg = $msg;
-
                        return $abort;
                }
 
@@ -1385,11 +1387,6 @@ class LoginForm extends SpecialPage {
                ) );
 
                if ( $this->mType == 'signup' ) {
-                       // XXX hack pending RL or JS parse() support for complex content messages
-                       // https://phabricator.wikimedia.org/T27349
-                       $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
-                               $this->msg( 'createacct-imgcaptcha-help' )->parse() );
-
                        // Additional styles and scripts for signup form
                        $out->addModules( array(
                                'mediawiki.special.userlogin.signup.js'
index 38baf5b..7e0f0b2 100644 (file)
@@ -518,16 +518,12 @@ class SpecialVersion extends SpecialPage {
         */
        protected function getExternalLibraries() {
                global $IP;
-               $path = "$IP/composer.lock";
+               $path = "$IP/vendor/composer/installed.json";
                if ( !file_exists( $path ) ) {
-                       // Maybe they're using mediawiki/vendor?
-                       $path = "$IP/vendor/composer.lock";
-                       if ( !file_exists( $path ) ) {
-                               return '';
-                       }
+                       return '';
                }
 
-               $lock = new ComposerLock( $path );
+               $installed = new ComposerInstalled( $path );
                $out = Html::element(
                        'h2',
                        array( 'id' => 'mw-version-libraries' ),
@@ -545,7 +541,7 @@ class SpecialVersion extends SpecialPage {
                        . Html::element( 'th', array(), $this->msg( 'version-libraries-authors' )->text() )
                        . Html::closeElement( 'tr' );
 
-               foreach ( $lock->getInstalledDependencies() as $name => $info ) {
+               foreach ( $installed->getInstalledDependencies() as $name => $info ) {
                        if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
                                // Skip any extensions or skins since they'll be listed
                                // in their proper section
index b971c00..e241383 100644 (file)
@@ -54,6 +54,7 @@
 class UploadStash {
        // Format of the key for files -- has to be suitable as a filename itself (e.g. ab12cd34ef.jpg)
        const KEY_FORMAT_REGEX = '/^[\w-\.]+\.\w*$/';
+       const MAX_US_PROPS_SIZE = 65535;
 
        /**
         * repository that this uses to store temp files
@@ -277,13 +278,22 @@ class UploadStash {
                wfDebug( __METHOD__ . " inserting $stashPath under $key\n" );
                $dbw = $this->repo->getMasterDb();
 
+               $serializedFileProps = serialize( $fileProps );
+               if ( strlen( $serializedFileProps ) > self::MAX_US_PROPS_SIZE ) {
+                       // Database is going to truncate this and make the field invalid.
+                       // Prioritize important metadata over file handler metadata.
+                       // File handler should be prepared to regenerate invalid metadata if needed.
+                       $fileProps['metadata'] = false;
+                       $serializedFileProps = serialize( $fileProps );
+               }
+
                $this->fileMetadata[$key] = array(
                        'us_id' => $dbw->nextSequenceValue( 'uploadstash_us_id_seq' ),
                        'us_user' => $this->userId,
                        'us_key' => $key,
                        'us_orig_path' => $path,
                        'us_path' => $stashPath, // virtual URL
-                       'us_props' => $dbw->encodeBlob( serialize( $fileProps ) ),
+                       'us_props' => $dbw->encodeBlob( $serializedFileProps ),
                        'us_size' => $fileProps['size'],
                        'us_sha1' => $fileProps['sha1'],
                        'us_mime' => $fileProps['mime'],
index 67c3b84..7bc5f51 100644 (file)
        "createaccountreason": "السبب:",
        "createacct-reason": "السبب",
        "createacct-reason-ph": "لماذا تقوم بإنشاء حساب آخر",
-       "createacct-captcha": "تحقق أمني",
-       "createacct-imgcaptcha-ph": "أدخل النص الذي تراه في الأعلى",
        "createacct-submit": "افتح الحساب",
        "createacct-another-submit": "أنشئ حسابا",
-       "createacct-benefit-heading": "موقع {{SITENAME}} أنشأه أشخاص مثلك.",
+       "createacct-benefit-heading": "{{SITENAME}} موقع يساهم فيه أشخاص مثلك.",
        "createacct-benefit-body1": "{{PLURAL:$1|تحريرا|تحريرات}}",
        "createacct-benefit-body2": "{{PLURAL:$1|صفحة}}",
        "createacct-benefit-body3": "آخر {{PLURAL:$1|مساهم|مساهمين}}",
index e8f6149..26b4ddd 100644 (file)
        "createaccountreason": "Motivu:",
        "createacct-reason": "Motivu",
        "createacct-reason-ph": "Por qué quier crear otra cuenta",
-       "createacct-captcha": "Comprobación de seguridá",
-       "createacct-imgcaptcha-ph": "Escriba'l testu qu'apaez arriba",
        "createacct-submit": "Crear la cuenta",
        "createacct-another-submit": "Crear una cuenta",
        "createacct-benefit-heading": "{{SITENAME}} failu xente como vusté.",
        "permissionserrors": "Fallu de permisos",
        "permissionserrorstext": "Nun tien permisu pa facer eso {{PLURAL:$1|pol siguiente motivu|polos siguientes motivos}}:",
        "permissionserrorstext-withaction": "Nun tien permisu pa $2 {{PLURAL:$1|pol siguiente motivu|polos siguientes motivos}}:",
+       "contentmodelediterror": "Nun ye posible editar esta revisión porque'l so modelu de conteníu ye <code>$1</code>, mentanto que'l modelu de conteníu actual de la páxina ye <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Avisu: Tas volviendo a crear una páxina que se desanició anteriormente.'''\n\nHabríes considerar si ye afechisco siguir editando esta páxina.\nEquí tienes el rexistru de desanicios y tresllaos d'esta páxina:",
        "moveddeleted-notice": "Esta páxina se desanició.\nComo referencia, embaxo s'ufre'l rexistru de desanicios y tresllaos de la páxina.",
        "moveddeleted-notice-recent": "Esta páxina desanicióse apocayá (dientro de les postreres 24 hores).\nLos rexistros de desaniciu y treslláu de la páxina amuésense de siguío como referencia.",
index 4d226b5..57d378e 100644 (file)
        "createaccountreason": "Прычына:",
        "createacct-reason": "Прычына",
        "createacct-reason-ph": "Зь якой мэтай вы ствараеце іншы рахунак",
-       "createacct-captcha": "Праверка бясьпекі",
-       "createacct-imgcaptcha-ph": "Увядзіце тэкст, што бачыце вышэй",
        "createacct-submit": "Стварыць рахунак",
        "createacct-another-submit": "Стварыць рахунак",
        "createacct-benefit-heading": "{{SITENAME}} створаная людзьмі, такімі як вы.",
        "foreign-structured-upload-form-label-own-work-message-local": "Я пацьвярджаю, што загружаю гэты файл згодна з правіламі і ліцэнзійнай палітыкай {{GRAMMAR:родны|{{SITENAME}}}}.",
        "foreign-structured-upload-form-label-not-own-work-message-local": "Калі вы ня можаце загрузіць файл у адпаведнасьці з правіламі {{GRAMMAR:родны|{{SITENAME}}}}, калі ласка, закрыйце гэтае акно і паспрабуйце іншы мэтад.",
        "foreign-structured-upload-form-label-not-own-work-local-local": "Вы таксама можаце паспрабаваць [[Special:Upload|старонку загрузкі па змоўчаньні]].",
+       "foreign-structured-upload-form-label-own-work-message-default": "Я разумею, што загружаю гэты файл у агульнае сховішча. Я пацьвярджаю, што раблю гэта ў адпаведнасьці з умовамі выкарыстаньня і ліцэнзійнай палітыкай.",
+       "foreign-structured-upload-form-label-not-own-work-message-default": "Калі вы ня можаце загрузіць гэты файл паводле правілаў агульнага сховішча, калі ласка, закрыйце гэты дыялёг і паспрабуйце іншы мэтад.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "Вы можаце паспрабаваць скарыстацца [[Special:Upload|старонкай загрузкі {{GRAMMAR:родны|{{SITENAME}}}}]], калі гэты файл можна туды загрузіць згодна з правіламі.",
        "backend-fail-stream": "Немагчыма накіраваць файл $1.",
        "backend-fail-backup": "Немагчыма зрабіць рэзэрвовую копію файла $1.",
        "backend-fail-notexists": "Файл $1 не існуе.",
index 3b2366e..7f4ed54 100644 (file)
        "createaccountreason": "Причина:",
        "createacct-reason": "Причина",
        "createacct-reason-ph": "Защо създавате друга сметка",
-       "createacct-captcha": "Проверка за сигурност",
-       "createacct-imgcaptcha-ph": "Въведете текста, който виждате по-горе",
        "createacct-submit": "Създаване на сметката",
        "createacct-another-submit": "Създаване на друга сметка",
        "createacct-benefit-heading": "{{SITENAME}} се създава от хора като вас.",
index 92d2a03..0f7a605 100644 (file)
@@ -12,6 +12,7 @@
        "tog-hideminor": "کمین تغیرات شه آخیرین تغیراتانی لڑلیستا چیهر داته بئینت",
        "tog-hidepatrolled": "گشت وارته گین ایڈ\tیٹان شه آخیرین ایڈ\tیٹانی لڑلیستا چیهر داته بئینت",
        "tog-newpageshidepatrolled": "گشت وارته ئین تاکدیمان شه نی نوکین تاکدیمانی لڑ لیستا زیان بیئنت",
+       "tog-hidecategorization": "تاکدیمانئ دسته بندی ێی چیهر داتین",
        "tog-extendwatchlist": "پراخ کورتین واچلیستی لڑلیست په موچین تغیراتان،نه فقط آخرینان",
        "tog-usenewrc": "گروپ بندی تغیراتانئ بی اساسئ تاکدیمانئ اخیر ئین تغیران و واچلیستئ لڑ لیستا",
        "tog-numberheadings": "ئنوانانئ اتوماتیکین شماره گذاری",
@@ -41,6 +42,7 @@
        "tog-watchlisthideliu": "لوگین بوته ئین کار زوروکانی ایڈیٹ شه نی واچلیست چیهر بیئنت",
        "tog-watchlisthideanons": "نا دروستي ئین کار زوروکانی ایڈیٹ شه نی واچلیست چیهر بیئنت",
        "tog-watchlisthidepatrolled": "گشت وارته ئین ایڈیٹ شه واچلیستی تا چیهر بیئنت",
+       "tog-watchlisthidecategorization": "تاکدیمانئ دسته بندی ێی چیهر داتین",
        "tog-ccmeonemails": "په من یک کاپی شه آ ایمیلان که من په دیگران دیم ئه دهین ، دیم داته بیئت",
        "tog-diffonly": "جهلگی تاکدیمی محتوا تفاوت نشان داته مه بئینت",
        "tog-showhiddencats": "چیهرین تهرئانه نشان دهی",
        "createaccountreason": "دلیل:",
        "createacct-reason": "دلیل",
        "createacct-reason-ph": "پچی شما لوٹیت که دیگرین کار زوروکین حساب جوڑ کنیت؟",
-       "createacct-captcha": "امنیتئ چیک کورتین",
-       "createacct-imgcaptcha-ph": "په بُرزه گین اکسا کی گیندیت متنی داخل کنیت",
        "createacct-submit": "وتئ کار زوروکین حسابا جوڑ کنیت",
        "createacct-another-submit": "دیگرین کار زوروکین حسابئ جوڑ کورتین",
        "createacct-benefit-heading": "ای {{SITENAME}} چو شمیا همیرنگین مردمانی واسطه ئا جوڑ بوته",
        "anonpreviewwarning": "<em>شما لوگین نه بوته ئیت. ذخیره کورتین باعث ئه بیئت که شمی آی پی ادرس بی ای تاکدیمی تاریخچه ئی تا ثبت بیئت.</em>",
        "missingcommenttext": "مهربانی بکنیت جهلگا توضیح دهیت.",
        "summary-preview": "دیم دیست ئی خلاصه:",
-       "subject-preview": "Ù\85Ù\88ضÙ\88 Ø¦Û\8c Ø¯Û\8cÙ\85 Ø¯Û\8cست/ئÙ\86Ù\88اÙ\86:",
+       "subject-preview": "Ù\85Ù\88زÙ\88Û\8cÛ\8c Ø¯Û\8cÙ\85 Ø¯Û\8cست:",
        "previewerrortext": "وختیکه شوما په وتي تغیراني نشان داتینا کوشش ئه کورتێت، یک خطاي مانجینا آت.",
        "blockedtitle": "کار زوروک بسته بوته",
        "blockedtext": "<strong>شمئ کارزورکین حساب یا آی پي ادرس بسته بوته.</strong>\n\nای بسته بوتین شه $1 ئی نیمگاه بوته.\nای نفر وتي دلیلا، ایرنگ گوشته: <em>$2</em>\n\n* بلاک یا بستین ئی شروع: $8\n* پاچ بوتین یا بلاک ئی الاسي: $6\n* هدفین بلاک بوته ئین کارزوروک: $7\n\nشوما توانێت که گۆ $1 یا گۆ [[{{MediaWiki:Grouppage-sysop}}|یک دیگرێن مدیري]] تماس بگیرێت و بئ ائ باره ئا هبر دهیت.\nتوجه کنیت که شما نه توانتیت شه  «ایمیل په ای کارزوروکا» استفاده بکنیت مگه ايکه یک معتبریندایمیل ادرسئ به وتي [[Special:Preferences|کارزوروکین حسابئ]] تا ثبت کورته به ئیت و هم  باید ای قابلیتئ امکان شه شوما گیپته مه بيت.\nشمئ انونین آی پي ادرس $3 و شمئ بلاک بوتینئ شماره $5 اینت.\nمهرباني بکنیت موچین مالوماتانا به وتئ درخواست ئانی تا ذکر بکنیت.",
+       "autoblockedtext": "شمی آی‌پی ادرس یسته بوته، په خاتیریکه ای  آی‌پی ادرس شه یک دیگرین کار زوروکئ نیمگاه استفاده بوته که آ شه $1 ئی نیمگاه بلاک بوته.\nدیم بوته ئین دلیل ایرنگ اینت:\n\n:''$2''\n\n* بلاک یا بستین ئی شروع: $8\n* پاچ بوتین یا بلاک ئی الاسي: $6\n* هدفین بلاک بوته ئین کارزوروک: $7\n\nشوما توانێت که گۆ $1 یا گۆ [[{{MediaWiki:Grouppage-sysop}}|یک دیگرێن مدیري]] تماس بگیرێت و بئ ائ باره ئا حبر دهیت.\nتوجه کنیت که شما نه توانتیت شه  «ایمیل په ای کارزوروکا» استفاده بکنیت مگه ايکه یک معتبرین ایمیل ادرسئ به وتي [[Special:Preferences|کارزوروکین حسابئ]] تا ثبت کورته به ئیت و هم  باید ای قابلیتئ امکان شه شوما گیپته مه بيت.\nشمئ انونین آی پي ادرس $3 و شمئ بلاک بوتینئ شماره $5 اینت.\nمهرباني بکنیت موچین مالوماتانا به وتئ درخواست ئانی تا ذکر بکنیت.",
        "blockednoreason": "دلیلی مشخص نه بوته",
        "whitelistedittext": "په مقاله ئانی ایڈیٹ ئا باید $1.",
+       "confirmedittext": "باید شما،دیم شه تاکدیماني دستکاری کورتینا، وتي ایمیل آدرسا مشخص و تائید بکنیت. مهرباني بکنیت شه [[Special:Preferences|کار زوروکئ تنظماتاني]] نیمگاه ای کارا بکنیت.",
        "nosuchsectiontitle": "ایرنگی بخشئ ودئ نه بوت",
+       "nosuchsectiontext": "شما کوشش کورته ایت که به ای تاکدیمی تا یک بخشیا دستکاری بکنیت که موجود نه اینت.\nمومکن اینت به همي وختا که شما تاکدیما سیل ئه کورتیت ای بخش جابجا یک پاک بوته.",
        "loginreqtitle": "لوگین بوتین ضروری اینت",
        "loginreqlink": "داخل بوتین",
        "loginreqpagetext": "په دیگه تاکدیمانئ دیستینا باید  $1.",
        "editundo": "خنثی‌ کورتین",
        "diff-empty": "(بدون تفاوت)",
        "diff-multi-sameuser": "(ای کار زوروکئ {{PLURAL:$1|یک میانی نخسه|$1 میانی نخسه}}ِ نمایش داته بوته)",
+       "diff-multi-otherusers": "({{PLURAL:$1|۱ میانین نخسه|$1 میانین نخسه}} دستکاری بوتگ شه {{PLURAL:$2|۱ کارزوروک|$2 کارزوروک}}ئ نیمگاه  نشان داته نه بوته)",
+       "diff-multi-manyusers": "({{PLURAL:$1|یک|$1}} میانین دستکاری بوته ئین نخسه گیشتیر شه {{PLURAL:$2|یک|$2}} کارزورکئ نیمگاه نشان داته نه بوته)",
        "searchresults": "گشتین ئی  نتیجه",
        "searchresults-title": "گشتینئ نتایج په «$1»",
        "titlematches": "مقاله ئی ئنوانئ یکی کورتین",
        "searchprofile-everything-tooltip": "گشتین په موچین محتوا ئان (شاملئ گپ ئی تاکدیمان)",
        "searchprofile-advanced-tooltip": "گشتین بی فضای نام دلخواه",
        "search-result-size": "$1 ({{PLURAL:$2|یک کلیمه|$2 کلیمه}})",
+       "search-result-category-size": "{{PLURAL:$1|یک عضو|$1 عضو}} ({{PLURAL:$2|یک گۆنڈ تهر|$2 گۆنڈ تهر}}، {{PLURAL:$3|یک فایل|$3 فایل}})",
        "search-redirect": "($1 یي گردینتین)",
        "search-section": "(بخش $1)",
        "search-category": "(تهر  $1)",
        "search-file-match": "(فایلی محتوایی یکرنگی)",
        "search-suggest": "آیا شمی منظور ایش ات: $1",
+       "search-rewritten": "$1 ئی نتیجه ئانی نمایش. گشتین په $2 جاها.",
        "search-interwiki-caption": "گوارین پروژه ئان",
        "search-interwiki-default": "نتایج شه $1 :",
        "search-interwiki-more": "(گیشتیر)",
        "recentchangescount": "پیش پرزین نشان داته بوته ئین ایڈیٹ ئانی اندازه گ:",
        "prefs-help-recentchangescount": "ای شامیل ئه بیت شه آخیرئین تغیران،که تاکدیمانی تاریخچه و خالیگاه انت.",
        "savedprefs": "شمی تنظیمات ذخیره بوتنت.",
+       "savedrights": "{{GENDER:$1|$1}}ئ کارزوروکین دسترسی ساتیته بوته انت.",
        "timezonelegend": "منطقهٔ زمانی:",
        "localtime": "محلی ئن وخت:",
        "timezoneuseserverdefault": "استفاده شه  پیش‌فرض ئین ویکی ($1)",
        "rcshowhidemine": "$1 نی ایڈیٹان",
        "rcshowhidemine-show": "نشان داتین",
        "rcshowhidemine-hide": "چیهر داتین",
+       "rcshowhidecategorization": "$1 تاکدیمئ تهربندی",
+       "rcshowhidecategorization-show": "نشان داتین",
+       "rcshowhidecategorization-hide": "چیهر داتین",
        "rclinks": "نشان داتین $1 آخیر ئین تغییر بئ $2 اخیرین روچا<br />$3",
        "diff": "تفاوت",
        "hist": "تاریخچه",
        "trackingcategories-msg": "ردیابئ تهر",
        "trackingcategories-name": "پیامی نام",
        "trackingcategories-desc": "تهرئ کنجایش ئی معیاران",
+       "hidden-category-category-desc": "تهر به وتي تاکدیمئ  محتوای تا شمل شه <code><nowiki>__HIDDENCAT__</nowiki></code> اینت، که به اتوماتیکین رقما شه تهرِ لینکاني بکسئ نشان‌داتینا به تاکدیماني تا دیمگیری ئه کنت.",
        "trackingcategories-nodesc": "هیچ توضیحی موجود نه اینت.",
        "trackingcategories-disabled": "تهر غیر پئال بوته",
        "mailnologin": "ادرسئ شه دیم دهۆک ئا موجود نه اینت",
        "deletepage": "تاکدیمی پاک کورتین",
        "confirm": "تأیید کورتین",
        "excontent": "تاکدیمی محتوا ایش ات: «$1»",
-       "excontentauthor": "تاکدیمی محتوا ایش ات: «$1» (و تانا مشارکت کنۆک «[[Special:Contributions/$2|$2]]» ات)",
+       "excontentauthor": "تاکدیمی محتوا ایش ات: «$1» (و تاناین مشارکت کنۆک «[[Special:Contributions/$2|$2]]» ([[User talk:$2|گپ]]) ات)",
        "exbeforeblank": "تاکدیمی محتوا دیم شه خالی کورتینا ایش ات: «$1»",
        "delete-confirm": "پاک کورتین «$1»",
        "delete-legend": "پاک کورتین",
        "tooltip-n-help": "جای په ودی کورتین ئا",
        "tooltip-t-whatlinkshere": "موچین تاکدیمانی لڑ لیست که گۆ ای دیما لینک وارته انت",
        "tooltip-t-recentchangeslinked": "تاکدیمانئ آخیرین تغیران که ای دیم گۆ آوان لینک داریت",
+       "tooltip-feed-rss": "آراس‌اس ئی خبرنامه په ای تاکدیما",
        "tooltip-feed-atom": "اتم ئی حبرنامه په ای دیما",
        "tooltip-t-contributions": "ای کار زوروکئ شراکتانی لڑ لیست",
        "tooltip-t-emailuser": "په ای کار زوروکا ایمیل ئی دیم داتین",
        "tooltip-save": "وتئ تغیرانا ذخیره بکنیت",
        "tooltip-preview": "شمی تغیرانی دیم دیست، مهربانی بکنیت دیما شه تاکدیمی ذخیره کورتینا شه ای کیلی ئا استفاده بکنیت.",
        "tooltip-diff": "شمی تغیرانی نمایش که شما بئ متن ئی تا داته ایت.",
+       "tooltip-compareselectedversions": "ای تاکدمئ دو انتخاب بوته ئین نخسه ئاني فرقاني دیستین",
+       "tooltip-watch": "ای دیما به وتي واچلیست یا پدگیری لیستئ تا اڈڈ بکنیت",
        "tooltip-watchlistedit-normal-submit": "ئنوانانی پاک کورتین",
        "tooltip-watchlistedit-raw-submit": "واچلیست ئی اپڈیٹ",
        "tooltip-recreate": "پداجۆڑ کورتین تاکدیمی نه دیستین ایشیرا که ای تاکدیم دیما پاک بوته",
        "patrol-log-page": "گشتئ سیاه چال",
        "patrol-log-header": "ای سیاهه شه گشت وارته ئین ایڈیٹ ئان است.",
        "log-show-hide-patrol": "$1 گشت جنوکین سیاهه",
+       "log-show-hide-tag": "$1 خالیجای ٹاپه",
        "deletedrevision": "$1 قدیمی پاک بوته ئین نخسه ئی است",
        "filedeleteerror-short": "خطا بئ فایلی پاک کورتین: $1",
        "filedeleteerror-long": "بی پدا  پاک کورتین ئی وختا خطا رخ دات:\n\n$1",
        "exif-gpslatitude-s": "جوگراپیایی جنوبی گ\tۆور",
        "exif-gpslongitude-e": "جوگراپیایی روچ درات ئی تچکی",
        "exif-gpslongitude-w": "جوگراپیایی روچ کپت ئی تچکی",
-       "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|میتر|میتر}} بُرزتیر شه دریابی روگا",
-       "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|میتر|میتر}} جهلتیر شه دریابی روگا",
+       "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|میتر}} بُرزتیر شه دریابی روگا",
+       "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|میتر}} جهلتیر شه دریابی روگا",
        "exif-gpsstatus-a": "بی اندازه گیری هالا",
        "exif-gpsstatus-v": "اندازه گ ئی موقایسه گیری",
        "exif-gpsmeasuremode-2": "دو بوئدین اندازه گیری",
        "version-libraries": "نصب بوته ئین کتابخانه",
        "version-libraries-library": "کتابجاه",
        "version-libraries-version": "نخسه",
+       "version-libraries-license": "اجازه نامه",
+       "version-libraries-description": "توضیحان",
+       "version-libraries-authors": "نویسوکان",
        "redirect-legend": "گردینتین په یک پایل یا تاکدیمئ",
        "redirect-submit": "برا",
        "redirect-lookup": "گشتین:",
        "tags-deactivate-reason": "دلیل:",
        "tags-deactivate-not-allowed": "«$1» ئی تگی غیر په هال کورتین ممکن نه اینت.",
        "tags-deactivate-submit": "غیرپئال کورتین",
+       "tags-edit-title": "ٹاپه ئی ایڈ\tیٹ کورتین",
+       "tags-edit-manage-link": "ٹاپه ئاني مدیریت کورتین",
+       "tags-edit-revision-selected": "{{PLURAL:$1|انتخاب بووته ئین نخسه|انتخابین نخسه ئان}} شه [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|انتخاب بوته ئین خالیجا}}:",
        "tags-edit-existing-tags": "موجودین ٹاپه ئان:",
        "tags-edit-existing-tags-none": "\"هیچگوجام\"",
        "tags-edit-new-tags": "نوکین ٹاپه:",
        "revdelete-uname-unhid": "کار زوروکئ ناما سر درا کورت",
        "revdelete-restricted": "مدیر ئانه محدود کورت",
        "revdelete-unrestricted": "مدیرئانی محدودیت ئا پروشت",
+       "logentry-block-reblock": "$1 بلاک ئی {{GENDER:$2|تنظیماتانه}} په {{GENDER:$4|$3}} ئانه به انبلاک گو بلاکئ وختي الاسیا $5 $6 تغییر دات.",
+       "logentry-suppress-block": "$1 {{GENDER:$2|بسته بوت}} {{GENDER:$4|$3}} گو انبلاک کورتینئ وختا به $5 $6",
+       "logentry-suppress-reblock": "$1 بلاک ئی {{GENDER:$2|تنظیماتانه}} په {{GENDER:$4|$3}} ئانه به انبلاک گو بلاکئ وختي الاسیا $5 $6 تغییر دات.",
+       "logentry-import-interwiki": "$1 $3 ئانه شه دیگه ویکیێ {{GENDER:$2|لَڈڈینت}}",
        "logentry-merge-merge": "$1  $3  را بئ  $4 {{GENDER:$2| ادغام کورت}} (نخسه تا  $5)",
        "logentry-move-move": "$1، $3 ئی تاکدیما بئ $4 {{GENDER:$2|جابجا کورت}}",
+       "logentry-move-move-noredirect": "$1  $3 ئی تاکدیما بغیر شه ایشی که گوجام تغیرمسیرئ بجا بیللیت  به $4 {{GENDER:$2|جابجا کورت}}",
        "logentry-newusers-newusers": "$1 ئی کار زوروکئ حساب {{GENDER:$2|جۆڑ بوت}}",
        "logentry-newusers-create": "$1 ئی کار زوروکئ حساب {{GENDER:$2|جۆڑ بوت}}",
        "logentry-newusers-create2": "$3 ئی کار زوروکئ حساب شه $1 ئی نیمگا {{GENDER:$2|جۆڑ بوت}}",
        "logentry-newusers-byemail": "$3 ئی کار زوروکئ حساب شه $1 ئی نیمگا {{GENDER:$2|جۆڑ بوت}} و چیهرگال یا پاسورد گو ایمیلا دیم داته بوت",
        "logentry-newusers-autocreate": "$1 ئی حساب بئ اتوماتیکین رکما {{GENDER:$2|جۆڑ بوت}}",
+       "logentry-protect-move_prot": "$1 قُلپئ تنظیمانا شه $4 به $3 {{GENDER:$2|جابجا کورت}}",
+       "logentry-protect-unprotect": "$1 $3 ئا شه قُپا {{GENDER:$2|دَر کورت}}",
+       "logentry-protect-protect": "$1 $3 ئا {{GENDER:$2|قُلپ کورت}} $4",
+       "logentry-protect-protect-cascade": "$1 $3 $4 {{GENDER:$2|قُلپ کورت}} [آبشاری]",
+       "logentry-protect-modify": "$1 $3 ئ قُلپئ اندازه گا به {{GENDER:$2|تغییر دات}} $4",
+       "logentry-protect-modify-cascade": "$1 $3 $4 ئ قُلپئ اندازه گا په {{GENDER:$2|تغییر دات}}[آپشاری]",
        "logentry-rights-rights": "$1 ، $3 ئی عضویتا شه $4 ئی گروپا بئ $5 {{GENDER:$2|تغییر دات}}",
        "logentry-rights-rights-legacy": "$1 عضویتئ گروپا بئ $3 ئا {{GENDER:$2|تغییر دات}}",
        "logentry-rights-autopromote": "$1 بئ اوتوماتیکین رکما وتر شه $4 بئ $5 {{GENDER:$2|ارتقاء دات}}",
        "logentry-upload-revert": "$1 {{GENDER:$2|بُرز کورت}} $3 ئا",
        "log-name-managetags": "تگ ئی تاریخچه ئی مدیریت",
        "logentry-managetags-create": "$1 تگ «$4» ئا {{GENDER:$2|ج\tۆڑ کورت}}",
+       "log-name-tag": "خالیجای ٹاپه",
        "rightsnone": "(هیچ)",
        "revdelete-summary": "ایڈیتی خاصه",
        "feedback-back": "بیئرگشت",
index 75746dc..9d3e8b1 100644 (file)
@@ -33,6 +33,7 @@
        "tog-hideminor": "অনুল্লেখ্য সম্পাদনাগুলো সাম্প্রতিক পরিবর্তনসমূহে আড়াল করো",
        "tog-hidepatrolled": "পরীক্ষিত সম্পাদনা গুলো সাম্প্রতিক পরিবর্তনসমূহে আড়াল করো",
        "tog-newpageshidepatrolled": "পরীক্ষিত পাতা গুলো নতুন পাতার তালিকায় আড়াল করো",
+       "tog-hidecategorization": "পাতার শ্রেণীবদ্ধকরণ লুকান",
        "tog-extendwatchlist": "শুধু সাম্প্রতিক পরিবর্তনই নয়, সকল পরিবর্তন দেখতে নজর তালিকা সম্প্রসারণ করুন",
        "tog-usenewrc": "সাম্প্রতিক পরিবর্তনসমূহ এবং নজরতালিকা পাতায় পরিবর্তনগুলো একত্রে প্রদর্শন",
        "tog-numberheadings": "শিরোনামগুলোকে স্বয়ংক্রিয়ভাবে ক্রমিক নম্বর দাও",
@@ -62,6 +63,7 @@
        "tog-watchlisthideliu": "নজরতালিকাতে অ্যাকাউন্টে লগ-ইন করা ব্যবহারকারীদের সম্পাদনা আড়ালে রাখা হোক",
        "tog-watchlisthideanons": "নজরতালিকাতে বেনামী ব্যবহারকারীদের সম্পাদনা আড়ালে রাখা হোক",
        "tog-watchlisthidepatrolled": "পরীক্ষিত সম্পাদনা গুলো নজরতালিকায় আড়াল করো",
+       "tog-watchlisthidecategorization": "পাতার শ্রেণীবদ্ধকরণ লুকান",
        "tog-ccmeonemails": "অন্য ব্যবহারকারীর কাছে আমার পাঠানো ইমেইলের একটি অনুলিপি আমাকে পাঠানো হোক",
        "tog-diffonly": "পার্থক্যের নিচে পাতার বিষয়বস্তু না দেখানো হোক",
        "tog-showhiddencats": "লুকায়িত বিষয়শ্রেণীসমূহ দেখাও",
        "createaccountreason": "কারণ:",
        "createacct-reason": "কারণ",
        "createacct-reason-ph": "কেন আপনি আরেকটি অ্যাকাউন্ট তৈরি করছেন",
-       "createacct-captcha": "নিরাপত্তা পরীক্ষা",
-       "createacct-imgcaptcha-ph": "উপরে যে লেখা দেখতে পাচ্ছেন তা লিখুন",
        "createacct-submit": "আপনার অ্যাকাউন্ট তৈরি করুন",
        "createacct-another-submit": "অ্যাকাউন্ট তৈরি করুন",
        "createacct-benefit-heading": "{{SITENAME}} আপনার মত লোকের দ্বারাই তৈরি।",
        "passwordreset-emailtext-ip": "কেউ একজন (সম্ভবত আপনি, $1 আইপি ঠিকানা থেকে) {{SITENAME}} ($4) সাইটের জন্য আপনার\nপাসওয়ার্ড বদলের জন্য অনুরোধ করেছে। নিচের ব্যবহারকারী {{PLURAL:$3|অ্যাকাউন্টটি|অ্যাকাউন্টগুলো}}\nএই ই-মেইল ঠিকানার সাথে সংযুক্ত:\n\n$2\n\n{{PLURAL:$3|এই অস্থায়ী পাসওয়ার্ডটি|এই অস্থায়ী পাসওয়ার্ডগুলো}} আগামী {{PLURAL:$5|এক দিন|$5 দিন}} পর মেয়াদোত্তীর্ণ হয়ে যাবে।\nআপনার অবশ্যই লগ-ইন করে একটি নতুন পাসওয়ার্ড পছন্দ করা উচিত। যদি অন্য কেউ এই অনুরোধ করে থাকে,\nঅথবা আপনি যদি পুরোনো পাসওয়ার্ড মনে করতে পারেন, এবং আপনার সেটি পরিবর্তন করার কোনো ইচ্ছা না থাকে, তবে\nআপনি এই বার্তাটি উপেক্ষা করতে পারে, এবং আপনার পুরোনো পাসওয়ার্ড ব্যবহার করা চালিয়ে যেতে পারেন।",
        "passwordreset-emailtext-user": "ব্যবহারকারী $1 {{SITENAME}} ($4) সাইটের জন্য আপনার পাসওয়ার্ড বদলের জন্য অনুরোধ করেছে। নিচের ব্যবহারকারী {{PLURAL:$3|অ্যাকাউন্টটি|অ্যাকাউন্টগুলো}}\nএই ই-মেইল ঠিকানার সাথে সংযুক্ত:\n\n$2\n\n{{PLURAL:$3|এই অস্থায়ী পাসওয়ার্ডটি|এই অস্থায়ী পাসওয়ার্ডগুলো}} আগামী {{PLURAL:$5|এক দিন|$5 দিন}} পর মেয়াদোত্তীর্ণ হয়ে যাবে।\nআপনার অবশ্যই লগ-ইন করে একটি নতুন পাসওয়ার্ড পছন্দ করা উচিত। যদি অন্য কেউ এই অনুরোধ করে থাকে,\nঅথবা আপনি যদি পুরোনো পাসওয়ার্ড মনে করতে পারেন, এবং আপনার সেটি পরিবর্তন করার কোনো ইচ্ছা না থাকে, তবে\nআপনি এই বার্তাটি উপেক্ষা করতে পারে, এবং আপনার পুরোনো পাসওয়ার্ড ব্যবহার করা চালিয়ে যেতে পারেন।",
        "passwordreset-emailelement": "ব্যবহারকারী নাম: \n$1\n\nঅস্থায়ী পাসওয়ার্ড: \n$2",
-       "passwordreset-emailsent": "পাসà¦\93য়ারà§\8dড à¦¬à¦¦à¦²à§\87র à¦\8fà¦\95à¦\9fি à¦\87-মà§\87à¦\87ল à¦ªà¦¾à¦ à¦¾à¦¨à§\8b à¦¹à¦¯à¦¼à§\87à¦\9bে।",
+       "passwordreset-emailsent": "যদি à¦\86পনার à¦\85à§\8dযাà¦\95াà¦\89নà§\8dà¦\9fà§\87র à¦\9cনà§\8dয à¦\8fà¦\9fি à¦\8fà¦\95à¦\9fি à¦¨à¦¿à¦¬à¦¨à§\8dধিত à¦\87মà§\87ল à¦ à¦¿à¦\95ানা à¦¹à¦¯à¦¼, à¦¤à¦¾à¦¹à¦²à§\87 à¦\8fà¦\95à¦\9fি à¦ªà¦¾à¦¸à¦\93য়ারà§\8dড à¦¬à¦¦à¦²à§\87র à¦\87মà§\87à¦\87ল à¦ªà¦¾à¦ à¦¾à¦¨à§\8b à¦¹à¦¬ে।",
        "passwordreset-emailsent-capture": "স্মরণ করিয়ে দেয়ার জন্য একটি ইমেইল করা হয়েছে, যা নিচে দেখানো হচ্ছে।",
        "passwordreset-emailerror-capture": "স্মরণ করিয়ে দেয়ার জন্য একটি ইমেইল তৈরী করা হয়েছিল, যা নিচে দেখানো হচ্ছে, তবে $1 {{GENDER:$2|ব্যবহারকারীকে}} এটি পাঠানো যায়নি!",
-       "changeemail": "ই-মেইল ঠিকানা পরিবর্তন",
+       "changeemail": "ই-মেইল ঠিকানা পরিবর্তন বা বাতিল",
        "changeemail-header": "অ্যাকাউন্ট ই-মেইল ঠিকানা পরিবর্তন",
        "changeemail-no-info": "এই পাতাটিতে সরাসরি প্রবেশাধিকার পেতে আপনাকে অবশ্যই লগইন করতে হবে।",
        "changeemail-oldemail": "বর্তমান ই-মেইল ঠিকানা:",
        "prefs-watchlist-token": "নজরতালিকা টোকেন:",
        "prefs-misc": "বিবিধ",
        "prefs-resetpass": "পাসওয়ার্ড পরিবর্তন",
-       "prefs-changeemail": "ই-মেইল পরিবর্তন",
+       "prefs-changeemail": "ইমেইল ঠিকানা পরিবর্তন বা বাতিল",
        "prefs-setemail": "একটি ই-মেইল ঠিকানা নির্ধারণ করুন",
        "prefs-email": "ই-মেইল অপশন",
        "prefs-rendering": "অবয়ব",
        "rows": "সারি:",
        "columns": "কলাম:",
        "searchresultshead": "অনুসন্ধান",
-       "stub-threshold": "<a href=\"#\" class=\"stub\">অসম্পূর্ণ নিবন্ধের সংযোগগুলির</a> বিশেষ ফরম্যাটিঙের সীমা (বাইটে):",
+       "stub-threshold": "অসম্পূর্ণ লিঙ্ক বিন্যাসের জন্য প্রান্তিক মাপ ($1):",
        "stub-threshold-sample-link": "নমুনা",
        "stub-threshold-disabled": "নিস্ক্রিয়",
        "recentchangesdays": "সাম্প্রতিক পরিবর্তনে দিনসমূহ দেখানোর জন্য:",
        "rcshowhidemine": "আমার সম্পাদনাগুলো $1",
        "rcshowhidemine-show": "দেখাও",
        "rcshowhidemine-hide": "আড়াল করো",
+       "rcshowhidecategorization": "পাতা শ্রেণীবদ্ধকরণ $1",
+       "rcshowhidecategorization-show": "দেখাও",
+       "rcshowhidecategorization-hide": "আড়াল করো",
        "rclinks": "'''প্রদর্শনের ধরন'''<br />\n* বিগত ($2) দিনের শেষ ($1)টি পরিবর্তন দেখাও\n* $3",
        "diff": "পরিবর্তন",
        "hist": "ইতিহাস",
        "upload-form-label-infoform-description": "বিবরণ",
        "upload-form-label-usage-title": "ব্যবহার",
        "upload-form-label-usage-filename": "ফাইলের নাম",
+       "foreign-structured-upload-form-label-own-work": "এটি আমার নিজের কাজ",
+       "foreign-structured-upload-form-label-infoform-categories": "বিষয়শ্রেণীসমূহ",
+       "foreign-structured-upload-form-label-infoform-date": "তারিখ",
        "backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
        "backend-fail-backup": "\"$1\" ফাইলের ব্যাকআপ তৈরী সম্ভব নয়।",
        "backend-fail-notexists": "\"$1\" নামের কোনো ফাইল নেই।",
        "deletepage": "পাতাটি মুছে ফেলা হোক",
        "confirm": "নিশ্চিত করুন",
        "excontent": "বিষয়বস্তু ছিল: '$1'",
-       "excontentauthor": "বিষয়বস্তু ছিল: \"$1\" (এবং একমাত্র অবদানকারী ছিলেন \"[[Special:Contributions/$2|$2]]\")",
+       "excontentauthor": "বিষয়বস্তু ছিল: \"$1\", এবং একমাত্র অবদানকারী ছিলেন \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|আলাপ]])",
        "exbeforeblank": "মুছে ফেলার আগে বিষয়বস্তু ছিল: '$1'",
        "delete-confirm": "\"$1\" অপসারণ",
        "delete-legend": "অপসারণ",
        "cant-move-to-user-page": "আপনার কোনো পাতা ব্যবহারকারী পাতার স্থানান্তরের অনুমতি নেই (ব্যবহারকারী উপপাতা ব্যতিত)।",
        "cant-move-category-page": "আপনার বিষয়শ্রেণী পাতা স্থানান্তরের অনুমতি নেই।",
        "cant-move-to-category-page": "আপনার পাতাটিকে বিষয়শ্রেণী পাতায় স্থানান্তরের অনুমতি নেই।",
-       "newtitle": "à¦\8fà¦\87 à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà§\87",
+       "newtitle": "নতà§\81ন à¦¶à¦¿à¦°à§\8bনাম:",
        "move-watch": "এই পাতাটি নজরে রাখুন",
        "movepagebtn": "পাতা স্থানান্তর করুন",
        "pagemovedsub": "সরিয়ে নেওয়া হয়েছে",
index b9afc7c..1d7b339 100644 (file)
        "redirectedfrom": "(Preusmjereno sa $1)",
        "redirectpagesub": "Preusmjerenje",
        "redirectto": "Preusmjerenje na:",
-       "lastmodifiedat": "Ova stranica je posljednji put izmijenjena $2, $1",
+       "lastmodifiedat": "Ova stranica je posljednji put izmijenjena u $2 na $1.",
        "viewcount": "Ovoj stranici je pristupljeno {{PLURAL:$1|$1 put|$1 puta}}.",
        "protectedpage": "Zaštićena stranica",
        "jumpto": "Idi na:",
        "viewsource": "Prikaži izvor",
        "viewsource-title": "Prikaz izvora stranice $1",
        "actionthrottled": "Akcija je usporena",
-       "actionthrottledtext": "Kao anti-spam mjera, ograničene su vam izmjene u određenom vremenu, i trenutačno ste dostigli to ograničenje. Pokušajte ponovo poslije nekoliko minuta.",
+       "actionthrottledtext": "U cilju borbe protiv zloupotrebe, ograničeno vam je da u kratkom vremenskom periodu previše puta vršite ovu radnju, a upravo ste prešli to ograničenje.\nPokušajte ponovo za nekoliko minuta.",
        "protectedpagetext": "Ova stranica je zaključana da bi se spriječile izmjene.",
        "viewsourcetext": "Možete vidjeti i kopirati izvorni kôd ove stranice.",
-       "viewyourtext": "Možete da pogledate i kopirate izvor '''vaših izmjena''' na ovoj stranici:",
+       "viewyourtext": "Možete vidjeti i kopirati izvor '''vaših izmjena''' na ovoj stranici.",
        "protectedinterface": "Ova stranica sadrži tekst korisničkog okruženja za softver na ovom wikiju i zaštićena je radi sprečavanja zloupotrebe.\nDa biste dodali ili izmjenili prijevode svih wikija, posjetite [//translatewiki.net/  translatewiki.net], projekat za lokalizaciju Mediawikija.",
        "editinginterface": "<strong>Upozorenje:</strong> Mijenjate stranicu koja sadrži aktivan tekst programa.\nPromjene na ovoj stranici dovode i do promjena za druge korisnike ovog wikija.\nZa dodavanje ili promjene prijevoda za sve wikije, molimo Vas koristite [//translatewiki.net/ translatewiki.net], projekt prijevoda za MediaWiki.",
        "translateinterface": "Za dodavanje ili promjenu prijevoda za sve wikije koristite [//translatewiki.net/ translatewiki.net], projekt za lokalizaciju MediaWikija.",
        "createaccountreason": "Razlog:",
        "createacct-reason": "Razlog",
        "createacct-reason-ph": "Zašto pravite još jedan korisnički račun?",
-       "createacct-captcha": "Sigurnosna provjera",
-       "createacct-imgcaptcha-ph": "Unesite tekst koji vidite iznad",
        "createacct-submit": "Napravite svoj korisnički račun",
-       "createacct-another-submit": "Napravi još jedan korisnički račun",
+       "createacct-another-submit": "Napravi korisnički račun",
        "createacct-benefit-heading": "{{SITENAME}} je napravljena od strane ljudi kao što ste Vi.",
        "createacct-benefit-body1": "{{PLURAL:$1|izmjena|izmjene}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stranica|stranice|stranica}}",
        "passwordreset-emailtext-ip": "Neko (vjerovatno Vi, s IP adrese $1) je zatražio podsjetnik Vaših detalja računa za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|račun korisnika je|računi korisnika su}} povezani s ovom e-mail adresom:\n\n$2\n\n{{PLURAL:$3|Ova privremena šifra|Ove privremene šifre}} će isteći za {{PLURAL:$5|jedan dan|$5 dana}}.\nTrebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj zahtjev, ili ako ste se sjetili Vaše početne šifre, a ne želite je promijeniti, možete zanemariti ovu poruku i nastaviti koristiti staru šifru.",
        "passwordreset-emailtext-user": "Korisnik $1 na {{SITENAME}} je zatražio podsjetnik o detaljima Vašeg računa za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|korisnički račun je|korisnički računi su}} povezani s ovom e-mail adresom:\n\n$2\n\n{{PLURAL:$3|Ova privremena šifra|Ove privremene šifre}} će isteći za {{PLURAL:$5|jedan dan|$5 dana}}.\nTrebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj zahtjev, ili ako ste se sjetili Vaše originalne šifre, a ne želite je više promijeniti, možete zanemariti ovu poruku i nastaviti koristiti staru šifru.",
        "passwordreset-emailelement": "Korisničko ime: \n$1\n\nPrivremena šifra: \n$2",
-       "passwordreset-emailsent": "Podsjetnik na lozinku poslan je na Vašu e-mail adresu.",
+       "passwordreset-emailsent": "Ako je ovo adresa e-pošte s kojom ste registrirali ovaj račun, podsjetnik šifre će vam biti poslan na vašu adresu e-pošte.",
        "passwordreset-emailsent-capture": "Poslan je podsjetnik preko e-pošte (prikazano ispod).",
        "passwordreset-emailerror-capture": "E-poruka za resetiranje lozinke, prikazano ispod, poslana je, ali slanje {{GENDER:$2|korisniku|korisnici}} nije uspjelo: $1",
-       "changeemail": "Promjena e-adrese",
-       "changeemail-header": "Promijeni e-mail adresu korisničkog računa",
+       "changeemail": "Promjena ili uklanjanje e-adrese",
+       "changeemail-header": "Ispunite sljedeći formular da biste promijenili adresu e-pošte. Ako želite ukloniti postojeću adresu e-pošte s vašeg korisničkog računa, pri ispunjavanju formulara, polje nove adrese e-pošte ostavite prazno.",
        "changeemail-no-info": "Morate biti prijavljeni za direktan pristup ovoj stranici.",
        "changeemail-oldemail": "Trenutna adresa e-pošte:",
        "changeemail-newemail": "Nova adresa e-pošte:",
        "sig_tip": "Vaš potpis sa trenutnim vremenom",
        "hr_tip": "Horizontalna linija (koristite oskudno)",
        "summary": "Sažetak:",
-       "subject": "Tema/naslov:",
+       "subject": "Tema:",
        "minoredit": "Ovo je manja izmjena",
        "watchthis": "Prati ovu stranicu",
        "savearticle": "Sačuvaj stranicu",
        "missingsummary": "'''Napomena:''' Niste unijeli sažetak izmjene.\nAko kliknete na Sačuvaj, Vaša izmjena će biti sačuvana bez sažetka.",
        "selfredirect": "<strong>Upozorenje:</strong> Preusmjerili ste stranicu na samu sebe.\nMožda ste naveli pogrešan cilj preusmjeravanja ili ste uređivali pogrešnu stranicu.\nAko ponovno kliknete \"{{int:savearticle}}\", ipak će nastati preusmjerenje.",
        "missingcommenttext": "Molimo unesite komentar ispod.",
-       "missingcommentheader": "<strong>Podsjetnik:</strong> Niste napisali temu/naslov za ovaj komentar.\nAko ponovo kliknete na \"{{int:savearticle}}\", vaša izmjena će biti sačuvana bez teme/naslova.",
+       "missingcommentheader": "<strong>Podsjetnik:</strong> Niste napisali temu za ovaj komentar.\nAko ponovo kliknete na \"{{int:savearticle}}\", vaša izmjena će biti sačuvana bez teme/naslova.",
        "summary-preview": "Pregled sažetka:",
-       "subject-preview": "Pregled tema/naslova:",
+       "subject-preview": "Pregled teme:",
        "previewerrortext": "Dogodila se greška prilikom prikazivanja vaših izmjena.",
        "blockedtitle": "Korisnik je blokiran",
        "blockedtext": "'''Vaše korisničko ime ili IP-adresa je blokirana.'''\n\nBlokada izvršena od strane $1.\nDati razlog je sljedeći: ''$2''.\n\n*Početak blokade: $8\n*Kraj perioda blokade: $6\n*Ime blokiranog korisnika: $7\n\nMožete kontaktirati sa $1 ili nekim drugim [[{{MediaWiki:Grouppage-sysop}}|administratorom]] da biste razgovarali o blokadi.\n\nNe možete koristiti opciju ''Pošalji e-mail korisniku'' osim ako niste unijeli e-mail adresu u [[Special:Preferences|Vaše postavke]].\nVaša trenutna IP-adresa je $3, a oznaka blokade je #$5.\nMolimo Vas da navedete gornje podatke pri zahtjevu za deblokadu.",
        "mergehistory-go": "Prikaži izmjene koje se mogu spojiti",
        "mergehistory-submit": "Spoji revizije",
        "mergehistory-empty": "Nema revizija za spajanje.",
-       "mergehistory-done": "$3 {{PLURAL:$3|revizija|revizije|revizija}} stranice $1 uspješno spojeno u [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|izmjena|izmjene|izmjena}} stranice $1 uspješno je spojeno u [[:$2]].",
        "mergehistory-fail": "Ne može se izvršiti spajanje historije, molimo provjerite opet stranicu i parametre vremena.",
        "mergehistory-fail-toobig": "Ne može se izvršiti spajanje historije jer će se više premjestiti više od ograničenja od $1 {{PLURAL:$1|revizije|revizija}}.",
        "mergehistory-no-source": "Izvorna stranica $1 ne postoji.",
        "prefs-watchlist-token": "Žeton praćenih članaka:",
        "prefs-misc": "Ostala podešavanja",
        "prefs-resetpass": "Promijeni šifru",
-       "prefs-changeemail": "Promijeni adresu e-pošte",
+       "prefs-changeemail": "Promijeni ili ukloni adresu e-pošte",
        "prefs-setemail": "Postavite e-mail adresu",
        "prefs-email": "Opcije e-pošte",
        "prefs-rendering": "Izgled",
        "group-bot": "Botovi",
        "group-sysop": "Administratori",
        "group-bureaucrat": "Birokrati",
-       "group-suppress": "Nadzornici",
+       "group-suppress": "Skrivači",
        "group-all": "(sve)",
        "group-user-member": "{{GENDER:$1|korisnik|korisnica}}",
        "group-autoconfirmed-member": "Potvrđeni korisnik",
        "group-bot-member": "bot",
        "group-sysop-member": "{{GENDER:$1|administrator|administratorica}}",
        "group-bureaucrat-member": "{{GENDER:$1|birokrat|birokratica}}",
-       "group-suppress-member": "Nadzornik",
+       "group-suppress-member": "{{GENDER:$1|skrivač|skrivačica}}",
        "grouppage-user": "{{ns:project}}:Korisnici",
        "grouppage-autoconfirmed": "{{ns:project}}:Potvrđeni korisnici",
        "grouppage-bot": "{{ns:project}}:Botovi",
        "grouppage-sysop": "{{ns:project}}:Administratori",
        "grouppage-bureaucrat": "{{ns:project}}:Birokrati",
-       "grouppage-suppress": "{{ns:project}}:Nadzornici",
+       "grouppage-suppress": "{{ns:project}}:Skrivač",
        "right-read": "Čitanje stranica",
        "right-edit": "Uređivanje stranica",
        "right-createpage": "Pravljenje stranica (neuključujući stranice za razgovor)",
        "rcshowhidemine": "$1 moje izmjene",
        "rcshowhidemine-show": "Prikaži",
        "rcshowhidemine-hide": "Sakrij",
+       "rcshowhidecategorization-show": "Prikaži",
+       "rcshowhidecategorization-hide": "Sakrij",
        "rclinks": "Prikaži posljednjih $1 izmjena u posljednjih $2 dana<br />$3",
        "diff": "razl",
        "hist": "hist",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|korisnik|korisnika}} koji pregledaju]",
-       "rc_categories": "Ograniči na kategorije (razdvojene sa \"|\")",
-       "rc_categories_any": "Sve",
+       "rc_categories": "Ograniči na kategorije (razdvoji sa \"|\"):",
+       "rc_categories_any": "Bilo koju odabranu",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajta|bajtova}} poslije izmjene",
        "newsectionsummary": "/* $1 */ novi odjeljak",
        "rc-enhanced-expand": "Pokaži detalje",
        "upload-too-many-redirects": "URL sadrži previše preusmjerenja",
        "upload-http-error": "Desila se HTTP greška: $1",
        "upload-copy-upload-invalid-domain": "Kopije postavljenih datoteka nisu dostupne sa ove domene.",
+       "upload-dialog-title": "Postavi datoteku",
+       "upload-dialog-button-cancel": "Odustani",
+       "upload-dialog-button-done": "Gotovo",
+       "upload-dialog-button-save": "Sačuvaj",
+       "upload-dialog-button-upload": "Postavi",
+       "upload-form-label-select-file": "Izaberi datoteku",
+       "upload-form-label-infoform-title": "Detalji",
+       "upload-form-label-infoform-name": "Ime",
+       "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-usage-title": "Korištenje",
+       "upload-form-label-usage-filename": "Ime datoteke",
+       "foreign-structured-upload-form-label-own-work": "Ovo je moje djelo",
+       "foreign-structured-upload-form-label-infoform-categories": "Kategorije",
+       "foreign-structured-upload-form-label-infoform-date": "Datum",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Potvrđujem da posjedujem autorska prava za ovu datoteku i slažem se da ću je neopozivo postaviti na Wikimedia Commons pod licencom [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], te se slažem s [https://wikimediafoundation.org/wiki/Terms_of_Use Uvjetima korištenja].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Ako ne posjedujete autorska prava za ovu datoteku ili je želite postaviti pod drugom licencom, imajte na umu da možete koristiti [https://commons.wikimedia.org/wiki/Special:UploadWizard čarobnjak za postavljanje datoteka na Commonsu].",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Također možete koristiti [[Special:Upload|stranicu za postavljanje datoteka na projektu {{SITENAME}}]] ako politika stranice dozvoljava postavljanje ove datoteke.",
        "backend-fail-stream": "Ne mogu emitirati datoteku $1.",
        "backend-fail-backup": "Ne može sigurnosno kopirati datoteku $1.",
        "backend-fail-notexists": "Datoteka $1 ne postoji.",
        "nopagetext": "Ciljna stranica koju ste naveli ne postoji.",
        "pager-newer-n": "{{PLURAL:$1|novija 1|novije $1}}",
        "pager-older-n": "{{PLURAL:$1|starija 1|starije $1}}",
-       "suppress": "Nazdor",
+       "suppress": "Sakrij",
        "querypage-disabled": "Ova posebna stranica je onemogućena jer smanjuje performanse.",
        "apihelp": "API pomoć",
        "apihelp-no-such-module": "Modul \"$1\" nije pronađen.",
        "trackingcategories-disabled": "Kategorija je onemogućena",
        "mailnologin": "Nema adrese za slanje",
        "mailnologintext": "Morate biti [[Special:UserLogin|prijavljeni]]\ni imati ispravnu adresu e-pošte u vašim [[Special:Preferences|podešavanjima]]\nda biste slali e-poštu drugim korisnicima.",
-       "emailuser": "Pošalji e-poštu",
+       "emailuser": "Pošalji e-poruku",
        "emailuser-title-target": "Pošalji e-poruku {{GENDER:$1|korisniku|korisnici|korisniku}}",
        "emailuser-title-notarget": "Pošalji e-mail korisniku",
        "emailpagetext": "Možete korisiti obrazac ispod za slanje poruke e-poštom {{GENDER:$1|ovom korisniku|ovoj korisnici|ovom korisniku}}.\nAdresa e-pošte koju ste unijeli u [[Special:Preferences|svojim korisničkim postavkama]] će biti prikazana kao adresa pošiljaoca, tako da će primaoc poruke moći da Vam odgovori.",
        "emailccsubject": "Kopiraj Vašu poruku za $1: $2",
        "emailsent": "Poruka poslata",
        "emailsenttext": "Vaša poruka je poslata e-poštom.",
-       "emailuserfooter": "Ovaj e-mail je poslao $1 korisniku $2 putem funkcije \"{{int:emailuser}}\" sa {{SITENAME}}.",
+       "emailuserfooter": "Ovu e-poruku {{GENDER:$1|poslao|poslala}} je $1 {{GENDER:$2|korisniku|korisnici}} $2 pomoću funkcije \"{{int:emailuser}}\" s projekta {{SITENAME}}.",
        "usermessage-summary": "Ostavljanje sistemske poruke.",
        "usermessage-editor": "Sistem za poruke",
        "watchlist": "Spisak praćenja",
        "watchlistanontext": "Morate biti prijavljeni kako biste vidjeli ili uređivali svoj spisak praćenih članaka.",
        "watchnologin": "Niste prijavljeni",
        "addwatch": "Dodaj na spisak praćenja",
-       "addedwatchtext": "Stranica \"[[:$1]]\" je dodata vašem [[Special:Watchlist|spisku praćenih članaka]]. \nBuduće promjene ove stranice i njoj pridružene stranice za razgovor će biti navedene ovdje.",
+       "addedwatchtext": "Stranica \"[[:$1]]\" i njena stranica za razgovor dodani su na vaš [[Special:Watchlist|spisak praćenja]].",
        "addedwatchtext-short": "Stranica \"$1\" je dodana na vaš spisak praćenja.",
        "removewatch": "Ukloni sa spiska praćenja",
        "removedwatchtext": "Stranica \"[[:$1]]\" je uklonjena iz [[Special:Watchlist|vašeg spiska praćenih članaka]].",
        "cant-move-to-user-page": "Nemate dopuštenje da premjestite stranicu na korisničku stranicu (osim na korisničku podstranicu).",
        "cant-move-category-page": "Nemate dopuštene da premještate stranice kategorija.",
        "cant-move-to-category-page": "Nemate dopuštenje da premjestite stranicu na stranicu kategorije.",
-       "newtitle": "Novi naslov",
+       "newtitle": "Novi naslov:",
        "move-watch": "Prati ovu stranicu",
        "movepagebtn": "Premjesti stranicu",
        "pagemovedsub": "Premještanje uspjelo",
        "anonymous": "{{PLURAL:$1|Anonimni korisnik|$1 anonimna korisnika|$1 anonimnih korisnika}} projekta {{SITENAME}}",
        "siteuser": "{{SITENAME}} korisnik $1",
        "anonuser": "{{SITENAME}} anonimni korisnik $1",
-       "lastmodifiedatby": "Ovu stranicu posljednji je put promijenio $3 u $2, $1",
+       "lastmodifiedatby": "Ovu stranicu posljednji je put {{GENDER:$4|izmijenio|izmijenila}} $3 dana $1 u $2.",
        "othercontribs": "Bazirano na radu od strane korisnika $1.",
        "others": "ostali",
        "siteusers": "{{SITENAME}} {{PLURAL:$2|korisnik|korisnika}} $1",
index 1604018..a89bc56 100644 (file)
@@ -58,6 +58,7 @@
        "tog-hideminor": "Amaga les edicions menors en la pàgina de canvis recents",
        "tog-hidepatrolled": "Amaga edicions patrullades als canvis recents",
        "tog-newpageshidepatrolled": "Amaga pàgines patrullades de la llista de pàgines noves",
+       "tog-hidecategorization": "Amaga la categorització de les pàgines",
        "tog-extendwatchlist": "Desplega la llista de seguiment per a mostrar tots els canvis afectats, no només els més recents",
        "tog-usenewrc": "Agrupa els canvis per pàgina en canvis recents i llista de seguiment",
        "tog-numberheadings": "Enumera automàticament els encapçalaments",
@@ -87,6 +88,7 @@
        "tog-watchlisthideliu": "Amaga a la llista les edicions d'usuaris registrats",
        "tog-watchlisthideanons": "Amaga a la llista les edicions d'usuaris anònims",
        "tog-watchlisthidepatrolled": "Amaga edicions patrullades de la llista de seguiment",
+       "tog-watchlisthidecategorization": "Amaga la categorització de les pàgines",
        "tog-ccmeonemails": "Envia’m còpies dels missatges que enviï als altres usuaris",
        "tog-diffonly": "Amaga el contingut de la pàgina davall de la taula de diferències",
        "tog-showhiddencats": "Mostra les categories ocultes",
        "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.",
+       "actionthrottledtext": "Com a mesura per prevenir l’abús, no podeu fer aquesta acció tantes vegades en un període de temps tan curt. Torneu-ho a intentar d’ací a uns minuts.",
        "protectedpagetext": "S'ha protegit la pàgina per evitar-hi modificacions.",
        "viewsourcetext": "Podeu veure i copiar el codi font d’aquesta pàgina.",
        "viewyourtext": "Vostè pot veure i copiar la font de <strong>les teves modificacions</strong> d'aquesta pàgina.",
        "userlogin-helplink2": "Ajuda amb el registre",
        "userlogin-loggedin": "Heu iniciat una sessió com {{GENDER:$1|$1}}.\nFeu servir el formulari de sota per iniciar la sessió com un altre usuari.",
        "userlogin-createanother": "Crea un altre compte",
-       "createacct-emailrequired": "Adreça de correu electrònic",
-       "createacct-emailoptional": "Adreça de correu electrònic (opcional)",
+       "createacct-emailrequired": "Adreça electrònica",
+       "createacct-emailoptional": "Adreça electrònica (opcional)",
        "createacct-email-ph": "Introduïu la vostra adreça de correu electrònic",
        "createacct-another-email-ph": "Introduïu una adreça de correu electrònic",
        "createaccountmail": "Utilitza una contrasenya aleatòria temporal i envia-la a l'adreça de correu indicada",
        "createaccountreason": "Motiu:",
        "createacct-reason": "Motiu",
        "createacct-reason-ph": "Per què creeu un altre compte",
-       "createacct-captcha": "Control de seguretat",
-       "createacct-imgcaptcha-ph": "Introduïu el text que apareix a dalt",
        "createacct-submit": "Crea el meu compte",
        "createacct-another-submit": "Crea un compte",
        "createacct-benefit-heading": "{{SITENAME}} és feta per gent com tu.",
        "passwordremindertitle": "Nova contrasenya temporal per al projecte {{SITENAME}}",
        "passwordremindertext": "Algú (vós mateix segurament, des de l'adreça l'IP $1) ha soŀlicitat que us enviéssim una nova contrasenya per a iniciar la sessió al projecte {{SITENAME}} ($4).\nLa nova contrasenya temporal per a l'usuari «$2» és ara «$3». Si aquesta fou la vostra intenció, ara hauríeu d'iniciar la sessió i canviar-la. Tingueu present que és temporal i caducarà d'aquí {{PLURAL:$5|un dia|$5 dies}}.\n\nSi algú altre hagués fet aquesta soŀlicitud o si ja haguéssiu recordat la vostra contrasenya i\nno volguéssiu canviar-la, ignoreu aquest missatge i continueu utilitzant\nla vostra antiga contrasenya.",
        "noemail": "No hi ha cap adreça electrònica registrada de l'usuari «$1».",
-       "noemailcreate": "Has d'indicar una adreça de correu electrònic vàlida",
+       "noemailcreate": "Heu d’indicar una adreça electrònica vàlida.",
        "passwordsent": "S'ha enviat una nova contrasenya a l'adreça electrònica registrada per «$1».\nInicieu una sessió després que la rebeu.",
        "blocked-mailpassword": "S'ha blocat la vostra adreça IP. Se us ha desactivat la funció de recuperació de contrasenya per a prevenir abusos.",
        "eauthentsent": "S'ha enviat un correu electrònic a la direcció especificada. Abans no s'enviï cap altre correu electrònic a aquesta adreça, cal verificar que és realment vostra. Per tant, cal que seguiu les instruccions presents en el correu electrònic que se us ha enviat.",
        "passwordreset-domain": "Domini",
        "passwordreset-capture": "Veure el missatge de correu electrònic?",
        "passwordreset-capture-help": "Si marqueu aquesta casella, el missatge de correu electrònic (amb la contrasenya temporal) es mostrarà al mateix moment que sigui enviat a l'usuari.",
-       "passwordreset-email": "Adreça de correu electrònic:",
+       "passwordreset-email": "Adreça electrònica:",
        "passwordreset-emailtitle": "Detalls del compte a {{SITENAME}}",
        "passwordreset-emailtext-ip": "Algú (vós mateix segurament, des de l'adreça IP $1) ha demanat una reinicialització de la vostra contrasenya al projecte {{SITENAME}} ($4). {{PLURAL:$3|El següent compte d'usuari està associat|Els següents comptes d'usuari estan associats}} amb aquesta adreça de correu electrònic:\n\n$2\n\n{{PLURAL:$3|Aquesta contrasenya temporal caducarà|Aquestes contrasenyes temporals caducaran}} en {{PLURAL:$5|un dia|$5 dies}}.\nHauríeu d'entrar al compte per a fixar-hi una nova contrasenya al més aviat possible. Si algú que no sou vós és qui ha fet aquesta petició o si heu recordat la contrasenya original i ja no la voleu canviar, podeu ignorar aquest missatge i seguir utilitzant la vostra antiga contrasenya.",
        "passwordreset-emailtext-user": "L'usuari $1 de {{SITENAME}} ha demanat una reinicialització de la vostra contrasenya per al projecte {{SITENAME}} ($4). {{PLURAL:$3|El següent compte d'usuari està associat|Els següents comptes d'usuari estan associats}} amb aquesta adreça de correu electrònic:\n\n$2\n\n{{PLURAL:$3|Aquesta contrasenya temporal caducarà|Aquestes contrasenyes temporals caducaran}} en {{PLURAL:$5|un dia|$5 dies}}.\nHauríeu d'entrar ara per fixar una nova contrasenya. Si algú que no sou vós és qui ha fet aquesta petició o si heu recordat la contrasenya original i ja no la voleu canviar, podeu ignorar aquest missatge i seguir utilitzant la vostra antiga contrasenya.",
        "passwordreset-emailelement": "Nom d'usuari: \n$1\n\nContrasenya temporal: \n$2",
-       "passwordreset-emailsent": "S'ha enviat un correu de reinicialització de contrasenya.",
+       "passwordreset-emailsent": "Si aquesta és una adreça electrònica registrada amb el vostre compte, s’hi enviarà un missatge de restabliment de contrasenya.",
        "passwordreset-emailsent-capture": "S'ha enviat un correu electrònic de reinicialització de contrasenya, tal com es mostra a continuació.",
        "passwordreset-emailerror-capture": "S'ha generat un correu electrònic de renovació de contrasenya, que es mostra a continuació, però ha fallat l'enviament a {{GENDER:$2:l'usuari|la usuària}}: $1",
-       "changeemail": "Canvi de correu electrònic",
-       "changeemail-header": "Canvi de l'adreça de correu electrònic del compte",
+       "changeemail": "Canvia o elimina l’adreça electrònica",
+       "changeemail-header": "Empleneu aquest formulari per canviar la vostra adreça electrònica. Si voleu eliminar qualssevol associacions d’adreces electròniques del vostre compte, deixeu en blanc el camp i envieu el formulari.",
+       "changeemail-passwordrequired": "Cal que introduïu la vostra contrasenya per confirmar el canvi.",
        "changeemail-no-info": "Heu d'entrar en un compte d'usuari per accedir directament a aquesta pàgina.",
-       "changeemail-oldemail": "Adreça de correu electrònic actual:",
+       "changeemail-oldemail": "Adreça electrònica actual:",
        "changeemail-newemail": "Adreça electrònica nova:",
        "changeemail-none": "(cap)",
        "changeemail-password": "La vostra contrasenya a {{SITENAME}}:",
        "changeemail-submit": "Canvia de correu electrònic",
        "changeemail-throttled": "Heu realitzat massa intents d'inici de sessió.\nEspereu $1 abans de tornar-ho a provar.",
+       "changeemail-nochange": "Introduïu una adreça electrònica distinta.",
        "resettokens": "Reinicia els testimonis",
        "resettokens-text": "Des d'aquí podeu reiniciar els testimonis que permeten l'accés a certes dades privades associades amb el vostre compte.\n\nHo hauríeu de fer si accidentalment els heu compartit amb algú o si el vostre compte ha estat compromès.",
        "resettokens-no-tokens": "No hi ha testimonis per reiniciar.",
        "sig_tip": "La vostra signatura amb marca horària",
        "hr_tip": "Línia horitzontal (feu-la servir amb moderació)",
        "summary": "Resum:",
-       "subject": "Tema/capçalera:",
+       "subject": "Assumpte:",
        "minoredit": "Aquesta és una modificació menor",
        "watchthis": "Vigila aquesta pàgina",
        "savearticle": "Desa la pàgina",
        "missingsummary": "'''Recordatori''': Heu deixat en blanc el resum de l'edició. Si torneu a clicar al botó de desar, l'edició es guardarà sense resum.",
        "selfredirect": "<strong>Avís:</strong> Esteu redirigint la pàgina a si mateixa.\nPodeu haver especificat un objectiu erroni per a la redirecció, o potser esteu modificant una pàgina incorrecta.\nSi feu clic a «{{int:savearticle}}» una vegada més, la redirecció es crearà de totes maneres.",
        "missingcommenttext": "Introduïu un comentari a continuació.",
-       "missingcommentheader": "'''Recordatori:''' No heu proporcionat un assumpte/encapçalament per al comentari. Si cliqueu de nou al botó \"{{int:savearticle}}\", la vostra contribució se desarà sense cap.",
+       "missingcommentheader": "<strong>Recordatori:</strong> no heu proporcionat un assumpte/encapçalament per al comentari.\nSi feu clic de nou al botó «{{int:savearticle}}», la vostra contribució es desarà sense cap.",
        "summary-preview": "Previsualització del resum:",
-       "subject-preview": "Previsualització de tema/capçalera:",
+       "subject-preview": "Previsualització de l’assumpte:",
        "previewerrortext": "S'ha produït un error quan es provava de previsualitzar els canvis.",
        "blockedtitle": "L'usuari està blocat",
        "blockedtext": "'''S'ha procedit al blocatge del vostre compte d'usuari o la vostra adreça IP.'''\n\nEl blocatge l'ha dut a terme l'usuari $1.\nEl motiu donat és ''$2''.\n\n* Inici del blocatge: $8\n* Final del blocatge: $6\n* Compte blocat: $7\n\nPodeu contactar amb $1 o un dels [[{{MediaWiki:Grouppage-sysop}}|administradors]] per a discutir-ho.\n\nTingueu en compte que no podeu fer servir el formulari d'enviament de missatges de correu electrònic a cap usuari, a menys que tingueu una adreça de correu vàlida registrada a les vostres [[Special:Preferences|preferències d'usuari]] i no ho tingueu tampoc blocat.\n\nLa vostra adreça IP actual és $3, i el número d'identificació del blocatge és #$5.\nSi us plau, incloeu aquestes dades en totes les consultes que feu.",
        "permissionserrorstext-withaction": "No teniu permís per a $2, {{PLURAL:$1|pel motiu següent|pels motius següents}}:",
        "recreate-moveddeleted-warn": "'''Avís: esteu creant una pàgina que s'ha suprimit prèviament.'''\n\nHauríeu de considerar si és realment necessari continuar editant aquesta pàgina.\nA continuació s'ofereix el registre de supressions i de reanomenaments de la pàgina:",
        "moveddeleted-notice": "S'ha suprimit aquesta pàgina.\nA continuació us mostrem com a referència el registre d'esborraments i reanomenaments de la pàgina.",
+       "moveddeleted-notice-recent": "S’ha suprimit aquesta pàgina recentment (en les últimes 24 hores).\nA continuació us mostrem com a referència el registre de supressions i reanomenaments de la pàgina.",
        "log-fulllog": "Veure tot el registre",
        "edit-hook-aborted": "Modificació avortada pel hook.\nNo s'ha donat cap explicació.",
        "edit-gone-missing": "No s'ha pogut actualitzar la pàgina.\nSembla haver estat esborrada.",
        "mergehistory-go": "Mostra les edicions que es poden fusionar",
        "mergehistory-submit": "Fusiona les revisions",
        "mergehistory-empty": "No pot fusionar-se cap revisió.",
-       "mergehistory-done": "$3 {{PLURAL:$3|revisió|revisions}} de $1 s'han fusionat amb èxit a [[:$2]].",
+       "mergehistory-done": "{{PLURAL:$3|S’ha|S’han}} fusionat correctament $3 {{PLURAL:$3|revisió|revisions}} de $1 a [[:$2]].",
        "mergehistory-fail": "No s'ha pogut realitzar la fusió de l'historial, comproveu la pàgina i els paràmetres horaris.",
        "mergehistory-fail-toobig": "No s'ha pogut realitzar la fusió de l'historial perquè es mourien més del limit de $1 {{PLURAL:$1|revisió|revisions}}.",
        "mergehistory-no-source": "La pàgina d'origen $1 no existeix.",
        "search-category": "(categoria $1)",
        "search-file-match": "(coincideix amb el contingut del fitxer)",
        "search-suggest": "Volíeu dir: $1",
+       "search-rewritten": "S’hi mostren els resultats de $1. Cerqueu «$2» en comptes d’aquest.",
        "search-interwiki-caption": "Projectes germans",
        "search-interwiki-default": "Resultats de $1:",
        "search-interwiki-more": "(més)",
        "prefs-watchlist-token": "Testimoni de llista de seguiment:",
        "prefs-misc": "Altres preferències",
        "prefs-resetpass": "Canvia la contrasenya",
-       "prefs-changeemail": "Canvia de correu electrònic",
+       "prefs-changeemail": "Canvia o elimina l’adreça electrònica",
        "prefs-setemail": "Indiqueu una adreça de correu electrònic",
        "prefs-email": "Opcions de correu electrònic",
        "prefs-rendering": "Aparença",
        "timezoneregion-europe": "Europa",
        "timezoneregion-indian": "Oceà Índic",
        "timezoneregion-pacific": "Oceà Pacífic",
-       "allowemail": "Permet que altres usuaris puguin enviar-me correus electrònics",
+       "allowemail": "Permet que altres usuaris puguin enviar-me missatges per correu electrònic",
        "prefs-searchoptions": "Cerca",
        "prefs-namespaces": "Espais de noms",
        "default": "per defecte",
        "rcshowhidemine": "$1 edicions pròpies",
        "rcshowhidemine-show": "Mostra",
        "rcshowhidemine-hide": "Amaga",
+       "rcshowhidecategorization": "$1 la categorització de la pàgina",
+       "rcshowhidecategorization-show": "Mostra",
+       "rcshowhidecategorization-hide": "Amaga",
        "rclinks": "Mostra els darrers $1 canvis en els darrers $2 dies<br />$3",
        "diff": "dif",
        "hist": "hist",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] i {{PLURAL:$2|una pàgina|$2 pàgines}} més afegides a la categoria",
        "recentchanges-page-removed-from-category": "[[:$1]] treta de la categoria",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] i {{PLURAL:$2|una pàgina|$2 pàgines}} més tretes de la categoria",
+       "autochange-username": "Canvi automàtic del MediaWiki",
        "upload": "Carregueu un fitxer",
        "uploadbtn": "Carrega un fitxer",
        "reuploaddesc": "Torna al formulari per apujar.",
        "php-uploaddisabledtext": "La càrrega de fitxer està desactivada al PHP. Comproveu les opcions del fitxer file_uploads.",
        "uploadscripted": "Aquest fitxer conté codi HTML o de seqüències que pot ser interpretat equivocadament per un navegador.",
        "upload-scripted-pi-callback": "No es poden carregar arxius que continguin instruccions de processament de pàgines d'estil XML",
+       "uploaded-script-svg": "S’ha trobat l’element programable «$1» al fitxer SVG carregat.",
+       "uploaded-hostile-svg": "S’ha trobat codi CSS no segur a l’element d’estil del fitxer SVG carregat.",
+       "uploaded-event-handler-on-svg": "No es permet establir els atributs de gestió d’events <code>$1=\"$2\"</code> als fitxers SVG.",
+       "uploaded-href-attribute-svg": "No es permeten els atributs d’«href» <code>&lt;$1 $2=\"$3\"&gt;</code> amb objectius no locals (p. ex., http:// i javascript:) als fitxers SVG.",
+       "uploaded-href-unsafe-target-svg": "S’ha trobat un element «href» amb un objectiu no segur <code>&lt;$1 $2=\"$3\"&gt;</code> al fitxer SVG carregat.",
        "uploadscriptednamespace": "Aquest fitxer SVG conté un espai de noms \"$1\" no autoritzat",
        "uploadinvalidxml": "No s'ha pogut analitzar l'XML del fitxer carregat.",
        "uploadvirus": "El fitxer conté un virus! Detalls: $1",
        "upload-form-label-infoform-description": "Descripció",
        "upload-form-label-usage-title": "Ús",
        "upload-form-label-usage-filename": "Nom del fitxer",
+       "foreign-structured-upload-form-label-own-work": "Això és el meu propi treball",
        "foreign-structured-upload-form-label-infoform-categories": "Categories",
        "foreign-structured-upload-form-label-infoform-date": "Data",
        "backend-fail-stream": "No s'ha pogut transmetre el fitxer $1.",
        "booksources-text": "A sota hi ha una llista d'enllaços d'altres llocs que venen llibres nous i de segona mà, i també podrien tenir més informació dels llibres que esteu cercant:",
        "booksources-invalid-isbn": "El codi ISBN donat no és vàlid. Comproveu si l'heu copiat correctament.",
        "specialloguserlabel": "Realitzador:",
-       "speciallogtitlelabel": "L'objectiu (títol o usuari):",
+       "speciallogtitlelabel": "Objectiu (títol o «{{ns:user}}:nom d’usuari» per a un usuari):",
        "log": "Registres",
        "all-logs-page": "Tots els registres públics",
        "alllogstext": "Presentació combinada de tots els registres disponibles de {{SITENAME}}.\nPodeu reduir l'extensió seleccionant el tipus de registre, el nom d'usuari realitzador (distingeix entre majúscules i minúscules), o la pàgina objectiu (també en distingeix).",
index 00281c2..daa185e 100644 (file)
        "category_header": "АгӀонаш категоречохь «$1»",
        "subcategories": "Бухаркатегореш",
        "category-media-header": "Файлаш оцу категори чохь «$1»",
-       "category-empty": "''ХӀара категори хӀинца йаьсса ю.''",
+       "category-empty": "''ХӀара категори хӀинца яьсса ю.''",
        "hidden-categories": "{{PLURAL:$1|1=Къайлаха категори|Къайлаха йолу категореш}}",
        "hidden-category-category": "Къайлаха йолу категореш",
        "category-subcat-count": "{{PLURAL:$2|ХӀокху категори чохь ю хӀокхуьнан бухара категори.|ХӀокху категори чохь ю $1 {{PLURAL:$1|бухара категори|бухара категореш}} $2 массо нах.}}",
        "databaseerror-query": "Дехар: $1",
        "databaseerror-function": "Функци: $1",
        "databaseerror-error": "ГӀалат: $1",
-       "laggedslavemode": "Тергам бе: агӀона чохь керла йаьхинарш ца хила мега.",
+       "laggedslavemode": "Тергам бе: агӀон чохь керла яьхнарш ца хила мега.",
        "readonly": "Блоктоьхна дӀайаздар хаамийн бухе",
        "enterlockreason": "Билгалде блоктохаран бахьана а, и чекх йолу хан а.",
        "readonlytext": "АгӀонаш тӀетохар а, кхин хийцамаш барна а блоктоьхна:\nБлокоьхначо биттина хаам: $1.",
        "translateinterface": "ХӀокху хааман гоч тӀетоха я хийца дехар до лелае локализацин сайт MediaWiki [//translatewiki.net/ translatewiki.net].",
        "cascadeprotected": "АгӀо хийцам ца байта ларйина ю {{PLURAL:$1|хӀокху агӀона|хӀокху агӀонийн}} юкъа йогӀуш хилар бахьнехь:\n$2",
        "namespaceprotected": "ХӀан бакъо яц анна цӀерш чохь тадарш да «$1».",
-       "customcssprotected": "Хьан бакъо яц хӀара CSS-агӀо тая, иза кхечу декъашхочун гӀерс болу дера.",
-       "customjsprotected": "Хьан бакъо яц хӀара JavaScript-агӀо тая, иза кхечу декъашхочун гӀерс болу дера.",
-       "mycustomcssprotected": "Хьан бакъо яц хӀара CSS агӀо тая.",
+       "customcssprotected": "Хьан бакъо яц хӀара CSS-агӀо таян, иза кхечу декъашхочун гӀирс болу дера.",
+       "customjsprotected": "Хьан бакъо яц хӀара JavaScript-агӀо таян, иза кхечу декъашхочун гӀирс болу дера.",
+       "mycustomcssprotected": "Хьан бакъо яц хӀара CSS агӀо таян.",
        "mycustomjsprotected": "Хьан бакъо яц JavaScript агӀо тая.",
        "myprivateinfoprotected": "Хьайн долара хаамна хийцам ба хьа йиш яц",
        "mypreferencesprotected": "Хьай гӀирс нисбан хьа бакъо яц.",
        "createaccountreason": "Бахьана:",
        "createacct-reason": "Бахьана",
        "createacct-reason-ph": "Стен кхуллуш ду ахьа керла декъашхочун дӀаяздар",
-       "createacct-captcha": "Кхерамзалла хьажар",
-       "createacct-imgcaptcha-ph": "ДӀаязде хьайна лакхахь гуш долу йоза",
        "createacct-submit": "Кхолла декъашхочун дӀаяздар",
        "createacct-another-submit": "Кхолла декъашхочун кхин дӀаяздар",
        "createacct-benefit-heading": "{{SITENAME}} кхолийна хьо санначу наха.",
        "explainconflict": "Ахьа хӀара агӀо тоечу хенахь, цхьам хийцамаш бина.\nЛакхарчу таяран корехь хьона гуш ду хӏинца агӏона чохь долу йоза.\nЛахарчу корехь ахь бина хийцам бу.\nХьой бина хийцам лахарчу корера лакхарчу коре баккха.\nКнопкан «{{int:savearticle}}» тӏетаӏича лакхара корера йоза дӏаязлурду.",
        "yourtext": "Хьан йоза",
        "storedversion": "Ӏалашйина верси",
-       "editingold": "'''ДӀахьедар. Ахьа таеш ю хӀокху агӀона шира елла верси.'''\nАгӀо Ӏалаш йинчул тӀехьа хьалхо бина хийцамаш дӀабяра бу.",
+       "editingold": "'''ДӀахьедар. Ахь таеш ю хӀокху агӀонан шира елла верси.'''\nАгӀо Ӏалаш йинчул тӀаьхьа, хьалхо бина хийцамаш дӀабевра бу.",
        "yourdiff": "Башхаллаш",
        "copyrightwarning": "Тергаме хьажа, массо яззаман чутухуш долу йозан хийцам хьажарехь бу, арахоьцуш санна оцу лицензи хьолехь.\nНагахь хьо лууш вацахь хьайн йозанаш маьрша даржа а кхечаьрга хийцам байта, мадаха уьш кху чу.<br />\nИштта чӀагӀо йой ахьа, айхьа далош долучуьнна хьо куьг да ву аьлла, я хьаэцна цхьан\nхьостера, хийцам ба а дӀаса даржада а чулацам болуш.<br />\n'''МА-КХИССА БАКЪО ЙОЦУ ГӀИРСАШ КХУ ЧУ, КУЬЙГАЛХОЧУЬННА БАКЪО ЛАРЪЕШ ЙОЛУ!'''",
        "readonlywarning": "'''ДӀАХЬЕДО. ГӀирса бух блоктоьхна оьшуш долучу хьаштан, цундера хӀинц хьоьга дӀа ца йазло хийцам.\nХила мега, ахьа Ӏалаш дан дезаш хьайн йоза, юха тӀаьхьо леладан иза йоза.'''\n\nКуьйгалхочо блоктоьхна гӀирса бух, цо битина кхетош хӀара хаам: $1",
        "protectedpagewarning": "'''ДӀахьедар. ХӀара агӀо ларйина ю хийцам цабайта, иза хийца я нисъян а бакъо йолуш куьйгалла лелош болу декъашхой бе бац.'''\nЛахахьа гойту хаамаш тӀаьххьара бина болу хийцамна тептар чура:",
        "semiprotectedpagewarning": "'''ДӀахьедо.''' ХӀара агӀо ларйина ю; дӀабазбиначу декъашхошка бе цӀе хийцалуш яц.\nЛахахьа тептаро балийна тӀаьххьаралера дӀаязбина хаам:",
-       "cascadeprotectedwarning": "<strong>ДӀахьедар:</strong> ХӀара агӀо таян бакъо йолуш [[Project:Куьйгалхой|куьйгалхой]] бу, хӀунда аьлча {{PLURAL:$1|1=и агӀо латийна кхечу агӀонашца|и агӀо латийна кхечу агӀонашца}} хӀоттделлачу ларйиначух:",
+       "cascadeprotectedwarning": "<strong>ДӀахьедар:</strong> ХӀара агӀо таян бакъо йолуш беккъа [[Project:Куьйгалхой|куьйгалхой]] бу, хӀунда аьлча {{PLURAL:$1|1=хӀара агӀо кхечу агӀонашца латийна ю|хӀара агӀо кхечу агӀонашца латийна ю}} ларъяш ю:",
        "templatesused": "{{PLURAL:$1|1=Кеп, лелош ю|Кепаш, лелош ю}} хӀокху агӀон башхонца:",
        "templatesusedpreview": "{{PLURAL:$1|1=Кеп, лелошдолу|Кепаш, лелойлу}} оцу хьалх хьожучу агӀонца:",
        "templatesusedsection": "ХӀокху декъан чохь {{PLURAL:$1|1=лелош йолу кеп|лелош йолу кепаш}}:",
        "permissionserrors": "ТӀекхачаре бакъона гӀалат",
        "permissionserrorstext": "Хьан бакъо яц кхочуш хилийта хийцам оцу {{PLURAL:$1|1=шолгlа бахьанца|шолгlа бахьанашца}}:",
        "permissionserrorstext-withaction": "Хьан бакъо яц хlумда «'''$2'''» оцу {{PLURAL:$1|1=шолгlа бахьанца|шолгlа бахьанашца}}:",
+       "contentmodelediterror": "Хьуна хӀара верси таян цало, цуна чохь <code>$1</code> модель хилар бахьнехь, ткъа агӀонан чура карара модель — <code>$2</code> ю.",
        "recreate-moveddeleted-warn": "'''Тидам бе. Ахьа кхуллуш ю, хьалхо дӀаяьккхина йолу агӀо.'''\n\nХьажа, билгалла оьши хьуна хӀара агӀо юха кхолла.\nЛахахь далина ду дӀаяхарш тӀяхь долу тептарш а хӀокх агӀона цӀе хийцарш а.",
        "moveddeleted-notice": "ХӀара агӀо дӀаяьккхина яра.\nЛахахьа гойту хӀара дӀаккхарш а, цӀе хийцарш а.",
        "log-fulllog": "Хьажа деригге тептаре",
        "prefs-help-recentchangescount": "Гойту керла нисдарш, агӀонийн истори, тептарш.",
        "prefs-help-watchlist-token2": "Иза хьан тергаме могӀан къайла догӀа ду.\nМуьлха и хуучунна йиш ю хьан тергаме могӀам беша, цундела ма хаийта иза кхечаьрга. [[Special:ResetTokens|ТӀетаӀа йе кхуза и хьайга кхосса лууш делахь]].",
        "savedprefs": "Хьан гӀирс Ӏалашбина.",
+       "savedrights": "{{GENDER:$1|$1}} декъашхочун бакъонаш Ӏалашйина.",
        "timezonelegend": "Сахьтан аса:",
        "localtime": "Меттигера хан:",
        "timezoneuseserverdefault": "Серверан ($1) гӀирс лелабе",
        "noautoblockblock": "ша блоктухарг дӏаяйина",
        "createaccountblock": "цамагдо керла дӀаяздарш кхоллар",
        "emailblock": "цамаго кехаташ кхехӀита",
-       "blocklist-nousertalk": "Ñ\88ин Ð´Ð¸Ð¹Ñ\86аÑ\80е Ð°Ð³Ó\80о Ñ\82аÑ\8fн Ð»Ñ\83Ñ\88 Ð´Ð°ц",
+       "blocklist-nousertalk": "Ñ\88ен Ð´Ð¸Ð¹Ñ\86аÑ\80е Ð°Ð³Ó\80о Ñ\82аÑ\8fлÑ\83Ñ\88 Ñ\8fц",
        "ipblocklist-empty": "Блоктохаран могӀам баьсса бу.",
        "ipblocklist-no-results": "И адрес блоктоьхна дац.",
        "blocklink": "блоктоха",
        "block-log-flags-nocreate": "цамагдо керла дӏаяздарш кхоллар",
        "block-log-flags-noautoblock": "ша блоктухарг дӏаяйина",
        "block-log-flags-noemail": "цамаго кехаташ кхехӀита",
-       "block-log-flags-nousertalk": "Ñ\88ин Ð´Ð¸Ð¹Ñ\86аÑ\80е Ð°Ð³Ó\80о Ñ\82аÑ\8fн Ð»Ñ\83Ñ\88 Ð´Ð°ц",
+       "block-log-flags-nousertalk": "Ñ\88ен Ð´Ð¸Ð¹Ñ\86аÑ\80е Ð°Ð³Ó\80о Ñ\82аÑ\8fлÑ\83Ñ\88 Ñ\8fц",
        "block-log-flags-angry-autoblock": "латина шуьйра автоблоктохар",
        "block-log-flags-hiddenname": "декъашхочун цӀе къайлаяьккхина",
        "range_block_disabled": "Куьйгалхошна диапазонашна блоктоха цамага до.",
        "imageinvalidfilename": "Файлан цӀе гӀалате ю",
        "fix-double-redirects": "Хьалхара цӀе йолу дӀасахьажорг нисъян",
        "move-leave-redirect": "Ӏадйита дӀасахьажораг",
-       "protectedpagemovewarning": "'''ДӀахьедар.''' ХӀара агӀо ларйина ю; цӀе хийца я нисъян а бакъо йолуш куьйгалхой бе бац.\nЛахахьа тептаро балийна тӀаьхьаралера дӀаязбина хаам:",
+       "protectedpagemovewarning": "'''ДӀахьедар.''' ХӀара агӀо ларйина ю; цӀе хийца я нисъян а бакъо йолуш куьйгалхой бен бац.\nЛахахь тептаро балийна тӀаьхьаралера дӀаязбина хаам:",
        "semiprotectedpagemovewarning": "'''ДӀахьедо.''' ХӀара агӀо ларйина ю; дӀабазбиначу декъашхошка бе цӀе хийцалуш яц.\nЛахахьа тептаро балийна тӀаьххьаралера дӀаязбина хаам:",
        "move-over-sharedrepo": "== Файл йолуш ю ==\nВикигулам чохь йолуш ю [[:$1]]. ХӀокху файлан цӀе хийцича Викигулам чуьраниг дӀакъовлу.",
        "export": "АгӀонаш араяхар",
        "tooltip-ca-nstab-main": "Яззамна чулацам",
        "tooltip-ca-nstab-user": "ХӀора декъашхочун долахь йолу агӀо ю",
        "tooltip-ca-nstab-media": "Медиа-файл",
-       "tooltip-ca-nstab-special": "ХӀара белхан агӀо ю, хӀара тая луш яц",
+       "tooltip-ca-nstab-special": "ХӀара белхан агӀо ю, хӀара таялуш яц",
        "tooltip-ca-nstab-project": "Кхолламан дакъа",
        "tooltip-ca-nstab-image": "Файлан агӀо",
        "tooltip-ca-nstab-mediawiki": "Хааман агlо MediaWiki",
index 987e08e..845338e 100644 (file)
        "createaccountreason": "Důvod:",
        "createacct-reason": "Důvod",
        "createacct-reason-ph": "Proč si vytváříte další účet",
-       "createacct-captcha": "Bezpečnostní kontrola",
-       "createacct-imgcaptcha-ph": "Opište výše zobrazený text",
        "createacct-submit": "Vytvořit účet",
        "createacct-another-submit": "Vytvořit účet",
        "createacct-benefit-heading": "{{grammar:4sg|{{SITENAME}}}} tvoří lidé jako vy.",
index 8e935b7..2224b7c 100644 (file)
        "logentry-delete-delete": "$1 {{GENDER:$2|поничьжилъ|поничьжила}} страницѫ ⁖ $3 ⁖",
        "logentry-move-move": "$1 {{GENDER:$2|нарєчє}} страницѫ ⁖ $3 ⁖ имєньмь ⁖ $4 ⁖",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|нарєчє}} страницѫ ⁖ $3 ⁖ имєньмь ⁖ $4 ⁖ бєꙁ прѣнаправлєниꙗ сътворѥниꙗ",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|нарєчє}} страницѧ ⁖ $3 ⁖ имєньмь ⁖ $4 ⁖ врьхоу прѣнаправлѥниꙗ",
        "logentry-newusers-create": "польꙃєватєльско мѣсто ⁖ $1 ⁖ {{GENDER:$2|сътворѥно}} ѥстъ",
        "logentry-upload-upload": "$1 {{GENDER:$2|положишє}} $3",
        "revdelete-summary": "мѣнꙑ опьсаниѥ",
index 2e1beb6..edb56e8 100644 (file)
@@ -52,7 +52,8 @@
                        "Macofe",
                        "Jyllanj",
                        "Matma Rex",
-                       "Poullindholm"
+                       "Poullindholm",
+                       "Mads Haupt"
                ]
        },
        "tog-underline": "Understreg henvisninger:",
        "createaccountreason": "Begrundelse:",
        "createacct-reason": "Årsag",
        "createacct-reason-ph": "Hvorfor du vil oprette endnu en konto",
-       "createacct-captcha": "Sikkerhedskontrol",
-       "createacct-imgcaptcha-ph": "Indtast venligst ovenstående tekst",
        "createacct-submit": "Opret din konto",
        "createacct-another-submit": "Opret konto",
        "createacct-benefit-heading": "{{SITENAME}} laves af mennesker som dig.",
        "prefs-watchlist-token": "Overvågningslistenøgle:",
        "prefs-misc": "Forskelligt",
        "prefs-resetpass": "Skift adgangskode",
-       "prefs-changeemail": "Ændre e-mailadresse",
+       "prefs-changeemail": "Skift eller fjern e-mailadresse",
        "prefs-setemail": "Angiv en e-mailadresse",
        "prefs-email": "Indstillinger for e-mail",
        "prefs-rendering": "Udseende",
        "upload-too-many-redirects": "URL'en indeholdt for mange omdirigeringer",
        "upload-http-error": "Der opstod en HTTP-fejl: $1",
        "upload-copy-upload-invalid-domain": "Uploads af kopier er ikke tilgængelig fra dette domæne.",
+       "upload-dialog-title": "Læg en fil op",
+       "upload-dialog-button-cancel": "Annuller",
+       "upload-dialog-button-done": "Færdig",
+       "upload-dialog-button-save": "Gem",
+       "upload-dialog-button-upload": "Læg op",
+       "upload-form-label-select-file": "Vælg filer",
+       "upload-form-label-infoform-name": "Navn",
+       "upload-form-label-infoform-description": "Beskrivelse",
+       "upload-form-label-usage-filename": "Filnavn",
+       "foreign-structured-upload-form-label-infoform-categories": "Kategorier",
+       "foreign-structured-upload-form-label-infoform-date": "Dato",
+       "foreign-structured-upload-form-label-own-work-message-local": "Jeg bekræfter at jeg uploader filen i overenstemmelse med betingelser for brug og licenseringspoltikken på {{SITENAME}}.",
        "backend-fail-stream": "Kunne ikke streame filen $1.",
        "backend-fail-backup": "Kunne ikke lave sikkerhedskopi af filen $1.",
        "backend-fail-notexists": "Filen $1 findes ikke.",
index 24f1eed..4414b1b 100644 (file)
        "createaccountreason": "Grund:",
        "createacct-reason": "Begründung",
        "createacct-reason-ph": "Warum erstellst du ein anderes Benutzerkonto?",
-       "createacct-captcha": "Sicherheitsprüfung",
-       "createacct-imgcaptcha-ph": "Gib den Text ein, den du oben siehst.",
        "createacct-submit": "Benutzerkonto erstellen",
        "createacct-another-submit": "Benutzerkonto erstellen",
        "createacct-benefit-heading": "{{SITENAME}} wird von Menschen wie dir geschaffen.",
index 781db89..bc2d65a 100644 (file)
        "createaccountreason": "Sebeb:",
        "createacct-reason": "Sebeb",
        "createacct-reason-ph": "Şımaye çı xo re zewbi hesab vırazeni?",
-       "createacct-captcha": "Qontrolê asayişi",
-       "createacct-imgcaptcha-ph": "Nuşteyo ke cor aseno ey cı ke",
        "createacct-submit": "Hesabê xo vıraze",
        "createacct-another-submit": "Zewbi hesab vıraz",
        "createacct-benefit-heading": "{{SITENAME}} meş de merduman şi",
index d8efcde..41bea49 100644 (file)
@@ -50,6 +50,7 @@
        "tog-hideminor": "Απόκρυψη μικροεπεξεργασιών στις πρόσφατες αλλαγές",
        "tog-hidepatrolled": "Απόκρυψη ελεγμένων επεξεργασιών στις πρόσφατες αλλαγές",
        "tog-newpageshidepatrolled": "Απόκρυψη ελεγμένων σελίδων από τον κατάλογο νέων σελίδων",
+       "tog-hidecategorization": "Απόκρυψη κατηγοριοποίησης σελίδων",
        "tog-extendwatchlist": "Επέκταση της λίστας παρακολούθησης ώστε να δείχνει όλες τις αλλαγές, όχι μόνο τις πιο πρόσφατες",
        "tog-usenewrc": "Ομαδοποίηση αλλαγών ανά σελίδα στις πρόσφατες αλλαγές και στη λίστα παρακολούθησης",
        "tog-numberheadings": "Αυτόματη αρίθμηση επικεφαλίδων",
@@ -79,6 +80,7 @@
        "tog-watchlisthideliu": "Απόκρυψη επεξεργασιών συνδεδεμένων χρηστών από τη λίστα παρακολούθησης",
        "tog-watchlisthideanons": "Απόκρυψη επεξεργασιών ανωνύμων χρηστών από τη λίστα παρακολούθησης",
        "tog-watchlisthidepatrolled": "Απόκρυψη ελεγμένων επεξεργασιών από τη λίστα παρακολούθησης",
+       "tog-watchlisthidecategorization": "Απόκρυψη κατηγοριοποίησης σελίδων",
        "tog-ccmeonemails": "Να μου αποστέλλονται αντίγραφα των μηνυμάτων ηλεκτρονικού ταχυδρομείου που στέλνω σε άλλους χρήστες",
        "tog-diffonly": "Να μην εμφανίζεται περιεχόμενο σελίδων κάτω από τις διαφορές των εκδόσεων",
        "tog-showhiddencats": "Εμφάνιση κρυμμένων κατηγοριών",
        "nstab-help": "Σελίδα βοήθειας",
        "nstab-category": "Κατηγορία",
        "mainpage-nstab": "Αρχική σελίδα",
-       "nosuchaction": "Δεν υπάρχει τέτοια ενέργεια.",
+       "nosuchaction": "Δεν υπάρχει τέτοια ενέργεια",
        "nosuchactiontext": "Η ενέργεια που καθορίστηκε από την διεύθυνση URL δεν είναι έγκυρη.\nΕνδέχεται να πληκτρολογήσατε λανθασμένα την διεύθυνση URL ή να ακολουθήσατε έναν μη έγκυρο σύνδεσμο.\nΜπορεί επίσης να είναι σημάδι κάποιου σφάλματος του λογισμικού που χρησιμοποιεί ο ιστότοπος {{SITENAME}}.",
        "nosuchspecialpage": "Δεν υπάρχει τέτοια ειδική σελίδα",
        "nospecialpagetext": "<strong>Ζητήσατε μια μη έγκυρη ειδική σελίδα.</strong>\n\nΈνας κατάλογος έγκυρων ειδικών σελίδων μπορεί να βρεθεί στη σελίδα [[Special:SpecialPages|{{int:specialpages}}]].",
        "mycustomjsprotected": "Δεν έχετε άδεια για να επεξεργαστείτε αυτήν τη σελίδα JavaScript.",
        "myprivateinfoprotected": "Δεν έχετε άδεια για να επεξεργαστείτε τα προσωπικά σας στοιχεία.",
        "mypreferencesprotected": "Δεν έχετε άδεια για να επεξεργαστείτε τις προτιμήσεις σας.",
-       "ns-specialprotected": "ΣελίδεÏ\82 Ï\83Ï\84ον Ï\84ομέα {{ns:special}} Î´ÎµÎ½ Î³Î¯Î½ÎµÏ\84αι Î½Î± ÎµÏ\80εξεÏ\81γαÏ\83Ï\84οÏ\8dν.",
+       "ns-specialprotected": "Î\97 ÎµÏ\80εξεÏ\81γαÏ\83ία Ï\83ελίδÏ\89ν Ï\83Ï\84ον Ï\84ομέα {{ns:special}} Î´ÎµÎ½ ÎµÎ¯Î½Î±Î¹ Î´Ï\85ναÏ\84ή.",
        "titleprotected": "Αυτός ο τίτλος έχει προστατευθεί από την δημιουργία από τον [[User:$1|$1]].\nΟ λόγος που δίνεται είναι «$2».",
        "filereadonlyerror": "Δεν είναι δυνατή η τροποποίηση του αρχείου «$1» επειδή το αποθετήριο αρχείων «$2» είναι σε κατάσταση λειτουργίας μόνο για ανάγνωση.\n\nΟ διαχειριστής που το κλείδωσε προσφέρει αυτή την αιτιολόγηση: «$3».",
        "invalidtitle-knownnamespace": "Μη έγκυρος τίτλος με χώρο ονομάτων «$2» και κείμενο «$3»",
        "invalidtitle-unknownnamespace": "Μη έγκυρος τίτλος με άγνωστο αριθμό χώρου ονομάτων $1 και κείμενο «$2»",
-       "exception-nologin": "Δεν έχετε συνδεθεί.",
+       "exception-nologin": "Δεν έχετε συνδεθεί",
        "exception-nologin-text": "Παρακαλούμε να συνδεθείτε για να μπορείτε να αποκτήσετε πρόσβαση σε αυτήν τη σελίδα ή την ενέργεια.",
        "exception-nologin-text-manual": "Παρακαλούμε $1 για να μπορείτε να έχετε πρόσβαση σε αυτή τη σελίδα ή ενέργεια.",
        "virus-badscanner": "Λάθος ρύθμιση: άγνωστος ανιχνευτής ιών: ''$1''",
        "userloginnocreate": "Είσοδος",
        "logout": "Έξοδος",
        "userlogout": "Έξοδος",
-       "notloggedin": "Δεν έχετε συνδεθεί.",
+       "notloggedin": "Δεν έχετε συνδεθεί",
        "userlogin-noaccount": "Δεν έχετε λογαριασμό;",
        "userlogin-joinproject": "Συνδεθείτε σε {{SITENAME}}",
        "nologin": "Δεν έχετε λογαριασμό; $1.",
        "userlogin-resetpassword-link": "Ξεχάσατε τον κωδικό πρόσβασης;",
        "userlogin-helplink2": "Βοήθεια για τη σύνδεση",
        "userlogin-loggedin": "Είστε ήδη {{GENDER:$1|συνδεδεμένος|συνδεδεμένη}} ως $1.\nΧρησιμοποιήστε την παρακάτω φόρμα για να συνδεθείτε ως άλλος χρήστης.",
-       "userlogin-createanother": "Δημιουργήσετε έναν άλλο λογαριασμό",
+       "userlogin-createanother": "Δημιουργήστε άλλο λογαριασμό",
        "createacct-emailrequired": "Διεύθυνση ηλεκτρονικού ταχυδρομείου",
        "createacct-emailoptional": "Διεύθυνση ηλεκτρονικού ταχυδρομείου (προαιρετικό)",
        "createacct-email-ph": "Εισάγετε το email σας",
        "createaccountreason": "Αιτία:",
        "createacct-reason": "Λόγος",
        "createacct-reason-ph": "Γιατί δημιουργείτε έναν άλλο λογαριασμό",
-       "createacct-captcha": "Έλεγχος ασφαλείας",
-       "createacct-imgcaptcha-ph": "Εισαγωγή του κειμένου που βλέπετε παραπάνω",
        "createacct-submit": "Δημιουργία λογαριασμού χρήστη",
        "createacct-another-submit": "Δημιουργήσετε έναν άλλο λογαριασμό",
        "createacct-benefit-heading": "{{SITENAME}} έχει γίνει από ανθρώπους όπως εσύ.",
        "throttled-mailpassword": "Ένα email επαναφοράς κωδικού έχει ήδη αποσταλεί, μέσα {{PLURAL:$1|στην τελευταία ώρα|στις τελευταίες $1 ώρες}}.\nΓια την αποφυγή κατάχρησης, μόνο ένα email επαναφοράς κωδικού θα στέλνεται ανά {{PLURAL:$1|ώρα|$1 ώρες}}.",
        "mailerror": "Σφάλμα στην αποστολή του μηνύματος: $1",
        "acct_creation_throttle_hit": "Επισκέπτες αυτού του wiki με την διεύθυνση IP σας έχουν ήδη δημιουργήσει {{PLURAL:$1|ένα λογαριασμό|$1 λογαριασμούς}}, κατά την τελευταία μία ημέρα, που είναι και ο μέγιστος επιτρεπόμενος αριθμός.\nΩς αποτέλεσμα, επισκέπτες αυτού του wiki με αυτήν την διεύθυνση IP δεν μπορούν αυτή την στιγμή να δημιουργήσουν περισσότερους λογαριασμούς.",
-       "emailauthenticated": "Î\97 Î·Î»ÎµÎºÏ\84Ï\81ονική Ï\83αÏ\82 Î´Î¹ÎµÏ\8dθÏ\85νÏ\83η ÎµÏ\80ιβεβαιÏ\8eθηκε Ï\83Ï\84ιÏ\82 $2, Ï\83Ï\84ιÏ\82 $3.",
+       "emailauthenticated": "Î\97 Î´Î¹ÎµÏ\8dθÏ\85νÏ\83ή Ï\83αÏ\82 Î·Î»ÎµÎºÏ\84Ï\81ονικοÏ\8d Ï\84αÏ\87Ï\85δÏ\81ομείοÏ\85 ÎµÏ\80ιβεβαιÏ\8eθηκε Ï\83Ï\84ιÏ\82 $2 ÎºÎ±Î¹ Ï\8eÏ\81α $3.",
        "emailnotauthenticated": "Η ηλεκτρονική σας διεύθυνση δεν έχει επαληθευτεί ακόμα.\nΚανένα μήνυμα ηλεκτρονικού ταχυδρομείου δεν θα σταλεί για τις ακόλουθες λειτουργίες.",
        "noemailprefs": "Δεν έχει ορισθεί ηλεκτρονική διεύθυνση, οι λειτουργίες που ακολουθούν δεν θα είναι δυνατόν να ολοκληρωθούν.",
        "emailconfirmlink": "Επαληθεύστε την ηλεκτρονική σας διεύθυνση",
        "pt-login": "Σύνδεση",
        "pt-login-button": "Σύνδεση",
        "pt-createaccount": "Δημιουργία λογαριασμού",
-       "pt-userlogout": "Αποσύνδεση;",
+       "pt-userlogout": "Αποσύνδεση",
        "php-mail-error-unknown": "Άγνωστο σφάλμα στη συνάρτηση mail() της PHP.",
        "user-mail-no-addy": "Προσπαθήσατε να στείλετε e-mail χωρίς μια διεύθυνση e-mail.",
        "user-mail-no-body": "Προσπάθησε να στείλει e-mail με ένα κενό ή αδικαιολόγητα σύντομο σώμα.",
        "resetpass-submit-cancel": "Ακύρωση",
        "resetpass-wrong-oldpass": "Λάθος προσωρινός ή κανονικός κωδικός.\nΜπορεί να έχετε ήδη αλλάξει επιτυχώς τον κωδικό σας ή να έχετε ζητήσει έναν νέο προσωρινό κωδικό.",
        "resetpass-recycled": "Παρακαλούμε επαναφέρετε τον κωδικό πρόσβασής σας σε κάτι διαφορετικό από τον τρέχοντα κωδικό πρόσβασης.",
-       "resetpass-temp-emailed": "Έχετε συνδεθεί με ένα προσωρινό κωδικό μέσω ηλεκτρονικού ταχυδρομείου.\nΓια να ολοκληρώσετε τη σύνδεση, πρέπει να ορίσετε ένα νέο κωδικό εδώ:",
+       "resetpass-temp-emailed": "Έχετε συνδεθεί με έναν προσωρινό κωδικό μέσω ηλεκτρονικού ταχυδρομείου.\nΓια να ολοκληρώσετε τη σύνδεση, πρέπει να ορίσετε έναν νέο κωδικό εδώ:",
        "resetpass-temp-password": "Προσωρινός κωδικός:",
        "resetpass-abort-generic": "Η αλλαγή του κωδικού έχει απορριφθεί από μια προέκταση.",
-       "resetpass-expired": "Τον ÎºÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ήÏ\82 Ï\83αÏ\82 Î­Ï\87ει Î»Î®Î¾ÎµÎ¹. Î\9fÏ\81ίÏ\83Ï\84ε Î­Î½Î± νέο κωδικό πρόσβασης για να συνδεθείτε.",
-       "resetpass-expired-soft": "Î\9f ÎºÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ήÏ\82 Ï\83αÏ\82 Î­Ï\87ει Î»Î®Î¾ÎµÎ¹ ÎºÎ±Î¹ Ï\80Ï\81έÏ\80ει Î½Î± Î³Î¯Î½ÎµÎ¹ ÎµÏ\80αναÏ\86οÏ\81ά Ï\84οÏ\85. Î Î±Ï\81ακαλοÏ\8dμε Î³Ï\81άÏ\88Ï\84ε Î­Î½Î± Î½Î­Î¿ ÎºÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Î® ÎºÎ¬Î½Ï\84ε ÎºÎ»Î¹Îº Ï\83Ï\84ο \"{{int:resetpass-submit-cancel}}\" για τον επαναφέρετε αργότερα.",
-       "resetpass-validity-soft": "Τον ÎºÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ήÏ\82 Ï\83αÏ\82 Î´ÎµÎ½ ÎµÎ¯Î½Î±Î¹ Î­Î³ÎºÏ\85Ï\81οÏ\82: $1 \nΠαÏ\81ακαλÏ\8e ÎµÏ\80ιλέξÏ\84ε Î­Î½Î± Î½Î­Î¿ ÎºÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Ï\84Ï\8eÏ\81α, Î® ÎºÎ¬Î½Ï\84ε ÎºÎ»Î¹Îº Ï\83Ï\84ο ÎºÎ¿Ï\85μÏ\80ί \"{{int:resetpass-submit-cancel}}\" Î³Î¹Î± Î½Î± Ï\84ο επαναφέρετε αργότερα.",
-       "passwordreset": "Î\9aÏ\89δικÏ\8cÏ\82 ÎµÏ\80αναÏ\86οÏ\81άÏ\82",
+       "resetpass-expired": "Î\9f ÎºÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Î­Ï\87ει Î»Î®Î¾ÎµÎ¹. Î\9fÏ\81ίÏ\83Ï\84ε Î­Î½Î±Î½ νέο κωδικό πρόσβασης για να συνδεθείτε.",
+       "resetpass-expired-soft": "Î\9f ÎºÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Î­Ï\87ει Î»Î®Î¾ÎµÎ¹ ÎºÎ±Î¹ Ï\80Ï\81έÏ\80ει Î½Î± Î³Î¯Î½ÎµÎ¹ ÎµÏ\80αναÏ\86οÏ\81ά Ï\84οÏ\85. Î\95Ï\80ιλέξÏ\84ε Î­Î½Î±Î½ Î½Î­Î¿ ÎºÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Ï\84Ï\8eÏ\81α Î® Ï\80αÏ\84ήÏ\83Ï\84ε Â«{{int:resetpass-submit-cancel}}» για τον επαναφέρετε αργότερα.",
+       "resetpass-validity-soft": "Î\9f ÎºÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Î´ÎµÎ½ ÎµÎ¯Î½Î±Î¹ Î­Î³ÎºÏ\85Ï\81οÏ\82: $1\n\nÎ\95Ï\80ιλέξÏ\84ε Î­Î½Î±Î½ Î½Î­Î¿ ÎºÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Ï\84Ï\8eÏ\81α Î® Ï\80αÏ\84ήÏ\83Ï\84ε Â«{{int:resetpass-submit-cancel}}» Î³Î¹Î± Î½Î± Ï\84ον επαναφέρετε αργότερα.",
+       "passwordreset": "Î\95Ï\80αναÏ\86οÏ\81ά ÎºÏ\89δικοÏ\8d",
        "passwordreset-text-one": "Συμπληρώστε την παρακάτω φόρμα για να επαναφέρετε τον κωδικό πρόσβασής σας.",
        "passwordreset-text-many": "{{PLURAL:$1|Συμπληρώστε ένα από τα πεδία για να λάβετε προσωρινό κωδικό πρόσβαση μέσω ηλεκτρονικού ταχυδρομείου.}}",
        "passwordreset-disabled": "Η επαναφορά κωδικού πρόσβασης έχει απενεργοποιηθεί σε αυτό το wiki",
        "passwordreset-emailsent-capture": "Έχει αποσταλεί email επαναφοράς κωδικού, το οποίο φαίνεται πιο κάτω.",
        "passwordreset-emailerror-capture": "Ένα email επαναφοράς κωδικού έχει δημιουργηθεί, το οποίο φαίνεται πιο κάτω, αλλά απέτυχε η αποστολή του στο  {{GENDER:$2|χρήστη}}: $1",
        "changeemail": "Αλλαγή ή αφαίρεση της διεύθυνσης ηλεκτρονικού ταχυδρομείου",
-       "changeemail-header": "Αλλαγή λογαριασμού ηλεκτρονικού ταχυδρομείου",
+       "changeemail-header": "Συμπληρώστε αυτήν τη φόρμα για να αλλάξετε τη διεύθυνσή σας ηλεκτρονικού ταχυδρομείου. Αν θέλετε να καταργήσετε τη σύνδεση οποιασδήποτε διεύθυνσης ηλεκτρονικού ταχυδρομείου με το λογαριασμό σας, αφήστε τη νέα διεύθυνση ηλεκτρονικού ταχυδρομείου κενή κατά την υποβολή της φόρμας.",
+       "changeemail-passwordrequired": "Θα χρειαστεί να εισάγετε τον κωδικό σας για να επιβεβαιώσετε την αλλαγή αυτή.",
        "changeemail-no-info": "Πρέπει να έχετε συνδεθεί για άμεση πρόσβαση σε αυτήν τη σελίδα.",
        "changeemail-oldemail": "Τρέχουσα διεύθυνση ηλεκτρονικού ταχυδρομείου:",
        "changeemail-newemail": "Νέα διεύθυνση ηλεκτρονικού ταχυδρομείου:",
+       "changeemail-newemail-help": "Αυτό το πεδίο θα πρέπει να μείνει κενό αν θέλετε να καταργήσετε τη διεύθυνσή σας ηλεκτρονικού ταχυδρομείου. Αν η διεύθυνση ηλεκτρονικού ταχυδρομείου καταργηθεί, δεν θα μπορείτε να επαναφέρετε τον κωδικό πρόσβασης σε περίπτωση που τον ξεχάσετε και δεν θα λαμβάνετε μηνύματα ηλεκτρονικού ταχυδρομείου από αυτό το wiki.",
        "changeemail-none": "(κανένα)",
        "changeemail-password": "Ο κωδικός πρόσβασής σας στο εγχείρημα {{SITENAME}}:",
        "changeemail-submit": "Αλλαγή διεύθυνσης ηλεκτρονικού ταχυδρομείου",
        "changeemail-throttled": "Κάνατε πάρα πολλές απόπειρες σύνδεσης.\nΠαρακαλούμε περιμένετε $1 προτού ξαναδοκιμάσετε.",
+       "changeemail-nochange": "Παρακαλώ εισάγετε μια διαφορετική νέα διεύθυνση ηλεκτρονικού ταχυδρομείου.",
        "resettokens": "Επαναφορά των κλειδιών",
        "resettokens-text": "Μπορείτε να επαναφέρετε τα κλειδιά, τα οποία επιτρέπουν την πρόσβαση σε ορισμένα ιδιωτικά δεδομένα που συνδέονται με τον λογαριασμό σας εδώ.\n\nΠρέπει να το κάνετε εάν κατά λάθος τα μοιραστήκατε με κάποιον ή αν ο λογαριασμός σας έχει παραβιαστεί.",
        "resettokens-no-tokens": "Δεν υπάρχουν κλειδιά για επαναφορά.",
        "sig_tip": "Η υπογραφή σας με ώρα και ημερομηνία",
        "hr_tip": "Οριζόντια γραμμή (να χρησιμοποιείται με φειδώ)",
        "summary": "Σύνοψη:",
-       "subject": "Θέμα/επικεφαλίδα:",
+       "subject": "Θέμα:",
        "minoredit": "Αυτή είναι μια μικροαλλαγή",
        "watchthis": "Παρακολούθηση αυτής της σελίδας",
        "savearticle": "Αποθήκευση σελίδας",
        "missingsummary": "'''Υπενθύμιση:''' Δεν έχετε συμπληρώσει τη σύνοψη επεξεργασίας. Αν κάνετε κλικ στο κουμπί Αποθήκευση πάλι, η επεξεργασία σας θα αποθηκευτεί χωρίς σύνοψη.",
        "selfredirect": "<strong>Προσοχή:</strong> Ανακατευθύνετε αυτή τη σελίδα στον εαυτό της. Μπορεί να δώσατε λάθος στόχο για την ανακατεύθυνση, ή μπορεί να επεξεργάζεστε λάθος σελίδα.\nΑν κάνε κλίκ στο \"{{int:savearticle}}\" ξανά, η ανακατεύθυνση θα δημιουργηθεί ούτως ή άλλως.",
        "missingcommenttext": "Παρακαλώ εισάγετε ένα σχόλιο παρακάτω.",
-       "missingcommentheader": "'''Υπενθύμιση:''' Δεν έχετε γράψει ένα θέμα/επικεφαλίδα για αυτό το σχόλιο.\nΑν κάνετε πάλι κλικ στο κουμπί \"{{int:savearticle}}\", η επεξεργασία σας θα αποθηκευτεί χωρίς θέμα ή επικεφαλίδα.",
+       "missingcommentheader": "<strong>Υπενθύμιση:</strong> δεν έχετε δώσει ένα θέμα γι' αυτό το σχόλιο.\nΕάν κάνετε κλικ στο κουμπί \"{{int:savearticle}}\" ξανά, η επεξεργασία σας θα αποθηκευτεί χωρίς αυτό.",
        "summary-preview": "Προεπισκόπηση σύνοψης:",
-       "subject-preview": "Προεπισκόπηση θέματος/επικεφαλίδας:",
+       "subject-preview": "Προεπισκόπηση θέματος:",
        "previewerrortext": "Παρουσιάστηκε σφάλμα κατά την προσπάθεια για να κάνετε προεπισκόπηση των αλλαγών σας.",
        "blockedtitle": "Ο χρήστης έχει υποστεί φραγή.",
        "blockedtext": "'''Το όνομα χρήστη σας ή η διεύθυνση IP σας έχει υποστεί φραγή.'''\n\nΗ φραγή έγινε από τον/την $1.\nΗ αιτιολογία που δόθηκε είναι: ''$2''.\n\n* Έναρξη φραγής: $8\n* Λήξη φραγής: $6\n* Η φραγή προορίζεται για το χρήστη: $7\n\nΜπορείτε να απευθυνθείτε στον/στην $1 ή σε κάποιον άλλον [[{{MediaWiki:Grouppage-sysop}}|διαχειριστή]] για να συζητήσετε τη φραγή.\nΔεν μπορείτε να χρησιμοποιήσετε την δυνατότητα «αποστολή e-mail σε αυτό το χρήστη» εκτός αν μια έγκυρη ηλεκτρονική διεύθυνση έχει οριστεί στις [[Special:Preferences|προτιμήσεις χρήστη]] σας.\nΗ τρέχουσα διεύθυνση IP σας είναι $3, και ο αριθμός αναγνώρισης της φραγής είναι #$5.\nΠαρακαλούμε περιλαμβάνετε οποιοδήποτε ή και τα δύο από αυτά σε οποιαδήποτε ερωτήματα σας.",
        "rcshowhidemine": "$1 των επεξεργασιών μου",
        "rcshowhidemine-show": "Εμφάνιση",
        "rcshowhidemine-hide": "Απόκρυψη",
+       "rcshowhidecategorization": "$1 κατηγοριοποίησης σελίδας",
+       "rcshowhidecategorization-show": "Εμφάνιση",
+       "rcshowhidecategorization-hide": "Απόκρυψη",
        "rclinks": "Εμφάνιση των τελευταίων $1 αλλαγών στο διάστημα των τελευταίων $2 ημερών<br />$3",
        "diff": "διαφορά",
        "hist": "ιστορικό",
        "recentchanges-page-added-to-category-bundled": "Η σελίδα [[:$1]] και {{PLURAL:$2|μία ακόμα σελίδα|$2 ακόμα σελίδες}} προστέθηκαν στην κατηγορία",
        "recentchanges-page-removed-from-category": "Η σελίδα [[:$1]] αφαιρέθηκε από την κατηγορία",
        "recentchanges-page-removed-from-category-bundled": "Η σελίδα [[:$1]] και {{PLURAL:$2|μία ακόμα σελίδα|$2 ακόμα σελίδες}} αφαιρέθηκαν από την κατηγορία",
+       "autochange-username": "Αυτόματη αλλαγή MediaWiki",
        "upload": "Ανέβασμα αρχείου",
        "uploadbtn": "Ανέβασμα αρχείου",
        "reuploaddesc": "Επιστροφή στη φόρμα φόρτωσης",
        "changecontentmodel-model-label": "Νέο μοντέλο περιεχομένου",
        "changecontentmodel-reason-label": "Αιτία:",
        "changecontentmodel-success-title": "Το περιεχόμενο πρότυπο άλλαξε",
+       "changecontentmodel-success-text": "Ο τύπος περιεχομένου του [[:$1]] έχει αλλάξει.",
+       "changecontentmodel-nodirectediting": "Το μοντέλο περιεχομένου $1 δεν υποστηρίζει την άμεση επεξεργασία",
+       "log-name-contentmodel": "Αρχείο καταγραφής αλλαγών μοντέλου περιεχομένου",
+       "log-description-contentmodel": "Συμβάντα που σχετίζονται με τα μοντέλα περιεχομένου μιας σελίδας",
        "logentry-contentmodel-change": "{{GENDER:$2|Ο|Η}} $1 άλλαξε το πρότυπο περιεχομένου της σελίδας $3 από «$4» σε «$5»",
        "logentry-contentmodel-change-revertlink": "αναστροφή",
        "logentry-contentmodel-change-revert": "αναστροφή",
        "movepagetext-noredirectfixer": "Χρησιμοποιώντας τη φόρμα που ακολουθεί μπορείτε να μετονομάσετε σελίδες και να μεταφέρετε όλο το ιστορικό τους στο νέο όνομα.\nΟ παλιός τίτλος της σελίδας θα γίνει μια σελίδα ανακατεύθυνσης στο νέο τίτλο.\nΜπορείτε να ενημερώσετε τις ανακατευθύνσεις που οδηγούν στον αρχικό τίτλο αυτόματα.\nΑν επιλέξετε να μην γίνει, θα πρέπει να ελέγξετε τις  [[Special:DoubleRedirects|διπλές]] και τις [[Special:BrokenRedirects|κατεστραμμένες ανακατευθύνσεις]].\nΕίστε υπεύθυνος να επιβεβαιώσετε ότι οι σύνδεσμοι εξακολουθούν να οδηγούν προς τις κατευθύνσεις που πρέπει.\n\nΛάβετε υπόψη σας ότι η σελίδα '''δεν''' θα μετακινηθεί αν υπάρχει ήδη μια άλλη σελίδα κάτω από το νέο τίτλο, εκτός αν η σελίδα αυτή είναι κενή ή είναι ανακατεύθυνση χωρίς ιστορικό επεξεργασίας.\nΑυτό σημαίνει ότι, στην περίπτωση που έχετε κάνει λάθος, μπορείτε να μετονομάσετε μια σελίδα ξαναδίνοντας της την αρχική της ονομασία αλλά δεν μπορείτε να αντικαταστήσετε μια υπάρχουσα σελίδα.\n\n'''ΠΡΟΣΟΧΗ!'''\nΗ μετονομασία σελίδας είναι μια αιφνίδια και δραστική αλλαγή όταν πρόκειται για δημοφιλείς σελίδες.\nΠαρακαλούμε, πριν το αποφασίσετε, να εξετάσετε προσεκτικά τις πιθανές επιπτώσεις αυτής της ενέργειας.",
        "movepagetalktext": "Αν τσεκάρετε αυτό το κουτί, η σχετιζόμενη σελίδα συζήτησης θα μετακινηθεί αυτόματα στο νέο τίτλο, εκτός αν υπάρχει κάτω από το νέο όνομα μια σελίδα συζήτησης που δεν είναι κενή.\n\nΣε αυτή την περίπτωση, θα πρέπει να μετακινήσετε ή να συγχωνεύσετε τη σελίδα με το χέρι αν είναι επιθυμητό.",
        "moveuserpage-warning": "'''Προσοχή:''' Ετοιμάζεστε να μετακινήσετε μια σελίδα χρήστη. Σημειώστε παρακαλώ ότι θα μετακινηθεί μόνο η σελίδα και ο χρήστης '''δεν''' θα μετονομαστεί.",
-       "movecategorypage-warning": "<strong>Προειδοποίηση:</strong>Πάτε να μετακινήσετε μια σελίδα κατηγορίας. Παρακαλούμε να σημειωθεί ότι μόνο η σελίδα θα μετακινηθεί και τυχόν σελίδες στην παλιά κατηγορία <em>δεν</em> θα επανακατηγοριοποιηθούν στη νέα κατηγορία.",
+       "movecategorypage-warning": "<strong>Προειδοποίηση:</strong>Πρόκειται να μετακινήσετε μια σελίδα κατηγορίας. Έχετε υπόψη ότι θα μετακινηθεί μόνο η σελίδα και τυχόν σελίδες στην παλιά κατηγορία <em>δεν</em> θα μεταφερθούν στη νέα κατηγορία.",
        "movenologintext": "Για να μετακινήσετε μια σελίδα πρέπει να είστε εγγεγραμένος χρήστης και [[Special:UserLogin|να έχετε συνδεθεί]] στο Wiκi.",
        "movenotallowed": "Δεν έχετε άδεια να μετακινείτε σελίδες.",
        "movenotallowedfile": "Δεν έχετε άδεια να μετακινείτε αρχεία.",
index 3e545d2..853c9ed 100644 (file)
        "createaccountreason": "Reason:",
        "createacct-reason": "Reason",
        "createacct-reason-ph": "Why you are creating another account",
-       "createacct-captcha": "Security check",
        "createacct-imgcaptcha-help": "",
-       "createacct-imgcaptcha-ph": "Enter the text you see above",
        "createacct-submit": "Create your account",
        "createacct-another-submit": "Create account",
        "createacct-benefit-heading": "{{SITENAME}} is made by people like you.",
index d5d538f..3943862 100644 (file)
        "createaccountreason": "Kialo:",
        "createacct-reason": "Kialo",
        "createacct-reason-ph": "Kial vi kreas plian konton",
-       "createacct-captcha": "Sekureca kontrolo",
-       "createacct-imgcaptcha-ph": "Entajpu la supran tekston",
        "createacct-submit": "Krei konton",
        "createacct-another-submit": "Krei alian konton",
        "createacct-benefit-heading": "{{SITENAME}} estas kreata de homoj kiel vi.",
index c8325b9..3b1f548 100644 (file)
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Por qué estás creando otra cuenta",
-       "createacct-captcha": "Comprobación de seguridad",
-       "createacct-imgcaptcha-ph": "Escribe el texto de arriba",
        "createacct-submit": "Crea tu cuenta",
        "createacct-another-submit": "Crear cuenta",
        "createacct-benefit-heading": "Personas como tú son las que construyen {{SITENAME}}.",
index 1f2a107..5c86d5d 100644 (file)
@@ -33,6 +33,7 @@
        "tog-hideminor": "Peida pisiparandused viimastes muudatustes",
        "tog-hidepatrolled": "Peida kontrollitud redaktsioonid viimastes muudatustes",
        "tog-newpageshidepatrolled": "Peida uute lehtede loendis kontrollitud leheküljed",
+       "tog-hidecategorization": "Peida lehekülgede kategoriseerimine",
        "tog-extendwatchlist": "Laienda jälgimisloendit, et näha kõiki muudatusi, mitte vaid kõige värskemaid",
        "tog-usenewrc": "Rühmita viimased muudatused ja muudatused jälgimisloendis lehekülje järgi",
        "tog-numberheadings": "Pealkirjade automaatnummerdus",
@@ -62,6 +63,7 @@
        "tog-watchlisthideliu": "Peida sisselogitud kasutajate muudatused jälgimisloendist",
        "tog-watchlisthideanons": "Peida anonüümsete kasutajate muudatused jälgimisloendist",
        "tog-watchlisthidepatrolled": "Peida kontrollitud muudatused jälgimisloendist",
+       "tog-watchlisthidecategorization": "Peida lehekülgede kategoriseerimine",
        "tog-ccmeonemails": "Saada mulle koopiad minu läkitatud e-kirjadest",
        "tog-diffonly": "Ära näita erinevuste vaate all lehe sisu",
        "tog-showhiddencats": "Näita peidetud kategooriaid",
        "createaccountreason": "Põhjus:",
        "createacct-reason": "Põhjus",
        "createacct-reason-ph": "Miks lood teist kontot?",
-       "createacct-captcha": "Turvakontroll",
-       "createacct-imgcaptcha-ph": "Sisesta ülalnähtav tekst",
        "createacct-submit": "Loo konto",
        "createacct-another-submit": "Loo konto",
        "createacct-benefit-heading": "{{SITENAME}} on sinusuguste inimeste tehtud.",
        "permissionserrors": "Loatõrge",
        "permissionserrorstext": "Sul pole õigust seda teha {{PLURAL:$1|järgmisel põhjusel|järgmistel põhjustel}}:",
        "permissionserrorstext-withaction": "Sul pole lubatud {{lcfirst:$2}} {{PLURAL:$1|järgmisel põhjusel|järgmistel põhjustel}}:",
+       "contentmodelediterror": "Sa ei saa seda redaktsiooni redigeerida, sest selle sisumudel on <code>$1</code> ning lehekülje praegune sisumudel on <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Hoiatus: Lood uuesti lehekülge, mis on varem kustutatud.'''\n\nKaalu, kas lehekülje uuesti loomine on kohane.\nLehekülje eelnevad kustutamised ja teisaldamised:",
        "moveddeleted-notice": "See lehekülg on kustutatud.\nAllpool on esitatud lehekülje kustutamis- ja teisaldamislogi.",
        "moveddeleted-notice-recent": "Kahjuks on see lehekülg hiljuti kustutatud (viimase 24 tunni jooksul).\nAllpool on ära toodud selle lehekülje sissekanded teisaldamis- ja kustutamislogis.",
        "prefs-help-recentchangescount": "See käib viimaste muudatuste, lehekülgede ajalugude ja logide kohta.",
        "prefs-help-watchlist-token2": "See on sinu jälgimisloendi veebivoo salavõti.\nIgaüks, kes seda teab, saab lugeda sinu jälgimisloendit. Seega ära jaga seda.\n[[Special:ResetTokens|Klõpsa siia, kui sul on vaja see lähtestada]].",
        "savedprefs": "Sinu eelistused on salvestatud.",
+       "savedrights": "Kasutaja $1 õigused on salvestatud.",
        "timezonelegend": "Ajavöönd:",
        "localtime": "Kohalik aeg:",
        "timezoneuseserverdefault": "Kasuta serveri vaikesätet ($1)",
        "rcshowhidemine": "Minu parandused ($1)",
        "rcshowhidemine-show": "näita",
        "rcshowhidemine-hide": "peida",
+       "rcshowhidecategorization": "Kategoriseerimine ($1)",
+       "rcshowhidecategorization-show": "näita",
+       "rcshowhidecategorization-hide": "peida",
        "rclinks": "Näita viimast $1 muudatust viimase $2 päeva jooksul<br />$3",
        "diff": "erin",
        "hist": "ajal",
        "foreign-structured-upload-form-label-not-own-work-local-local": "Võimalik, et soovid kasutada [[Special:Upload|harilikku üleslaadimislehekülge]].",
        "foreign-structured-upload-form-label-own-work-message-default": "Saan aru, et laadin selle faili jagatud varamusse. Kinnitan, et teen seda kooskõlas sealsete kasutustingimuste ja litsentsipõhimõtetega.",
        "foreign-structured-upload-form-label-not-own-work-message-default": "Kui sul pole võimalik laadida seda faili üles kooskõlas jagatud varamu reeglitega, siis palun sule see dialoog ja proovi teisiti toimida.",
-       "foreign-structured-upload-form-label-not-own-work-local-default": "Võimalik, et soovid [[Special:Upload|laadida selle faili üles saidil {{SITENAME}}]], kui seda on võimalik teha kooskõlas sealsete reeglitega.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "Võimalik, et soovid [[Special:Upload|laadida selle faili üles saidil {{SITENAME}}]], kui seda on võimalik teha kooskõlas siinsete reeglitega.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Kinnitan, et olen selle faili autoriõiguse valdaja ja nõustun faili avaldamisega Wikimedia Commonsis pöördumatult Creative Commonsi litsentsi \"[https://creativecommons.org/licenses/by-sa/4.0/deed.et Autorile viitamine + jagamine samadel tingimustel 4.0]\" all. Samuti nõustun [https://wikimediafoundation.org/wiki/Terms_of_Use kasutustingimustega].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Kui sa pole selle faili autoriõiguse valdaja või kui soovid avaldada seda teise litsentsi all, siis on sul võimalik kasutada [https://commons.wikimedia.org/wiki/Special:UploadWizard Commonsi üleslaadimisviisardit].",
-       "foreign-structured-upload-form-label-not-own-work-local-shared": "Võimalik, et soovid kasutada [[Special:Upload|saidi {{SITENAME}} üleslaadimislehekülge]], kui seda faili on lubatud üles laadida kooskõlas sealsete reeglitega.",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Võimalik, et soovid kasutada [[Special:Upload|saidi {{SITENAME}} üleslaadimislehekülge]], kui seda faili on lubatud üles laadida kooskõlas siinsete reeglitega.",
        "backend-fail-stream": "Faili $1 ei saanud edastada.",
        "backend-fail-backup": "Faili $1 ei saanud varundada.",
        "backend-fail-notexists": "Faili $1 pole olemas.",
index f3701af..473ae02 100644 (file)
@@ -55,6 +55,7 @@
        "tog-hideminor": "تغییرات جزئی از فهرست تغییرات اخیر پنهان شوند",
        "tog-hidepatrolled": "ویرایش‌های گشت‌خورده از فهرست تغییرات اخیر پنهان شوند",
        "tog-newpageshidepatrolled": "صفحه‌های گشت‌خورده از فهرست صفحه‌های تازه پنهان شوند",
+       "tog-hidecategorization": "نهفتن رده‌بندی صفحه‌ها",
        "tog-extendwatchlist": "گسترش فهرست پیگیری‌ها برای نمایش همهٔ تغییرات، نه فقط آخرین‌ها",
        "tog-usenewrc": "گروه‌بندی تغییرات بر پایهٔ صفحات تغییرات اخیر و فهرست پیگیری‌ها",
        "tog-numberheadings": "شماره‌گذاری خودکار عنوان‌ها",
@@ -84,6 +85,7 @@
        "tog-watchlisthideliu": "ویرایش‌های کاربران وارد شده به سامانه در فهرست پی‌گیری‌ها پنهان شود",
        "tog-watchlisthideanons": "ویرایش‌های کاربران ناشناس در فهرست پی‌گیری‌های من پنهان شود",
        "tog-watchlisthidepatrolled": "ویرایش‌های گشت‌خورده در فهرست پی‌گیری‌ها پنهان شود",
+       "tog-watchlisthidecategorization": "نهفتن رده‌بندی صفحه‌ها",
        "tog-ccmeonemails": "رونوشتی از ایمیلی که به دیگران ارسال می‌کنم برای خودم هم فرستاده شود",
        "tog-diffonly": "محتوای صفحه، زیر تفاوت نمایش داده نشود",
        "tog-showhiddencats": "رده‌های پنهان نمایش داده شود",
        "viewsource": "نمایش مبدأ",
        "viewsource-title": "نمایش مبدأ برای $1",
        "actionthrottled": "جلوی عمل شما گرفته شد",
-       "actionthrottledtext": "بÙ\87 Ù\85Ù\86ظÙ\88ر Ø¬Ù\84Ù\88Ú¯Û\8cرÛ\8c Ø§Ø² Ø§Ù\86تشار Ø§Ø³Ù¾Ù\85، اجازه ندارید که چنین عملی را بیش از چند بار در یک مدت زمان کوتاه انجام بدهید.\nلطفاً پس از چند دقیقه دوباره تلاش کنید.",
+       "actionthrottledtext": "بÙ\87 Ù\85Ù\86ظÙ\88ر Ø¬Ù\84Ù\88Ú¯Û\8cرÛ\8c Ø§Ø² Ø§Ù\86تشار Ø®Ø±Ø§Ø¨Ú©Ø§Ø±Û\8c، اجازه ندارید که چنین عملی را بیش از چند بار در یک مدت زمان کوتاه انجام بدهید.\nلطفاً پس از چند دقیقه دوباره تلاش کنید.",
        "protectedpagetext": "این صفحه برای جلوگیری از ویرایش یا فعالیت دیگر محافظت شده‌است.",
        "viewsourcetext": "می‌توانید متن مبدأ این صفحه را مشاهده کنید یا از آن نسخه بردارید:",
        "viewyourtext": "می‌توانید کد مبدأ <strong>ویرایش‌هایتان</strong> در این صفحه را ببینید و کپی کنید.",
        "createaccountreason": "دلیل:",
        "createacct-reason": "دلیل",
        "createacct-reason-ph": "چرا شما حساب دیگری می‌سازید؟",
-       "createacct-captcha": "بررسی امنیتی",
-       "createacct-imgcaptcha-ph": "متن تصویری که در بالا می‌بینید، وارد کنید",
        "createacct-submit": "حسابتان را بسازید",
        "createacct-another-submit": "ایجاد حساب کاربری",
        "createacct-benefit-heading": "{{SITENAME}} توسط افرادی مانند شما ساخته شده است",
        "passwordreset-emailtext-ip": "یک نفر (احتمالاً شما، با نشانی آی‌پی $1) درخواست بازنشانی گذرواژه‌تان در {{SITENAME}} ($4) را کرده‌است. {{PLURAL:$3|حساب|حساب‌های}} کاربری زیر با این آدرس ایمیل مرتبط هستند:\n\n$2\n\n{{PLURAL:$3|این گذرواژهٔ موقت|این گذرواژه‌های موقت}} پس از {{PLURAL:$5|یک روز|$5 روز}} باطل خواهند شد.\nشما باید هم‌اکنون ثبت ورود کنید و گذرواژه‌ای جدید برگزینید. اگر فکر می‌کنید شخص دیگری این درخواست را داده است یا اگر گذرواژهٔ اصلی‌تان را به یاد آوردید و دیگر نمی‌خواهید آن را تغییر دهید، می‌توانید این پیغام را نادیده بگیرید و به استفاده از گذرواژهٔ قبلی‌تان ادامه دهید.",
        "passwordreset-emailtext-user": "کاربر $1 از {{SITENAME}} درخواست بازنشانی گذرواژهٔ شما در {{SITENAME}} ($4) را کرده است. {{PLURAL:$3|حساب|حساب‌های}} کاربری زیر با این آدرس ایمیل مرتبط است:\n\n$2\n\n{{PLURAL:$3|این گذرواژهٔ موقت|این گذرواژه‌های موقت}} تا {{PLURAL:$5|یک روز|$5 روز}} باطل می‌شود.\nشما باید هم‌اکنون وارد شده و یک گذرواژهٔ جدید برگزینید. اگر شخص دیگری این درخواست را داده است، یا اگر گذرواژهٔ اصلی‌تان را به خاطر آوردید و دیگر نمی‌خواهید آن را تغییر دهید، می‌توانید این پیغام را نادیده بگیرید و به استفاده از گذرواژهٔ قبلی‌تان ادامه دهید.",
        "passwordreset-emailelement": "نام کاربری: \n$1\n\nگذرواژهٔ موقت: \n$2",
-       "passwordreset-emailsent": "یک نامهٔ بازنشانی گذرواژه فرستاده شده‌است.",
+       "passwordreset-emailsent": "اگر ایمیلی را برای حساب کاربریتان مشخص کرده باشید، یک نامهٔ بازنشانی گذرواژه فرستاده شده‌است.",
        "passwordreset-emailsent-capture": "یک ایمیل بازنشانی که در پایین نمایش داده شده، فرستاده شده است.",
        "passwordreset-emailerror-capture": "ایمیل بازنشانی، که در زیر نمایش داده شده، ایجاد شد، ولی ارسال آن به {{GENDER:$2|کاربر}} موفقیت‌آمیز نبود: $1",
        "changeemail": "تغییر یا حذف نشانی ایمیل",
-       "changeemail-header": "تغییر آدرس ایمیل حساب کاربری",
+       "changeemail-header": "برای تغییر ایمیلتان این فرم را کامل کنید. برای حذف ایملیتان کافی است بخش ایمیل را خالی رها کنید و فرم را ارسال کنید.",
+       "changeemail-passwordrequired": "برای تائید این تغییر باید گذرواژه‌تان را وارد کنید.",
        "changeemail-no-info": "برای دسترسی مستقیم به این صفحه شما باید به سامانه وارد شده باشید.",
        "changeemail-oldemail": "آدرس ایمیل کنونی:",
        "changeemail-newemail": "آدرس ایمیل جدید:",
+       "changeemail-newemail-help": "برای حذف ایمیل باید این بخش را خالی رها کنید در نتیجه امکان بازگردانی گذرواژه و دریافت ایمیل از ویکی برای شما مقدور نخواهد بود.",
        "changeemail-none": "(هیچ)",
        "changeemail-password": "گذرواژهٔ {{SITENAME}} شما:",
        "changeemail-submit": "تغییر ایمیل",
        "missingsummary": "'''یادآوری:''' شما خلاصهٔ ویرایش ننوشته‌اید.\nاگر دوباره دکمهٔ «{{int:savearticle}}» را فشار دهید ویرایش شما بدون آن ذخیره خواهد شد.",
        "selfredirect": "<strong>هشدار:</strong> شما در حال تغییرمسیر صفحه به خودش هستید.\nامکان دارد هدف اشتباهی را برای تغییرمسیر انتخاب کردید، یا ممکن است صفحهٔ اشتباهی را ویرایش می‌کنید.\n\nاگر بر روی \"{{int:savearticle}}\" دوباره کلیک کنید، تغییرمسیر ساخته خواهد شد.",
        "missingcommenttext": "لطفاً توضیحی در زیر بیفزایید.",
-       "missingcommentheader": "'''یادآوری:''' شما موضوع/عنوان این یادداشت را مشخص نکرده‌اید.\nاگر دوباره دکمهٔ «{{int:savearticle}}» را فشار دهید ویرایش شما بدون آن ذخیره خواهد شد.",
+       "missingcommentheader": "<strong>یادآوری:</strong> شما موضوع/عنوان این یادداشت را مشخص نکرده‌اید.\nاگر دوباره دکمهٔ «{{int:savearticle}}» را فشار دهید ویرایش شما بدون آن ذخیره خواهد شد.",
        "summary-preview": "پیش‌نمایش خلاصه:",
-       "subject-preview": "پیش‌نمایش موضوع/عنوان:",
+       "subject-preview": "پیش‌نمایش موضوع:",
        "previewerrortext": "در زمان تلاش برای نمایش دادن تغییرات شما، خطای رخ داد.",
        "blockedtitle": "کاربر بسته شده‌است",
        "blockedtext": "<strong>دسترسی حساب کاربری یا نشانی آی‌پی شما بسته شده‌است.</strong>\n\nاین قطع دسترسی توسط $1 انجام شده است.\nدلیل ارائه‌شده چنین است: <em>$2</em>\n\n* شروع قطع دسترسی: $8\n* پایان قطع دسترسی: $6\n* کاربری هدف قطع دسترسی: $7\n\nشما می‌توانید با $1 یا [[{{MediaWiki:Grouppage-sysop}}|مدیری]] دیگر تماس بگیرید و در این باره صحبت کنید.\nتوجه کنید که شما نمی‌توانید از قابلیت «ایمیل به این کاربر» استفاده کنید مگر آنکه آدرس ایمیل معتبری در [[Special:Preferences|ترجیحات کاربری]] خودتان ثبت کرده باشید و نیز باید امکان استفاده از این قابلیت برای شما قطع نشده باشد.\nنشانی آی‌پی فعلی شما $3 و شمارهٔ قطع دسترسی شما $5 است.\nلطفاً تمامی جزئیات فوق را در کلیهٔ درخواست‌هایی که در این باره مطرح می‌کنید ذکر کنید.",
        "permissionserrors": "خطای سطح دسترسی",
        "permissionserrorstext": "شما اجازهٔ انجام این کار را به این {{PLURAL:$1|دلیل|دلایل}} ندارید:",
        "permissionserrorstext-withaction": "شما اجازهٔ $2 را به این {{PLURAL:$1|دلیل|دلایل}} ندارید:",
+       "contentmodelediterror": "امکان ویرایش این نسخه برای شما نیست چون نوع محتوای آن <code>$1</code> است و نوع محتوای کنونی صفحه <code>$2</code> است.",
        "recreate-moveddeleted-warn": "<strong>هشدار: شما در حال ایجاد صفحه‌ای هستید که قبلاً حذف شده‌است.</strong>\n\nدر نظر داشته باشید که آیا ادامهٔ ویرایش این صفحه کار درستی‌است یا نه.\nسیاههٔ حذف و انتقال این صفحه در زیر نشان داده شده‌است:",
        "moveddeleted-notice": "این صفحه حذف شده‌است.\nدر زیر سیاههٔ حذف و انتقال این صفحه نمایش داده شده‌است.",
        "moveddeleted-notice-recent": "متاسفانه صفحه قبلا حذف شده‌است (در ۲۴ ساعت اخیر) \nدلیل حذف و سیاههٔ انتقال در پائین موجود است.",
        "prefs-help-recentchangescount": "این گزینه شامل تغییرات اخیر، تاریخچهٔ صفحه‌ها و سیاهه‌ها می‌شود.",
        "prefs-help-watchlist-token2": "این کلید رمز خوراک وب فهرست پی‌گیری‌های شماست.\nهرکس آن را بداند می‌تواند فهرست پی‌گیری‌هایتان را بخواند، بنابراین آن را به اشتراک نگذارید. [[Special:ResetTokens|اگر لازم است آن را تغییر دهید اینجا را کلیک کنید]].",
        "savedprefs": "ترجیحات شما ذخیره شد.",
+       "savedrights": "دسترسی کاربری {{GENDER:$1|$1}} ذخیره شده‌است.",
        "timezonelegend": "منطقهٔ زمانی:",
        "localtime": "زمان محلی:",
        "timezoneuseserverdefault": "استفاده از پیش‌فرض ویکی ($1)",
        "rcshowhidemine": "$1 ویرایش‌های من",
        "rcshowhidemine-show": "نمایش",
        "rcshowhidemine-hide": "نهفتن",
+       "rcshowhidecategorization": "$1 رده‌بندی صفحه‌ها",
+       "rcshowhidecategorization-show": "نمایش",
+       "rcshowhidecategorization-hide": "نهفتن",
        "rclinks": "نمایش آخرین $1 تغییر در $2 روز اخیر<br />$3",
        "diff": "تفاوت",
        "hist": "تاریخچه",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] و {{PLURAL:$2|یک صفحه|$2 صفحه}}ٔ دیگر به رده اضافه شدند",
        "recentchanges-page-removed-from-category": "[[:$1]] از رده حذف شد",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] و {{PLURAL:$2|یک صفحه|$2 صفحه}}ٔ دیگر از رده حذف شدند",
+       "autochange-username": "تغییرات خودکار مدیاویکی",
        "upload": "بارگذاری پرونده",
        "uploadbtn": "بارگذاری پرونده",
        "reuploaddesc": "بازگشت به فرم بارگذاری",
        "upload-options": "گزینه‌های بارگذاری",
        "watchthisupload": "پی‌گیری این پرونده",
        "filewasdeleted": "پرونده‌ای با همین نام پیشتر بارگذاری و پس از آن پاک شده‌است.\nشما باید $1 را قبل از بارگذاری مجدد آن ببینید.",
+       "filename-thumb-name": "به نظر می رسد که عنوان thumbnail است. لطفاً تصویر thumbnail  از دیگر ویکی‌ها را بارگذاری نکنید. عنوان پرونده را اصلاح کنید به صورتی که پیشوند thumbnail نداشته باشد.",
        "filename-bad-prefix": "نام پرونده‌ای که بارگذاری می‌کنید با '''$1''' آغاز می‌شود که یک پیشوند مخصوص تصاویر ثبت شده توسط دوربین‌های دیجیتال است.\nلطفاً نامی بهتر برای پرونده برگزینید.",
        "upload-success-subj": "بارگذاری با موفقیت انجام شد",
        "upload-success-msg": "بارگذاری شما از [$2] موفق بود. این پرونده در اینجا قابل دسترسی است: [[:{{ns:file}}:$1]]",
        "foreign-structured-upload-form-label-own-work": "این کار خودم است",
        "foreign-structured-upload-form-label-infoform-categories": "رده‌ها",
        "foreign-structured-upload-form-label-infoform-date": "تاریخ",
+       "foreign-structured-upload-form-label-own-work-message-local": "تائید می کنم که این پرونده را تحت مجوزها و سیاست‌های {{SITENAME}} بارگذاری می‌کنم.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "اگر امکان بارگذاری پرونده تحت سیاست‌های {{SITENAME}} را ندارید، لطفاً این پنجره را ببندید و از روش‌های دیگر استفاده کنید.",
+       "foreign-structured-upload-form-label-not-own-work-local-local": "ممکن بخواهید از [[Special:Upload|پنجرهٔ بارگذاری پیش‌فرض]] استفاده کنید.",
+       "foreign-structured-upload-form-label-own-work-message-default": "متوجهم که این پرونده را بر روی مخزن مشترک بارگذاری می‌کنم و تائید می‌کنم که تحت سیاست‌ها و مجوزهای آنجا عمل می‌کنم.",
+       "foreign-structured-upload-form-label-not-own-work-message-default": "اگر امکان بارگذاری پرونده تحت سیاست‌ها و مجوزهای مخزن اشتراک‌گذاری را ندارید، لطفاً این پنجره را ببندید و از روش‌های دیگر استفاده کنید.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "در صورتی که نمی‌شود پرونده را تحت سیاست‌ها بارگذاری کنید ممکن است بخواهید از [[Special:Upload|پنجرهٔ بارگذاری در {{SITENAME}}]] استفاده کنید.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "تصدیق می‌کنم که مالک حق تکثیر این پرونده هستم و موافق اشتراک‌گذاری آن تحت مجوز [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] هستم و موافق [https://wikimediafoundation.org/wiki/Terms_of_Use سیاست نحوهٔ استفاده] هستم.",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "اگر مالک حق تکثیر این پرونده نیستید یا قصد بارگذاری تحت مجوز دیگری دارید، از [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] استفاده کنید.",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "در صورتی که سایت امکان بارگذاری پرونده را تحت سیاست‌ها بارگذاری می‌دهد ممکن است بخواهید از [[Special:Upload|پنجرهٔ بارگذاری در {{SITENAME}}]] استفاده کنید.",
        "backend-fail-stream": "نمی‌توان پروندهٔ $1 را ارسال کرد.",
        "backend-fail-backup": "نمی‌توان نسخهٔ پشتیبان برای پروندهٔ $1 ایجاد کرد.",
        "backend-fail-notexists": "پروندهٔ $1 وجود ندارد.",
        "nopagetext": "صفحهٔ هدفی که شما مشخص کردید وجود ندارد.",
        "pager-newer-n": "{{PLURAL:$1|یک مورد جدیدتر|$1 مورد جدیدتر}}",
        "pager-older-n": "{{PLURAL:$1|یک مورد قدیمی‌تر|$1 مورد قدیمی‌تر}}",
-       "suppress": "نظارت",
+       "suppress": "سرکوب",
        "querypage-disabled": "این صفحه ویژه به دلایل عملکردی غیرفعال شده‌است.",
        "apihelp": "راهنمای API",
        "apihelp-no-such-module": "پودمان \" $1 \" یافت نشد.",
        "move-page-legend": "انتقال صفحه",
        "movepagetext": "با استفاده از فرم زیر نام صفحه تغییر خواهد کرد، و تمام تاریخچه‌اش به نام جدید منتقل خواهد شد.\nعنوان قدیمی تبدیل به یک صفحهٔ تغییرمسیر به عنوان جدید خواهد شد.\nشما می‌توانید تغییرمسیرهایی که به عنوان اصلی اشاره دارند را به صورت خودکار به‌روزرسانی کنید.\nپیوندهای که به عنوان صفحهٔ قدیمی وجود دارند، تغییر نخواهند کرد؛ حتماً تغییرمسیرهای [[Special:DoubleRedirects|دوتایی]] یا [[Special:BrokenRedirects|خراب]] را بررسی کنید.\n'''شما''' مسئول اطمینان از این هستید که پیوندها هنوز به همان‌جایی که قرار است بروند.\n\nتوجه کنید که اگر از قبل صفحه‌ای در عنوان جدید وجود داشته باشد صفحه منتقل '''نخواهد شد'''،\nمگر این آخرین ویرایش تغییرمسیر باشد و در  تاریخچهٔ ویرایشی نداشته باشد.\nاین یعنی اگر اشتباه کردید می‌توانید صفحه را به همان جایی که از آن منتقل شده بود برگردانید، و این که نمی‌توانید روی صفحات موجود بنویسید.\n\n'''هشدار!'''\nانتقال صفحات به نام جدید ممکن است تغییر اساسی و غیرمنتظره‌ای برای صفحات محبوب باشد؛\nلطفاً مطمئن شوید که قبل از انتقال دادن صفحه، عواقب این کار را درک می‌کنید.",
        "movepagetext-noredirectfixer": "استفاده از فرم زیر سبب تغییر نام یک صفحه و انتقال تمام تاریخچهٔ آن به نام جدید می‌شود.\nعنوان پیشین تغییرمسیری به عنوان جدید خواهد شد.\nبه خاطر داشته باشید که [[Special:DoubleRedirects|تغییرمسیرهای دوتایی]] یا [[Special:BrokenRedirects|تغییرمسیرهای خراب]] را بررسی کنید.\nشما مسئولید که مطمئن شوید پس از انتقال، پیوندها به عنوان پیشین به جایی منتهی می‌شوند که باید.\n\nتوجه کنید که اگر صفحه‌ای تحت عنوان جدید از قبل موجود باشد، انتقال انجام '''نخواهد شد'''، مگر اینکه صفحه خالی و یا تغییرمسیر باشد و تاریخچهٔ ویرایشی دیگری نداشته باشد.\nاین یعنی اگر صفحه را به نامی اشتباه منتقل کردید می‌توانید این تغییر را واگردانی کنید، اما نمی‌توانید یک صفحه را به صفحه‌ای که از قبل موجود است انتقال دهید.\n\n'''هشدار!'''\nانتقال صفحه‌های پربیننده ممکن است عملی غیرمنتظره باشد؛\nلطفاً پیش از انتقال مطمئن شوید از نتیجهٔ کار آگاهید.",
-       "movepagetalktext": "صÙ\81Ø­Ù\87Ù\94 Ø¨Ø­Ø« Ù\85ربÙ\88Ø·Ø\8c Ø§Ú¯Ø± Ù\88جÙ\88د Ø¯Ø§Ø´ØªÙ\87 Ø¨Ø§Ø´Ø¯Ø\8c Ø¨Ø·Ù\88ر Ø®Ù\88دکار Ù\87Ù\85راÙ\87 Ø¨Ø§ Ù\85Ù\82اÙ\84Ù\87Ù\94 Ø§ØµÙ\84Û\8c Ù\85Ù\86تÙ\82Ù\84 Ø®Ù\88اÙ\87د Ø´Ø¯ <strong>Ù\85گر Ø§Û\8cÙ\86Ú©Ù\87:</strong>\n* Ø¯Ø± Ø­Ø§Ù\84 Ø§Ù\86تÙ\82اÙ\84 ØµÙ\81Ø­Ù\87 Ø§Ø² Ø§Û\8cÙ\86 Ù\81ضاÛ\8c Ù\86اÙ\85 Ø¨Ù\87 Ù\81ضاÛ\8c Ù\86اÙ\85 Ø¯Û\8cگرÛ\8c Ø¨Ø§Ø´Û\8cدØ\8c\n* Û\8cÚ© ØµÙ\81Ø­Ù\87Ù\94 Ø¨Ø­Ø« ØºÛ\8cرخاÙ\84Û\8c ØªØ­Øª Ø§Û\8cÙ\86 Ù\86اÙ\85 Ø¬Ø¯Û\8cد Ù\88جÙ\88د Ø¯Ø§Ø´ØªÙ\87 Ø¨Ø§Ø´Ø¯Ø\8c Û\8cا\n* Ø¬Ø¹Ø¨Ù\87Ù\94 Ø²Û\8cر Ø±Ø§ ØªÛ\8cÚ© Ù\86زدÙ\87 Ø¨Ø§Ø´Û\8cد.\n\nدر Ø§Û\8cÙ\86 Ø­Ø§Ù\84ات، باید صفحه را بطور دستی انتقال داده و یا محتویات دو صفحه را با ویرایش ادغام کنید.",
+       "movepagetalktext": "اگر Ø§Û\8cÙ\86 Ú¯Ø²Û\8cÙ\86Ù\87 Ø±Ø§ Ø§Ù\86تخاب Ú©Ù\86Û\8cدØ\8c ØµÙ\81Ø­Ù\87Ù\94 Ø¨Ø­Ø« Ù\85رتبط Ø¨Ù\87 ØµÙ\88رت Ø®Ù\88دکار Ø§Ù\86تÙ\82اÙ\84 Ø¯Ø§Ø¯Ù\87 Ù\85Û\8câ\80\8cØ´Ù\88د Ø¨Ù\87 Ø¹Ù\86Ù\88اÙ\86 Ø¬Ø¯Û\8cد Ù\88 Ø¯Ø± ØµÙ\88رت Ø¹Ø¯Ù\85 Ø§Ù\86تخاب Ú¯Ø²Û\8cÙ\86Ù\87Ø\8c ØµÙ\81Ø­Ù\87Ù\94 Ø¨Ø­Ø« Ø¬Ø¯Û\8cد Û\8cÚ© ØµÙ\81Ø­Ù\87Ù\94 Ø®Ø§Ù\84Û\8c Ø®Ù\88اÙ\87د Ø¨Ù\88د Ù\88 Ø¯Ø± Ø§Û\8cÙ\86 Ø­Ø§Ù\84ت، باید صفحه را بطور دستی انتقال داده و یا محتویات دو صفحه را با ویرایش ادغام کنید.",
        "moveuserpage-warning": "'''هشدار:''' شما در حال انتقال دادن یک صفحهٔ کاربر هستید. توجه داشته باشید که تنها صفحه منتقل می‌شود و نام کاربر تغییر '''نمی‌یابد'''.",
        "movecategorypage-warning": "<strong>هشدار:</strong> شما در حال انتقال صفحه رده هستید. لطفاً توجه داشته باشید که فقط صفحه منتقل خواهد شد و  صفحات در رده قدیمی می‌مانند و به رده جدید <em>نمی‌روند</em>.",
        "movenologintext": "برای انتقال صفحات باید کاربر ثبت‌شده بوده و [[Special:UserLogin|به سامانه وارد شوید]].",
        "svg-long-error": "پرونده SVG نامجاز: $1",
        "show-big-image": "پروندهٔ اصلی",
        "show-big-image-preview": "اندازهٔ این پیش‌نمایش: $1.",
+       "show-big-image-preview-differ": "حجم پیش‌نمایش $3 این $2 file:$1",
        "show-big-image-other": "{{PLURAL:$2|کیفیت|کیفیت‌های}} دیگر: $1.",
        "show-big-image-size": "<span dir=\"ltr\">$1 × $2</span> پیکسل",
        "file-info-gif-looped": "چرخش‌دار",
        "logentry-protect-move_prot": "$1 تنظیمات محافظت را از $4 به $3 {{GENDER:$2|منتقل کرد}}",
        "logentry-protect-unprotect": "$1 $3 را از محافظت {{GENDER:$2|خارج کرد}}",
        "logentry-protect-protect": "$1 $3 را {{GENDER:$2|محافظت کرد}} $4",
+       "logentry-protect-protect-cascade": "$1 $3 $4 {{GENDER:$2|محافظت کرد}} [آبشاری]",
        "logentry-protect-modify": "$1 سطح محافظت $3 را {{GENDER:$2|تغییر داد}} $4",
+       "logentry-protect-modify-cascade": "$1 سطح حفاظت برای $3 $4 را {{GENDER:$2|تغییر داد}}[آبشاری]",
        "logentry-rights-rights": "$1 عضویت $3 را از گروه $4 به $5 {{GENDER:$2|تغییر داد}}",
        "logentry-rights-rights-legacy": "$1 گروه عضویت $3 را {{GENDER:$2|تغییر داد}}",
        "logentry-rights-autopromote": "$1 به طور خودکار از $4 به $5 {{GENDER:$2|ارتقاء داد}}",
index c0233f2..de13ad1 100644 (file)
@@ -53,6 +53,7 @@
        "tog-hideminor": "Piilota pienet muutokset tuoreet muutokset -listasta",
        "tog-hidepatrolled": "Piilota tarkastetut muutokset tuoreet muutokset -listasta",
        "tog-newpageshidepatrolled": "Piilota tarkastetut sivut uusien sivujen listalta",
+       "tog-hidecategorization": "Piilota sivujen luokittelu",
        "tog-extendwatchlist": "Laajenna tarkkailulista näyttämään kaikki tehdyt muutokset eikä vain viimeisimmät",
        "tog-usenewrc": "Ryhmittele muutokset sivun mukaan tuoreiden muutosten listalla ja tarkkailulistalla",
        "tog-numberheadings": "Numeroi otsikot automaattisesti",
@@ -82,6 +83,7 @@
        "tog-watchlisthideliu": "Piilota kirjautuneiden käyttäjien muokkaukset tarkkailulistalta",
        "tog-watchlisthideanons": "Piilota rekisteröitymättömien käyttäjien muokkaukset tarkkailulistalta",
        "tog-watchlisthidepatrolled": "Piilota muutostentarkastajien hyväksymät muokkaukset tarkkailulistalta",
+       "tog-watchlisthidecategorization": "Piilota muutokset, jotka koskevat sivujeen lisäämistä tai poistamista luokkiin",
        "tog-ccmeonemails": "Lähetä minulle kopio MediaWikin kautta lähetetyistä sähköposteista",
        "tog-diffonly": "Älä näytä sivun sisältöä eroavaisuusvertailun alapuolella",
        "tog-showhiddencats": "Näytä piilotetut luokat",
        "listingcontinuesabbrev": "jatkuu",
        "index-category": "Indeksoidut sivut",
        "noindex-category": "Indeksointikiellolliset sivut",
-       "broken-file-category": "Sivut, joilla toimimattomia tiedostolinkkejä",
+       "broken-file-category": "Sivut, joissa on toimimattomia tiedostolinkkejä",
        "about": "Tietoja",
        "article": "Sivu",
        "newwindow": "(avautuu uuteen ikkunaan)",
        "createaccountreason": "Syy:",
        "createacct-reason": "Syy",
        "createacct-reason-ph": "Miksi olet luomassa toista käyttäjätunnusta",
-       "createacct-captcha": "Turvatarkastus",
-       "createacct-imgcaptcha-ph": "Kirjoita teksti, jonka näet edellä",
        "createacct-submit": "Luo tunnus",
        "createacct-another-submit": "Luo käyttäjätunnus",
        "createacct-benefit-heading": "{{SITENAME}} on sinun kaltaistesi ihmisten tekemä.",
        "permissionserrors": "Puutteelliset oikeudet",
        "permissionserrorstext": "Sinulla ei ole oikeutta suorittaa toimintoa {{PLURAL:$1|seuraavasta syystä|seuraavista syistä}}:",
        "permissionserrorstext-withaction": "Sinulla ei ole oikeutta {{lcfirst:$2}} {{PLURAL:$1|seuraavasta syystä|seuraavista syistä}}:",
+       "contentmodelediterror": "Et voi muokata tätä versiota, koska sen sisältömalli on <code>$1</code>, ja sivun nykyinen sisältömalli on <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Varoitus: Olet luomassa sellaista sivua, joka on aikaisemmin poistettu.'''\n\nHarkitse, kannattaako tätä sivua luoda uudelleen. \nAlla on tämän sivun poisto- ja siirtohistoria:",
        "moveddeleted-notice": "Tämä sivu on poistettu. Alla on tämän sivun poisto- ja siirtohistoria.",
        "moveddeleted-notice-recent": "Valitettavasti tämä sivu on poistettu aivan äskettäin (viimeisen 24 tunnin aikana).\nAlla näkyy sivun poisto- ja siirtolokin tietoja.",
        "prefs-help-recentchangescount": "Tämä sisältää tuoreet muutokset, muutoshistoriat ja lokit.",
        "prefs-help-watchlist-token2": "Tämä on salainen avain tarkkailulistasi verkkosyötteeseen.\nKuka tahansa, joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä.\n[[Special:ResetTokens|Napsauta tästä, jos sinun pitää uudistaa se]].",
        "savedprefs": "Asetuksesi on tallennettu.",
+       "savedrights": "Käyttäjän {{GENDER:$1|$1}} käyttöoikeudet on tallennettu.",
        "timezonelegend": "Aikavyöhyke",
        "localtime": "Paikallinen aika",
        "timezoneuseserverdefault": "Käytä oletusta ($1)",
        "rcshowhidemine": "$1 omat muutokset",
        "rcshowhidemine-show": "Näytä",
        "rcshowhidemine-hide": "Piilota",
+       "rcshowhidecategorization": "$1 sivujen luokkien muutokset",
+       "rcshowhidecategorization-show": "Näytä",
+       "rcshowhidecategorization-hide": "Piilota",
        "rclinks": "Näytä $1 tuoretta muutosta viimeisten $2 päivän ajalta.<br />$3",
        "diff": "ero",
        "hist": "historia",
        "pager-newer-n": "← {{PLURAL:$1|1 uudempi|$1 uudempaa}}",
        "pager-older-n": "{{PLURAL:$1|1 vanhempi|$1 vanhempaa}} →",
        "suppress": "Häivytys",
-       "querypage-disabled": "Tämä toimintosivu on poistettu käytöstä suorituskykysyistä.",
+       "querypage-disabled": "Tämä toimintosivu on poistettu käytöstä suorituskykyyn liittyvien syiden vuoksi.",
        "apihelp": "API-apu",
        "apihelp-no-such-module": "Moduulia ”$1” ei löydy.",
        "booksources": "Kirjalähteet",
        "post-expand-template-inclusion-category-desc": "Sivun koko on suurempi kuin <code>$wgMaxArticleSize</code>, kun kaikki mallineet on laajennettu. Tämän vuoksi joitakin mallineita ei laajennettu.",
        "post-expand-template-argument-category-desc": "Sivu on suurempi kuin <code>$wgMaxArticleSize</code>, kun mallineen argumentti on laajennettu (argumentti on merkkijono kolmen aaltosulun sisällä kuten <code>{{{Foo}}}</code>).",
        "expensive-parserfunction-category-desc": "Tämä sivu käyttää liian monta resursseja vaativaa jäsenninfunktiota (kuten <code>#ifexist</code>). Katso [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
-       "broken-file-category-desc": "Tämä sivu sisältää rikkinäisen tiedostolinkin. Tällä tarkoitetaan linkkiä sellaiseen tiedostoon, jota ei olemassa.",
+       "broken-file-category-desc": "Tämä sivu sisältää toimimattoman tiedostolinkin (eli linkin sellaiseen tiedoston upottamiseksi, jota ei olemassa).",
        "hidden-category-category-desc": "Tämä luokka sisältää koodin <code><nowiki>__HIDDENCAT__</nowiki></code> sen tekstisisällössä. Koodi estää luokan näkymisen sivujen alareunassa olevassa luokkien laatikossa kuten yleensä.",
        "trackingcategories-nodesc": "Ei kuvausta olemassa.",
        "trackingcategories-disabled": "Luokka on poistettu käytöstä",
        "email-legend": "Sähköpostin lähetys {{GRAMMAR:genitive|{{SITENAME}}}} käyttäjälle",
        "emailfrom": "Lähettäjä",
        "emailto": "Vastaanottaja",
-       "emailsubject": "Aihe",
+       "emailsubject": "Aihe:",
        "emailmessage": "Viesti",
        "emailsend": "Lähetä",
        "emailccme": "Lähetä kopio viestistä minulle.",
        "feedback-error3": "Virhe: Ohjelmointirajapinta ei vastaa",
        "feedback-error4": "Virhe: Annetun palautteen otsikkoa ei voida lähettää",
        "feedback-message": "Viesti",
-       "feedback-subject": "Otsikko",
+       "feedback-subject": "Aihe:",
        "feedback-submit": "Lähetä",
        "feedback-terms": "Ymmärrän, että minua koskeva \"user agent\" -tieto sisältää tiedon siitä, mitä yksittäistä selainta ja käyttöjärjestelmää minä käytän ja että nämä tiedot tulevat näkymään julkisesti kaikille palautteeni yhteydessä.",
        "feedback-termsofuse": "Sitoudun lähettämään palautteen käyttöehtojen määräysten mukaisesti.",
index 0c3fb7a..44bb4af 100644 (file)
        "createaccountreason": "Motif :",
        "createacct-reason": "Motif",
        "createacct-reason-ph": "Pourquoi créez-vous un autre compte",
-       "createacct-captcha": "Contrôle de sécurité",
-       "createacct-imgcaptcha-ph": "Entrez le texte que vous voyez ci-dessus",
        "createacct-submit": "Créez votre compte",
        "createacct-another-submit": "Créer le compte",
        "createacct-benefit-heading": "{{SITENAME}} est écrit par des gens comme vous.",
        "hijri-calendar-m12": "Dhou al-hijja",
        "hebrew-calendar-m1": "tichri",
        "hebrew-calendar-m2": "hechvan",
-       "hebrew-calendar-m3": "Kislev",
-       "hebrew-calendar-m4": "Téveth",
+       "hebrew-calendar-m3": "kislev",
+       "hebrew-calendar-m4": "tévet",
        "hebrew-calendar-m5": "Schébat",
        "hebrew-calendar-m6": "Adar",
        "hebrew-calendar-m7": "Nissane",
        "hebrew-calendar-m12": "Éloul",
        "hebrew-calendar-m1-gen": "tichri",
        "hebrew-calendar-m2-gen": "hechvan",
-       "hebrew-calendar-m3-gen": "Kislev",
-       "hebrew-calendar-m4-gen": "Téveth",
+       "hebrew-calendar-m3-gen": "kislev",
+       "hebrew-calendar-m4-gen": "tévet",
        "hebrew-calendar-m5-gen": "Schébat",
        "hebrew-calendar-m6-gen": "Adar",
        "hebrew-calendar-m7-gen": "Nissane",
index 7df66f8..1e6f40f 100644 (file)
        "createaccountreason": "Grund:",
        "createacct-reason": "Grund",
        "createacct-reason-ph": "Wurum Du ne ander Benutzerkonto aaleisch",
-       "createacct-captcha": "Sicherheitspriefig",
-       "createacct-imgcaptcha-ph": "Gib dr Tekscht yy, wu Du obe siisch",
        "createacct-submit": "Dyy Benutzerkonto aalege",
        "createacct-another-submit": "Benutzerkonto aalege",
        "createacct-benefit-heading": "{{SITENAME}} wird vu Mänsche wie Dir gschaffe.",
index 1662718..98e762e 100644 (file)
        "createaccountreason": "סיבה:",
        "createacct-reason": "סיבה",
        "createacct-reason-ph": "סיבה ליצירת חשבון נוסף",
-       "createacct-captcha": "בדיקת אבטחה",
-       "createacct-imgcaptcha-ph": "יש להקליד את הטקסט המופיע למעלה",
        "createacct-submit": "יצירת החשבון שלך",
        "createacct-another-submit": "יצירת חשבון",
        "createacct-benefit-heading": "אנשים כמוך יוצרים את {{SITENAME}}.",
index 8aea5cb..e4f567f 100644 (file)
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण",
        "createacct-reason-ph": "आप एक अन्य खाता क्यों बना रहे हैं",
-       "createacct-captcha": "सुरक्षा जाँच",
-       "createacct-imgcaptcha-ph": "उपरोक्त पाठ लिखें",
        "createacct-submit": "अपना खाता बनाएँ",
        "createacct-another-submit": "खाता बनाएँ",
        "createacct-benefit-heading": "{{SITENAME}} आपके जैसे लोगों द्वारा बनायी गई है।",
index eccf3ea..a8a22b1 100644 (file)
        "createaccountreason": "Razlog:",
        "createacct-reason": "Razlog",
        "createacct-reason-ph": "Zašto stvarate drugi račun",
-       "createacct-captcha": "Sigurnosna provjera",
-       "createacct-imgcaptcha-ph": "Unesite tekst koji vidite iznad",
        "createacct-submit": "Stvorite svoj suradnički račun",
        "createacct-another-submit": "Stvori još jedan račun",
        "createacct-benefit-heading": "{{SITENAME}} su stvorili ljudi poput Vas.",
index 0c8cc3a..d0ff39a 100644 (file)
        "nstab-template": "Předłoha",
        "nstab-help": "Pomoc",
        "nstab-category": "Kategorija",
+       "mainpage-nstab": "Hłowna strona",
        "nosuchaction": "Žana tajka akcija",
        "nosuchactiontext": "Akcija, kotruž URL podawa, je njepłaćiwa.\nSy so snano při zapodaću URL zapisał abo sy wopačnemu wotkazej slědował.\nTo móhło tež programowanski zmylk w {{GRAMMAR:lokatiw|{{SITENAME}}}} być.",
        "nosuchspecialpage": "Tuta specialna strona njeeksistuje.",
        "createaccountreason": "Přičina:",
        "createacct-reason": "Přičina",
        "createacct-reason-ph": "Čehodla załožuješ druhe konto?",
-       "createacct-captcha": "Wěstotna kontrola",
-       "createacct-imgcaptcha-ph": "Zapodaj tekst, kotryž deleka widźiš",
        "createacct-submit": "Twoje konto załožić",
        "createacct-another-submit": "Dalše konto załožić",
        "createacct-benefit-heading": "{{SITENAME}} je so wot ludźi kaž ty wutworił.",
        "protect-locked-dblock": "Datowa banka je zawrjena, tohodla njemóžeš škit strony změnić. Tu widźiš aktualne škitne nastajenja za stronu'''„$1“:'''",
        "protect-locked-access": "Nimaš trěbne prawa, zo by škit strony změnił. Tu widźiš aktualne škitne nastajenja za stronu'''„$1“:'''",
        "protect-cascadeon": "Tuta strona je tuchwilu škitana, dokelž je w {{PLURAL:$1|slědowacej stronje|slědowacych stronach}} zapřijata, {{PLURAL:$1|kotraž je|kotrež su}} přez kaskadowu opciju {{PLURAL:$1|škitana|škitane}}. Změny na škitnym schodźenku tuteje strony njebudu kaskadowy škit wobwliwować.",
-       "protect-default": "Wšěch wužiwarjow dowolić",
+       "protect-default": "Wšěm wužiwarjam dowolić",
        "protect-fallback": "Jenož wužiwarjow z prawom \"$1\" dowolić",
-       "protect-level-autoconfirmed": "Jenož awtomatisce wobkrućenych wužiwarjow dowolić",
-       "protect-level-sysop": "Jenož administratorow dowolić",
+       "protect-level-autoconfirmed": "Jenož awtomatisce wobkrućenym wužiwarjam dowolić",
+       "protect-level-sysop": "Jenož administratoram dowolić",
        "protect-summary-cascade": "kaskadowacy",
        "protect-expiring": "spadnje $1 (UTC)",
        "protect-expiring-local": "płaćiwy hač do $1",
        "tooltip-ca-nstab-main": "stronu wobhladać",
        "tooltip-ca-nstab-user": "wužiwarsku stronu wobhladać",
        "tooltip-ca-nstab-media": "datajowu stronu wobhladać",
-       "tooltip-ca-nstab-special": "To je specialna strona. Njemóžeš ju wobdźěłać.",
+       "tooltip-ca-nstab-special": "To je specialna strona, kotruž wobdźěłać njemóžeš.",
        "tooltip-ca-nstab-project": "projektowu stronu wobhladać",
        "tooltip-ca-nstab-image": "Datajowu stronu pokazać",
        "tooltip-ca-nstab-mediawiki": "systemowu zdźělenku wobhladać",
index c81c306..ae96253 100644 (file)
        "createaccountreason": "Indoklás:",
        "createacct-reason": "Indoklás",
        "createacct-reason-ph": "Miért hozol létre egy másik fiókot",
-       "createacct-captcha": "Biztonsági ellenőrzés",
-       "createacct-imgcaptcha-ph": "Írd be a szöveget, amit fent látsz",
        "createacct-submit": "Felhasználói fiók létrehozása",
        "createacct-another-submit": "Újabb felhasználói fiók létrehozása",
        "createacct-benefit-heading": "A(z) {{SITENAME}}-t hozzád hasonló emberek készítik.",
index bd773d0..ba0af4e 100644 (file)
        "createaccountreason": "Պատճառը՝",
        "createacct-reason": "Պատճառ",
        "createacct-reason-ph": "Ինչո՞ւ եք փորձում ստեղծել մեկ այլ հաշիվ",
-       "createacct-captcha": "Անվտանգության ստուգում",
-       "createacct-imgcaptcha-ph": "Մուտքագրեք վերը բերված գրվածքը",
        "createacct-submit": "Ստեղծել ձեր հաշիվը",
        "createacct-another-submit": "Ստեղծել մեկ այլ հաշիվ",
        "createacct-benefit-heading": "{{SITENAME}}՝ ստեղծվում է ձեր պես մարդկանց կողմից։",
index 0a177eb..4effd6e 100644 (file)
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Proque crea tu un altere conto?",
-       "createacct-captcha": "Controlo de securitate",
-       "createacct-imgcaptcha-ph": "Scribe le texto que tu vide hic supra",
        "createacct-submit": "Crear tu conto",
        "createacct-another-submit": "Crear conto",
        "createacct-benefit-heading": "{{SITENAME}} es facite per gente como tu.",
index 4f4a6c3..4532ada 100644 (file)
@@ -41,7 +41,8 @@
                        "Totosunarto",
                        "Mirws",
                        "Ilham",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "WongKentir"
                ]
        },
        "tog-underline": "Garis bawahi pranala:",
        "createaccountreason": "Alasan:",
        "createacct-reason": "Alasan",
        "createacct-reason-ph": "Mengapa Anda membuat akun lain",
-       "createacct-captcha": "Pemeriksaan keamanan",
-       "createacct-imgcaptcha-ph": "Masukkan teks yang Anda lihat di atas",
        "createacct-submit": "Buat akun Anda",
        "createacct-another-submit": "Buat akun lain",
        "createacct-benefit-heading": "{{SITENAME}} dibuat oleh orang-orang seperti Anda.",
        "rcshowhidemine": "$1 suntingan saya",
        "rcshowhidemine-show": "Tampilkan",
        "rcshowhidemine-hide": "Sembunyikan",
+       "rcshowhidecategorization-show": "Tampilkan",
+       "rcshowhidecategorization-hide": "Sembunyikan",
        "rclinks": "Perlihatkan $1 perubahan terbaru dalam $2 hari terakhir<br />$3",
        "diff": "beda",
        "hist": "versi",
        "upload-form-label-infoform-description": "Deskripsi",
        "upload-form-label-usage-title": "Penggunaan",
        "upload-form-label-usage-filename": "Nama berkas",
-       "foreign-structured-upload-form-label-own-work": "Ini karya saya sendiri",
+       "foreign-structured-upload-form-label-own-work": "Ini adalah karya saya sendiri",
        "foreign-structured-upload-form-label-infoform-categories": "Kategori",
        "foreign-structured-upload-form-label-infoform-date": "Tanggal",
        "backend-fail-stream": "Tidak bisa mengalikan berkas $1.",
        "changecontentmodel-reason-label": "Alasan:",
        "changecontentmodel-success-title": "Model konten ini telah diubah",
        "changecontentmodel-success-text": "Jenis konten [[:$1]] telah diubah",
+       "logentry-contentmodel-change-revertlink": "batalkan",
+       "logentry-contentmodel-change-revert": "batalkan",
        "protectlogpage": "Log pelindungan",
        "protectlogtext": "Di bawah ini adalah daftar perubahan terhadap perlindungan halaman.\nLihat [[Special:ProtectedPages|daftar halaman terlindungi]] untuk daftar perlindungan halaman terkini.",
        "protectedarticle": "melindungi \"[[$1]]\"",
        "cant-move-to-user-page": "Anda tidak memiliki hak akses untuk memindahkan halaman ke suatu halaman pengguna (kecuali ke subhalaman pengguna).",
        "cant-move-category-page": "Anda tidak memiliki izin untuk memindahkan halaman kategori.",
        "cant-move-to-category-page": "Anda tidak memiliki izin untuk memindahkan halaman ke halaman kategori.",
-       "newtitle": "Ke judul baru:",
+       "newtitle": "Judul baru:",
        "move-watch": "Pantau halaman ini",
        "movepagebtn": "Pindahkan halaman",
        "pagemovedsub": "Pemindahan berhasil",
        "spam_reverting": "Membatalkan ke versi terakhir yang tak memiliki pranala ke $1",
        "spam_blanking": "Semua revisi yang memiliki pranala ke $1, kosong",
        "spam_deleting": "Semua revisi yang memiliki pranala ke $1, penghapusan",
-       "simpleantispam-label": "Pemeriksaan anti-spam.\n'''Jangan''' mengisi ini!",
+       "simpleantispam-label": "Pemeriksaan anti-spam.\n<strong>Jangan</strong> diisi!",
        "pageinfo-title": "Informasi untuk \"$1\"",
        "pageinfo-not-current": "Maaf, tidak mungkin memberikan informasi ini ke revisi lama.",
        "pageinfo-header-basic": "Informasi dasar",
        "autosumm-replace": "←Mengganti halaman dengan '$1'",
        "autoredircomment": "←Mengalihkan ke [[$1]]",
        "autosumm-new": "←Membuat halaman berisi '$1'",
+       "autosumm-newblank": "Membuat halaman kosong",
        "lag-warn-normal": "Perubahan yang lebih baru dari $1 {{PLURAL:$1|detik|detik}} mungkin tidak muncul di daftar ini.",
        "lag-warn-high": "Karenanya besarnya keterlambatan basis data server, perubahan yang lebih baru dari $1 {{PLURAL:$1|detik|detik}} mungkin tidak muncul di daftar ini.",
        "watchlistedit-normal-title": "Sunting daftar pantauan",
        "version-entrypoints-header-url": "URL",
        "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath Artikel path]",
        "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Skrip path]",
+       "version-libraries-library": "Perpustakaan",
+       "version-libraries-version": "Versi",
+       "version-libraries-license": "Lisensi",
+       "version-libraries-description": "Deskripsi",
+       "version-libraries-authors": "Pembuat",
        "redirect": "Pengalihan berdasarkan ID berkas, pengguna, halaman atau revisi",
        "redirect-legend": "Pengalihan ke sebuah berkas atau halaman",
        "redirect-summary": "Halaman istimewa ini beralih ke berkas (sesuai nama berkasnya), halaman (sesuai ID revisinya), atau halaman pengguna (sesuai ID numerik penggunanya). Penggunaan: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], atau [[{{#Special:Redirect}}/user/101]].",
        "tags-tag": "Nama tag",
        "tags-display-header": "Tampilan di daftar perubahan",
        "tags-description-header": "Deskripsi lengkap atau makna",
+       "tags-source-header": "Sumber",
        "tags-active-header": "Aktif?",
        "tags-hitcount-header": "Perubahan bertag",
+       "tags-actions-header": "Tindakan",
        "tags-active-yes": "Ya",
        "tags-active-no": "Tidak",
+       "tags-source-none": "Tidak digunakan lagi",
        "tags-edit": "sunting",
        "tags-delete": "hapus",
        "tags-activate": "aktifkan",
        "tags-deactivate": "nonaktifkan",
        "tags-hitcount": "$1 {{PLURAL:$1|perubahan}}",
+       "tags-create-heading": "Buat sebuah tag baru",
+       "tags-create-reason": "Alasan:",
+       "tags-create-submit": "Buat",
+       "tags-delete-reason": "Alasan:",
+       "tags-activate-reason": "Alasan:",
+       "tags-activate-submit": "Aktifkan",
+       "tags-deactivate-reason": "Alasan:",
+       "tags-deactivate-submit": "Matikan",
+       "tags-edit-reason": "Alasan:",
        "comparepages": "Bandingkan halaman",
        "compare-page1": "Halaman 1",
        "compare-page2": "Halaman 2",
        "htmlform-cloner-create": "Tambahkan lebih banyak",
        "htmlform-cloner-delete": "Hapus",
        "htmlform-cloner-required": "Paling sedikit satu nilai diperlukan.",
+       "htmlform-title-not-exists": "[[:$1]] tidak ada.",
        "sqlite-has-fts": "$1 dengan dukungan pencarian teks lengkap",
        "sqlite-no-fts": "$1 tanpa dukungan pencarian teks lengkap",
        "logentry-delete-delete": "$1 {{GENDER:$2|menghapus}} halaman $3",
        "rightsnone": "(tidak ada)",
        "revdelete-summary": "ringkasan",
        "feedback-adding": "Menambahkan umpan balik ke halaman...",
+       "feedback-back": "Kembali",
        "feedback-bugcheck": "Hebat! Hanya periksa bahwa itu bukan satu di antara [$1 bug yang telah dikenal].",
        "feedback-bugnew": "Saya telah memeriksa. Laporkan bug baru",
        "feedback-bugornote": "Jika Anda sudah siap untuk mendeskripsikan masalah teknis secara rinci silakan [$1 melaporkan bug].\nJika tidak, Anda dapat menggunakan formulir mudah di bawah ini. Komentar Anda akan ditambahkan ke halaman \"[$3 $2]\", bersama dengan nama pengguna Anda dan apa browser yang Anda gunakan.",
        "feedback-cancel": "Batal",
        "feedback-close": "Selesai",
+       "feedback-error-title": "Kesalahan",
        "feedback-error1": "Galat: Hasil tidak dikenal dari API",
        "feedback-error2": "Galat: Penyuntingan gagal",
        "feedback-error3": "Error: API tidak merespons",
        "feedback-subject": "Perihal:",
        "feedback-submit": "Kirim",
        "feedback-thanks": "Terima kasih! Umpan balik Anda telah dikirimkan ke halaman \"[$2 $1]\".",
+       "feedback-thanks-title": "Terima kasih!",
        "searchsuggest-search": "Cari",
        "searchsuggest-containing": "berisi...",
        "api-error-badaccess-groups": "Anda tidak diizinkan mengunggah berkas ke wiki ini.",
index bb09356..f0b98c9 100644 (file)
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Perché stai creando un'altra utenza",
-       "createacct-captcha": "Controllo di sicurezza",
-       "createacct-imgcaptcha-ph": "Inserisci il testo che vedi sopra",
        "createacct-submit": "Crea la tua utenza",
        "createacct-another-submit": "Crea utenza",
        "createacct-benefit-heading": "{{SITENAME}} cresce grazie a persone come te.",
index 5b9f114..05f0f7a 100644 (file)
@@ -74,6 +74,7 @@
        "tog-hideminor": "最近の更新に、細部の編集を表示しない",
        "tog-hidepatrolled": "最近の更新に、巡回済みの編集を表示しない",
        "tog-newpageshidepatrolled": "新しいページの一覧に、巡回済みのページを表示しない",
+       "tog-hidecategorization": "ページのカテゴリ化を隠す",
        "tog-extendwatchlist": "ウォッチリストを拡張し、最新のものだけではなくすべての変更を表示",
        "tog-usenewrc": "最近の更新とウォッチリストで、複数の変更をページごとにまとめる",
        "tog-numberheadings": "見出しに番号を自動的に振る",
        "tog-watchlisthideliu": "ログイン利用者による編集をウォッチリストに表示しない",
        "tog-watchlisthideanons": "匿名利用者による編集をウォッチリストに表示しない",
        "tog-watchlisthidepatrolled": "巡回済みの編集をウォッチリストに表示しない",
+       "tog-watchlisthidecategorization": "ページのカテゴリ化を隠す",
        "tog-ccmeonemails": "他の利用者に送信したメールの控えを自分にも送信",
        "tog-diffonly": "差分の下にページ内容を表示しない",
        "tog-showhiddencats": "隠しカテゴリを表示",
        "createaccountreason": "理由:",
        "createacct-reason": "理由",
        "createacct-reason-ph": "アカウントを作成する理由",
-       "createacct-captcha": "自動作成防止チェック",
-       "createacct-imgcaptcha-ph": "上に表示されている文字列を入力",
        "createacct-submit": "アカウントを作成",
        "createacct-another-submit": "アカウントを作成",
        "createacct-benefit-heading": "{{SITENAME}}は、あなたのような人々が創っています。",
        "svg-long-error": "無効な SVG ファイル: $1",
        "show-big-image": "元のファイル",
        "show-big-image-preview": "このプレビューのサイズ: $1。",
+       "show-big-image-preview-differ": "この $2 ファイルのこの $3 プレビューのサイズ: $1.",
        "show-big-image-other": "その他の{{PLURAL:$2|解像度}}: $1。",
        "show-big-image-size": "$1 × $2 ピクセル",
        "file-info-gif-looped": "ループします",
index 9c082b8..f5afec6 100644 (file)
        "createaccountreason": "Alesan:",
        "createacct-reason": "Alesan",
        "createacct-reason-ph": "Kenapa sampeyan nggawe akun liyane",
-       "createacct-captcha": "Periksa kamanan",
-       "createacct-imgcaptcha-ph": "Lebokna tulisan ing dhuwur",
        "createacct-submit": "Gawé akun",
        "createacct-another-submit": "Gawé akun anyar",
        "createacct-benefit-heading": "{{SITENAME}} digawe karo wong-wong kaya sampeyan.",
index 01e467f..c693f22 100644 (file)
        "createaccountreason": "მიზეზი:",
        "createacct-reason": "მიზეზი",
        "createacct-reason-ph": "რატომ ქმნით ახალ ანგარიშს?",
-       "createacct-captcha": "უსაფრთხოების შემოწმება",
-       "createacct-imgcaptcha-ph": "შეიყვანეთ ზემოთ მოცემული ტექსტი",
        "createacct-submit": "შექმენით თქვენი ანგარიში",
        "createacct-another-submit": "ანგარიშის შექმნა",
        "createacct-benefit-heading": "{{SITENAME}} შექმნილია თქვენნაირი ადამიანების მიერ.",
        "templatesused": "ამ გვერდზე გამოყენებული {{PLURAL:$1|თარგი|თარგები}}:",
        "templatesusedpreview": "{{PLURAL:$1|თარგი, რომელიც|თარგები, რომლებიც}} წინასწარ გადახედვის გვერდზეა გამოყენებული:",
        "templatesusedsection": "ამ სექციაში გამოყენებული {{PLURAL:$1|თარგი|თარგები}}:",
-       "template-protected": "(დაცული)",
+       "template-protected": "(დაცული)",
        "template-semiprotected": "(ნახევრად დაცული)",
        "hiddencategories": "ეს გვერდი გაერთიანებულია $1 დამალულ კატეგორიაში.",
        "edittools": "<!-- აქ განთავსებული ტექსტი ნაჩვენები იქნება რედაქტირებისა და ატვირთვის ფორმების ქვეშ. -->",
index 4a042ed..af5b715 100644 (file)
        "createaccountreason": "មូលហេតុ៖",
        "createacct-reason": "មូលហេតុ",
        "createacct-reason-ph": "មូលហេតុដែលអ្នកចង់បង្កើតគណនីមួយទៀត",
-       "createacct-captcha": "ត្រួតពិនិត្យសុវត្ថិភាព",
-       "createacct-imgcaptcha-ph": "បញ្ចូលឃ្លាដែលអ្នកឃើញខាងលើ",
        "createacct-submit": "បង្កើតគណនីរបស់អ្នក",
        "createacct-another-submit": "បង្កើតគណនី",
        "createacct-benefit-heading": "{{SITENAME}} ត្រូវបង្កើតឡើងដោយបុគ្គលស្ម័គ្រចិត្តដូចជាអ្នកជាដើម។",
index 52aea4a..7b783cc 100644 (file)
        "createaccountreason": "이유:",
        "createacct-reason": "이유",
        "createacct-reason-ph": "왜 다른 계정을 만들어야 합니까",
-       "createacct-captcha": "보안 검사",
-       "createacct-imgcaptcha-ph": "위에 보이는 텍스트를 입력하세요",
        "createacct-submit": "계정 만들기",
        "createacct-another-submit": "계정 만들기",
        "createacct-benefit-heading": "{{SITENAME}} 프로젝트는 여러분과 같은 사람들이 만듭니다.",
index 5958156..ba06d01 100644 (file)
        "createaccountreason": "Jrond:",
        "createacct-reason": "Der Jrond udder Aanlaß",
        "createacct-reason-ph": "Woröm deihs De noch ene Zohjang aanlääje?",
-       "createacct-captcha": "Zor Sescherheit",
-       "createacct-imgcaptcha-ph": "Jiv dä Täx en, dä De heh drövver sühs!",
        "createacct-submit": "Lohß Jonn!",
        "createacct-another-submit": "Donn jäz enne zohsäzlejje Zohjang aanlääje",
        "createacct-benefit-heading": "{{ucfirst:{{GRAMMAR:Nominative|{{ucfirst:{{SITENAME}}}}}}}} weed vun Minsche wi Dir jemaat.",
        "permissionserrors": "Dat jeit nit, dat darfs De nit.",
        "permissionserrorstext": "Do häs nit dat Rääch, dat ze maache, {{PLURAL:$1|dä Jrund es:|de Jründe sin:|oohne Jrund.}}",
        "permissionserrorstext-withaction": "Do häs nit dat Rääch $2, {{PLURAL:$1|dä Jrond es:|de Jrönde sin:|ävver ohne aanjävbahre Jrond.}}",
-       "contentmodelediterror": "Di Väsjohn km_mer nit änndere, weil its content model is <code>$1</code>, but the current content model of the page is <code>$2</code>.",
+       "contentmodelediterror": "Di Väsjohn kam_mer nit änndere, weil its content model is <code>$1</code>, but the current content model of the page is <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Opjepaß:''' Do bes om bäste Wääsch, en Sigg neu aanzelähje, di doför ald ens fottjeschmeße woode wohr.\n\nBes förseschtesch un övverlääsch Der, of dat en johde Ideh es, di Sigg widder opzemaache. Domet De Bescheid weiß, heh de Endrääsh em Logbohch vum Sigge-Ömnänne, un em Logbohch vum Sigge-Fottschmihße mem Jrond, woröm di Sigg dohmohls fottjeschmeße woode es:",
        "moveddeleted-notice": "Heh di Sigg es fottjeschmeße.\nE Schtök uß dä Logböhscher fum Sigge_Fottschmihße un fum Sigge-Ömnänne för di Sigg kütt jäz, en dä Hoffnung, dat dat hellef.",
        "moveddeleted-notice-recent": "Schahd, di Sigg wohd en de läzde 24 Schtonde fottjeschmeße.\nDe Enndrähsch för heh di Sigg em Logbohch fum Sigge Fottschmiiße un Ömnänne küntt heh för zom Nohlohre.",
index d4f0b14..48526c8 100644 (file)
        "createaccountreason": "Grond:",
        "createacct-reason": "Grond",
        "createacct-reason-ph": "Fir wat Dir een anere Benotzerkonnt uleet",
-       "createacct-captcha": "Sécherheets-Check",
-       "createacct-imgcaptcha-ph": "Gitt den Text an deen Dir hei driwwer gesitt",
        "createacct-submit": "Äre Benotzerkont uleeën",
        "createacct-another-submit": "Benotzerkont uleeën",
        "createacct-benefit-heading": "{{SITENAME}} gëtt vu Leit wéi Iech gemaach.",
index 75d2db3..bc0f0c0 100644 (file)
@@ -13,6 +13,7 @@
        "tog-hideminor": "قام کئردئن ڤیرایشتیا کوچئک مین آلئشتیا تازە",
        "tog-hidepatrolled": "قام کئردئن ڤیرایشتیا تیە دیار کئردە مین آلئشتیا تازە",
        "tog-newpageshidepatrolled": "بألگە یا تیە دیار کئردە نە مئن نومگە بألگە یا تازە قام کو",
+       "tog-hidecategorization": "قام کئردئن جأرغە کاری بألگە یا",
        "tog-extendwatchlist": "سئیل بأرگە نە سی نئشوٙ دأئن تأموٙم آلئشتیا ڤا کو نە فأقأط سی بیشتئر تازە باڤیا.",
        "tog-usenewrc": "دأسە بأنی آلئشتیا ڤا بألگە د آلئشتیا تازە و سئیل بأرگ",
        "tog-numberheadings": "سأربألگە خود شمارئشت کو",
@@ -42,6 +43,7 @@
        "tog-watchlisthideliu": "ڤبرایشتیایی نە کئ ڤئ دأس کاریاریا ڤامین ئوٙماە أنجوم گئرئتە د سئیل بأرگ نئهوٙ بأک",
        "tog-watchlisthideanons": "ڤیرایئشتیا کاریاریایی کئ نادیارئن د سئیل بأرگ نئهوٙ بأک",
        "tog-watchlisthidepatrolled": "ڤیرایشتیایی که هان د تیە رأس د سئیل بأرگ قام کو",
+       "tog-watchlisthidecategorization": "قام کئردئن جأرغە کاری بألگە یا",
        "tog-ccmeonemails": "ڤورداشتە یا أنجومانامه یا مئنە کئ سی کاریاریا هأنی کئل می کئم سیم کئل بأک",
        "tog-diffonly": "بألگە یایی کئ د ڤأر گئرتە فأرخیا هارئن دیاریشوٙ بأک",
        "tog-showhiddencats": "دأسە یا نئهوٙ بییأنئ دیاری بأک",
        "createaccountreason": "دألیل:",
        "createacct-reason": "دألیل",
        "createacct-reason-ph": "سی چی شوما داریت یئ گئل حئساڤ هأنی راس میکید",
-       "createacct-captcha": "ڤارئسی أمنیأت دار بییئن",
-       "createacct-imgcaptcha-ph": "نیسئسە یی نە کئ د ڤارو مئینیت ڤارئد بأکیت",
        "createacct-submit": "حئسأڤ خوتوٙنە راس بأکیت",
        "createacct-another-submit": "یئ گئل حئساڤ هأنی راس بأکیت",
        "createacct-benefit-heading": "{{SITENAME}}  ڤئ دأس خألکی چی شوما رأڤأندیاری بییە.",
        "passwordreset-emailsent-capture": "رازینە گوڤاردئن تازە توٙ سی أنجومانامە توٙ کئ ها د هار کئل بییە.",
        "passwordreset-emailerror-capture": "رازینە گوڤاردئن د أنجومانامە د نۊ زئنە کون کئل بییە،و ڤئ د هار دیاری میکە، ڤألی کئل بییئن ڤئ سی {{GENDER:$2|کاریار}} ناخوش سأرنجوم بییە:$1",
        "changeemail": "أنجومانامە توٙنە آلئشت کاری بأکیت",
-       "changeemail-header": "اÛ\8cÙ\85Û\8cÙ\84 Ø­Ø³Ø§Ù\88تÙ\88Ù\86Ù\87 Ø¢Ù\84شت Ø¨Ú©Û\8cد",
+       "changeemail-header": "ئÛ\8c Ù\81Ù\88رÙ\85ئ Ù\86Û\95 Ø³Û\8c Ø¢Ù\84ئشتکارÛ\8c Ø£Ù\86جÙ\88Ù\85اÙ\86اÙ\85Û\95 ØªÙ\88Ù\99 Ù¾Ù\88ر Ø¨Ø£Ú©Û\8cت. Ø£Ø± Ù\85Û\8cھاÛ\8cت Ú¾Ø£Ø± Ø¬Ù\88Ù\99ر Ø£Ù\86جÙ\88Ù\85اÙ\86اÙ\85Û\95 Û\8cÛ\8c Ù\86Û\95 Ø¯ Ù\85Û\8cÙ\86جا Ø­Ø¦Ø³Ø§Ú¤Ø¦ØªÙ\88Ù\99 Ù¾Ø§Ú©Ø³Ø§ Ø¨Ø£Ú©Û\8cتØ\8c Ø¬Ø§Ú¯Û\95 Ø¯Ø£Ø¦Ù\86 Ø£Ù\86جÙ\88Ù\85اÙ\86اÙ\85Û\95 Ù\86ئ Ø¯ Ú\86ئÙ\86Û\8c Ù\81Ù\88رÙ\85Û\8c Ø­Ø§Ù\84Û\8c Ø¨Ø£Ù\86Û\8cت.",
        "changeemail-passwordrequired": "شوما سی پوشت راستکاری ئی آلئشت بایأد یئ گئل رازینە گوڤاردئن بأزئنیت.",
        "changeemail-no-info": "شوما سی یە کئ د ئی بألگە دأسرئسی داشتوٙییت باس بیاییت ڤامین.",
        "changeemail-oldemail": "تیرنئشوٙن أنجومانامە ئیسئنی:",
        "changeemail-newemail": "تیرنئشوٙن أنجومانامە تازە:",
+       "changeemail-newemail-help": "أر شوما میھایت تیرنئشوٙن أنجفونامە توٙنە ڤئرداریت چئنی جاگە یی نە بایأد حالی بأنیت. شوما. ئوٙسئ أر رازینە گورادئنئتوٙ د ڤیرتوٙ رأتە با نئمی توٙنیت د نۊ زئنەش بأکیت و ھأنی ھیچ جوٙر أنجومانامە یی د ئی ڤیکی ڤئ دأس شوما نئمی رئسە.-",
        "changeemail-none": "(هيش كوم)",
        "changeemail-password": "رازینە گوڤاردئن{{SITENAME}} شوما:",
        "changeemail-submit": "آلئشت دأئن أنجومانامە",
        "rcshowhidemine": "ڤیرایئشتیا مئ $1",
        "rcshowhidemine-show": "نئشوٙ دأئن",
        "rcshowhidemine-hide": "قام کئردئن",
+       "rcshowhidecategorization": "جأرغە کاری بألگە $1",
+       "rcshowhidecategorization-show": "نئشوٙ دأئن",
+       "rcshowhidecategorization-hide": "قام کئردئن",
        "rclinks": "آخرین آلشتیا $1 نشو بیه د اخرین روزیا $2",
        "diff": "فأرخ",
        "hist": "ڤیرگار",
        "boteditletter": "ب",
        "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 دینه {{PLURAL:$1|کاریار|کاریاریا}}]",
-       "rc_categories": "دسÙ\87 Û\8cا Ù\86Ù\87 Ù\85حدÙ\88د Ú©Ù\88\88ا \"|\" Ø¬Ú¯Ø§ Ø¨Ù\88ئÙ\86",
-       "rc_categories_any": "هرکوم",
+       "rc_categories": "جأرغÛ\95 Û\8cا Ù\86Û\95 Ù\85أدÙ\88Ù\99د Ú©Ù\88(ڤا \"|\" Ø¬Ø¦Ú¯Ø§ Ø¨Ø§Ù\86)",
+       "rc_categories_any": "ھأرکوم کئ گولئ ڤورچیە بیینە",
        "rc-change-size": "$1",
        "rc-change-size-new": "$1 {{PLURAL:$1|بایت|بایتیا}} نئها آلئشتکاری",
        "newsectionsummary": "/* $1 */ بهرجا تازه",
        "recentchangeslinked-summary": "ای نوم گه تازه د بلگیایی که وا بلگیا ویجه هوم پیوند بینه آلشت بیه(یا سی اندومیا دسه بنی بیه)\nبلگیا یی که هان [[Special:Watchlist|your watchlist]]و گپ بینه",
        "recentchangeslinked-page": "نوم بألگە:",
        "recentchangeslinked-to": "آلشتیایی که د بلگه یا هوم پیوند بینه وه جا بلگه دئیه بیه نشو بیه",
+       "recentchanges-page-added-to-category": "[[:$1]]د دأسە ئضاف بی",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] و {{PLURAL:$2|بألگە تأکی|$2 بألگە یا}} د دأسە ئضاف بییئن",
+       "recentchanges-page-removed-from-category": "[[:$1]] د دأسە جئگا بی",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] و {{PLURAL:$2|بألگە تأکی|$2 بألگە یا}} د دأسە ئضاف بییئن",
+       "autochange-username": "آلئشتکاری خودأنجوم مئدیاڤیکی",
        "upload": "سوڤار کئردئن جانیا",
        "uploadbtn": "سوڤار کئردئن جانیا",
        "reuploaddesc": "سوار کردن نه انجوم شیو بکیت و د ورئردیت جابلگ سوارکرد",
        "uploaderror": "خأطا د سوڤار کئردئن",
        "upload-recreate-warning": "'''زئنار: جانیایی که وه ای نومه یا پاکسا بیه یا جا وه جا بیه.'''\n\nسی رائتی، نومگه پاکساگری و جا وه جا کردن ای بلگه ها د هار:",
        "uploadtext": "دسی دئین جانیایی که  د دماتر سوار بینه روئیت وه  [[Special:FileList|نوم گه جانیایا]] . د نو سوارکردن  د [[Special:Log/upload|نوم گه سوارکردیا]] و پاکساگری جانیایا د [[Special:Log/delete|deletion log]] جاگئر موئه.\n\nنها یه که یه گل جانیانه سوار کردیت می تونیت د سه شکل هاری وه نه د بلگه یا وه کار بونیت:\n*'''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''' سی به کار بسن نسقه کامل جانیا\n*'''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|alt text]]</nowiki></code>''' سی به کار بسن یه گل نسقه 200 پیکسلی  د جانیایی که ها د مینه یه گل جعوه  که ها د لا چپ نیسسه که د وه عبارت alt text چی توضیح وه کار بسه بیه\n*'''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>''' سی دروس کردن یه گل هوم پیوند سرراست وه جانیا بی یه که جانیا دیاری بکه نوم بلگه هاری سی سوارکرد جانیایا تازه استفاده بکیت.",
-       "upload-permitted": "جورا جانیا صلادار:$1 .",
-       "upload-preferred": "جورا جانیا صلادار:$1.",
+       "upload-permitted": "جوٙرا صئلادار جانیا {{PLURAL:$2|جوٙر|جوٙرا}}:$1 .",
+       "upload-preferred": "جوٙرا حاستئنی جانیا {{PLURAL:$2|جوٙر|جوٙرا}}:$1 .",
        "upload-prohibited": "جورا جانیا صلادار:$1{{PLURAL:$2|.}}",
        "uploadlogpage": "سوارکرد",
        "uploadlogpagetext": "نومگه هاری یه گل نومگه د آخری سوارکرد جانیایا هئ.\nسی د نو سیل کردن[[Special:NewFiles|عسگدونی جانیایا تازه نه]] به ونیت.",
        "upload-too-many-redirects": "ای یو آر ال د ورگیرنه واگردونیا فرئی هئ",
        "upload-http-error": "یه گل خطا اچ تی تی پی پیش اومائه:$1",
        "upload-copy-upload-invalid-domain": "ورداشتن سوارکردیا د ای پوشگئر د دسرس نئ.",
+       "upload-dialog-title": "سوڤار کئردئن جانیا",
+       "upload-dialog-button-cancel": "أنجوم شیڤئسئن",
        "upload-dialog-button-done": "أنجوم بییە",
        "upload-dialog-button-save": "ئمایە کئردئن",
        "upload-dialog-button-upload": "سوڤار کئردئن",
+       "upload-form-label-select-file": "ئنتئخاڤ جانیا",
+       "upload-form-label-infoform-title": "جوزئیات",
        "upload-form-label-infoform-name": "نوم",
        "upload-form-label-infoform-description": "توضی",
        "upload-form-label-usage-title": "ڤئ کار گئرئتئن",
        "upload-form-label-usage-filename": "نوم جانیا",
+       "foreign-structured-upload-form-label-own-work": "یە کار مئنە",
+       "foreign-structured-upload-form-label-infoform-categories": "دأسە یا",
+       "foreign-structured-upload-form-label-infoform-date": "گات",
        "backend-fail-stream": "نبوئه جانیا\"$1\" کل بوئه.",
        "backend-fail-backup": "نبوئه سی \"$1\" پشتجا گرت.",
        "backend-fail-notexists": "جانیا $1 وجود ناره.",
        "randomincategory-nopages": "هیچ بلگه ای د ای  [[:دسه نئ:$1|$1]] دسه.",
        "randomincategory-category": "دأسە:",
        "randomincategory-legend": "بلگه بختی د ای دسه",
+       "randomincategory-submit": "رو",
        "randomredirect": "واگردونی بختکی",
        "randomredirect-nopages": "هیچ واگردونی د نومجا \"$1\" نئ.",
        "statistics": "آماريا",
        "booksources-text": "د هار نومگه ای د هوم پیوندیا د دیارگه یا هنی اومائه که کتاویا نو و دس دوئم می فروشن، و همچنو شایت دونسمنیا بیشتری راجع وه کتاو حاستنی شما داشتوئن:",
        "booksources-invalid-isbn": "شازک که دئه بیه معتور نئ؛ وارسی خطایا د گات ؤرداشتن د سرچشمه اولی وه کار گرته بوئه.",
        "specialloguserlabel": "انجومکار:",
-       "speciallogtitlelabel": "حاستنی(موضوع یا کاریار):",
+       "speciallogtitlelabel": "حاستئنی(داسوٙن یا نوم کاریاری سی کاریار):",
        "log": "پهرستنومه یا",
        "all-logs-page": "همه پهرستنومه یا عمومی",
        "alllogstext": "نماشت یه جا همه پهرستنومه یا که هان د{{SITENAME}}.\nمی تونید وا انتخاو نوع پهرستنومه، نوم کاریاری(حساس وه کؤچکی و گپی حرفیا) و بلگه یا آلشت کرده(حساس و گپی و کؤچکی حرنیا) نمایشت نه دیر د ویرتر بکیت.\n\n{{SITENAME}}.",
        "listgrouprights-members": "(نوم گه اندومیا)",
        "listgrouprights-right-display": "<span class=\"listgrouprights-granted\">$1 <code>($2)</code></span>",
        "listgrouprights-right-revoked": "<span class=\"listgrouprights-revoked\">$1 <code>($2)</code></span>",
-       "listgrouprights-addgroup": "{{PLURAL:$2|گرویا|گرویا}} اضاف بکیتو: $1",
-       "listgrouprights-removegroup": "{{PLURAL:$2|گرویا|گرویا}} ورداریت: $1",
-       "listgrouprights-addgroup-all": "همه گرویا نه اضاف کو",
-       "listgrouprights-removegroup-all": "همه گرویا نه وردار",
-       "listgrouprights-addgroup-self": " {{PLURAL:$2|گروه|گرویا}} نه د حساو: $1 اضاف کو",
-       "listgrouprights-removegroup-self": "{{PLURAL:$2|گرویا|گرویا}} نه د حساو ورداریت: $1",
+       "listgrouprights-addgroup": "{{PLURAL:$2|جأرغە|جأرغە یا}} نە ئضاف بأکیت: $1",
+       "listgrouprights-removegroup": "{{PLURAL:$2|جأرغە|جأرغە یا}} نە ڤئرداریت: $1",
+       "listgrouprights-addgroup-all": "هأمە جأرغە یا نە ئضاف کو",
+       "listgrouprights-removegroup-all": "ھأمە جأرغە یا نە ڤئردار",
+       "listgrouprights-addgroup-self": " {{PLURAL:$2|جأرغە|جأرغە یا}} نە د حئساڤ: $1 ئضاف کو",
+       "listgrouprights-removegroup-self": " {{PLURAL:$2|جأرغە|جأرغە یا}} نە د حئساڤ: $1 ڤئرداریت",
        "listgrouprights-addgroup-self-all": "همه گرویا نه د حساو خوشو اضاف بکیت",
        "listgrouprights-removegroup-self-all": "همه گرویا نه د حساو خوشو ورداریت",
        "listgrouprights-namespaceprotection-header": "محدودیت نومجا",
        "listgrouprights-namespaceprotection-namespace": "نوم جا",
        "listgrouprights-namespaceprotection-restrictedto": "دسرسیا مجاز کاریار سی ویرایشت",
-       "trackingcategories": "دما گری دسه یا",
+       "trackingcategories": "دئماگئری دأسە یا",
        "trackingcategories-summary": "ای بلگه نومگه دسه یایی دماگری بیه ئه که وه شکل خودانجوم وه دس ویکی وارسگر پر بوئن . نومیا ونو نها آلشت کردن پیغومیا سامونه ای مرتوط د نومجا {{ns:8}} آلشت دئه با.",
-       "trackingcategories-msg": "دماگری دسه",
-       "trackingcategories-name": "نوم پیغوم",
+       "trackingcategories-msg": "دئماگئری دأسە",
+       "trackingcategories-name": "نوم پئیغوم",
        "trackingcategories-desc": "جادیارکنیا گنجایشت دسه",
        "noindex-category-desc": "ای بلگه وا رباتیا نومگه کاری نبیه و سی یه کلیمه یا جادویی <code><nowiki>__NOINDEX__</nowiki></code> د وه یا د جاگه ای که بیرق مجازه دش هئ.",
        "index-category-desc": "ای بلگه<code><nowiki>__INDEX__</nowiki></code> که ها دش(و د نومجایی یه که بیرق دش مجازه)،  سی یه وا رباط ما مشگلی ناره که وه شکل عادی نباید با.",
        "trackingcategories-disabled": "دسه ناکشتگر بیه",
        "mailnologin": "هیپچ نشونی یی کل نبیه",
        "mailnologintext": "سی کل کردن انجومانامه وه کاریاریا هنی واس [[Special:UserLogin|بیایت وامین سامونه]] و تیرنشون انجومانامه معتوری د [[Special:Preferences|ترجیحات]] خوتو داشتوئیت.",
-       "emailuser": "اÛ\8c Ù\83ارÙ\88ر Ù\86Ù\87 Ø§Ù\8aÙ\85Ù\8aÙ\84 Ù\83و",
-       "emailuser-title-target": "اÛ\8cÙ\85Û\8cÙ\84 Ø³Û\8c Ø§Û\8c {{GENDER:$1|کارÙ\88ر}}",
+       "emailuser": "ئÛ\8c Ú©Ø§Ø±Û\8cار Ù\86Û\95 Ø£Ù\86جÙ\88Ù\85اÙ\86اÙ\85Û\95 Ú©Ø§Ø±Û\8c Ú©و",
+       "emailuser-title-target": "Ø£Ù\86جÙ\88Ù\85اÙ\86اÙ\85Û\95 Ø³Û\8c Ø¦Û\8c {{GENDER:$1|کارÛ\8cار}}",
        "emailuser-title-notarget": "أنجومانامە کاریار",
        "emailpagetext": "شما می تونیت  نوم بلگه هار نه سی کل کردن یه گل انجومانامه وه ای  {{GENDER:$1|کاریار}} وه کار بئیرت.\nتیرنشون انجومانامه یی که د [[Special:Preferences|ترجیحات کاریارتو]] دئیه ته د تیرنشون کلکار انجومانامه میا، سی یه که گیرنه بتونه جواوش بیه.",
        "defemailsubject": "{{نوم سیل جا}} ایمیل د کارور \"$1\"",
        "email-legend": "کل کردن یه گل انجومانامه سی یه گل کاریار هنی د {{SITENAME}}",
        "emailfrom": "د:",
        "emailto": "سی:",
-       "emailsubject": "سرÙ\88ن:",
-       "emailmessage": "پيغوم:",
-       "emailsend": "كل بيه",
+       "emailsubject": "داسÙ\88Ù\99ن:",
+       "emailmessage": "پئیغوٙم",
+       "emailsend": "کئل کئردئن",
        "emailccme": "یه گل وردار د پیغوم مه نه سیم ایمیل کو.",
        "emailccsubject": " پیغومتو سی $1:$2 ورداشته بی",
-       "emailsent": "اÛ\8cÙ\85Û\8cÙ\84 Ú©Ù\84 Ø¨Û\8cÙ\87",
+       "emailsent": "Ø£Ù\86جÙ\88Ù\85اÙ\86اÙ\85Û\95 Ú©Ø¦Ù\84 Ø¨Û\8cÛ\8cÛ\95",
        "emailsenttext": "پیغوم ایمیلی تو کل بیه.",
        "emailuserfooter": "ای انجومانامه وا به کار گرتن د خصوصیت \"کل کردن نومه د ای کاریار\"{{SITENAME}} وه دس $1 به $2 کل بی.",
        "usermessage-summary": "رئتن د سامونه پیغوم",
        "usermessage-editor": "پیغوم فرسن سیستم",
        "usermessage-template": "ویکی وارسگر:پیغوم کاریار",
-       "watchlist": "سیل برگ",
-       "mywatchlist": "سیل برگ",
+       "watchlist": "سئیل بأرگ",
+       "mywatchlist": "سئیل بأرگ",
        "watchlistfor2": "سي $1 $2",
        "nowatchlist": "شما هیچی د سیل برگ خوتو ناریت",
        "watchlistanontext": "لطفن بیایت وامین و ویرایشتیا نه د سیل برگتو سیل بکیت.",
-       "watchnologin": "وارد نبیه",
-       "addwatch": "اضاÙ\81 Ú©Ø±Ø¯Ù\86 Ø¯ Ø³Û\8cÙ\84 Ø¨رگ",
+       "watchnologin": "ھأنی نیوٙماە ڤامین",
+       "addwatch": "ئضاÙ\81 Ú©Ø¦Ø±Ø¯Ø¦Ù\86 Ø¯ Ø³Ø¦Û\8cÙ\84 Ø¨Ø£رگ",
        "addedwatchtext": "بلگه «[[:$1]]» د [[Special:Watchlist|نومگه دماگردی]] شما اضاف بی.\nآلشتیا ای بلگه بلگه چک چنه ری وه ریش د نهاتر د ایچه نومگه کاری بوئه.",
        "addedwatchtext-short": "بلگه \"$1\" وه سیل برگ شما اضاف بیه.",
-       "removewatch": "جا وه جا کردن د سیل برگ",
+       "removewatch": "جا ڤئ جا کئردئن د سئیل بأرگ",
        "removedwatchtext": "بلگه\"[[:$1]]\" د [[Special:سیل برگ|سیل برگ خوتو]] جا وه جا بیه.",
        "removedwatchtext-short": "بلگه \"$1\" د سیل برگ جا وه جا بیه.",
-       "watch": "سيل كردن",
-       "watchthispage": "ديئن ای بلگه",
-       "unwatch": "ديه نبيه",
-       "unwatchthispage": "Ù\88اداشتÙ\86 دیئن",
-       "notanarticle": "مینونه هیچ بلگه ای نئ",
+       "watch": "سئیل کئردئن",
+       "watchthispage": "دیئن ئی بألگە",
+       "unwatch": "دیە نأبییە",
+       "unwatchthispage": "Ù\86ئھاگئرÛ\8c دیئن",
+       "notanarticle": "ھیچ بألگە مینوٙنە داری نی",
        "notvisiblerev": "آخری وانئری که د دس یه کاریار هنی انجوم بیه پاکسا بیه.",
        "watchlist-details": "{{PLURAL:$1|$1 بلگه|$1 بلگیا}} د سیل برگتو هیش بلگه قسه کردن نی.",
-       "wlheader-enotif": "وارسیاری ایمیل فعال بیه.",
+       "wlheader-enotif": "ڤارئسیاری أنجومانامە کونئشتکار بییە.",
        "wlheader-showupdated": "بلگه یایی که د آخرین کرتی که شما دشو دیئن کردیته آلشت بینه د <strong>توپر</strong>نشون دئه بینه",
        "wlnote": "د هار {{PLURAL:$1|آلشت|<strong>$1</strong> آلشتی}} که د {{PLURAL:$2|ساعت|<strong>$2</strong> ساعت}} دماتر انجوم بیه هیئش، ویرگار آخرین واجوری انجام شده موجود است، ویرگار آخری واجوری: $3، $4",
        "wlshowlast": "آخرین$1 ساعتیا $2و روزیا  نشو بیئه",
-       "watchlist-options": "گزÛ\8cÙ\86Û\8cا Ø³Û\8cÙ\84 Ø¨رگ",
-       "watching": "د حال دیئن...",
+       "watchlist-options": "Ú\86Û\8cا Ø³Ø¦Û\8cÙ\84 Ø¨Ø£رگ",
+       "watching": "د حال و بال دیئن...",
        "unwatching": "د حال ندیئن...",
        "watcherrortext": "یه گل اشگال د گات آلشت کردن میزونکاری نومگه سیل برگتو سی «$1» پیش اوما.",
        "enotif_reset": "همه بلگه یا دیئه بینه نشودار بکید",
        "enotif_body_intro_changed": "{{SITENAME}} بلگه $1 د ویرگار $PAGEEDITDATE وه دس {{gender:$2|$2}} آلشت بیه، سی وانئری ایسنی سیل $3 بکیت.",
        "enotif_lastvisited": "همه آلشتیا$1 د اوسه که شما د آخرین بار دیئته بوینیت.",
        "enotif_lastdiff": "سی دیئن ای آلشتیا $1 نه سیل بکیت.",
-       "enotif_anon_editor": "کارو ناشناس$1",
+       "enotif_anon_editor": "کاریار نادیار $1",
        "enotif_body": "$WATCHINGUSERNAME نازار،\n\n$PAGEINTRO $NEWPAGE\n\n\nتوضیح ویراشتکار: $PAGESUMMARY $PAGEMINOREDIT\n\nپیوند گرتن وا ویراشتکار:\nنومه: $PAGEEDITOR_EMAIL\nویکی: $PAGEEDITOR_WIKI\n\nتا گاتی که سر نه دئیته وه بلگه، د حال و بار پیش اومائن ائتمالی کنشتیاری بیشتر، تا گاتی که وا نوم کاریاریتو هایت د سامونه، گوته دیاری سی شما کل نبوئه.\nشما همچنی می تونید د بلگه دماگریا خوتو بیرقیا مربوط وه وارسکاری نه صفر بکیت همچنی می تونیت بیرقیا وارسکاری نه د نو نشوکاری بکیت.\n\nدوسیار شما، سامونه وارسکاری {{SITENAME}}\n\n--\nسی آلشت دئن میزونکاریا نومگه انجومانامه یا گوته دیاری روئیت وه {{canonicalurl:{{#special:EditWatchlist}}}}.\n\nسی آلشت دئن میزونکاری نومگه دماگریاتو روئیت وه {{canonicalurl:{{#special:EditWatchlist}}}}.\n\nسی پاکساکاری بلگه د نومگه دماگریاتو روئیت د $UNWATCHURL.\n\nبازحرد و هومیاری بیشتر:\n$HELPPAGE",
        "created": "دروس بیه",
        "changed": "آلشت بیه",
-       "deletepage": "پاک کردن بلگه",
-       "confirm": "Ù\85ئÙ\83Ù\85 Ù\83ردن",
-       "excontent": "Ù\85Û\8cÙ\86Ù\88Ù\86Ù\87 :\"$1\" بی",
+       "deletepage": "پاکسا کئردئن بألگە",
+       "confirm": "Ù¾Ù\88شت Ø±Ø§Ø³ Ú©Ø§Ø±Û\8c Ú©Ø¦Ø±Ø¯Ø¦ن",
+       "excontent": "Ù\85Û\8cÙ\86Ù\88Ù\99Ù\86Û\95 :\"$1\" بی",
        "excontentauthor": "مینونه بلگه یه بی: «$1» (و تئنا هومیار«[[Special:Contributions/$2|$2]]» بی)",
        "exbeforeblank": "مینونه حالی دمایی:\"$1\" بی",
-       "delete-confirm": "پاکسا Ú©Ø±Ø¯Ù\86\"$1\"",
-       "delete-legend": "پاك كردن",
+       "delete-confirm": "پاکسا Ú©Ø¦Ø±Ø¯Ø¦Ù\86 \"$1\"",
+       "delete-legend": "پاکسا کئردئن",
        "historywarning": "<strong>هشدار:</strong> بلگه یی که شما میهایت پاکساش بکیت دش یه گل ویرگارچه واگرد $1 {{PLURAL:$1|وانئری|وانئریا}} ئه:",
        "confirmdeletetext": "شما د حال و بار پاکسا کردن یه گل بلگه یا عسگ د رسینه جا واگرد همه ویرگارچه ونیت.\nلطف بکیت ای کنشتکاری نه پشت راسکاری بکیت و یه دل بوئیت که سرانجوم ای کار نه دونیت و ای کار نه مطابق وا [[{{MediaWiki:Policy-url}}|سیاستیا]] انجوم دئیته.",
        "actioncomplete": "عملكرد كامل بيه",
        "dellogpagetext": "نومگه هاری یه گل نومگه د آخری چیا پاکسا بیه هئ.",
        "deletionlog": "پهرستنومه پاک بیئن",
        "reverted": "لرسه د نزیکترین وانئری",
-       "deletecomment": "دليل:",
-       "deleteotherreason": "دليليا هنی:",
-       "deletereasonotherlist": "دلیل هنی",
+       "deletecomment": "دألیل:",
+       "deleteotherreason": "دألیلیا/ئضافی ھأنی:",
+       "deletereasonotherlist": "دألیل ھأنی",
        "deletereason-dropdown": "* دلیلیا پاکسا کردن رسم بیه\n** اسپم\n** خراوکاری\n** رعایت نبین کپی رایت\n** درحاست نیسنه\n** نهاورگشت شکست حرده",
        "delete-edit-reasonlist": "دلیلیا پاکسا کردنه نه ویرایشت بکید",
        "delete-toobig": "ای بلگه ویرگارچه ویرایشت فره گپی داره، که د ور گرته بیشتر د یه گل د $1 {{PLURAL:$1|نسقه|نسقه}} ئه.\nسی یه که د اختلال ناحاستنی د {{SITENAME}} نهاگری با پاکسا کردن ای جوراین بلگه یا محدود بیه.c",
        "rollback-success": "ویرایشتیا $1 پاکساگری بی؛\nبلگه وه آخری ویرایشت $2 آلشت بیه.",
        "sessionfailure-title": "شکست حردن نشینگه",
        "sessionfailure": "چنی وه نظری میا که مشکلی ها د نشتجا کاریاری شما؛\nانجومگر حاستنی سی یه که د دزیه بیین دونسمنیاتو د نشسجا کاریاری نهاگری با انجومشیو بیه.\nلطف بکیت د ری دگمه ؤرئشتن بپورنیت و بلگه یی که رسسینه دش د نو واحونی بکیت، اوسه دنو تلاش بکیت.",
+       "changecontentmodel-title-label": "داسوٙن بألگە",
+       "changecontentmodel-reason-label": "دألیل:",
        "protectlogpage": "پر و پیم کاری کردن",
        "protectlogtext": "د هار یه گل نومگه د آلشتیا ریتراز پر و پیم کاری بلگه یا اومائه.\n[[Special:ProtectedPages|نومگه بلگه یا پر و پیم کار بیه]] نه سی دیئن نومگه پر و پیم کاری کارگرا بلگه یا سیل بکیت.",
        "protectedarticle": "حفاظت بيه [[$1]]",
        "protect-norestrictiontypes-text": "امکان پر و پیم کردن ای بلگه سی یه که نوع محدودیتی ناره، وجود ناره.",
        "protect-norestrictiontypes-title": "بلگه بی حامین گر",
        "protect-legend": "پشت راس کردن حامین گری",
-       "protectcomment": "دلیل:",
+       "protectcomment": "دألیل:",
        "protectexpiry": "تموم بين:",
        "protect_expiry_invalid": "گات تموم بیین نامعتوره.",
        "protect_expiry_old": "گات تموم بیین مال دماتره.",
index c384743..717fbad 100644 (file)
        "helppage-top-gethelp": "Pagalba",
        "mainpage": "Pagrindinis puslapis",
        "mainpage-description": "Pagrindinis puslapis",
-       "policy-url": "Projektas:Politika",
+       "policy-url": "Project:Politika",
        "portal": "Bendruomenė",
        "portal-url": "Project:Community portal",
        "privacy": "Privatumo politika",
        "createaccountreason": "Priežastis:",
        "createacct-reason": "Priežastis",
        "createacct-reason-ph": "Kodėl kuriate kitą paskyrą",
-       "createacct-captcha": "Saugumo patikrinimas",
-       "createacct-imgcaptcha-ph": "Įveskite tekstą, kurį matote aukščiau",
        "createacct-submit": "Sukurkite savo paskyrą",
        "createacct-another-submit": "Sukurti paskyrą",
        "createacct-benefit-heading": "{{SITENAME}} sukurtas žmonių kaip jūs.",
        "permissionserrors": "Teisių klaida",
        "permissionserrorstext": "Jūs neturite teisių tai daryti dėl {{PLURAL:$1|šios priežasties|šių priežasčių}}:",
        "permissionserrorstext-withaction": "Jūs neturite leidimo $2 dėl {{PLURAL:$1|šios priežasties|šių priežasčių}}:",
+       "contentmodelediterror": "Jūs negalite redaguoti šios versijos, nes jos turinio modelis yra <code>$1</code> ir dabartinis puslapio turinio modelis yra <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Dėmesio: Jūs atkuriate puslapį, kuris anksčiau buvo ištrintas.'''\n\nTurėtumėte nuspręsti, ar reikėtų toliau redaguoti šį puslapį.\nJūsų patogumui čia pateikiamas šio puslapio šalinimų ir perkėlimų sąrašas:",
        "moveddeleted-notice": "Šis puslapis buvo ištrintas.\nŽemiau pateikiamas puslapio šalinimų ir pervadinimų sąrašas.",
        "moveddeleted-notice-recent": "Atsiprašome, šis puslapis nesenai buvo ištrintas (per pastarąsias 24 valandas). Puslapio ištrynimo ir perkėlimo istorija yra pateikiama žemiau kaip nuoroda.",
        "upload-options": "Įkėlimo nustatymai",
        "watchthisupload": "Stebėti šią rinkmeną",
        "filewasdeleted": "Failas šiuo vardu anksčiau buvo įkeltas, o paskui ištrintas. Jums reikėtų patikrinti $1 prieš bandant įkelti jį vėl.",
+       "filename-thumb-name": "Tai panašu į miniatiūros pavadinimą. Prašome neįkėlinėti miniatiūrų atgal į tą patį vikį. Kitu atveju, prašome pataisyti failo pavadinimą taip, kad jis būtų prasmingesnis ir neturėtų miniatiūros prefikso.",
        "filename-bad-prefix": "Jūsų įkeliamas failas prasideda su '''„$1“''', bet tai yra neapibūdinantis pavadinimas, dažniausiai priskirtas skaitmeninių kamerų. Prašome suteikti labiau apibūdinantį pavadinimą savo failui.",
        "upload-success-subj": "Įkelta sėkmingai",
        "upload-success-msg": "Jūsų įkėlimas iš [$2] buvo sėkmingas. Jį galima rasti čia: [[:{{ns:file}}:$1]]",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorijos",
        "foreign-structured-upload-form-label-infoform-date": "Data",
        "foreign-structured-upload-form-label-own-work-message-local": "Patvirtinu, kad įkeliu šį failą su šiomis naudojimosi sąlygomis ir licencijavimo politika į {{SITENAME}}.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "Jeigu Jūs negalite įkelti šio failo su {{SITENAME}}  politika, prašome uždaryti dialogą ir pabandyti kitą metodą.",
+       "foreign-structured-upload-form-label-not-own-work-local-local": "Jūs taip pat galite norėti išbandyti [[Special:Upload|numatytąjį įkėlimo puslapį]].",
+       "foreign-structured-upload-form-label-own-work-message-default": "Aš suprantu, kad įkeliu šį failą į dalinimosi repozitoriją. Aš patvirtinu, kad tai darau laikydamasis jų paslaugų teikimo sąlygų ir licencijavimo politikos.",
+       "foreign-structured-upload-form-label-not-own-work-message-default": "Jeigu negalite įkelti šio failo su dalinimosi repozitorijos politika, prašome uždaryti šį dialogą ir bandyti kitą metodą.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "Jūs taip pat galite norėti išbandyti [[Special:Upload|{{SITENAME}} įkėlimo puslapį]], jeigu šis failas gali būti įkeltas su jų politika.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Aš patvirtinu, kad man priklauso šio failo autorinės teisės ir sutinku neatšaukiamai išleisti šį failą į Wikimedia Commons su [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] licencija, ir aš sutinku su [https://wikimediafoundation.org/wiki/Terms_of_Use paslaugų teikimo sąlygomis].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Jeigu Jums nepriklauso šio failo autorinės teisės arba Jūs norite išleisti jį su kitokia licencija, apsvarstykite naudojimą [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons įkėlimo vedlį].",
        "backend-fail-stream": "Negali būti apdorotas failas $1.",
        "backend-fail-backup": "Negali būti išsaugotas failas $1.",
        "backend-fail-notexists": "Failas $1 neegzistuoja.",
index b00c78c..2620562 100644 (file)
        "createaccountreason": "Iemesls:",
        "createacct-reason": "Iemesls",
        "createacct-reason-ph": "Kāpēc jūs veidojat citu kontu",
-       "createacct-captcha": "Drošības pārbaude",
-       "createacct-imgcaptcha-ph": "Ievadiet tekstu, kuru jūs redzat augstāk",
        "createacct-submit": "Izveidot savu kontu",
        "createacct-another-submit": "Izveidot citu dalībnieka kontu",
        "createacct-benefit-heading": "{{SITENAME}} darbojas ar tādu cilvēku kā Tu ieguldījumu.",
index 1112215..1e22c00 100644 (file)
        "createaccountreason": "Причина:",
        "createacct-reason": "Причина",
        "createacct-reason-ph": "Зошто правите друга сметка",
-       "createacct-captcha": "Безбедносна проверка",
-       "createacct-imgcaptcha-ph": "Внесете го гореприкажаниот текст",
        "createacct-submit": "Направи ја",
        "createacct-another-submit": "Создај сметка",
        "createacct-benefit-heading": "{{SITENAME}} е дело на луѓе како вас.",
index 513811e..dfdcc75 100644 (file)
        "createaccountreason": "കാരണം:",
        "createacct-reason": "കാരണം",
        "createacct-reason-ph": "താങ്കൾ എന്തുകൊണ്ടാണ് മറ്റൊരു അംഗത്വം എടുക്കുന്നത്",
-       "createacct-captcha": "സുരക്ഷാ പരിശോധന",
-       "createacct-imgcaptcha-ph": "മുകളിൽ കാണുന്ന എഴുത്ത് ഇവിടെ നൽകുക",
        "createacct-submit": "താങ്കളുടെ അംഗത്വം സൃഷ്ടിക്കുക",
        "createacct-another-submit": "അംഗത്വമെടുക്കുക",
        "createacct-benefit-heading": "താങ്കളെപ്പോലെയുള്ളവരാണ്  {{SITENAME}}  പടുത്തുയർത്തിയിരിക്കുന്നത്.",
index 7248767..5d552a3 100644 (file)
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण",
        "createacct-reason-ph": "आपण दुसरे खाते कां बनवीत आहात",
-       "createacct-captcha": "सुरक्षा तपासणी",
-       "createacct-imgcaptcha-ph": "वर दिसत असलेला मजकूर येथे टाका",
        "createacct-submit": "आपले खाते निर्माण करा",
        "createacct-another-submit": "खाते तयार करा",
        "createacct-benefit-heading": "{{SITENAME}} हे आपल्यासारख्याच लोकांनी बनविलेले आहे.",
        "searchall": "सर्व",
        "showingresults": "#'''$2'''पासून {{PLURAL:$1|'''1'''पर्यंतचा निकाल|'''$1'''पर्यंतचे निकाल}} खाली दाखवले आहे.",
        "showingresultsinrange": "खाली #<strong>$2</strong> ते #<strong>$3</strong> पर्यंतच्या कक्षेतील {{PLURAL:$1|<strong>१</strong> निकाल दाखविला आहे|<strong>$1</strong> निकाल दाखविले आहेत}}.",
+       "search-showingresults": "{{PLURAL:$4|Result <strong>$1</strong> of <strong>$3</strong>|निकाल <strong>$3</strong> चे<strong>$1 - $2</strong>}}",
        "search-nonefound": "दिलेल्या पृच्छेशी जुळणारे निकाल नाहीत.",
        "powersearch-legend": "प्रगत शोध",
        "powersearch-ns": "नामविश्वांमध्ये शोधा:",
index e52f060..04b324e 100644 (file)
        "createaccountreason": "Mutivo:",
        "createacct-reason": "Mutivo",
        "createacct-reason-ph": "Pecché staje crianno n'at'utenza",
-       "createacct-captcha": "Cuntrollo 'e sicurezza",
-       "createacct-imgcaptcha-ph": "Scrivite 'o testo ca vedite ncoppa",
        "createacct-submit": "Cria 'a toja utenza",
        "createacct-another-submit": "Cria nu cunto",
        "createacct-benefit-heading": "{{SITENAME}} è fatta 'e perzone comme te.",
index bcf8572..4adc0c0 100644 (file)
        "createaccountreason": "Årsak:",
        "createacct-reason": "Årsak",
        "createacct-reason-ph": "Hvorfor lager du en annen bruker",
-       "createacct-captcha": "Sikkerhetssjekk",
-       "createacct-imgcaptcha-ph": "Fyll inn teksten du ser ovenfor",
        "createacct-submit": "Opprett konto",
        "createacct-another-submit": "Opprett konto",
        "createacct-benefit-heading": "{{SITENAME}} er laget av folk som deg.",
index d258392..df18827 100644 (file)
        "createaccountreason": "Reden:",
        "createacct-reason": "Reden",
        "createacct-reason-ph": "Waarom u een andere account aanmaakt",
-       "createacct-captcha": "Veiligheidscontrole",
-       "createacct-imgcaptcha-ph": "Geef de tekst in die u hierboven ziet",
        "createacct-submit": "Account aanmaken",
        "createacct-another-submit": "Account aanmaken",
        "createacct-benefit-heading": "{{SITENAME}} wordt gemaakt door mensen zoals u.",
        "watchlistanontext": "Om uw volglijst te bekijken of te bewerken moet u zich aanmelden.",
        "watchnologin": "U bent niet aangemeld",
        "addwatch": "Toevoegen aan volglijst",
-       "addedwatchtext": "\"[[:$1]]\" de overlegpagina zijn toegevoegd aan uw [[Special:Watchlist|volglijst]].",
+       "addedwatchtext": "\"[[:$1]]\" en de bijhorende overlegpagina zijn toegevoegd aan uw [[Special:Watchlist|volglijst]].",
        "addedwatchtext-short": "De pagina \"$1\" is aan uw volglijst toegevoegd.",
        "removewatch": "Verwijderen uit volglijst",
        "removedwatchtext": "\"[[:$1]]\" en de overlegpagina zijn verwijderd van [[Special:Watchlist|uw volglijst]].",
index ebe1506..f146b27 100644 (file)
@@ -30,7 +30,7 @@
        "tog-enotifrevealaddr": "Näytä minun sähköpoštuadressii muile lähetettylöis ilmoituksis",
        "tog-shownumberswatching": "Ozuta tädä sivuu tarkailevien käyttäjien miäriä",
        "tog-oldsig": "Nygöine allekirjutus:",
-       "tog-fancysig": "Allekirjutus wikitekstannu (automuattizettah linkittäh)",
+       "tog-fancysig": "Allekirjutus wikitekstannu (ilmai automuattistu linkii)",
        "tog-uselivepreview": "Käytä välittömiä ezikaččeluu",
        "tog-forceeditsummary": "Huomavuta minuu, gu en olle kirjutannuh yhtehveduo",
        "tog-watchlisthideown": "Peitä minun korjavukset valvonduluvettelospäi",
@@ -47,7 +47,7 @@
        "tog-prefershttps": "Käytä ainos suojattuu yhtevytty ku olet kirjutannuhes",
        "underline-always": "Ainos",
        "underline-never": "Nikonzu",
-       "underline-default": "Ketun libo livaimen piäazetus",
+       "underline-default": "Käytä livaimen piäazetuksii",
        "editfont-style": "Edituičendualovehen kirjainstiil'u:",
        "editfont-default": "Livaimen piäazetus",
        "editfont-monospace": "Tazalevyhine kirjain",
        "createaccountreason": "Syy:",
        "createacct-reason": "Syy",
        "createacct-reason-ph": "Mindäh olet luadimas tostu käyttäitilii",
-       "createacct-captcha": "Turvallizusvarmistus",
-       "createacct-imgcaptcha-ph": "Kirjuta ylähän olii tekstu",
        "createacct-submit": "Luaji tili",
        "createacct-another-submit": "Luaji tili",
        "createacct-benefit-heading": "Sivun {{SITENAME}} ollah luajittu sinunjyttymät rahvas.",
        "pt-userlogout": "Kirjuttai ullos",
        "php-mail-error-unknown": "Tundematoi haireh PHP:n mail()-funktsies",
        "user-mail-no-addy": "Opit työndiä sähköpoštan sähköpoštuadressittah.",
-       "user-mail-no-body": "Sähköpoštan syväindön pidäy olla pitkembi.",
+       "user-mail-no-body": "Sähköpoštan syväindö pidäy olla suurembi.",
        "changepassword": "Vaihta peittosana",
        "resetpass_announce": "Suaja sizähkirjuamine loppuh, sinul pidäy keksie uuzi peittosana.",
        "resetpass_header": "Vaihta tilin peittosana",
        "showdiff": "Luajitut korjavukset",
        "anoneditwarning": "<strong>Varaitus:</strong> Et ole kirjutannuhes. Luadinet muutoksii syväindölöih, sinun Ip-adressu tulou nägövih kaikile. Ku <strong>[$1 kirjutannuttos]</strong> libo <strong>[$2 registriiruičettos]</strong>, sinun syväindömuutokset nävytäh sinun käyttäinimel, toizien eduloin ližäkse.",
        "summary-preview": "Yhtehvevon ezikačondu:",
-       "subject-preview": "Tiemam ezikačondu:",
+       "subject-preview": "Teeman ezikačondu:",
        "previewerrortext": "Rodih haireh oppijes ezikaččuo muutoksii.",
        "blockedtitle": "Käyttäi on estetty",
        "blockednoreason": "ei viärysty annettu",
        "whitelistedittext": "Sinun pidäy $1 ku edituija sivuloi.",
-       "nosuchsectiontitle": "Nengostu sektua ei ole",
-       "loginreqtitle": "Pidäy kirjautuakseh",
+       "nosuchsectiontitle": "Nengostu sektsiedu ei ole",
+       "loginreqtitle": "Pidäy kirjuttuakseh",
        "loginreqlink": "kirjuttai",
        "loginreqpagetext": "Toizien sivuloin kaččomizekse pidäy $1.",
        "accmailtitle": "Peittosana työtty",
        "editing": "Edituijah sivuu $1",
        "creating": "Luajitah sivuu \"$1\"",
        "editingsection": "Korjuandu $1 (alalugu)",
-       "editingcomment": "Edituijah $1 (uuzi sektu)",
+       "editingcomment": "Edituijah $1 (uuzi sektsii)",
        "yourtext": "Sinun tekstu",
        "yourdiff": "Erot",
        "templatesused": "{{PLURAL:$1|Šablon|Šablonat}} käytetty täl sivul:",
        "template-protected": "(suojattu)",
        "template-semiprotected": "(puolekse suojattu)",
        "hiddencategories": "Tämä sivu kuuluu {{PLURAL:$1|1 peitettyh kategourieh|$1 peitettyh kategourieh}}:",
-       "sectioneditnotsupported-title": "Sektan kohendustu ei tuveta.",
-       "sectioneditnotsupported-text": "Sektan kohendustu ei tuveta täl sivul.",
+       "sectioneditnotsupported-title": "Sektsien kohendustu ei tuveta.",
+       "sectioneditnotsupported-text": "Sektsieb kohendustu ei tuveta täl sivul.",
        "permissionserrors": "Ei oigevuksii",
        "permissionserrorstext": "Sinul ei ole lubua toimindoh {{PLURAL:$1|tämän syyn periä|nämmien syylöin periä}}:",
        "permissionserrorstext-withaction": "Sinul ei ole lubua toimindoh $2 niškoi, {{PLURAL:$1|tämän syyn periä|nämmien syylöin periä}}:",
        "moveddeleted-notice": "Tämä sivu on otettu iäre. Sivun iäreotandu- da siirdohistourii ollah annettu al viittavuksennu.",
        "postedit-confirmation-created": "Sivu on nygöi luajittu.",
-       "postedit-confirmation-restored": "Sivus on otettu järilleh sen aijembi versii.",
+       "postedit-confirmation-restored": "Sivun aijembi versii on tuodu järilleh.",
        "postedit-confirmation-saved": "Sinun kohendus tallendettih.",
        "edit-already-exists": "Ei voi luadie uuttu sivuu. Se on jo olemas.",
        "content-model-wikitext": "wikitekstu",
        "content-json-empty-object": "Tyhjy objektu",
        "cantcreateaccounttitle": "Ei voi luadie tunnustu",
        "cantcreateaccount-text": "Tunnuksien luadimine täs IP-adressaspäi ('''$1''') on estetty. Estäjänny on [[User:$3|$3]].\n\nKäyttäjän $3 annettu syy on ''$2''",
-       "cantcreateaccount-range-text": "Tunnuksien luadimine IP-adressilois adressualovehel '''$1''', kuduah kuuluu dai sinun käytetty IP-adressu('''$4'''), on estetty. Eston on azettanuh [[User:$3|$3]].\n\nKäyttäjän $3 annettu syy estole on \"$2\".",
+       "cantcreateaccount-range-text": "Tunnuksien luadimine IP-adressilois adressualovehel '''$1''', kuduah kuuluu sinungi käytetty IP-adressu('''$4'''), on estetty. Eston on azetannuh [[User:$3|$3]].\n\nKäyttäjän $3 annettu syy estole on \"$2\".",
        "viewpagelogs": "Ozuta tämän sivun logat",
        "nohistory": "Täl sivul ei ole kohendushistoriedu.",
        "currentrev": "Jälgimäzin versii",
        "history-feed-description": "Tämän sivun versiihistourii",
        "history-feed-item-nocomment": "$1 ($2)",
        "rev-deleted-user": "(käyttäinimi on otettu iäre)",
-       "rev-deleted-event": "(logan tiijot on otettu iäre)",
+       "rev-deleted-event": "(login tiijot on otettu iäre)",
        "rev-deleted-user-contribs": "[käyttäinimi libo IP-adressu on otettu iäre – edituičendu on peitetty edituičenduluvettelospäi]",
        "rev-delundel": "vaihta nägyvys",
        "rev-showdeleted": "ozuttua",
        "pagehist": "Sivuhistourii",
        "deletedhist": "Iäre otettuloin versielöin histourii",
        "revdelete-offender": "Versien luadii:",
-       "suppressionlog": "Peitändyloga",
+       "suppressionlog": "Peitändylog",
        "mergehistory": "Yhtistä sivuloin edituičenduhistourii",
        "mergehistory-header": "Täl sivul voit yhtistiä lähtehsivun sivuhistourien versielöi uvvemban sivun histourieh. Varmista, ku sivuloin yhtenäine edituičenduhistourii pyzyy eigo mene ristakkai immin-kummin!",
        "mergehistory-box": "Yhtistä kahten sivun versielöi:",
index dd04a2b..380d8d5 100644 (file)
        "createaccountreason": "Powód:",
        "createacct-reason": "Powód",
        "createacct-reason-ph": "Dlaczego zakładasz kolejne konto",
-       "createacct-captcha": "Kontrola bezpieczeństwa",
-       "createacct-imgcaptcha-ph": "Wpisz tekst widoczny powyżej",
        "createacct-submit": "Utwórz konto",
        "createacct-another-submit": "Utwórz konto",
        "createacct-benefit-heading": "{{grammar:B.lp|{{SITENAME}}}} tworzą ludzie tacy jak Ty.",
index 147f55f..192b927 100644 (file)
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Por que você está criando outra conta",
-       "createacct-captcha": "Verificação de segurança",
-       "createacct-imgcaptcha-ph": "Digite o texto acima",
        "createacct-submit": "Crie sua conta",
        "createacct-another-submit": "Criar conta",
        "createacct-benefit-heading": "{{SITENAME}} é feita por pessoas como você.",
index b034a2e..486ad66 100644 (file)
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Porque está a criar outra conta",
-       "createacct-captcha": "Verificação de segurança",
-       "createacct-imgcaptcha-ph": "Digite o texto que vê acima",
        "createacct-submit": "Crie a sua conta",
        "createacct-another-submit": "Criar conta",
        "createacct-benefit-heading": "{{SITENAME}} é feito por pessoas como você.",
        "passwordreset-emailsent-capture": "Foi enviado um correio eletrónico para recuperação da palavra-passe, que é mostrado abaixo.",
        "passwordreset-emailerror-capture": "Foi gerado um correio eletrónico para redefinição da palavra-passe, mostrado abaixo, mas o seu envio para {{GENDER:$2|o utilizador|a utilizadora}} falhou: $1",
        "changeemail": "Alterar ou remover o endereço de correio eletrónico",
-       "changeemail-header": "Completa este formulário para alterar o seu endereço de correio electrónico. Se quer eliminar a associação de qualquer endereço de correio electrónico com a sua conta, deixa em branco o novo endereço de correio electrónico ao enviar o formulário.",
+       "changeemail-header": "Complete este formulário para alterar o seu endereço de correio eletrónico. Se quer eliminar a associação de qualquer endereço de correio eletrónico com a sua conta, deixe em branco o novo endereço de correio eletrónico ao submeter o formulário.",
        "changeemail-passwordrequired": "Necessita de introduzir a sua palavra-passe para confirmar esta alteração.",
        "changeemail-no-info": "Precisa de iniciar sessão para aceder diretamente a esta página.",
        "changeemail-oldemail": "Correio eletrónico atual:",
        "recentchangeslinked-page": "Nome da página:",
        "recentchangeslinked-to": "Inversamente, mostrar mudanças às páginas que contêm ligações para esta",
        "recentchanges-page-added-to-category": "[[:$1]] foi adicionada à categoria",
-       "recentchanges-page-added-to-category-bundled": "[[:$1]] e {{PLURAL:$2|uma página|$2 páginas}} foram adicionadas à categoria",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] e {{PLURAL:$2|uma outra página|$2 outras páginas}} foram adicionadas à categoria",
        "recentchanges-page-removed-from-category": "[[:$1]] foi removida da categoria",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]] e {{PLURAL:$2|uma página|$2 páginas}} foram removidas da categoria",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] e {{PLURAL:$2|uma outra página|$2 outras páginas}} foram removidas da categoria",
        "upload": "Carregar ficheiro",
        "uploadbtn": "Carregar ficheiro",
        "reuploaddesc": "Cancelar o envio e voltar ao formulário de carregamento",
index 3592562..17d259d 100644 (file)
        "createaccountreason": "Since 1.22 no longer used in core, but may be used by some extensions. DEPRECATED\n\n{{Identical|Reason}}",
        "createacct-reason": "In create account form, label for field to enter reason to create an account when already logged-in.\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]\n{{Identical|Reason}}",
        "createacct-reason-ph": "Placeholder in vertical-layout create account form for reason field.\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]",
-       "createacct-captcha": "Label in vertical-layout create account form for CAPTCHA input field when repositioned by JavaScript.",
        "createacct-imgcaptcha-help": "{{Optional}} Optional help text in vertical-layout create account form for image CAPTCHA input field when repositioned by JavaScript.\n\nBlank by default.",
-       "createacct-imgcaptcha-ph": "Placehodler text in vertical-layout create account form for image CAPTCHA input field when repositioned by JavaScript.",
        "createacct-submit": "Submit button on vertical-layout create account form.\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]",
        "createacct-another-submit": "Submit button of  [[Special:UserLogin/signup]] ([[Special:CreateAccount]]) when accessed by a registered user.\n\nThe original means \"create an account in addition to the one you already have\"; sometimes, but not always, it means you are going to \"Create the account on behalf of somebody else\" or \"Create account for another\".\n{{Identical|Create another account}}",
        "createacct-benefit-heading": "In vertical-layout create account form, the heading for the section describing the benefits of creating an account. See example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]\n\nIf in your language you need to know the gender of the name for the wiki (which is the subject of the English sentence), please adapt the sentence as much as you need for your translation to fit.",
index 0e8f173..a6919f8 100644 (file)
        "createaccountreason": "Motiv:",
        "createacct-reason": "Motiv",
        "createacct-reason-ph": "De ce creați un alt cont",
-       "createacct-captcha": "Verificare de securitate",
-       "createacct-imgcaptcha-ph": "Introduceți textul pe care îl vedeți deasupra",
        "createacct-submit": "Creați-vă contul",
        "createacct-another-submit": "Creează contul",
        "createacct-benefit-heading": "{{SITENAME}} este un proiect clădit de oameni ca dumneavoastră.",
        "permissionserrors": "Eroare de permisiune",
        "permissionserrorstext": "Nu aveți permisiune pentru a face acest lucru, din următoarele {{PLURAL:$1|motiv|motive}}:",
        "permissionserrorstext-withaction": "Nu aveți permisiunea să $2, din {{PLURAL:$1|următorul motiv|următoarele motive}}:",
+       "contentmodelediterror": "Nu puteți modifica această versiune, deoarece modelul de conținut al acesteia este <code>$1</code>, iar actualul model de conținut al paginii este <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Atenție: Recreați o pagină care a fost ștearsă anterior.'''\n\nAsigurați-vă că este oportună recrearea acestei pagini.\nJurnalul ștergerilor și al mutărilor pentru această pagină este disponibil:",
        "moveddeleted-notice": "Această pagină a fost ștearsă.\nJurnalul ștergerilor și al redenumirilor este disponibil mai jos.",
        "moveddeleted-notice-recent": "Ne cerem scuze, dar această pagină a fost ștearsă recent (în ultimele 24 de ore).\nJurnalele de ștergere și redenumire ale paginii sunt disponibile mai jos cu scop informativ.",
index f5c9600..38f16df 100644 (file)
        "createaccountreason": "Mutive:",
        "createacct-reason": "Mutive",
        "createacct-reason-ph": "Purcé tu ste ccreje 'n'otre cunde utende?",
-       "createacct-captcha": "Verifiche de securezze",
-       "createacct-imgcaptcha-ph": "Mitte 'u teste tune aqquà sus",
        "createacct-submit": "Ccreje 'u cunde utende tune",
        "createacct-another-submit": "Ccreje 'nu cunde utende",
        "createacct-benefit-heading": "{{SITENAME}} jè fatte da crestiane cumme a te.",
index bbb58e8..d8c2cb1 100644 (file)
        "createaccountreason": "Причина:",
        "createacct-reason": "Причина",
        "createacct-reason-ph": "Зачем вы создаёте другую учетную запись",
-       "createacct-captcha": "Проверка безопасности",
-       "createacct-imgcaptcha-ph": "Введите текст, который вы видите выше",
        "createacct-submit": "Создать учётную запись",
        "createacct-another-submit": "Создать учётную запись",
        "createacct-benefit-heading": "{{SITENAME}} — совместный труд таких же людей, как вы.",
        "permissionserrors": "Ошибка прав доступа",
        "permissionserrorstext": "У вас нет прав на выполнение этой операции по {{PLURAL:$1|1=следующей причине|следующим причинам}}:",
        "permissionserrorstext-withaction": "У вас нет прав на $2 по {{PLURAL:$1|1=следующей причине|следующим причинам}}:",
+       "contentmodelediterror": "Вы не можете редактировать эту версию, поскольку её моделью содержимого является <code>$1</code>, а текущая модель содержимого страницы — <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Внимание. Вы пытаетесь воссоздать страницу, которая ранее удалялась.'''\n\nПроверьте, действительно ли вам нужно воссоздавать эту страницу.\nНиже приведены журналы удалений и переименований этой страницы.",
        "moveddeleted-notice": "Эта страница была удалена.\nДля справки ниже показаны соответствующие записи из журналов удалений и переименований.",
        "moveddeleted-notice-recent": "К сожалению, эта страница была недавно удалена (в течение последних 24 часов).\nНиже для справки приведены журналы удаления и перемещения для этой страницы.",
        "move-page-legend": "Переименование страницы",
        "movepagetext": "Воспользовавшись нижеприведённой формой, вы переименуете страницу, одновременно переместив на новое место её журнал изменений.\nСтарое название станет перенаправлением на новое.\nВы можете автоматически обновить перенаправления, которые вели на старое название.\nЕсли вы этого не сделаете, пожалуйста, проверьте наличие [[Special:DoubleRedirects|двойных]] и [[Special:BrokenRedirects|разорванных перенаправлений]].\nВы отвечаете за то, чтобы ссылки продолжали и далее указывать туда, куда предполагалось.\n\nОбратите внимание, что страница <strong>не будет</strong> переименована, если уже существует страница с названием, идентичным выбранному, кроме случаев, когда такая страница пуста или представляет собой перенаправление, и при этом не имеет истории правок.\nЭто означает, что сделав ошибочное переименование, вы можете переименовать страницу обратно в то название, которое у неё только что было, но не можете случайно затереть существующую страницу.\n\n<strong>Предупреждение!</strong>\nПереименование популярных страниц может привести к масштабным и неожиданным изменениям.\nПожалуйста, прежде чем продолжать, убедитесь, что понимаете все возможные последствия.",
        "movepagetext-noredirectfixer": "Воспользовавшись формой ниже, вы переименуете страницу, одновременно переместив на новое место её журнал изменений.\nСтарое название станет перенаправлением на новое название.\nПожалуйста, проверьте наличие [[Special:DoubleRedirects|двойных]] и [[Special:BrokenRedirects|разорванных перенаправлений]].\nВы отвечаете за то, чтобы ссылки продолжали и далее указывать туда, куда предполагалось.\n\nОбратите внимание, что страница <strong>не будет</strong> переименована, если страница с новым названием уже существует, кроме случаев, если она пуста или представляет собой перенаправление, и при этом не имеет истории правок.\nЭто означает, что сделав ошибочное переименование, вы сможете переименовать страницу обратно в то название, которое у неё только что было, но не сможете случайно затереть существующую страницу.\n\n<strong>Предупреждение!</strong>\nПереименование может привести к масштабным и неожиданным изменениям для популярных страниц.\nПожалуйста, прежде чем продолжить, убедитесь, что понимаете все возможные последствия.",
-       "movepagetalktext": "Ð\9fÑ\80иÑ\81оединÑ\91ннаÑ\8f Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¾Ð±Ñ\81Ñ\83ждениÑ\8f Ð±Ñ\83деÑ\82 Ñ\82акже Ð°Ð²Ñ\82омаÑ\82иÑ\87еÑ\81ки Ð¿ÐµÑ\80еименована, '''кÑ\80оме Ñ\81лÑ\83Ñ\87аев, ÐºÐ¾Ð³Ð´Ð°:'''\n\n*Ð\9dе Ð¿Ñ\83Ñ\81Ñ\82аÑ\8f Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¾Ð±Ñ\81Ñ\83ждениÑ\8f Ñ\83же Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83еÑ\82 Ð¿Ð¾Ð´ Ñ\82аким Ð¶Ðµ Ð¸Ð¼ÐµÐ½ÐµÐ¼ Ð¸Ð»Ð¸\n*Ð\92Ñ\8b Ð½Ðµ Ð¿Ð¾Ñ\81Ñ\82авили Ð³Ð°Ð»Ð¾Ñ\87кÑ\83 Ð² Ð¿Ð¾Ð»Ðµ Ð½Ð¸Ð¶Ðµ.\n\nÐ\92 Ñ\8dÑ\82иÑ\85 Ñ\81лÑ\83Ñ\87аÑ\8fÑ\85, Ð²Ñ\8b Ð±Ñ\83деÑ\82е Ð²Ñ\8bнÑ\83жденÑ\8b Ð¿ÐµÑ\80емеÑ\81Ñ\82иÑ\82Ñ\8c Ð¸Ð»Ð¸ Ð¾Ð±Ñ\8aединиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð²Ñ\80Ñ\83Ñ\87нÑ\83Ñ\8e, ÐµÑ\81ли Ñ\8dÑ\82о Ð½Ñ\83жно.",
+       "movepagetalktext": "Ð\95Ñ\81ли Ð²Ñ\8b Ð¾Ñ\82меÑ\82иÑ\82е Ñ\8dÑ\82оÑ\82 Ð¿Ñ\83нкÑ\82, Ñ\81вÑ\8fзаннаÑ\8f Ñ\81 Ð½ÐµÐ¹ Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¾Ð±Ñ\81Ñ\83ждениÑ\8f Ð±Ñ\83деÑ\82 Ñ\82акже Ð°Ð²Ñ\82омаÑ\82иÑ\87еÑ\81ки Ð¿ÐµÑ\80еименована, ÐµÑ\81ли Ñ\82олÑ\8cко Ñ\83же Ð½Ðµ Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83еÑ\82 Ð½ÐµÐ¿Ñ\83Ñ\81Ñ\82аÑ\8f Ñ\81Ñ\82Ñ\80аниÑ\86а Ð¾Ð±Ñ\81Ñ\83ждениÑ\8f Ñ\81 Ñ\82аким Ð¶Ðµ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼.\n\nÐ\92 Ñ\8dÑ\82ом Ñ\81лÑ\83Ñ\87ае Ð²Ð°Ð¼ Ð½Ñ\83жно Ð±Ñ\83деÑ\82 Ð¿ÐµÑ\80еименоваÑ\82Ñ\8c Ð¸Ð»Ð¸ Ð¾Ð±Ñ\8aединиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð²Ñ\80Ñ\83Ñ\87нÑ\83Ñ\8e, ÐµÑ\81ли Ñ\8dÑ\82о Ð½ÐµÐ¾Ð±Ñ\85одимо.",
        "moveuserpage-warning": "'''Внимание.''' Вы собираетесь переименовать страницу участника. Пожалуйста, обратите внимание, что переименована будет только страница, участник '''не''' будет переименован.",
        "movecategorypage-warning": "<strong>Предупреждение:</strong> Вы собираетесь переименовать страницу категории. Пожалуйста, обратите внимание, что будет переименована только эта страница, а все страницы старой категории <em>не</em> будут перекатегоризованы в новую.",
        "movenologintext": "Вы должны [[Special:UserLogin|представиться системе]],\nчтобы иметь возможность переименовать страницы.",
        "tags-edit-revision-submit": "Применить изменения к {{PLURAL:$1|этой версии|$1 версиям}}",
        "tags-edit-logentry-submit": "Применить изменения к {{PLURAL:$1|этой записи журнала|$1 записям журнала}}",
        "tags-edit-success": "Изменения были успешно применены.",
-       "tags-edit-failure": "Ð\98зменениÑ\8f Ð½Ðµ Ñ\83далоÑ\81Ñ\8c Ð¿Ñ\80именениÑ\82Ñ\8c.\n$1",
+       "tags-edit-failure": "Изменения не удалось применить.\n$1",
        "tags-edit-nooldid-title": "Не задана целевая версия",
        "tags-edit-nooldid-text": "Вы не задали целевую версию (версии) для выполнения этой функции, или указанная версия не существует.",
        "tags-edit-none-selected": "Пожалуйста, выберите по крайней мере один тег, чтобы добавить или удалить.",
index bbd3272..a9df913 100644 (file)
@@ -20,6 +20,7 @@
        "tog-hideminor": "Кыра көннөрүүлэри көрдөрбөккө",
        "tog-hidepatrolled": "Ботурууллааччы көрбүт көннөрүүтүн саҥа көннөрүүлэр тиһиктэригэр көрдөрүмэ",
        "tog-newpageshidepatrolled": "Ботуруулламмыт сирэйдэри саҥа сирэйдэр тиһиктэригэр көрдөрүмэ",
+       "tog-hidecategorization": "Сирэй категорияларын көрдөрүмэ",
        "tog-extendwatchlist": "Кэтээһин тупсарыллыбыт тиһигэ. Бары уларытыылар көстөллөр (бүтэһиктэр эрэ буолбатах)",
        "tog-usenewrc": "Саҥа уларытыы уонна кэтэбил тиһиктэригэр уларыйыылары бөлөхтүүргэ",
        "tog-numberheadings": "Түһүмэхтэр ааттарын нүөмэрдээ",
@@ -49,6 +50,7 @@
        "tog-watchlisthideliu": "Бэлиэтэммит кыттааччылар уларытыыларын кэтиир тиһиккэ көрдөрүмэ",
        "tog-watchlisthideanons": "Ааттарын эппэтэх кыттааччылар уларытыыларын кэтээһин тиһигэр көрдөрүмэ",
        "tog-watchlisthidepatrolled": "Ботурууллааччы көрбүт көннөрүүтүн кэтээһин испииһэгэр көрдөрүмэ",
+       "tog-watchlisthidecategorization": "Сирэй категорияларын көрдөрүмэ",
        "tog-ccmeonemails": "Атын кыттааччыларга суруйбут суруктарбын бэйэбэр эмиэ ыыт",
        "tog-diffonly": "Икки барылы тэҥнииргэ сирэй иһин көрдөрүмэ",
        "tog-showhiddencats": "Кистэммит категориялары көрдөр",
        "createaccountreason": "Төрүөтэ:",
        "createacct-reason": "Төрүөтэ",
        "createacct-reason-ph": "Саҥа аатынан тоҕо киирэҕиний",
-       "createacct-captcha": "Куттал суох буолуутун тургутуу",
-       "createacct-imgcaptcha-ph": "Үөһэ көстөрү хатылаа",
        "createacct-submit": "Бэлиэтэнии",
        "createacct-another-submit": "Бэлиэтэн",
        "createacct-benefit-heading": "{{SITENAME}} ситим-сири эн курдук дьон оҥороллор.",
        "permissionserrors": "Киирии алҕаһа",
        "permissionserrorstext": "Маны оҥорор кыаҕыҥ суох, {{PLURAL:$1|төрүтэ|төрүттэрэ}}:",
        "permissionserrorstext-withaction": "Бу дьайыыны ($2) оҥорор кыаҕыҥ суох.  {{PLURAL:$1|Биричиинэтэ|Биричиинэлэрэ}}:",
+       "contentmodelediterror": "Бу торуму уларытар кыаҕыҥ суох эбит, тоҕо диэтэххэ иһинээҕитин мадьыала маннык <code>$1</code>, оттон сирэй иһинээҕитин билиҥҥитэ маннык — <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Болҕой: сотулубут сирэйи төттөрү оҥорон эрэҕин.'''\n\nТолкуйдаан көр, кырдьык бу сирэйи оҥорор туһалаах дуо.\nАллара сотуулар уонна аат уларыйыытын сурунааллара көрдөрүлүннэ.",
        "moveddeleted-notice": "Бу сирэй сотуллубут.\nАллара сотуу уонна аат уларытыытын сурунаалларыгар онно сыһыаннаах туох суруллубута көстөр.",
        "moveddeleted-notice-recent": "Бу сирэй соторутааҕыта (тиһэх 24 чаас иһигэр) сотуллубут эбит.\nАллара сотуу уонна көһөрүү сурунаалларыгар сигэлэр көстөллөр.",
        "foreign-structured-upload-form-label-own-work-message-default": "Уопсай репозиторийга угарбын өйдөөн туран угабын. Туһаныы сиэрин уонна лиссиэнсийэлиир бэлиитикэни кытта сөп түбэһэрин мэктиэлиибин.",
        "foreign-structured-upload-form-label-not-own-work-message-default": "Бу билэҕин уопсай репозиторий быраабылатынан угар кыаҕыҥ суох буоллаҕына, маны сап уонна атын ньыманы туһанан көр.",
        "foreign-structured-upload-form-label-not-own-work-local-default": "Өскө, {{SITENAME}} быраабылатынан угар сатанар буоллаҕына, кини [[Special:Upload|киллэрии тэрилин]] туһаныаххын сөп.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Бу билэни бас билэрбин уонна Биики Ыскылаакка төнүннэрбэттии [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0] лиссиэнсийэннэн угары бигэргэтэбин. Ону тэҥэ [https://wikimediafoundation.org/wiki/Условия_использования Туһаныы усулуобуйатын кытта] сөбүлэһэбин.",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Өскөтө билэни бас билбэт буоллаххына, биитэр атын лиссиэнсийэннэн угуоххун баҕарар буоллаххына, манна баар ньыманы туһаныаххын сөп: [https://commons.wikimedia.org/wiki/Special:UploadWizard Биики Ыскылаакка угуу маастара].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Өскө, {{SITENAME}} быраабылатынан угар сатанар буоллаҕына, кини [[Special:Upload|киллэрии тэрилин]] туһаныаххын эмиэ сөп.",
        "backend-fail-stream": "$1 билэни ыытар табыллыбата.",
        "backend-fail-backup": "Бу билэ $1 резервнэй куопуйатын оҥорор табыллыбата.",
        "index-category-desc": "Сирэйгэ <nowiki>__INDEX__</nowiki> диэн «аптаах тыл» баар эбит (сирэй ону көҥүллүүр аат далыгар баар эбит), онон көрдүүр роботтар кинини болҕомтоҕо ылыа да суох түгэннэргэ көрөллөр эбит.",
        "post-expand-template-inclusion-category-desc": "Халыыптары барытын көрдөрдөххө сирэй ыйааһына маны <code>$wgMaxArticleSize</code> куоһарыан сөп, онон сорҕото эрэ көрдөрүлүннэ.",
        "post-expand-template-argument-category-desc": "Халыып аргуменын арыйдахха (фигурнай ускуопка иһигэр баары, холобур, <code>{{{Foo}}})</code>, сирэй маннааҕар улахан буолуо: <code>$wgMaxArticleSize</code>.",
-       "expensive-parserfunction-category-desc": "СиÑ\80Ñ\8dйгÑ\8d Ð½Ð°Ò»Ð°Ð° Ñ\8dлбÑ\8dÑ\85 Ñ\80еÑ\81Ñ\83Ñ\80Ñ\81анÑ\8b Ñ\81ииÑ\80 Ñ\84Ñ\83нкÑ\86иÑ\8f Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллÑ\83бÑ\83Ñ\82 (Ñ\85олобÑ\83Ñ\80, Ð¼Ð°Ð½Ð½Ñ\8bк <code>#ifexist</code>). Сиһилии — бу сирэйгэ: [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
-       "broken-file-category-desc": "Ð\91Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\8f Ð±Ð¸Ð»Ñ\8dÒ\95Ñ\8d Ð°Ð»Ò\95аÑ\81Ñ\82ааÑ\85 Ñ\81игÑ\8d Ð±Ð°Ð°Ñ\80 Ð±Ñ\83оллаÒ\95Ñ\8bна Ñ\8dбиллÑ\8dÑ\80 (Ñ\81Ñ\83оÑ\85 Ð±Ð¸Ð»Ñ\8dÒ\95Ñ\8d Ñ\81игÑ\8dнии).",
-       "hidden-category-category-desc": "Ð\9cаннÑ\8bк Ð±Ñ\8dлиÑ\8dлÑ\8dÑ\8dÑ\85 ÐºÐ°Ñ\82егоÑ\80иÑ\8f <code><nowiki>__HIDDENCAT__</nowiki></code> ÐºÐ¸Ð½Ð¸Ð½Ð¸ ÐºÐ°Ñ\82егоÑ\80иÑ\8f Ñ\81алааÑ\82Ñ\8bгаÑ\80 ÐºÓ©Ñ\81Ñ\82Ó©Ñ\80үн Ð±Ð¾Ð±ор.",
+       "expensive-parserfunction-category-desc": "СиÑ\80Ñ\8dйгÑ\8d Ð½Ð°Ò»Ð°Ð° Ñ\8dлбÑ\8dÑ\85 Ñ\80еÑ\81Ñ\83Ñ\80Ñ\81анÑ\8b Ñ\81ииÑ\80 Ñ\84Ñ\83нкÑ\86иÑ\8f Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллÑ\83бÑ\83Ñ\82 (Ñ\85олобÑ\83Ñ\80, Ð¼Ð°Ð½Ð½Ñ\8bгÑ\8b <code>#ifexist</code>). Сиһилии — бу сирэйгэ: [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
+       "broken-file-category-desc": "СиÑ\80Ñ\8dй Ð±Ð¸Ð»Ñ\8dÒ\95Ñ\8d Ñ\81игÑ\8dÑ\82Ñ\8d Ð°Ð»Ò\95аÑ\81Ñ\82ааÑ\85 (Ñ\81Ñ\83оÑ\85 Ð±Ð¸Ð»Ñ\8dÒ\95Ñ\8d Ñ\81игÑ\8dнÑ\8dÑ\80).",
+       "hidden-category-category-desc": "Ð\9cаннÑ\8bк Ð±Ñ\8dлиÑ\8dлÑ\8dÑ\8dÑ\85 ÐºÐ°Ñ\82егоÑ\80иÑ\8f <code><nowiki>__HIDDENCAT__</nowiki></code> ÐºÐ¸Ð½Ð¸Ð½Ð¸ ÐºÐ°Ñ\82егоÑ\80иÑ\8f Ñ\81алааÑ\82Ñ\8bн Ñ\81иÑ\80Ñ\8dйдÑ\8dÑ\80игÑ\8dÑ\80 ÐºÓ©Ñ\81Ñ\82үбÑ\8dÑ\82 Ð¾Ò¥Ð¾Ñ\80ор.",
        "trackingcategories-nodesc": "Ойуулааһына суох.",
        "trackingcategories-disabled": "Араарыллыбыт категория",
        "mailnologin": "Аадырыһа суох",
        "emailccsubject": "Эн суругуҥ куоппуйата $1: $2",
        "emailsent": "Сурук барда",
        "emailsenttext": "Эн суругуҥ ыытылынна.",
-       "emailuserfooter": "Ð\91Ñ\83 Ñ\81Ñ\83Ñ\80Ñ\83к $2 ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8bга $1 ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8bÑ\82Ñ\82ан Â«Ð¡Ñ\83Ñ\80Ñ\83кÑ\82а Ñ\8bÑ\8bÑ\82» Ð´Ð¸Ñ\8dн Ñ\84Ñ\83нкÑ\86иÑ\8f ÐºÓ©Ð¼Ó©Ñ\82үнÑ\8dн {{SITENAME}} Ñ\81аайÑ\82ан ыытыллыбыт.",
+       "emailuserfooter": "Ð\91Ñ\83 Ñ\81Ñ\83Ñ\80Ñ\83к $2 ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8bга $1 ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8bÑ\82Ñ\82ан Â«Ð¡Ñ\83Ñ\80Ñ\83кÑ\82а Ñ\8bÑ\8bÑ\82» Ð´Ð¸Ñ\8dн Ñ\82Ñ\8dÑ\80ил ÐºÓ©Ð¼Ó©Ñ\82үнÑ\8dн {{SITENAME}} Ñ\81иÑ\82им-Ñ\81иÑ\80Ñ\82Ñ\8dн ыытыллыбыт.",
        "usermessage-summary": "Тиһилик биллэриитин хааллар.",
        "usermessage-editor": "Тиһилик биллэрээччитэ",
        "watchlist": "Кэтэбилим тиһигэ",
        "mywatchlist": "Кэтэбил тиһигэ",
        "watchlistfor2": "$1 $2 аналлаах",
        "nowatchlist": "Эн кэтиир сирэйдэриҥ суохтар.",
-       "watchlistanontext": "Кэтэбилиҥ сирэйин көрөргөр эбэтэр уларытаргар маны оҥор: $1.",
+       "watchlistanontext": "Кэтэбилиҥ сирэйин көрөргө эбэтэр уларытарга бэлиэ-аатынан киириэхтээххин.",
        "watchnologin": "Бэйэҕин билиһиннэр",
        "addwatch": "Кэтэбил тиһигэр киллэр",
        "addedwatchtext": "«[[:$1]]» сирэй [[Special:Watchlist|кэтэбил тиһигэр]] киирдэ.\nСирэй уларытыылара уонна кинини кытта ситимнээх ырытыы сирэйин уларытыылара бүгүҥҥүттэн онно көстөр буолуохтара.",
        "javascripttest": "JavaScript тургутуу",
        "javascripttest-pagetext-noframework": "Бу сирэй JavaScript тургутууларга анаммыт.",
        "javascripttest-pagetext-unknownframework": "\"$1\" тургутуу биллибэт эйгэтэ.",
+       "javascripttest-pagetext-unknownaction": "Биллибэт дьайыы «$1».",
        "javascripttest-pagetext-frameworks": "Бука диэн, бу тургуутуу эйгэлэриттэн биирин тал: $1",
        "javascripttest-pagetext-skins": "Тургутууну ыытарга тас көрүҥүн бастаан тал:",
        "javascripttest-qunit-intro": "[$1 тургутуу документациятын] манна mediawiki.org көр.",
        "tooltip-feed-atom": "Atom бу сирэйгэ",
        "tooltip-t-contributions": "Бу кыттааччы уларыппыт сирэйдэрин испииһэгэ",
        "tooltip-t-emailuser": "Бу киһиэхэ сурук ыытарга",
+       "tooltip-t-info": "Бу сирэй туһунан сиһилии",
        "tooltip-t-upload": "Билэлэри суруттарыы",
        "tooltip-t-specialpages": "Анал сирэйдэр испииһэктэрэ",
        "tooltip-t-print": "Сирэй бэчээккэ ыытыллар торума",
        "pageinfo-robot-index": "Көҥүллэммит",
        "pageinfo-robot-noindex": "Араарыллыбыт",
        "pageinfo-watchers": "Кэтээнэр сирэйдэр ахсааннара",
+       "pageinfo-visiting-watchers": "Сирэйи кэтиир уонна тиһэх көннөрүүлэри көрбүт дьон ахсаана",
        "pageinfo-few-watchers": "$1 кыттааччыттан аҕыйах кэтээччи",
        "pageinfo-redirects-name": "Бу сирэйгэ утаарыы ахсаана",
        "pageinfo-subpages-name": "Сирэй аннынааҕы сирэйдэр ахсааннара",
        "pageinfo-protect-cascading-yes": "Сөп",
        "pageinfo-protect-cascading-from": "Каскадынан көмүскэл мантан",
        "pageinfo-category-info": "Категория туһунан",
+       "pageinfo-category-total": "Бары",
        "pageinfo-category-pages": "Сирэй ахсаана",
        "pageinfo-category-subcats": "Субкатегория ахсаана",
        "pageinfo-category-files": "Билэ ахсаана",
        "tags-create-invalid-chars": "Бэлиэ аатыгар сопутуой (<code>,</code>) эбэтэр слэш  (<code>/</code>) буолуохтаах.",
        "tags-create-invalid-title-chars": "Тиэк аатыгар сирэй баһыгар туттуллуо суохтаах бэлиэ киириэ суохтаах",
        "tags-create-already-exists": "«$1» тиэк хайыы-үйэ баар эбит.",
+       "tags-delete-title": "Тиэги сот",
+       "tags-delete-explanation-initial": "«$1» тиэги билии олоҕуттан сотон эрэҕин.",
        "tags-delete-reason": "Төрүөтэ:",
        "tags-delete-submit": "Бу тиэги букатыннаахтык сот",
        "tags-activate-title": "Тиэги холбоо",
+       "tags-activate-question": "\"$1\" тиэги холбоон эрэҕин.",
        "tags-activate-reason": "Төрүөтэ:",
+       "tags-activate-not-allowed": "\"$1\" тиэги холбуур табыллыбат.",
+       "tags-activate-not-found": "Маннык $1 тиэк суох эбит.",
        "tags-activate-submit": "Холбоо",
        "tags-deactivate-title": "Тиэги араар",
        "tags-deactivate-question": "\"$1\" тиэги арааран эрэҕин.",
        "tags-deactivate-reason": "Төрүөтэ:",
        "tags-deactivate-not-allowed": "\"$1\" тиэги араарар табыллыбат.",
        "tags-deactivate-submit": "араар",
+       "tags-apply-no-permission": "Бэйэҥ уларытыыгар уларытыы тиэгин туруорар кыаҕыҥ суох эбит.",
+       "tags-apply-not-allowed-one": "«$1» тиэги илииннэн туруорар табыллыбат эбит.",
        "tags-edit-title": "Тиэктэри уларытыы",
        "tags-edit-manage-link": "Тиэктэри дьаһайыы",
        "tags-edit-existing-tags": "Баар тиэктэр:",
        "tags-edit-chosen-placeholder": "Биир эбэтэр хас да тиэги тал",
        "tags-edit-chosen-no-results": "Сөп түбэһэр тиэк көстүбэтэ",
        "tags-edit-reason": "Төрүөтэ:",
+       "tags-edit-success": "Уларытыы сөпкө бигэргэннэ.",
        "comparepages": "Сирэйдэри тэҥнииргэ",
        "compare-page1": "Бастакы сирэй",
        "compare-page2": "Иккис сирэй",
index 1be5ee8..7cbbda8 100644 (file)
        "createaccountreason": "Raison:",
        "createacct-reason": "Raison",
        "createacct-reason-ph": "Why ar ye creating anither accoont",
-       "createacct-captcha": "Security check.",
-       "createacct-imgcaptcha-ph": "Enter the tex ye see abuin",
        "createacct-submit": "Mak yer accoont",
        "createacct-another-submit": "Mak anither accoont",
        "createacct-benefit-heading": "{{SITENAME}} is makit bi fowk like ye.",
index 09a87de..f0f21a1 100644 (file)
        "tog-hideminor": "تازيون معمولي تبديليون لڪايو",
        "tog-hidepatrolled": "تازيون گھميل تبديليون لڪايو",
        "tog-newpageshidepatrolled": "نَوَن صفحن واري فهرست مان تازو گھميل صفحا لڪايو",
+       "tog-hidecategorization": "صفحن جا ذمرا لڪايو",
+       "tog-extendwatchlist": "تازه ترين بدران سموريون تبديليون ڏيکارڻ لاءِ ٽيٽ لسٽ کي وسيع ڪريو.",
        "tog-numberheadings": "سُرخين کي خودڪار طريقي سان نمبر ڏيو",
        "tog-showtoolbar": "سنوار اوزار ڏيکاريو",
        "tog-editondblclick": "ٻٽي ڪلڪ تي صفحا سنواريو",
        "tog-watchcreations": "منهنجا سرجيل صفحا منهنجي ٽيٽ فهرست ۾ رکو",
        "tog-watchdefault": "منهنجا ترميميل صفحا منهنجي ٽيٽ فهرست تي رکو",
+       "tog-watchmoves": "جيڪي صفحا ۽ فائيلس آئون چوريان، سي منهنجي ٽيٽ لسٽ ۾ شامل ڪريو.",
        "tog-watchdeletion": "آئون جيڪي صفحا ڊاهيان، سي منهنجي ٽيٽ فهرست تي رکو",
        "tog-watchrollback": "انهن صفحن کي منهنجي ٽيٽ فهرست تي رکو، جن ۾ تبديلين کي مون واپس ورايو آهي.",
        "tog-minordefault": "سمورين تبديلين کي بنان چئي معمولي ترميم تصور ڪريو",
        "listingcontinuesabbrev": "جاري..",
        "index-category": "ڏسڻيل صفحا",
        "noindex-category": "غيرڏسڻيل صفحا",
+       "broken-file-category": "فائيل جي ٽٽل ڳنڍڻن وارا صفحا",
        "about": "بابت",
        "article": "موادي صفحو",
        "newwindow": "(نئين دريءَ ۾ کلندو)",
        "protectedpagetext": "هيءُ صفحو ترميمن کان تحفظيل آهي.",
        "viewsourcetext": "توهان هن صفحي جو ڪوڊ ڏسي ۽ نقل ڪري سگھو ٿا:",
        "namespaceprotected": "توهان کي نانءُ پولار '''$1''' جا صفحا سنوارڻ جا اختيار ناهن.",
+       "mycustomjsprotected": "توهان کي هيءُ جاوا اسڪرپٽ صفحو سنوارڻ جي اجازت حاصل ڪانهي.",
+       "mypreferencesprotected": "توهان جي پنهنجون ترجيحات سنوارڻ جي اجات حاصل ڪانهي.",
        "ns-specialprotected": "خاص صفحا سنواري نٿا سگھجن.",
        "exception-nologin": "لا اِن ٿيل ناهيو",
        "virus-unknownscanner": "اڻ ڄاتل نِس وائرس:",
        "createaccountreason": "سبب:",
        "createacct-reason": "سبب",
        "createacct-reason-ph": "توهان ٻيو کاتو ڇو کولي رهيا آهيو",
-       "createacct-captcha": "حفاظتي روڪ",
-       "createacct-imgcaptcha-ph": "مٿي ظاهر ٿيندڙ ٽيڪسٽ ٽائيپ ڪريو",
        "createacct-submit": "پنهنجو کاتو کوليو",
        "createacct-another-submit": "کاتو کوليو",
        "createacct-benefit-heading": "{{SITENAME}} توهان جهڙن سڄڻن ٺاهيو آهي.",
        "passwordremindertitle": "{{SITENAME}} لاءِ نئون عارضي ڳجھو لفظ",
        "passwordremindertext": "ڪنهن (شايد توهان آءِ پي پتي $1 تان) اسان کي {{SITENAME}} ($4) لاءِ نئون ڳجھو لفظ اماڻڻ جي گھُرَ ڪئي.\n\nهاڻي يوزر \"$2\" لاءِ ڳجھو لفظ \"$3\" آهي. توهان کي هينئر ئي لاگ اِن ٿي پنهنجو ڳجھو لفظ تبديل ڪرڻ گھرجي.\n\nجيڪڏهن اها گھُرَ اوهان نه ڪئي هئي، يا هاڻي اوهان کي پنهنجو ڳجھو لفظ ياد اچي ويو آهي ۽ توهان ان کي تبديل ڪرڻ نه ٿا چاهيو، ته توهان هن نياپي کي نظر انداز ڪندي پنهنجو پراڻو ڳجھو لفظ ئي استعمال ڪري سگھو ٿا.",
        "noemail": "يُوزر \"$1\" جي ڪو به برق ٽپال پتو درج ٿيل ناهي.",
+       "noemailcreate": "توهان کي قابل ڪار برق ٽپال پتو مهيا ڪرڻو پوندو.",
        "passwordsent": "يوزر \"$1\" لاءِ هڪ نئون ڳجھو لفظ برق ٽپال ذريعي اماڻيو ويو آهي.  مهرباني ڪري اهو حاصل ڪرڻ بعد لاگ اِن ٿيندا.",
        "mailerror": "ٽپال اماڻڻ ۾ چُڪَ: $1",
        "acct_creation_throttle_hit": "توهان جي آءِ پي پتي تان هن وڪيءَ تي پوئين ڏينهن $1 کاتا کلي چڪا آهن. ايتري عرصي ۾ ان کان وڌيڪ کاتا نہ ٿا کولي سگھجن. نتيجتاً ساڳي آءِ پي پتي تان في‌الوقت وڌيڪ کاتا کولي نہ ٿا سگھجن.",
        "emailauthenticated": "توهان جي برق ٽپال پتي جي تصديق $2 تي $3 بجي ڪئي وئي.",
        "emailnotauthenticated": "توهان جو برق ٽپال پتي جي تصديق اڃا ٿي نہ سگھي آهي.",
+       "noemailprefs": "انهن فيچرس کي فعال بڻائڻ لاءِ پنهنجي ترجيحات ۾ برق ٽپال پتو ڄاڻايو.",
        "emailconfirmlink": "پنهنجي برق ٽپال پتي جي پَڪَ ڪندا",
+       "cannotchangeemail": "هن وڪيءَ تي کاتيدار جو برق ٽپال پتو بدلائي نہ ٿو سگھجي.",
        "emaildisabled": "هيءَ سرزمين برق ٽپال اماڻي نہ ٿي سگھي.",
        "accountcreated": "کاتو کلي چڪو",
        "accountcreatedtext": "$1 نالي يوزر کاتو کلي چڪو آھي.",
        "pt-login-button": "لاگ اِن",
        "pt-createaccount": "کاتو کوليو",
        "pt-userlogout": "لاگ آئوٽ",
+       "php-mail-error-unknown": "پي ايڇ پي جي  ڪاڄ اندر اڻڄاتل چُڪَ.",
        "user-mail-no-addy": "برق ٽپال پتو ڄاڻائڻ کان سواءِ برق ٽپال اماڻڻ جي ڪوشش ڪئي وئي.",
        "changepassword": "ڳجھو لفظ تبديل ڪريو",
        "resetpass_header": "کاتي جو ڳجھو لفظ بدلايو",
        "passwordreset-domain": "ميدان:",
        "passwordreset-email": "برق ٽپال پتو:",
        "passwordreset-emailtitle": "{{SITENAME}} واري کاتي جا تفصيل",
+       "changeemail": "برق ٽپال پتو مِٽايو يا بدلايو",
        "changeemail-passwordrequired": "توهانکي هن تبديلي جي تصديق ڪرڻ جي لاءِ پنهنجو ڳجھو لفظ داخل ڪرڻ جي ضرورت پوندي.",
        "changeemail-oldemail": "هاڻوڪو برق ٽپال پتو:",
        "changeemail-newemail": "نئون برق ٽپال پتو:",
        "summary-preview": "تت تي پيش نگاهہ:",
        "subject-preview": "موضوع پيش نگاهہ:",
        "blockedtitle": "يُوزر بندشيل آهي.",
-       "blockedtext": "'''توهان جي يوزرنانءُ يا آءِ پي کي بندشيو ويو آهي.'''\n\nبندش $1 هنئي. جڏهن تہ ڄاڻايل سبب ''$2'' آهي.\n\n\n* بندش جو آغاز: $8\n* بندش جو انجام: $6\n* بندش جو هدف: $7\n\nاهڙي روڪ تي بحث ڪرڻ لاءِ توهان $1 يا ڪنهن ٻي [[{{MediaWiki:Grouppage-sysop}}|منتظم]] سان رابطو ڪري سگھو ٿا. جيڪڏهن توهان جو درست [[Special:ترجيحات|کاتو ترجيحات]] ۾ درست برق ٽپال پتو درج ٿيل نہ آهي تہ توهان 'هن يوزر کي برق ٽپال ڪريو' وارو فيچر نہ ٿا \nYou cannot use the 'e-mail this user' feature unless a valid e-mail address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nاستعمال ڪري سگھو. توهان جو هاڻوڪو آءِ پي پتو $3 آهي، ۽ بندش سڃاڻپ $5 آهي. مهرباني ڪري ڪنهن به پڇا ڳاڇا يا لهوچڙ لاءِ انهن مان ڪنهن هڪ يا ٻنهي جو حوالو ڏيندا.",
+       "blockedtext": "'''توهان جي يوزرنانءُ يا آءِ پي کي بندشيو ويو آهي.'''\n\nبندش $1 هنئي. جڏهن تہ ڄاڻايل سبب ''$2'' آهي.\n\n\n* بندش جو آغاز: $8\n* بندش جو انجام: $6\n* بندش جو هدف: $7\n\nاهڙي روڪ تي بحث ڪرڻ لاءِ توهان $1 يا ڪنهن ٻي [[{{MediaWiki:Grouppage-sysop}}|منتظم]] سان رابطو ڪري سگھو ٿا. جيڪڏهن توهان جو درست [[Special:ترجيحات|کاتو ترجيحات]] ۾ درست برق ٽپال پتو درج ٿيل نہ آهي تہ توهان 'هن يوزر کي برق ٽپال ڪريو' وارو فيچر نہ ٿا \nYou cannot use the 'e-mail this user' feature unless a valid e-mail address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nاستعمال ڪري سگھو. توهان جو هاڻوڪو آءِ پي پتو $3 آهي، ۽ بندش سڃاڻپ $5 آهي. مهرباني ڪري ڪنهن بہ پڇا ڳاڇا يا لهوچڙ لاءِ انهن مان ڪنهن هڪ يا ٻنهي جو حوالو ڏيندا.",
        "blockednoreason": "سبب اڻڄاڻايل",
+       "whitelistedittext": "صفحا سنوارڻ لاءِ مهرباني ڪري $1.",
+       "confirmedittext": "صفحا سنوارڻ کان اڳ توهان کي پنهنجو برق ٽپال پتي جي تصديق ڪرڻي پوندي. مهرباني ڪري [[Special:ترجيحات|يُوزر ترجيات]] ذريعي پنهنجو برق ٽپال پتو ڄاڻايو ۽ تصديقيو.",
+       "nosuchsectiontitle": "سيڪشن لڀجي نه سگھيو",
        "loginreqtitle": "لاگ اِن گھربل آهي",
        "loginreqlink": "لاگ اِن",
        "loginreqpagetext": "ٻيا صفحا ڏسڻ لاءِ مهرباني ڪري $1",
        "content-not-allowed-here": "\"$1\" مواد هن صفحي [[$2]] تي رکي نہ ٿو سگھجي.",
        "content-model-wikitext": "وڪي‌ٽيڪسٽ",
        "content-model-text": "سادو ٽيڪسٽ",
+       "content-model-javascript": "جاوا اسڪرپٽ",
        "content-json-empty-object": "خالي آبجيڪٽ",
        "content-json-empty-array": "خالي اري",
        "duplicate-args-warning": "وارننگ: [[:$2]]کي [[:$1]] ڪال ڪري رهيو آهي، ساڻ هڪ کان وڌيڪ قدرن لاءِ ’$3‘ پيراميٽرس لاءِ. فقط آخري قدر مهيا ڪيل استعمال ڪيو ويندو.",
        "parser-template-loop-warning": "سانچو چڪر لڌو ويو: [[$1]]",
+       "cantcreateaccounttitle": "کاتو کولي نہ ٿو سگھجي",
+       "cantcreateaccount-text": "$3 جو ڄاڻايل سبب <em>$2</em> آهي.",
+       "cantcreateaccount-range-text": "$3 جو ڄاڻايل سبب \"$2\" آهي.",
        "viewpagelogs": "هن صفحي جا لاگ ڏسو",
        "currentrev": "هاڻوڪو مسودو",
        "currentrev-asof": "$1 جو تازو ترين مسودو",
        "nextrevision": "اڃا نئون پرت→",
        "currentrevisionlink": "هاڻوڪو پرت",
        "cur": "ھاڻوڪو",
+       "next": "اڳيون",
        "last": "پويون",
        "page_first": "پهريون",
        "page_last": "آخري",
+       "history-fieldset-title": "جھانگ جي سوانح",
        "history-show-deleted": "رڳو ڊاٺل",
        "histfirst": "اوائلي ترين",
        "histlast": "تازه ترين",
        "historysize": "({{PLURAL:$1|1 ٻاٽڻ|$1 ٻاٽڻيون}})",
        "historyempty": "(خالي)",
+       "history-feed-title": "ترميمي سوانح",
+       "history-feed-description": "وڪي جي هن صفحي جي ترميمي سوانح",
        "history-feed-item-nocomment": "$2 تي $1",
        "rev-deleted-user": "(يُوزرنانءُ ڊاٺو ويو)",
+       "rev-showdeleted": "نمايو",
+       "revisiondelete": "مسوادا ڊاهيو/اڻ‌ڊاهيو",
        "revdelete-show-file-submit": "ها",
        "revdelete-radio-set": "لڪل",
        "revdelete-radio-unset": "ظاهر",
+       "revdelete-log": "سبب:",
        "pagehist": "صفحي جي سوانح",
        "deletedhist": "ڊاٺل سوانح",
        "revdelete-otherreason": "ٻيا/اضافي ڪارڻ:",
        "search-result-size": "$1 ({{PLURAL:$2|لفظُ|$2 لفظَ}})",
        "search-redirect": "($1 کي چوريو)",
        "search-section": "(سيڪشن $1)",
+       "search-category": "(ذمرو $1)",
        "search-suggest": "ڇا توهان جو مطلب $1 آهي؟",
        "search-interwiki-caption": "برادر رٿائون",
        "search-interwiki-more": "(وڌيڪ)",
        "searchrelated": "لاڳاپيل",
        "searchall": "سڀ",
        "search-nonefound": "توهان جي ڳولا جي نتيجي ۾ ڪجھہ بہ ڪو نہ لڌو.",
+       "powersearch-togglelabel": "چڪاسيو:",
        "powersearch-toggleall": "سڀ",
        "powersearch-togglenone": "ڪو بہ نہ",
        "search-external": "خارجي ڳولا",
        "prefs-editwatchlist-edit": "پنهنجي ٽيٽ فهرست ۾ موجود عنوان ڏسو ۽ مٽايو",
        "prefs-editwatchlist-raw": "ڪچي ٽيٽ فهرست سنواريو",
        "prefs-editwatchlist-clear": "پنهنجي ٽيٽ فهرست ڊاهيو",
+       "prefs-watchlist-days": "ٽيٽ فهرست هيترن ڏينهن تي ٻڌل هجي:",
        "prefs-watchlist-days-max": "وڌ ۾ وڌ $1 {{PLURAL:$1|ڏينهن|ڏينهن}}",
        "prefs-watchlist-edits-max": "وڌ ۾ وڌ تعداد: 1000",
+       "prefs-watchlist-token": "ٽيٽ لسٽ جو ٽوڪن:",
        "prefs-resetpass": "ڳجھو لفظ بدلايو",
        "prefs-changeemail": "برق ٽپال پتو مِٽايو يا بدلايو",
        "prefs-setemail": "ڪو برق ٽپال پتو ڄاڻايو",
        "prefs-email": "برق ٽپال چارا",
        "prefs-rendering": "حليو",
        "saveprefs": "سانڍيو",
+       "rows": "قطارون:",
+       "searchresultshead": "ڳولا",
        "stub-threshold-sample-link": "نمونو",
        "stub-threshold-disabled": "غيرفعال",
        "recentchangesdays-max": "وڌ ۾ وڌ $1 {{PLURAL:$1|ڏينهن|ڏينهن}}",
        "userrights-nodatabase": "اعداخانو $1 يا تہ وجود نہ ٿو رکي يا تہ اهو مقامي اعدادخانو نہ آهي.",
        "group": "گروپ:",
        "group-user": "يوزرس",
+       "group-all": "(سڀ)",
+       "group-user-member": "{{GENDER:$1|يُوزر}}",
+       "group-sysop-member": "{{GENDER:$1|منتظم}}",
+       "grouppage-user": "{{ns:project}}:يُوزرس",
        "grouppage-sysop": "{{ns:project}}:منتظمين",
+       "grouppage-bureaucrat": "{{ns:project}}:ڪامورا",
+       "right-read": "صفحا پڙهو",
+       "right-edit": "صفحا سنواريو",
+       "right-createpage": "صفحا سنواريو (جيڪي مباحثي صفحا نہ آهن)",
+       "right-createtalk": "مباحثي صفحا سرجيو",
+       "right-createaccount": "نوان يُوزر کاتا کوليو",
+       "right-move": "صگحا چوريو",
+       "right-move-subpages": "ذيلي صفحن سميت صفحا چوريو",
+       "right-movefile": "فائيل چوريو",
+       "right-upload": "فائيل چاڙهيو",
+       "right-upload_by_url": "ڪنهن يُو آر ايل کان فائيل چاڙهيو",
+       "right-delete": "صفحا ڊاهيو",
+       "right-bigdelete": "ڊگھيون سوانح رکندڙ صفحا ڊاهيو",
+       "right-browsearchive": "ڊاٺل صفحن ۾ ڳوليو",
        "right-undelete": "ڪو صفحو اڻڊاهيو",
+       "right-editinterface": "يُوزر باهمرُو کي سنواريو",
+       "right-viewmywatchlist": "پنهنجي ٽيٽ لسٽ ڏسو",
+       "right-editmyoptions": "پنهنجون ترجيحات سنواريو",
+       "right-import": "ٻين وڪيز کان صفحا درآمديو",
+       "right-mergehistory": "صفحن جي سوانح سنواريو",
        "newuserlogpage": "يوزر کاتن جو لاگ",
        "rightslog": "يُوزر حق لاگ",
+       "action-move": "هيءَُ صفحو چوريو",
+       "action-move-subpages": "هيءُ صفحو، ۽ ان جا ذيلي صفحا چوريو",
+       "action-move-categorypages": "زمرن جا صفحا چوريو",
        "action-movefile": "هيءُ فائيل چوريو",
        "action-upload": "هيءُ فائيل چاڙهيو",
+       "action-delete": "هيءُ صفحو ڊاهيو",
        "action-browsearchive": "ڊاٺل صفحن ۾ ڳوليو",
        "action-undelete": "هيءُ صفحو اڻڊاهيو",
+       "action-import": "ٻي ڪنهن وڪي کان صفحا درآمديو",
+       "action-importupload": "ڪو فائيل چاڙهي صفحا درآمديو",
+       "action-mergehistory": "هن صفحي جي سوانح ضم ڪريو",
+       "action-siteadmin": "اعدادخاني کي بنديو يا کوليو",
+       "action-sendemail": "برق ٽپال اماڻيو",
        "action-editmywatchlist": "پنهنجي ٽيٽ فهرست سنواريو",
        "action-viewmywatchlist": "پنهنجي ٽيٽ فهرست ڏسو",
        "nchanges": "$1 {{PLURAL:$1|تبديلي|تبديليون}}",
        "rcshowhideanons-show": "نمايو",
        "rcshowhideanons-hide": "لڪايو",
        "rcshowhidepatr": "$1 تاڻيل ترميمون",
+       "rcshowhidepatr-show": "نمايو",
        "rcshowhidepatr-hide": "لڪايو",
        "rcshowhidemine": "منهنجون ترميمون $1",
        "rcshowhidemine-show": "نمايو",
        "uploaded-remote-url-svg": "ايس وي جي جيڪا سيٽ ڪري ٿي ڪنهن اسٽائيل وصف  رموٽ يو آر ايل سان  بلاڪ ٿيل آهي.\n <code>$1=\"$2\"</code> اپلوڊ ٿيل ايس وي جي فائيل ۾ مليو",
        "uploaded-image-filter-svg": "هن يو آر ايل سان <code>&lt;$1 $2=\"$3\"&gt;</code> اميج فلٽر مليو آهي، اپلوڊ ٿيل ايس وي جي فائيل ۾،",
        "uploadvirus": "هن فائيل ۾ وائرس آهي! \nتفصيل: $1",
+       "upload-description": "فائيل جي تشريح",
        "watchthisupload": "هيءُ فائيل ٽيٽيو",
        "upload-success-subj": "چاڙهہ ڪامياب",
+       "upload-file-error": "اندروني چُڪَ",
        "upload-dialog-title": "فائيل چاڙهيو",
        "upload-dialog-button-cancel": "رد",
        "upload-dialog-button-save": "سانڍيو",
        "upload-dialog-button-upload": "چاڙهيو",
        "upload-form-label-select-file": "فائيل چونڊيو",
+       "upload-form-label-infoform-title": "تفصيل",
        "upload-form-label-infoform-name": "نالو",
        "upload-form-label-infoform-description": "تشريح",
        "upload-form-label-usage-title": "استعمال",
index f988e8c..160d151 100644 (file)
        "createacct-realname": "Tėkros vardos (nie būtėns)",
        "createaccountreason": "Dingstės:",
        "createacct-reason": "Dingstės",
-       "createacct-captcha": "Sargtėis patikrėnėms",
-       "createacct-imgcaptcha-ph": "Ožrašīkėt ženklus, katrus veizėt viršō",
        "createacct-submit": "Padėrbkat savėi paskīra",
        "createacct-another-submit": "Padėrbtė kėta paskīra",
        "createacct-benefit-heading": "{{SITENAME}} īr sokorta prietėliu, tuokiu, kāp Tamsta.",
index f293154..a9336da 100644 (file)
        "createaccountreason": "Razlog:",
        "createacct-reason": "Razlog",
        "createacct-reason-ph": "Zakaj ustvarjate drug račun",
-       "createacct-captcha": "Varnostno preverjanje",
-       "createacct-imgcaptcha-ph": "Vnesite zgornje besedilo",
        "createacct-submit": "Ustvarite svoj račun",
        "createacct-another-submit": "Ustvarite račun",
        "createacct-benefit-heading": "{{GRAMMAR:tožilnik|{{SITENAME}}}} ustvarjajo ljudje, kot ste vi.",
index 77c6418..b364e93 100644 (file)
        "createaccountreason": "Разлог:",
        "createacct-reason": "Разлог",
        "createacct-reason-ph": "Зашто правите још један налог?",
-       "createacct-captcha": "Сигурносна провера",
-       "createacct-imgcaptcha-ph": "Унесите текст који видите изнад",
        "createacct-submit": "Отвори налог",
        "createacct-another-submit": "Отвори налог",
        "createacct-benefit-heading": "{{SITENAME}} је направљен од стране људи као што сте ви.",
        "right-applychangetags": "примењивање [[Special:Tags|ознака]] на нечије измене",
        "right-changetags": "додавање и уклањање разних [[Special:Tags|ознака]] на појединачним изменама и уносима у дневницима",
        "newuserlogpage": "Дневник нових корисника",
-       "newuserlogpagetext": "Ð\9eво Ñ\98е Ð¸Ñ\81Ñ\82оÑ\80иÑ\98а нових корисника.",
+       "newuserlogpagetext": "Ð\9eво Ñ\98е Ð´Ð½ÐµÐ²Ð½Ð¸Ðº нових корисника.",
        "rightslog": "Дневник корисничких права",
        "rightslogtext": "Ово је дневник измена корисничких права.",
        "action-read": "читање ове странице",
        "log": "Дневници",
        "all-logs-page": "Сви јавни дневници",
        "alllogstext": "Скупни приказ свих доступних историја овог викија.\nМожете сузити приказ одабирући врсту историје, корисничког имена или тражене странице.",
-       "logempty": "Ð\9dема Ð¿Ñ\80онаÑ\92ениÑ\85 Ñ\81Ñ\82авки Ñ\83 Ð¸Ñ\81Ñ\82оÑ\80иÑ\98и.",
+       "logempty": "Ð\9dема Ð¿Ñ\80онаÑ\92ениÑ\85 Ñ\83ноÑ\81а Ñ\83 Ð´Ð½ÐµÐ²Ð½Ð¸ÐºÑ\83.",
        "log-title-wildcard": "Тражи наслове који почињу с овим текстом",
        "showhideselectedlogentries": "Прикажи/сакриј изабране догађаје",
        "log-edit-tags": "Уреди ознаке изабраних уноса у дневницима",
        "creditspage": "Аутори странице",
        "nocredits": "Не постоје подаци о аутору ове странице.",
        "spamprotectiontitle": "Филтер за заштиту од непожељних порука",
-       "spamprotectiontext": "Филтера против нежељених порука је блокирао чување ове странице.\nОво је вероватно изазвано везом до спољашњег сајта који се налази на црној листи.",
+       "spamprotectiontext": "Филтера против нежељених порука је блокирао чување ове странице.\nОво је вероватно изазвано везом до спољашњег сајта који се налази на црном списку.",
        "spamprotectionmatch": "Следећи текст је активирао наш филтер за нежељене поруке: $1",
        "spambot_username": "Чишћење непожељних порука у Медијавикији",
        "spam_reverting": "Враћам на последњу измену која не садржи везе до $1",
        "logentry-managetags-activate": "$1 је {{GENDER:$2|активирао|активирала}} ознаку „$4“ за употребу од стране корисника и ботова",
        "logentry-managetags-deactivate": "$1 је {{GENDER:$2|деактивирао|деактивирала}} ознаку „$4“ за употребу од стране корисника и ботова",
        "log-name-tag": "Дневник ознака",
+       "log-description-tag": "Овај дневник приказује додавање/уклањање [[Special:Tags|ознака]] на појединачне измене или уносе у дневницима. Овај дневник не приказује означавање када су она део уређивања, брисања или неке друге радње.",
        "rightsnone": "(нема)",
        "revdelete-summary": "опис измене",
        "feedback-adding": "Додајем повратну информацију на страницу…",
        "feedback-bugornote": "Ако сте спремни да детаљно опишете технички проблем, онда [$1 пријавите грешку].\nУ супротном, послужите се једноставним обрасцем испод. Ваш коментар ће стајати на страници „[$3 $2]“, заједно с корисничким именом и прегледачем који користите.",
        "feedback-cancel": "Откажи",
        "feedback-close": "Урађено",
+       "feedback-external-bug-report-button": "Пријави баг",
        "feedback-error-title": "Грешка",
        "feedback-error1": "Грешка: непрепознат резултат од АПИ-ја",
        "feedback-error2": "Грешка: уређивање није успело",
index b9c646c..0b184e9 100644 (file)
        "createaccountreason": "Razlog:",
        "createacct-reason": "Razlog",
        "createacct-reason-ph": "Zašto pravite još jedan nalog?",
-       "createacct-captcha": "Sigurnosna provera",
-       "createacct-imgcaptcha-ph": "Unesite tekst koji vidite iznad",
        "createacct-submit": "Otvori nalog",
        "createacct-another-submit": "Otvori nalog",
        "createacct-benefit-heading": "{{SITENAME}} je napravljen od strane ljudi kao što ste vi.",
        "right-passwordreset": "pregledanje poruka za obnavljanje lozinke",
        "right-managechangetags": "pravljenje i/ili brisanje [[Special:Tags|oznaka]] iz baze podataka",
        "newuserlogpage": "Dnevnik novih korisnika",
-       "newuserlogpagetext": "Ovo je istorija novih korisnika.",
+       "newuserlogpagetext": "Ovo je dnevnik novih korisnika.",
        "rightslog": "Dnevnik korisničkih prava",
        "rightslogtext": "Ovo je dnevnik izmena korisničkih prava.",
        "action-read": "čitanje ove stranice",
        "log": "Dnevnici",
        "all-logs-page": "Svi javni dnevnici",
        "alllogstext": "Skupni prikaz svih dostupnih istorija ovog vikija.\nMožete suziti prikaz odabirući vrstu istorije, korisničkog imena ili tražene stranice.",
-       "logempty": "Nema pronađenih stavki u istoriji.",
+       "logempty": "Nema pronađenih unosa u dnevniku.",
        "log-title-wildcard": "Traži naslove koji počinju s ovim tekstom",
        "showhideselectedlogentries": "Prikaži/sakrij izabrane događaje",
        "allpages": "Sve stranice",
        "creditspage": "Autori stranice",
        "nocredits": "Ne postoje podaci o autoru ove stranice.",
        "spamprotectiontitle": "Filter za zaštitu od nepoželjnih poruka",
-       "spamprotectiontext": "Filtera protiv neželjenih poruka je blokirao čuvanje ove stranice.\nOvo je verovatno izazvano vezom do spoljašnjeg sajta koji se nalazi na crnoj listi.",
+       "spamprotectiontext": "Filtera protiv neželjenih poruka je blokirao čuvanje ove stranice.\nOvo je verovatno izazvano vezom do spoljašnjeg sajta koji se nalazi na crnom spisku.",
        "spamprotectionmatch": "Sledeći tekst je aktivirao naš filter za neželjene poruke: $1",
        "spambot_username": "Čišćenje nepoželjnih poruka u Medijavikiji",
        "spam_reverting": "Vraćam na poslednju izmenu koja ne sadrži veze do $1",
index 5c50e95..1692650 100644 (file)
        "createaccountreason": "Orsak:",
        "createacct-reason": "Orsak",
        "createacct-reason-ph": "Varför du skapar ett annat konto",
-       "createacct-captcha": "Säkerhetskontroll",
-       "createacct-imgcaptcha-ph": "Fyll i texten du ser ovan",
        "createacct-submit": "Skapa ditt konto",
        "createacct-another-submit": "Skapa konto",
        "createacct-benefit-heading": "{{SITENAME}} är skapad av människor som dig.",
index 7d8807d..44d65fb 100644 (file)
        "createaccountreason": "Sebep:",
        "createacct-reason": "Gerekçe",
        "createacct-reason-ph": "Neden başka bir hesap oluşturuyorsunuz",
-       "createacct-captcha": "Güvenlik kontrolü",
-       "createacct-imgcaptcha-ph": "Yukarıda gördüğünüz metni girin",
        "createacct-submit": "Hesabınızı oluşturun",
        "createacct-another-submit": "Hesap oluşturun",
        "createacct-benefit-heading": "{{SITENAME}} sizin gibi insanlar tarafından geliştirilir.",
index 4636d0b..60fddad 100644 (file)
        "createacct-email-ph": "Электрон почта юлламагызны языгыз",
        "createaccountmail": "электрон почта аша",
        "createaccountreason": "Сәбәп:",
-       "createacct-captcha": "Саклыкны тикшерү",
-       "createacct-imgcaptcha-ph": "Өстә күрсәтелгән текстны кертегез",
        "createacct-submit": "Хисап язмасы төзү",
        "createacct-benefit-heading": "{{SITENAME}} — сезнең шикелле кешеләрнең хезмәте.",
        "createacct-benefit-body1": "{{PLURAL:$1|төзәтмә}}",
index e835306..33ac76d 100644 (file)
        "createaccountreason": "Sabab:",
        "createacct-reason": "Sabab",
        "createacct-reason-ph": "Nimaga yana boshqa hisob yaratyapsiz",
-       "createacct-captcha": "Xavfsizlik tekshiruvi",
-       "createacct-imgcaptcha-ph": "Yuqoridagi yozuvni bu yerga kiriting",
        "createacct-submit": "Hisob yaratish",
        "createacct-another-submit": "Boshqa hisob yaratish",
        "createacct-benefit-heading": "{{SITENAME}} Sizga oʻxshagan odamlar tomonidan yaratiladi",
        "passwordreset-email": "Elektron pochta manzilingiz:",
        "passwordreset-emailelement": "Foydalanuvchi ismi: \n$1\n\nVaqtinchalik maxfiy so'z: \n$2",
        "changeemail": "Elektron pochta manzilini oʻzgartirish",
+       "changeemail-header": "Elektron pochta manzilini o'zgaritish",
        "changeemail-oldemail": "Joriy elektron pochta manzili",
        "changeemail-newemail": "Elektron pochtaning yangi manzili",
        "changeemail-none": "(yoʻq)",
        "undeleteviewlink": "koʻrib chiqish",
        "undeleteinvert": "Tanlash tartibini almashtirish",
        "undeletecomment": "Sababi:",
+       "undeletedrevisions": "{{PLURAL:$1|1 ta oʻzgarish|$1 ta tahrirlar}} tiklandi",
+       "undeletedfiles": "$1 ta fayl tiklandi",
        "undelete-search-title": "O'chirilgan sahifalarni qidirish",
        "undelete-search-box": "O'chirilgan sahifalarni qidirish",
        "undelete-search-prefix": "Bundan boshlangan sahifalarni koʻrsatish:",
        "block-log-flags-nousertalk": "o'zining munozara sahifasini tahrirlay olmaydi",
        "move-page": "$1 — qayta nomlash",
        "move-page-legend": "Sahifani qayta nomlash",
-       "movearticle": "Hozirgi nomi:",
        "newtitle": "Yangi nom:",
        "move-watch": "Ushbu sahifani kuzatuv roʻyxatingizga qoʻshish",
        "movepagebtn": "Sahifani koʻchirish",
        "htmlform-reset": "Oʻzgarishlarni bekor qilish",
        "htmlform-selectorother-other": "Boshqa",
        "logentry-delete-delete": "$1 $3 sahifasini {{GENDER:$2|oʻchirdi}}",
+       "logentry-delete-restore": "$1 $3 sahifasini {{GENDER:$2|tikladi}}",
        "logentry-move-move": "$1 $3 sahifasini $4ga koʻchirdi",
        "logentry-move-move-noredirect": "$1 $3 sahifasini $4ga {{GENDER:$2|koʻchirdi}}",
        "logentry-move-move_redir": "$1 sahifa nomini $3dan $4ga yoʻnaltirish ustidan {{GENDER:$2|koʻchirdi}}",
index 07dbf4b..d5ef92c 100644 (file)
@@ -17,6 +17,7 @@
        "tog-hideminor": "Tago-a an mga gagmay nga pagliwat ha mga bag-o pa la nga mga kabag-ohan",
        "tog-hidepatrolled": "Tago-a in mga gin-patrol o binantayan nga mga pagliwat ha mga dipala naiha nga mga kabag-ohan",
        "tog-newpageshidepatrolled": "Tago-a an mga gin-patrol o binantayan nga mga pakli tikang han talaan hin bag-o nga pakli",
+       "tog-hidecategorization": "Igtago an kategorisasyon han mga pakli",
        "tog-extendwatchlist": "Padako-a an angay timan-an agod makita an tanan nga kabag-ohan, diri la an gibag-ohi",
        "tog-usenewrc": "Grupo nga mga pagbag-o kada pakli ha kababag-o pala ngan mga barantayon nga talaan",
        "tog-numberheadings": "Auto-nga-ihap nga mga pagngaran",
@@ -27,6 +28,7 @@
        "tog-watchdefault": "Igdugang in mga pakli ngan mga paypay nga akon ginliwat ngadto han akon angay timan-an",
        "tog-watchmoves": "Igdugang in mga pakli nga mga paypay nga akon ginpamalhin ngadto han akon angay timan-an",
        "tog-watchdeletion": "Igdugang in mga pakli ngan mga paypay nga akon ginpamara ngadto han akon angay timan-an",
+       "tog-watchrollback": "Igdugang an mga pakli ha akon watchlist an ak mga gin-rollback",
        "tog-minordefault": "Tigamni an ngatanan nga mga pagliwat nga gudti hin default",
        "tog-previewontop": "Igpakita in prevista o pan-ugsa-nga-lantaw ugsa hiton pagliwat nga kahon",
        "tog-previewonfirst": "Igpakita in prevista o pan-ugsa-nga-lantaw ha syahan nga pagliwat",
@@ -37,7 +39,7 @@
        "tog-shownumberswatching": "Igpakita an ihap han mga nangingita nga mga nagamit",
        "tog-oldsig": "Aada nga pirma:",
        "tog-fancysig": "Tratuha it pirma komo uska wikitext (nga waray automatiko nga sumpay)",
-       "tog-uselivepreview": "Gamita an buhi nga pahiuna nga pagawas (eksperimental)",
+       "tog-uselivepreview": "Gamita an buhi nga pahiuna nga pagawas",
        "tog-forceeditsummary": "Pasabti ako kun waray ko ginsurat ha dalikyat-nga-tigaman han pagliwat (edit summary)",
        "tog-watchlisthideown": "Tago-a an akon mga ginliwat tikang han angay timan-an",
        "tog-watchlisthidebots": "Tago-a an ginliwat hin bot tikang han angay timan-an",
@@ -45,6 +47,7 @@
        "tog-watchlisthideliu": "Igatag an mga ginliwat han naka log-in nga mga gumaramit tikang ha gintitiman-an",
        "tog-watchlisthideanons": "Igtago an mga ginliwat han mga waray nagpakilala nga nagamit tikang ha gintitiman-an",
        "tog-watchlisthidepatrolled": "Igatag an mga pinatrolya nga mga pagliwat tikang ha angay timan-an",
+       "tog-watchlisthidecategorization": "Igtago an kategorisasyon han mga pakli",
        "tog-ccmeonemails": "Padad-i ak hin mga kopya hin mga email nga akon ginpapadara ha iba nga mga gumaramit",
        "tog-diffonly": "Ayaw igpakita an sulod han pakli ha ilarom han pagkakaiba",
        "tog-showhiddencats": "Igpakita an mga tinago nga mga kaarangay",
        "pool-queuefull": "Puno an katitirok nga pila",
        "pool-errorunknown": "Waray kasabti nga kasaypanan",
        "pool-servererror": "An pool nga pag-ihap nga serbisyo diri yanâ magagamit ($)",
+       "poolcounter-usage-error": "Sayop ha paggamit: $1",
        "aboutsite": "Mahitungod han {{SITENAME}}",
        "aboutpage": "Project:Mahitungod han",
        "copyright": "An sulod mabiblingan ha ilarom han $1 antes may-ada pasabot.",
        "disclaimers": "Mga Disclaimer",
        "disclaimerpage": "Project:Kasahiran nga disclaimer",
        "edithelp": "Bulig hin pagliwat",
+       "helppage-top-gethelp": "Bulig",
        "mainpage": "Syahan nga Pakli",
        "mainpage-description": "Syahan nga Pakli",
        "policy-url": "Project:Polisiya",
        "hidetoc": "tago-a",
        "collapsible-collapse": "Rumpag",
        "collapsible-expand": "Latag",
+       "confirmable-confirm": "Sigurado {{GENDER:$1|ka}}?",
+       "confirmable-yes": "Oo",
+       "confirmable-no": "Diri",
        "thisisdeleted": "¿Kitaa o balika in $1?",
        "viewdeleted": "¿Kitaa in $1?",
        "restorelink": "{{PLURAL:$1|usa nga ginpara nga pagliwat|$1 ka ginpara nga mga pagliwat}}",
        "readonly_lag": "Ginlugaring pagtranka han database samtang an mga nasunod nga mga database nga server naglalanat pa han agaron",
        "internalerror": "Sayop ha sulod",
        "internalerror_info": "Sayop ha sulod: $1",
+       "internalerror-fatal-exception": "Fatal exception han klase \"$1\"",
        "filecopyerror": "Diri nakokopya an paypay nga ''$1'' ha ''$2''.",
        "filerenameerror": "Diri nababalyuan an ngaran han paypay nga ''$1'' ha ''$2''.",
        "filedeleteerror": "Diri napapara an paypay nga ''$1''.",
        "directorycreateerror": "Waray makahimo han direktoryo nga \"$1\".",
+       "directoryreadonlyerror": "Direktorya \"$1\" in pagbasa la.",
+       "directorynotreadableerror": "Direktorya \"$1\" in diri nababasahan.",
        "filenotfound": "Diri nabibilngan an paypay nga \"$1\"",
        "unexpected": "Diri ginlalauman nga balor: \"$1\"=\"$2\".",
        "formerror": "Sayop: Diri nasusumite an porma.",
        "no-null-revision": "Diri nakakahimo hin bag-o nga null rebisyon para han pakli \"$1\"",
        "badtitle": "Maraot nga titulo",
        "badtitletext": "An ginhangyo nga pakli diri puyde, waray sulod, o sayop nga nasumpay nga inter-pinunongan o inter-wiki nga titulo.\nBangin mayda usa o damo nga mga agi nga diri puyde magamit ha mga titulo.",
+       "title-invalid-empty": "An ginhangyo nga titulo han pakli in waray sulod o nagsusulod la han ngaran han uska ngaran-latáng.",
+       "title-invalid-utf8": "An ginhangyo nga titulo han pakli in nagsusulod hin invalid UTF-8 sequence.",
+       "title-invalid-interwiki": "An ginhangyo nga titulo han pakli in nagsusulod hin uska interwiki nga sumpay nga diri magagamitan ha mga titulo.",
+       "title-invalid-talk-namespace": "An ginhangyo nga titulo hin pakli in natudlok ha hiruhimangraw nga pakli nga waray pa.",
+       "title-invalid-characters": "An ginhangyo nga titulo han pakli in nagsusulod hin invalid characters: \"$1\".",
+       "title-invalid-relative": "An titulo in may-ada relative path. An mga relative page title (./, ../) in diri gintutugotan, tungod nga hira in agsob diri maaabtan kun gamiton han kanan gumaramit browser.",
+       "title-invalid-magic-tilde": "An ginhangyo nga titulo han pakli in nagsusulod hin invalid magic tilde sequence (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "An ginhangyo nga titulo han pakli in halaba hin duro. Diri dapat mas halaba kaysa $1 {{PLURAL:$1|ka byte|ka mga byte}} ha UTF-8 encoding.",
+       "title-invalid-leading-colon": "An ginhangyo nga titulo han pakli in nagsusulod hin invalid colon ha katikangan.",
        "perfcached": "An nasunod nga data gin-cache ngan bangin diri amo an yana. In maximum hin {{PLURAL:$1|usa ka resulta|$1 ka mga resulta}} aada hit cache.",
        "perfcachedts": "An nasunod nga data gin-cache, ngan kataposan ginbag-o dida han $1. In maximum hin {{PLURAL:$4|usa ka resulta|$4 ka resulta}} aada hit cache.",
        "querypage-no-updates": "An mga kabag-ohan para hini nga pakli ha yana diri mahihimo.\nAn data dini diri mahihimo nga bag-o.",
        "viewsource": "Kitaa an ginkuhaan",
        "viewsource-title": "Kitaa an tinikangan para han $1",
        "actionthrottled": "Ginpahinay an ginbuhat",
-       "actionthrottledtext": "Komo uska pangontra ha spam, ikaw in ginlilimitaran paghimo hini nga pagbuhat hin sobra kadamo ha sulod hin gutiay nga oras, ngan ikaw in naglapos hini nga katubtuban.\nAlayon pagutro kahuman hin pipira ka mga minuto.",
+       "actionthrottledtext": "Komo uska pangontra han abuso, ikaw in ginlilimitahan paghimo hini nga pagbuhat hin sobra kadamo ha sulod hin gutiay nga panahon, ngan ikaw in naglapos hini nga katubtuban.\nAlayon pagutro kahuman hin pipira ka mga minuto.",
        "protectedpagetext": "Ginpanalipdan ini nga pakli basi mapugngan an pagliwat o iba pa nga mga maburuhat.",
-       "viewsourcetext": "Puydi ka kinmita ngan kinmopya han gintikangan han pakli:",
-       "viewyourtext": "Puydi nim makit-an ngan makopya an tinikangan han '''imo mga pagliwat''' ha dinhi nga pakli:",
+       "viewsourcetext": "Puydi ka kumita ngan kumopya han gintikangan han pakli.",
+       "viewyourtext": "Puydi nim makit-an ngan makopya an tinikangan <strong>imo mga pagliwat</strong> dinhi nga pakli:",
        "protectedinterface": "Ini nga pakli in nahatag hin teksto hit interface para han software han hin nga wiki, ngan in pinasasaliporan para makalikay hit pag-abuso.\nPara makadugang o makaliwat hin mga paghubad para han tanan nga mga wiki, alayon paggamit han [//translatewiki.net/ translatewiki.net], an kanan MediaWiki proyekto hin lokalisasyon.",
        "editinginterface": "'''Pahimatngon:''' Imo ginliliwat an pakli nga gingagamit paghatag hin interface text para han software.\nAn mga pagbag-o hini nga pakli in makakaapekto han user interface han iba nga mga gumaramit hini nga wiki.\nPara makadugang o makabag-o han mga paghubad para han ngatanan nga mga wiki, alayon paggamit han [//translatewiki.net/ translatewiki.net], an lokalisasyon nga proyekto han MediaWiki.",
        "cascadeprotected": "Ini nga pakli in pinapasaliporan hin pagliwat tungod ini in nalalakip ha masunod nga {{PLURAL:$1|pakli, kun diin |mga pakli, kun diin}} pinapasaliporan hit \"cascading\" nga pagpili nga pinaandar:\n$2",
        "createaccountreason": "Rason:",
        "createacct-reason": "Rason",
        "createacct-reason-ph": "Kay ano nahimo ka hin usa pa nga akawnt",
-       "createacct-captcha": "Pagkita han seguridad",
-       "createacct-imgcaptcha-ph": "Igbutang an sinurat nga nakikita mo ha igbaw",
        "createacct-submit": "Ighimo an im akawnt",
-       "createacct-another-submit": "Paghimo hin lain nga akant",
+       "createacct-another-submit": "Paghimo hin account",
        "createacct-benefit-heading": "{{SITENAME}} in ginhimo hin tawo nga sugad ha imo.",
        "createacct-benefit-body1": "{{PLURAL:$1|pagliwat|mga pagliwat}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ka pakli|ka mga pakli}}",
        "createacct-benefit-body3": "bag-o pala nga {{PLURAL:$1|mag-aramot|mga mag-aramot}}",
        "badretype": "Diri naangay an mga tigaman-pagsulod nga im ginbutang",
+       "usernameinprogress": "An paghimo hin account para hinin nga ngaran hit gumaramit in ginpoproseso na. Alayon paghulat.",
        "userexists": "An agnay hiton gumaramit nga im ginbutang in gingamit na.\nAlayon pagpili hin lain nga ngaran.",
        "loginerror": "Sayop hin pagsakob",
        "createacct-error": "Pakyas an paghimo han akawnt",
        "wrongpassword": "Sayop nga tigaman-pagsulod an nahibutang.\nAlayon pagutro pagbutang.",
        "wrongpasswordempty": "An tigaman-pagsulod nga ginbutang in waray sulod.\nAlayon pagutro pagbutang.",
        "passwordtooshort": "An tigaman-pagsulod dapat diri maubos hit {{PLURAL:$1|1 nga agi|$1 nga agi}}.",
+       "passwordtoolong": "It mga password in diri puydi mas huruhilaba hin {{PLURAL:$1|1 ka karakter|$1 ka mga karakter}}.",
        "password-name-match": "An imo tigaman-pagsulod in kinahanglan iba ha imo agnay-hiton-gumaramit.",
        "password-login-forbidden": "An paggamit hini nga agnay-hit-gumaramit ngan tigaman-pagsulod in diri gintutugotan.",
        "mailmypassword": "Ig-reset an tigaman-pagsulod",
        "passwordreset-emailsent": "Ginpadangat an password reset email.",
        "passwordreset-emailsent-capture": "Ginpadangat an password reset email, nga ginpakita ha ubos.",
        "passwordreset-emailerror-capture": "Ginhimo an password reset email, kun diin nakikita ha ubos, pero pakyas an pagpadara ha  {{GENDER:$2|gumaramit}}: $1",
-       "changeemail": "Igliwan an e-mail address",
+       "changeemail": "Igliwat o igtanggal an e-mail address",
        "changeemail-header": "Igliwan an e-mail address akawnt",
+       "changeemail-passwordrequired": "Kinahanglan nim igbutang an imo password para igkompirma inin nga pagbag-o.",
        "changeemail-no-info": "Kinahanglanon mo mag-log-in para ka direkta makasakob hini nga pakli.",
        "changeemail-oldemail": "Yana nga e-mail address:",
        "changeemail-newemail": "Bag-o nga e-mail address:",
        "compareselectedversions": "Igkumpara an mga pinili nga pagbabag-o",
        "editundo": "Igpawara an ginbuhat",
        "diff-empty": "(Waray pagkakaiba)",
+       "diff-multi-sameuser": "({{PLURAL:$1|Usa nga intermediate revision|$1 mga intermediate revision}} han pareho nga gumaramit nga waray ginpakita)",
        "diff-multi-manyusers": "({{PLURAL:$1|Uska sapit-nahiuna nga rebisyon|$1 nga mga sapit-nanhiuna nga rebisyon}} nga may labaw nga $2 {{PLURAL:$2|gumaramit|mga gumaramit}} in diri ginpapakita)",
        "searchresults": "Mga nabilingan han pagbiling",
        "searchresults-title": "Mga nabilngan han pagbiling para han \"$1\"",
        "search-relatedarticle": "kasumapy",
        "searchrelated": "kadugtong",
        "searchall": "ngatanan",
+       "search-showingresults": "{{PLURAL:$4|Resulta <strong>$1</strong> han <strong>$3</strong>|Mga resulta <strong>$1 - $2</strong> han <strong>$3</strong>}}",
        "search-nonefound": "Waray resulta an nakakabaton han pakiana.",
        "powersearch-legend": "Abansado nga pagbiling",
        "powersearch-ns": "Pamiling ha mga ngaran-lat'ang:",
        "recentchanges-label-unpatrolled": "Ini nga pagliwat in diri pa nakapatrol",
        "recentchanges-label-plusminus": "An kadako han pakli in nabag-o hin ini nga numero nga mga byte",
        "recentchanges-legend-heading": "'''Leyenda:'''",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (kitaa gihapon [[Special:NewPages|talaan han mga bag-o nga pakli]])",
        "rcnotefrom": "An ha ubos in mga pagbabag-o tikang han <strong>$2</strong> (kutob ngadto ha <strong>$1</strong> nga ginpakita).",
        "rclistfrom": "Pakit-a an mga ginbag-ohan tikang han $3 $2",
        "rcshowhideminor": "$1 gudti nga mga pagliwat",
        "undelete-show-file-submit": "Oo",
        "namespace": "Ngaran-lat'ang",
        "invert": "Baliskara an pirilion",
+       "tooltip-invert": "Ig-check inin nga kahon para igtago an mga pagbag-o han mga pakli ha sulod han pipira nga ngaran-latáng (ngan an may pagkahisumpay nga ngaran-latáng kun gin-check)",
        "namespace_association": "Kasumpay nga mga ngaran-lat'ang",
+       "tooltip-namespace_association": "Igcheck inin nga kahon para liwat iglakip an hiruhimangraw o himangrawon nga ngaran-latáng nga may pagkahisumpay ha pinili nga ngaran-lat'ang",
        "blanknamespace": "(Panguna)",
        "contributions": "Mga ámot ni {{GENDER:$1|User}}",
        "contributions-title": "Mga amot han gumaramit para ha $1",
index 2f86d45..c7704ae 100644 (file)
        "createaccountreason": "אורזאַך:",
        "createacct-reason": "אורזאך",
        "createacct-reason-ph": "פֿארוואס שאפֿט איר נאך א קאנטע",
-       "createacct-captcha": "פארזיכערן קאנטראל",
-       "createacct-imgcaptcha-ph": "קלאפט ארײַן דעם טעקסט איר זעט אויבן",
        "createacct-submit": "שאפֿט אײַער קאנטע",
        "createacct-another-submit": "שאַפֿן קאנטע",
        "createacct-benefit-heading": "{{SITENAME}} איז געמאכט דורך מענטשן ווי איר.",
index dca2e5a..b64ff91 100644 (file)
        "createaccountreason": "原因:",
        "createacct-reason": "原因",
        "createacct-reason-ph": "您为什么要创建另一个账户",
-       "createacct-captcha": "安全检查",
-       "createacct-imgcaptcha-ph": "请输入上图中的文字",
        "createacct-submit": "创建您的账户",
        "createacct-another-submit": "创建账户",
        "createacct-benefit-heading": "{{SITENAME}}是由同你一样的人们构筑的。",
index fb637f7..8b2d6e5 100644 (file)
@@ -96,9 +96,9 @@
                $( '#config_wgSitename' ).on( 'keyup change', syncText ).each( syncText );
 
                // Show/Hide memcached servers when needed
-               $( 'input[name$="config_wgMainCacheType"]' ).change( function () {
+               $( 'input[name$="config__MainCacheType"]' ).change( function () {
                        var $memc = $( '#config-memcachewrapper' );
-                       if ( $( 'input[name$="config_wgMainCacheType"]:checked' ).val() === 'memcached' ) {
+                       if ( $( 'input[name$="config__MainCacheType"]:checked' ).val() === 'memcached' ) {
                                $memc.show( 'slow' );
                        } else {
                                $memc.hide( 'slow' );
index 3ae7b78..209d325 100644 (file)
@@ -13,7 +13,7 @@
     "grunt-contrib-jshint": "0.11.3",
     "grunt-contrib-watch": "0.6.1",
     "grunt-jscs": "2.1.0",
-    "grunt-jsonlint": "1.0.4",
+    "grunt-jsonlint": "1.0.5",
     "grunt-karma": "0.12.1",
     "karma": "0.13.10",
     "karma-chrome-launcher": "0.2.0",
index 6d0175e..125b1b3 100644 (file)
@@ -8,7 +8,7 @@
         * @param {jQuery.Event} e
         */
        function doLivePreview( e ) {
-               var isDiff, api, request, postData, copySelectors, section,
+               var isDiff, api, parseRequest, diffRequest, postData, copySelectors, section,
                        $wikiPreview, $wikiDiff, $editform, $textbox, $summary, $copyElements, $spinner, $errorBox;
 
                isDiff = ( e.target.name === 'wpDiff' );
                api = new mw.Api();
                postData = {
                        action: 'parse',
-                       uselang: mw.config.get( 'wgUserLanguage' ),
                        title: mw.config.get( 'wgPageName' ),
-                       text: $textbox.textSelection( 'getContents' ),
                        summary: $summary.textSelection( 'getContents' ),
-                       sectionpreview: section !== ''
+                       prop: ''
                };
 
-               if ( section === 'new' ) {
-                       postData.section = 'new';
-                       postData.sectiontitle = postData.summary;
-               }
-
                if ( isDiff ) {
                        $wikiPreview.hide();
 
-                       // First PST the input, then diff it
-                       postData.onlypst = true;
-                       request = api.post( postData );
-                       request.done( function ( response ) {
-                               api.post( {
-                                       action: 'query',
-                                       indexpageids: true,
-                                       prop: 'revisions',
-                                       titles: mw.config.get( 'wgPageName' ),
-                                       rvdifftotext: response.parse.text[ '*' ],
-                                       rvprop: [],
-                                       rvsection: section === '' ? undefined : section
-                               } ).done( function ( response ) {
-                                       try {
-                                               var diffHtml = response.query.pages[ response.query.pageids[ 0 ] ]
-                                                       .revisions[ 0 ].diff[ '*' ];
-                                               $wikiDiff.find( 'table.diff tbody' ).html( diffHtml );
-                                       } catch ( e ) {
-                                               // "result.blah is undefined" error, ignore
-                                               mw.log.warn( e );
-                                       }
-                                       $wikiDiff.show();
-                               } );
+                       if ( postData.summary ) {
+                               parseRequest = api.post( postData );
+                       }
+
+                       diffRequest = api.post( {
+                               action: 'query',
+                               indexpageids: true,
+                               prop: 'revisions',
+                               titles: mw.config.get( 'wgPageName' ),
+                               rvdifftotext: $textbox.textSelection( 'getContents' ),
+                               rvdifftotextpst: true,
+                               rvprop: '',
+                               rvsection: section === '' ? undefined : section
+                       } );
+
+                       // Wait for the summary before showing the diff so the page doesn't jump twice
+                       $.when( diffRequest, parseRequest ).done( function ( response ) {
+                               var diffHtml,
+                                       query = response[ 0 ].query;
+                               try {
+                                       diffHtml = query.pages[ query.pageids[ 0 ] ]
+                                               .revisions[ 0 ].diff[ '*' ];
+                                       $wikiDiff.find( 'table.diff tbody' ).html( diffHtml );
+                               } catch ( e ) {
+                                       // "result.blah is undefined" error, ignore
+                                       mw.log.warn( e );
+                               }
+                               $wikiDiff.show();
                        } );
                } else {
                        $wikiDiff.hide();
+
                        $.extend( postData, {
+                               prop: 'text|displaytitle|modules|jsconfigvars|categorieshtml|templates|langlinks|limitreporthtml',
+                               text: $textbox.textSelection( 'getContents' ),
                                pst: true,
                                preview: true,
-                               prop: 'text|displaytitle|modules|jsconfigvars|categorieshtml|templates|langlinks|limitreporthtml',
-                               disableeditsection: true
+                               sectionpreview: section !== '',
+                               disableeditsection: true,
+                               uselang: mw.config.get( 'wgUserLanguage' )
                        } );
-                       request = api.post( postData );
-                       request.done( function ( response ) {
+                       if ( section === 'new' ) {
+                               postData.section = 'new';
+                               postData.sectiontitle = postData.summary;
+                       }
+
+                       parseRequest = api.post( postData );
+                       parseRequest.done( function ( response ) {
                                var li, newList, $displaytitle, $content, $parent, $list;
                                if ( response.parse.jsconfigvars ) {
                                        mw.config.set( response.parse.jsconfigvars );
                                        $wikiPreview.append( $content );
 
                                        $wikiPreview.show();
-
                                }
                        } );
                }
-               request.done( function ( response ) {
-                       var isSubject = ( section === 'new' ),
+               $.when( parseRequest, diffRequest ).done( function ( parseResp ) {
+                       var parse = parseResp && parseResp[ 0 ].parse,
+                               isSubject = ( section === 'new' ),
                                summaryMsg = isSubject ? 'subject-preview' : 'summary-preview',
                                $summaryPreview = $editform.find( '.mw-summary-preview' ).empty();
-                       if ( response.parse.parsedsummary && response.parse.parsedsummary[ '*' ] !== '' ) {
+                       if ( parse && parse.parsedsummary && parse.parsedsummary[ '*' ] !== '' ) {
                                $summaryPreview.append(
                                        mw.message( summaryMsg ).parse(),
                                        ' ',
                                        $( '<span>' ).addClass( 'comment' ).html(
                                                // There is no equivalent to rawParams
                                                mw.message( 'parentheses' ).escaped()
-                                                       .replace( '$1', response.parse.parsedsummary[ '*' ] )
+                                                       .replace( '$1', parse.parsedsummary[ '*' ] )
                                        )
                                );
                        }
                        mw.hook( 'wikipage.editform' ).fire( $editform );
-               } );
-               request.always( function () {
+               } ).always( function () {
                        $spinner.hide();
                        $copyElements.animate( {
                                opacity: 1
                        }, 'fast' );
-               } );
-               request.fail( function ( code, result ) {
-                       var errorMsg = 'API error: ' +  code;
+               } ).fail( function ( code, result ) {
+                       // This just shows the error for whatever request failed first
+                       var errorMsg = 'API error: ' + code;
                        if ( code === 'http' ) {
                                errorMsg = 'HTTP error: ';
                                if ( result.exception ) {
index 2b028ae..15f4e4d 100644 (file)
        font-style: italic;
 }
 
-/* Special:Allpages */
-.mw-allpages-nav {
-       text-align: right;
-       margin-bottom: 1em;
-}
-table.mw-allpages-table-form {
-       width: 100%;
-}
-table.mw-allpages-table-form tr {
-       vertical-align: top;
-}
-
-/* Special:Prefixindex */
-.mw-prefixindex-nav {
-       text-align: right;
-}
-table#mw-prefixindex-nav-table {
-       width: 100%;
-}
-td#mw-prefixindex-nav-form {
-       margin-bottom: 1em;
-       vertical-align: top;
-}
-
 /* Special:Block */
 p.mw-ipb-conveniencelinks {
        font-size: 90%;
index 9fcec02..8c89ed9 100644 (file)
@@ -85,7 +85,9 @@
                },
 
                setWarning: function ( warning ) {
-                       $( '#wpDestFile-warning' ).html( warning );
+                       var $warning = $( $.parseHTML( warning ) );
+                       mw.hook( 'wikipage.content' ).fire( $warning );
+                       $( '#wpDestFile-warning' ).empty().append( $warning );
 
                        // Set a value in the form indicating that the warning is acknowledged and
                        // doesn't need to be redisplayed post-upload
index 30f000b..831ba8c 100644 (file)
@@ -38,7 +38,7 @@ section.mw-form-header {
        margin-top: 6px;
 }
 
-.mw-createacct-captcha-container {
+.fancycaptcha-captcha-container {
        background-color: #f8f8f8;
        border: 1px solid #c9c9c9;
        padding: 10px;
@@ -52,7 +52,7 @@ section.mw-form-header {
 }
 
 /* Put a border around the fancycaptcha-image-container. */
-.mw-createacct-captcha-and-reload {
+.fancycaptcha-captcha-and-reload {
        border: 1px solid #c9c9c9;
        /* Other display formats end up too wide */
        display: table-cell;
index 77b3f9d..d66973a 100644 (file)
@@ -243,6 +243,12 @@ a.mw-ui-button {
        &:focus {
                text-decoration: none;
        }
+
+       // a-tags behave different to inputs if the line-height attribute is inherited
+       // from another element (e.g. mw-body-content). They appear bigger as input
+       // tags. See Bug T116427. To fix that, apply the correct line-height (used
+       // for inputs) to a-tags, too.
+       line-height: normal;
 }
 
 // Button groups
index 2eb8c55..86d4cfe 100644 (file)
@@ -22,7 +22,7 @@
                        min-height: 3.75em;
                        padding-left: 4.75em;
 
-                       &:not(:last-child) {
+                       &:not( :last-child ) {
                                margin-bottom: 2px;
                        }
 
                        }
 
                        &.oo-ui-iconElement {
-                               >.oo-ui-iconElement-icon {
+                               > .oo-ui-iconElement-icon {
                                        display: block;
                                        width: 3.75em;
                                        height: 3.75em;
                                        left: 0;
-                                       background-color: #ccc;
-                                       opacity: 0.4;
-                               }
-
-                               > .mw-widget-titleOptionWidget-hasImage {
-                                       border: 0;
-                                       background-size: cover;
-                                       opacity: 0.7;
+                                       &:not( .mw-widget-titleOptionWidget-hasImage ) {
+                                               background-color: #ccc;
+                                               opacity: 0.4;
+                                       }
+                                       &.mw-widget-titleOptionWidget-hasImage {
+                                               border: 0;
+                                               background-size: cover;
+                                               opacity: 0.7;
+                                       }
                                }
                        }
-               }
 
-               &.oo-ui-optionWidget-highlighted, &.oo-ui-optionWidget-selected {
-                       &.oo-ui-iconElement > .mw-widget-titleOptionWidget-hasImage {
-                               opacity: 1;
+                       &.oo-ui-optionWidget-highlighted, &.oo-ui-optionWidget-selected {
+                               &.oo-ui-iconElement > .mw-widget-titleOptionWidget-hasImage {
+                                       opacity: 1;
+                               }
                        }
                }
        }
index 86fb91b..01917f8 100644 (file)
         * @inheritdoc
         */
        mw.ForeignStructuredUpload.BookletLayout.prototype.getText = function () {
-               this.upload.addDescription( 'en', this.descriptionWidget.getValue() );
+               var language = mw.config.get( 'wgContentLanguage' );
+               this.upload.addDescription( language, this.descriptionWidget.getValue() );
                this.upload.setDate( this.dateWidget.getValue() );
                this.upload.addCategories( this.categoriesWidget.getItemsData() );
                return this.upload.getText();
index 632ae82..188af6a 100644 (file)
@@ -5,6 +5,8 @@
        width: 20em;
        line-height: 1.35;
        z-index: 10000;
+       /* Avoid horizontal scrollbar on fade in and on fade out */
+       overflow: hidden;
 }
 
 .mw-notification {
        margin-bottom: 0.5em;
        border: solid 1px #ddd;
        background-color: #fcfcfc;
-       /* Message hides on-click */
-       /* See also mediawiki.notification.js */
+       /* Click handler in mediawiki.notification.js */
        cursor: pointer;
+
+       opacity: 0;
+       -webkit-transform: translateX(35px);
+       transform: translateX(35px);
+       -webkit-transition: opacity 0.35s ease-in-out, -webkit-transform 0.35s ease-in-out;
+       transition: opacity 0.35s ease-in-out, transform 0.35s ease-in-out;
+}
+
+.mw-notification-visible {
+       opacity: 1;
+       -webkit-transform: translateX(0);
+       transform: translateX(0);
+}
+
+.mw-notification-replaced {
+       opacity: 0;
+       -webkit-transform: translateY(-35px);
+       transform: translateY(-35px);
+       pointer-events: none;
 }
 
 .mw-notification-title {
index eeb7bb3..3f7b8fd 100644 (file)
@@ -7,7 +7,8 @@
                // Number of open notification boxes at any time
                openNotificationCount = 0,
                isPageReady = false,
-               preReadyNotifQueue = [];
+               preReadyNotifQueue = [],
+               rAF = window.requestAnimationFrame || setTimeout;
 
        /**
         * A Notification object for 1 message.
         * @private
         */
        Notification.prototype.start = function () {
-               var
-                       // Local references
-                       $notification, options,
-                       // Original opacity so that we can animate back to it later
-                       opacity,
-                       // Other notification elements matching the same tag
-                       $tagMatches,
-                       outerHeight,
-                       placeholderHeight,
-                       autohideCount,
-                       notif;
+               var options, $notification, $tagMatches, autohideCount;
 
                $area.show();
 
                options = this.options;
                $notification = this.$notification;
 
-               opacity = this.$notification.css( 'opacity' );
-
-               // Set the opacity to 0 so we can fade in later.
-               $notification.css( 'opacity', 0 );
-
                if ( options.tag ) {
-                       // Check to see if there are any tagged notifications with the same tag as the new one
+                       // Find notifications with the same tag
                        $tagMatches = $area.find( '.mw-notification-tag-' + options.tag );
                }
 
-               // If we found a tagged notification use the replacement pattern instead of the new
-               // notification fade-in pattern.
+               // If we found existing notification with the same tag, replace them
                if ( options.tag && $tagMatches.length ) {
 
-                       // Iterate over the tag matches to find the outerHeight we should use
-                       // for the placeholder.
-                       outerHeight = 0;
+                       // While there can be only one "open" notif with a given tag, there can be several
+                       // matches here because they remain in the DOM until the animation is finished.
                        $tagMatches.each( function () {
                                var notif = $( this ).data( 'mw.notification' );
-                               if ( notif ) {
-                                       // Use the notification's height + padding + border + margins
-                                       // as the placeholder height.
-                                       outerHeight = notif.$notification.outerHeight( true );
-                                       if ( notif.$replacementPlaceholder ) {
-                                               // Grab the height of a placeholder that has not finished animating.
-                                               placeholderHeight = notif.$replacementPlaceholder.height();
-                                               // Remove any placeholders added by a previous tagged
-                                               // notification that was in the middle of replacing another.
-                                               // This also makes sure that we only grab the placeholderHeight
-                                               // for the most recent notification.
-                                               notif.$replacementPlaceholder.remove();
-                                               delete notif.$replacementPlaceholder;
-                                       }
-                                       // Close the previous tagged notification
-                                       // Since we're replacing it do this with a fast speed and don't output a placeholder
-                                       // since we're taking care of that transition ourselves.
-                                       notif.close( { speed: 'fast', placeholder: false } );
+                               if ( notif && notif.isOpen ) {
+                                       // Detach from render flow with position absolute so that the new tag can
+                                       // occupy its space instead.
+                                       notif.$notification
+                                               .css( {
+                                                       position: 'absolute',
+                                                       width: notif.$notification.width()
+                                               } )
+                                               .css( notif.$notification.position() )
+                                               .addClass( 'mw-notification-replaced' );
+                                       notif.close();
                                }
                        } );
-                       if ( placeholderHeight !== undefined ) {
-                               // If the other tagged notification was in the middle of replacing another
-                               // tagged notification, continue from the placeholder's height instead of
-                               // using the outerHeight of the notification.
-                               outerHeight = placeholderHeight;
-                       }
 
                        $notification
-                               // Insert the new notification before the tagged notification(s)
                                .insertBefore( $tagMatches.first() )
-                               .css( {
-                                       // Use an absolute position so that we can use a placeholder to gracefully push other notifications
-                                       // into the right spot.
-                                       position: 'absolute',
-                                       width: $notification.width()
-                               } )
-                               // Fade-in the notification
-                               .animate( { opacity: opacity },
-                                       {
-                                               duration: 'slow',
-                                               complete: function () {
-                                                       // After we've faded in clear the opacity and let css take over
-                                                       $( this ).css( { opacity: '' } );
-                                               }
-                                       } );
-
-                       notif = this;
-
-                       // Create a clear placeholder we can use to make the notifications around the notification that is being
-                       // replaced expand or contract gracefully to fit the height of the new notification.
-                       notif.$replacementPlaceholder = $( '<div>' )
-                               // Set the height to the space the previous notification or placeholder took
-                               .css( 'height', outerHeight )
-                               // Make sure that this placeholder is at the very end of this tagged notification group
-                               .insertAfter( $tagMatches.eq( -1 ) )
-                               // Animate the placeholder height to the space that this new notification will take up
-                               .animate( { height: $notification.outerHeight( true ) },
-                                       {
-                                               // Do space animations fast
-                                               speed: 'fast',
-                                               complete: function () {
-                                                       // Reset the notification position after we've finished the space animation
-                                                       // However do not do it if the placeholder was removed because another tagged
-                                                       // notification went and closed this one.
-                                                       if ( notif.$replacementPlaceholder ) {
-                                                               $notification.css( 'position', '' );
-                                                       }
-                                                       // Finally, remove the placeholder from the DOM
-                                                       $( this ).remove();
-                                               }
-                                       } );
+                               .addClass( 'mw-notification-visible' );
                } else {
-                       // Append to the notification area and fade in to the original opacity.
-                       $notification
-                               .appendTo( $area )
-                               .animate( { opacity: opacity },
-                                       {
-                                               duration: 'fast',
-                                               complete: function () {
-                                                       // After we've faded in clear the opacity and let css take over
-                                                       $( this ).css( 'opacity', '' );
-                                               }
-                                       }
-                               );
+                       $area.append( $notification );
+                       rAF( function () {
+                               // This frame renders the element in the area (invisible)
+                               rAF( function () {
+                                       $notification.addClass( 'mw-notification-visible' );
+                               } );
+                       } );
                }
 
                // By default a notification is paused.
        };
 
        /**
-        * Close/hide the notification.
-        *
-        * @param {Object} options An object containing options for the closing of the notification.
-        *
-        *  - speed: Use a close speed different than the default 'slow'.
-        *  - placeholder: Set to false to disable the placeholder transition.
+        * Close the notification.
         */
-       Notification.prototype.close = function ( options ) {
+       Notification.prototype.close = function () {
+               var notif = this;
+
                if ( !this.isOpen ) {
                        return;
                }
+
                this.isOpen = false;
                openNotificationCount--;
+
                // Clear any remaining timeout on close
                this.pause();
 
-               options = $.extend( {
-                       speed: 'slow',
-                       placeholder: true
-               }, options );
-
                // Remove the mw-notification-autohide class from the notification to avoid
                // having a half-closed notification counted as a notification to resume
                // when handling {autoHideLimit}.
                // notification that has now become one of the first {autoHideLimit} notifications.
                notification.resume();
 
-               this.$notification
-                       .css( {
-                               // Don't trigger any mouse events while fading out, just in case the cursor
-                               // happens to be right above us when we transition upwards.
-                               pointerEvents: 'none',
-                               // Set an absolute position so we can move upwards in the animation.
-                               // Notification replacement doesn't look right unless we use an animation like this.
-                               position: 'absolute',
-                               // We must fix the width to avoid it shrinking horizontally.
-                               width: this.$notification.width()
-                       } )
-                       // Fix the top/left position to the current computed position from which we
-                       // can animate upwards.
-                       .css( this.$notification.position() );
-
-               // This needs to be done *after* notification's position has been made absolute.
-               if ( options.placeholder ) {
-                       // Insert a placeholder with a height equal to the height of the
-                       // notification plus it's vertical margins in place of the notification
-                       var $placeholder = $( '<div>' )
-                               .css( 'height', this.$notification.outerHeight( true ) )
-                               .insertBefore( this.$notification );
-               }
-
-               // Animate opacity and top to create fade upwards animation for notification closing
-               this.$notification
-                       .animate( {
-                               opacity: 0,
-                               top: '-=35'
-                       }, {
-                               duration: options.speed,
-                               complete: function () {
-                                       // Remove the notification
-                                       $( this ).remove();
-                                       // Hide the area manually after closing the last notification, since it has padding,
-                                       // causing it to obscure whatever is behind it in spite of being invisible (bug 52659).
-                                       // It's okay to do this before getting rid of the placeholder, as it's invisible as well.
-                                       if ( openNotificationCount === 0 ) {
-                                               $area.hide();
-                                       }
-                                       if ( options.placeholder ) {
-                                               // Use a fast slide up animation after closing to make it look like the notifications
-                                               // below slide up into place when the notification disappears
-                                               $placeholder.slideUp( 'fast', function () {
-                                                       // Remove the placeholder
-                                                       $( this ).remove();
-                                               } );
-                                       }
+               rAF( function () {
+                       notif.$notification.removeClass( 'mw-notification-visible' );
+
+                       setTimeout( function () {
+                               if ( openNotificationCount === 0 ) {
+                                       // Hide the area after the last notification closes. Otherwise, the padding on
+                                       // the area can be obscure content, despite the area being empty/invisible (T54659). // FIXME
+                                       $area.hide();
+                                       notif.$notification.remove();
+                               } else {
+                                       notif.$notification.slideUp( 'fast',  function () {
+                                               $( this ).remove();
+                                       } );
                                }
-                       } );
+                       }, 500 );
+               } );
        };
 
        /**
index 577dc3c..622fce2 100644 (file)
@@ -79,6 +79,35 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase {
                $this->assertInternalType( 'string', $obj->setterArgs[0] );
                $this->assertSame( 'unwrapped', $obj->setterArgs[0] );
        }
+
+       /**
+        * @covers ObjectFactory::constructClassInstance
+        * @dataProvider provideConstructClassInstance
+        */
+       public function testConstructClassInstance( $args ) {
+               $obj = ObjectFactory::constructClassInstance(
+                       'ObjectFactoryTestFixture', $args
+               );
+               $this->assertSame( $args, $obj->args );
+       }
+
+       public function provideConstructClassInstance() {
+               // These args go to 11. I thought about making 10 one louder, but 11!
+               return array(
+                       '0 args' => array( array() ),
+                       '1 args' => array( array( 1, ) ),
+                       '2 args' => array( array( 1, 2, ) ),
+                       '3 args' => array( array( 1, 2, 3, ) ),
+                       '4 args' => array( array( 1, 2, 3, 4, ) ),
+                       '5 args' => array( array( 1, 2, 3, 4, 5, ) ),
+                       '6 args' => array( array( 1, 2, 3, 4, 5, 6, ) ),
+                       '7 args' => array( array( 1, 2, 3, 4, 5, 6, 7, ) ),
+                       '8 args' => array( array( 1, 2, 3, 4, 5, 6, 7, 8, ) ),
+                       '9 args' => array( array( 1, 2, 3, 4, 5, 6, 7, 8, 9, ) ),
+                       '10 args' => array( array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ) ),
+                       '11 args' => array( array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ) ),
+               );
+       }
 }
 
 class ObjectFactoryTestFixture {
diff --git a/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php b/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php
new file mode 100644 (file)
index 0000000..94b74cb
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * @author Matthias Mullie <mmullie@wikimedia.org>
+ * @group BagOStuff
+ */
+class BagOStuffTest extends MediaWikiTestCase {
+       /** @var BagOStuff */
+       private $cache;
+
+       protected function setUp() {
+               parent::setUp();
+
+               // type defined through parameter
+               if ( $this->getCliArg( 'use-bagostuff' ) ) {
+                       $name = $this->getCliArg( 'use-bagostuff' );
+
+                       $this->cache = ObjectCache::newFromId( $name );
+               } else {
+                       // no type defined - use simple hash
+                       $this->cache = new HashBagOStuff;
+               }
+
+               $this->cache->delete( wfMemcKey( 'test' ) );
+       }
+
+       /**
+        * @covers BagOStuff::makeGlobalKey
+        * @covers BagOStuff::makeKeyInternal
+        */
+       public function testMakeKey() {
+               $cache = ObjectCache::newFromId( 'hash' );
+
+               $localKey = $cache->makeKey( 'first', 'second', 'third' );
+               $globalKey = $cache->makeGlobalKey( 'first', 'second', 'third' );
+
+               $this->assertStringMatchesFormat(
+                       '%Sfirst%Ssecond%Sthird%S',
+                       $localKey,
+                       'Local key interpolates parameters'
+               );
+
+               $this->assertStringMatchesFormat(
+                       'global%Sfirst%Ssecond%Sthird%S',
+                       $globalKey,
+                       'Global key interpolates parameters and contains global prefix'
+               );
+
+               $this->assertNotEquals(
+                       $localKey,
+                       $globalKey,
+                       'Local key and global key with same parameters should not be equal'
+               );
+
+               $this->assertNotEquals(
+                       $cache->makeKeyInternal( 'prefix', array( 'a', 'bc:', 'de' ) ),
+                       $cache->makeKeyInternal( 'prefix', array( 'a', 'bc', ':de' ) )
+               );
+       }
+
+       /**
+        * @covers BagOStuff::merge
+        * @covers BagOStuff::mergeViaLock
+        */
+       public function testMerge() {
+               $key = wfMemcKey( 'test' );
+
+               $usleep = 0;
+
+               /**
+                * Callback method: append "merged" to whatever is in cache.
+                *
+                * @param BagOStuff $cache
+                * @param string $key
+                * @param int $existingValue
+                * @use int $usleep
+                * @return int
+                */
+               $callback = function ( BagOStuff $cache, $key, $existingValue ) use ( &$usleep ) {
+                       // let's pretend this is an expensive callback to test concurrent merge attempts
+                       usleep( $usleep );
+
+                       if ( $existingValue === false ) {
+                               return 'merged';
+                       }
+
+                       return $existingValue . 'merged';
+               };
+
+               // merge on non-existing value
+               $merged = $this->cache->merge( $key, $callback, 0 );
+               $this->assertTrue( $merged );
+               $this->assertEquals( $this->cache->get( $key ), 'merged' );
+
+               // merge on existing value
+               $merged = $this->cache->merge( $key, $callback, 0 );
+               $this->assertTrue( $merged );
+               $this->assertEquals( $this->cache->get( $key ), 'mergedmerged' );
+
+               /*
+                * Test concurrent merges by forking this process, if:
+                * - not manually called with --use-bagostuff
+                * - pcntl_fork is supported by the system
+                * - cache type will correctly support calls over forks
+                */
+               $fork = (bool)$this->getCliArg( 'use-bagostuff' );
+               $fork &= function_exists( 'pcntl_fork' );
+               $fork &= !$this->cache instanceof HashBagOStuff;
+               $fork &= !$this->cache instanceof EmptyBagOStuff;
+               $fork &= !$this->cache instanceof MultiWriteBagOStuff;
+               if ( $fork ) {
+                       // callback should take awhile now so that we can test concurrent merge attempts
+                       $pid = pcntl_fork();
+                       if ( $pid == -1 ) {
+                               // can't fork, ignore this test...
+                       } elseif ( $pid ) {
+                               // wait a little, making sure that the child process is calling merge
+                               usleep( 3000 );
+
+                               // attempt a merge - this should fail
+                               $merged = $this->cache->merge( $key, $callback, 0, 1 );
+
+                               // merge has failed because child process was merging (and we only attempted once)
+                               $this->assertFalse( $merged );
+
+                               // make sure the child's merge is completed and verify
+                               usleep( 3000 );
+                               $this->assertEquals( $this->cache->get( $key ), 'mergedmergedmerged' );
+                       } else {
+                               $this->cache->merge( $key, $callback, 0, 1 );
+
+                               // Note: I'm not even going to check if the merge worked, I'll
+                               // compare values in the parent process to test if this merge worked.
+                               // I'm just going to exit this child process, since I don't want the
+                               // child to output any test results (would be rather confusing to
+                               // have test output twice)
+                               exit;
+                       }
+               }
+       }
+
+       /**
+        * @covers BagOStuff::add
+        */
+       public function testAdd() {
+               $key = wfMemcKey( 'test' );
+               $this->assertTrue( $this->cache->add( $key, 'test' ) );
+       }
+
+       public function testGet() {
+               $value = array( 'this' => 'is', 'a' => 'test' );
+
+               $key = wfMemcKey( 'test' );
+               $this->cache->add( $key, $value );
+               $this->assertEquals( $this->cache->get( $key ), $value );
+       }
+
+       /**
+        * @covers BagOStuff::getWithSetCallback
+        */
+       public function testGetWithSetCallback() {
+               $key = wfMemcKey( 'test' );
+               $value = $this->cache->getWithSetCallback(
+                       $key,
+                       30,
+                       function () {
+                               return 'hello kitty';
+                       }
+               );
+
+               $this->assertEquals( 'hello kitty', $value );
+               $this->assertEquals( $value, $this->cache->get( $key ) );
+       }
+
+       /**
+        * @covers BagOStuff::incr
+        */
+       public function testIncr() {
+               $key = wfMemcKey( 'test' );
+               $this->cache->add( $key, 0 );
+               $this->cache->incr( $key );
+               $expectedValue = 1;
+               $actualValue = $this->cache->get( $key );
+               $this->assertEquals( $expectedValue, $actualValue, 'Value should be 1 after incrementing' );
+       }
+
+       /**
+        * @covers BagOStuff::getMulti
+        */
+       public function testGetMulti() {
+               $value1 = array( 'this' => 'is', 'a' => 'test' );
+               $value2 = array( 'this' => 'is', 'another' => 'test' );
+               $value3 = array( 'testing a key that may be encoded when sent to cache backend' );
+               $value4 = array( 'another test where chars in key will be encoded' );
+
+               $key1 = wfMemcKey( 'test1' );
+               $key2 = wfMemcKey( 'test2' );
+               // internally, MemcachedBagOStuffs will encode to will-%25-encode
+               $key3 = wfMemcKey( 'will-%-encode' );
+               $key4 = wfMemcKey(
+                       'flowdb:flow_ref:wiki:by-source:v3:Parser\'s_"broken"_+_(page)_&_grill:testwiki:1:4.7'
+               );
+
+               $this->cache->add( $key1, $value1 );
+               $this->cache->add( $key2, $value2 );
+               $this->cache->add( $key3, $value3 );
+               $this->cache->add( $key4, $value4 );
+
+               $this->assertEquals(
+                       array( $key1 => $value1, $key2 => $value2, $key3 => $value3, $key4 => $value4 ),
+                       $this->cache->getMulti( array( $key1, $key2, $key3, $key4 ) )
+               );
+
+               // cleanup
+               $this->cache->delete( $key1 );
+               $this->cache->delete( $key2 );
+               $this->cache->delete( $key3 );
+               $this->cache->delete( $key4 );
+       }
+
+       /**
+        * @covers BagOStuff::getScopedLock
+        */
+       public function testGetScopedLock() {
+               $key = wfMemcKey( 'test' );
+               $value1 = $this->cache->getScopedLock( $key, 0 );
+               $value2 = $this->cache->getScopedLock( $key, 0 );
+
+               $this->assertType( 'ScopedCallback', $value1, 'First call returned lock' );
+               $this->assertNull( $value2, 'Duplicate call returned no lock' );
+
+               unset( $value1 );
+
+               $value3 = $this->cache->getScopedLock( $key, 0 );
+               $this->assertType( 'ScopedCallback', $value3, 'Lock returned callback after release' );
+               unset( $value3 );
+
+               $value1 = $this->cache->getScopedLock( $key, 0, 5, 'reentry' );
+               $value2 = $this->cache->getScopedLock( $key, 0, 5, 'reentry' );
+
+               $this->assertType( 'ScopedCallback', $value1, 'First reentrant call returned lock' );
+               $this->assertType( 'ScopedCallback', $value1, 'Second reentrant call returned lock' );
+       }
+}
diff --git a/tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php b/tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
new file mode 100644 (file)
index 0000000..1d8f43a
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @group Database
+ */
+class MultiWriteBagOStuffTest extends MediaWikiTestCase {
+       /** @var HashBagOStuff */
+       private $cache1;
+       /** @var HashBagOStuff */
+       private $cache2;
+       /** @var MultiWriteBagOStuff */
+       private $cache;
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->cache1 = new HashBagOStuff();
+               $this->cache2 = new HashBagOStuff();
+               $this->cache = new MultiWriteBagOStuff( array(
+                       'caches' => array( $this->cache1, $this->cache2 ),
+                       'replication' => 'async',
+                       'asyncHandler' => 'DeferredUpdates::addCallableUpdate'
+               ) );
+       }
+
+       public function testSetImmediate() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $this->cache->set( $key, $value );
+
+               // Set in tier 1
+               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               // Set in tier 2
+               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+       }
+
+       public function testSyncMerge() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $func = function () use ( $value ) {
+                       return $value;
+               };
+
+               // XXX: DeferredUpdates bound to transactions in CLI mode
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->begin();
+               $this->cache->merge( $key, $func );
+
+               // Set in tier 1
+               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               // Not yet set in tier 2
+               $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
+
+               $dbw->commit();
+
+               // Set in tier 2
+               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+
+               $key = wfRandomString();
+
+               $dbw->begin();
+               $this->cache->merge( $key, $func, 0, 1, BagOStuff::WRITE_SYNC );
+
+               // Set in tier 1
+               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               // Also set in tier 2
+               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+
+               $dbw->commit();
+       }
+
+       public function testSetDelayed() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+
+               // XXX: DeferredUpdates bound to transactions in CLI mode
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->begin();
+               $this->cache->set( $key, $value );
+
+               // Set in tier 1
+               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               // Not yet set in tier 2
+               $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
+
+               $dbw->commit();
+
+               // Set in tier 2
+               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+       }
+}
diff --git a/tests/phpunit/includes/libs/objectcache/ReplicatedBagOStuffTest.php b/tests/phpunit/includes/libs/objectcache/ReplicatedBagOStuffTest.php
new file mode 100644 (file)
index 0000000..a419f5b
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+class ReplicatedBagOStuffTest extends MediaWikiTestCase {
+       /** @var HashBagOStuff */
+       private $writeCache;
+       /** @var HashBagOStuff */
+       private $readCache;
+       /** @var ReplicatedBagOStuff */
+       private $cache;
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->writeCache = new HashBagOStuff();
+               $this->readCache = new HashBagOStuff();
+               $this->cache = new ReplicatedBagOStuff( array(
+                       'writeFactory' => $this->writeCache,
+                       'readFactory' => $this->readCache,
+               ) );
+       }
+
+       /**
+        * @covers ReplicatedBagOStuff::set
+        */
+       public function testSet() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $this->cache->set( $key, $value );
+
+               // Write to master.
+               $this->assertEquals( $this->writeCache->get( $key ), $value );
+               // Don't write to slave. Replication is deferred to backend.
+               $this->assertEquals( $this->readCache->get( $key ), false );
+       }
+
+       /**
+        * @covers ReplicatedBagOStuff::get
+        */
+       public function testGet() {
+               $key = wfRandomString();
+
+               $write = wfRandomString();
+               $this->writeCache->set( $key, $write );
+               $read = wfRandomString();
+               $this->readCache->set( $key, $read );
+
+               // Read from slave.
+               $this->assertEquals( $this->cache->get( $key ), $read );
+       }
+
+       /**
+        * @covers ReplicatedBagOStuff::get
+        */
+       public function testGetAbsent() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $this->writeCache->set( $key, $value );
+
+               // Don't read from master. No failover if value is absent.
+               $this->assertEquals( $this->cache->get( $key ), false );
+       }
+}
diff --git a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
new file mode 100644 (file)
index 0000000..c3702c5
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+
+class WANObjectCacheTest extends MediaWikiTestCase {
+       /** @var WANObjectCache */
+       private $cache;
+       /**@var BagOStuff */
+       private $internalCache;
+
+       protected function setUp() {
+               parent::setUp();
+
+               if ( $this->getCliArg( 'use-wanobjectcache' ) ) {
+                       $name = $this->getCliArg( 'use-wanobjectcache' );
+
+                       $this->cache = ObjectCache::getWANInstance( $name );
+               } else {
+                       $this->cache = new WANObjectCache( array(
+                               'cache' => new HashBagOStuff(),
+                               'pool' => 'testcache-hash',
+                               'relayer' => new EventRelayerNull( array() )
+                       ) );
+               }
+
+               $wanCache = TestingAccessWrapper::newFromObject( $this->cache );
+               $this->internalCache = $wanCache->cache;
+       }
+
+       /**
+        * @dataProvider provider_testSetAndGet
+        * @covers WANObjectCache::set()
+        * @covers WANObjectCache::get()
+        * @param mixed $value
+        * @param integer $ttl
+        */
+       public function testSetAndGet( $value, $ttl ) {
+               $key = wfRandomString();
+               $this->cache->set( $key, $value, $ttl );
+
+               $curTTL = null;
+               $this->assertEquals( $value, $this->cache->get( $key, $curTTL ) );
+               if ( is_infinite( $ttl ) || $ttl == 0 ) {
+                       $this->assertTrue( is_infinite( $curTTL ), "Current TTL is infinite" );
+               } else {
+                       $this->assertGreaterThan( 0, $curTTL, "Current TTL > 0" );
+                       $this->assertLessThanOrEqual( $ttl, $curTTL, "Current TTL < nominal TTL" );
+               }
+       }
+
+       public static function provider_testSetAndGet() {
+               return array(
+                       array( 14141, 3 ),
+                       array( 3535.666, 3 ),
+                       array( array(), 3 ),
+                       array( null, 3 ),
+                       array( '0', 3 ),
+                       array( (object)array( 'meow' ), 3 ),
+                       array( INF, 3 ),
+                       array( '', 3 ),
+                       array( 'pizzacat', INF ),
+               );
+       }
+
+       public function testGetNotExists() {
+               $key = wfRandomString();
+               $curTTL = null;
+               $value = $this->cache->get( $key, $curTTL );
+
+               $this->assertFalse( $value, "Non-existing key has false value" );
+               $this->assertNull( $curTTL, "Non-existing key has null current TTL" );
+       }
+
+       public function testSetOver() {
+               $key = wfRandomString();
+               for ( $i = 0; $i < 3; ++$i ) {
+                       $value = wfRandomString();
+                       $this->cache->set( $key, $value, 3 );
+
+                       $this->assertEquals( $this->cache->get( $key ), $value );
+               }
+       }
+
+       public function testStaleSet() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $this->cache->set( $key, $value, 3, array( 'since' => microtime( true ) - 30 ) );
+
+               $this->assertFalse( $this->cache->get( $key ), "Stale set() value ignored" );
+       }
+
+       /**
+        * @covers WANObjectCache::getWithSetCallback()
+        */
+       public function testGetWithSetCallback() {
+               $cache = $this->cache;
+
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $cKey1 = wfRandomString();
+               $cKey2 = wfRandomString();
+
+               $wasSet = 0;
+               $func = function( $old, &$ttl ) use ( &$wasSet, $value ) {
+                       ++$wasSet;
+                       $ttl = 20; // override with another value
+                       return $value;
+               };
+
+               $wasSet = 0;
+               $v = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value regenerated" );
+
+               $curTTL = null;
+               $cache->get( $key, $curTTL );
+               $this->assertLessThanOrEqual( 20, $curTTL, 'Current TTL between 19-20 (overriden)' );
+               $this->assertGreaterThanOrEqual( 19, $curTTL, 'Current TTL between 19-20 (overriden)' );
+
+               $wasSet = 0;
+               $v = $cache->getWithSetCallback( $key, 30, $func, array(
+                       'lowTTL' => 0,
+                       'lockTSE' => 5,
+               ) );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 0, $wasSet, "Value not regenerated" );
+
+               $priorTime = microtime( true );
+               usleep( 1 );
+               $wasSet = 0;
+               $v = $cache->getWithSetCallback( $key, 30, $func,
+                       array( 'checkKeys' => array( $cKey1, $cKey2 ) ) );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value regenerated due to check keys" );
+               $t1 = $cache->getCheckKeyTime( $cKey1 );
+               $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check keys generated on miss' );
+               $t2 = $cache->getCheckKeyTime( $cKey2 );
+               $this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check keys generated on miss' );
+
+               $priorTime = microtime( true );
+               $wasSet = 0;
+               $v = $cache->getWithSetCallback( $key, 30, $func,
+                       array( 'checkKeys' => array( $cKey1, $cKey2 ) ) );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value regenerated due to still-recent check keys" );
+               $t1 = $cache->getCheckKeyTime( $cKey1 );
+               $this->assertLessThanOrEqual( $priorTime, $t1, 'Check keys did not change again' );
+               $t2 = $cache->getCheckKeyTime( $cKey2 );
+               $this->assertLessThanOrEqual( $priorTime, $t2, 'Check keys did not change again' );
+
+               $curTTL = null;
+               $v = $cache->get( $key, $curTTL, array( $cKey1, $cKey2 ) );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertLessThanOrEqual( 0, $curTTL, "Value has current TTL < 0 due to check keys" );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $v = $cache->getWithSetCallback( $key, 30, $func, array( 'pcTTL' => 5 ) );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $cache->delete( $key );
+               $v = $cache->getWithSetCallback( $key, 30, $func, array( 'pcTTL' => 5 ) );
+               $this->assertEquals( $value, $v, "Value still returned after deleted" );
+               $this->assertEquals( 1, $wasSet, "Value process cached while deleted" );
+       }
+
+       /**
+        * @covers WANObjectCache::getWithSetCallback()
+        */
+       public function testLockTSE() {
+               $cache = $this->cache;
+               $key = wfRandomString();
+               $value = wfRandomString();
+
+               $calls = 0;
+               $func = function() use ( &$calls, $value ) {
+                       ++$calls;
+                       return $value;
+               };
+
+               $cache->delete( $key );
+               $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
+               $this->assertEquals( $value, $ret );
+               $this->assertEquals( 1, $calls, 'Value was populated' );
+
+               // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
+               $this->internalCache->lock( $key, 0 );
+               $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
+               $this->assertEquals( $value, $ret );
+               $this->assertEquals( 1, $calls, 'Callback was not used' );
+       }
+
+       /**
+        * @covers WANObjectCache::getMulti()
+        */
+       public function testGetMulti() {
+               $cache = $this->cache;
+
+               $value1 = array( 'this' => 'is', 'a' => 'test' );
+               $value2 = array( 'this' => 'is', 'another' => 'test' );
+
+               $key1 = wfRandomString();
+               $key2 = wfRandomString();
+               $key3 = wfRandomString();
+
+               $cache->set( $key1, $value1, 5 );
+               $cache->set( $key2, $value2, 10 );
+
+               $curTTLs = array();
+               $this->assertEquals(
+                       array( $key1 => $value1, $key2 => $value2 ),
+                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs )
+               );
+
+               $this->assertEquals( 2, count( $curTTLs ), "Two current TTLs in array" );
+               $this->assertGreaterThan( 0, $curTTLs[$key1], "Key 1 has current TTL > 0" );
+               $this->assertGreaterThan( 0, $curTTLs[$key2], "Key 2 has current TTL > 0" );
+
+               $cKey1 = wfRandomString();
+               $cKey2 = wfRandomString();
+               $curTTLs = array();
+               $this->assertEquals(
+                       array( $key1 => $value1, $key2 => $value2 ),
+                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs ),
+                       'Result array populated'
+               );
+
+               $priorTime = microtime( true );
+               usleep( 1 );
+               $curTTLs = array();
+               $this->assertEquals(
+                       array( $key1 => $value1, $key2 => $value2 ),
+                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs, array( $cKey1, $cKey2 ) ),
+                       "Result array populated even with new check keys"
+               );
+               $t1 = $cache->getCheckKeyTime( $cKey1 );
+               $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check key 1 generated on miss' );
+               $t2 = $cache->getCheckKeyTime( $cKey2 );
+               $this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check key 2 generated on miss' );
+               $this->assertEquals( 2, count( $curTTLs ), "Current TTLs array set" );
+               $this->assertLessThanOrEqual( 0, $curTTLs[$key1], 'Key 1 has current TTL <= 0' );
+               $this->assertLessThanOrEqual( 0, $curTTLs[$key2], 'Key 2 has current TTL <= 0' );
+
+               usleep( 1 );
+               $curTTLs = array();
+               $this->assertEquals(
+                       array( $key1 => $value1, $key2 => $value2 ),
+                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs, array( $cKey1, $cKey2 ) ),
+                       "Result array still populated even with new check keys"
+               );
+               $this->assertEquals( 2, count( $curTTLs ), "Current TTLs still array set" );
+               $this->assertLessThan( 0, $curTTLs[$key1], 'Key 1 has negative current TTL' );
+               $this->assertLessThan( 0, $curTTLs[$key2], 'Key 2 has negative current TTL' );
+       }
+
+       /**
+        * @covers WANObjectCache::delete()
+        */
+       public function testDelete() {
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $this->cache->set( $key, $value );
+
+               $curTTL = null;
+               $v = $this->cache->get( $key, $curTTL );
+               $this->assertEquals( $value, $v, "Key was created with value" );
+               $this->assertGreaterThan( 0, $curTTL, "Existing key has current TTL > 0" );
+
+               $this->cache->delete( $key );
+
+               $curTTL = null;
+               $v = $this->cache->get( $key, $curTTL );
+               $this->assertFalse( $v, "Deleted key has false value" );
+               $this->assertLessThan( 0, $curTTL, "Deleted key has current TTL < 0" );
+
+               $this->cache->set( $key, $value . 'more' );
+               $this->assertFalse( $v, "Deleted key is tombstoned and has false value" );
+               $this->assertLessThan( 0, $curTTL, "Deleted key is tombstoned and has current TTL < 0" );
+       }
+
+       /**
+        * @covers WANObjectCache::touchCheckKey()
+        * @covers WANObjectCache::resetCheckKey()
+        * @covers WANObjectCache::getCheckKeyTime()
+        */
+       public function testTouchKeys() {
+               $key = wfRandomString();
+
+               $priorTime = microtime( true );
+               usleep( 100 );
+               $t0 = $this->cache->getCheckKeyTime( $key );
+               $this->assertGreaterThanOrEqual( $priorTime, $t0, 'Check key auto-created' );
+
+               $priorTime = microtime( true );
+               usleep( 100 );
+               $this->cache->touchCheckKey( $key );
+               $t1 = $this->cache->getCheckKeyTime( $key );
+               $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check key created' );
+
+               $t2 = $this->cache->getCheckKeyTime( $key );
+               $this->assertEquals( $t1, $t2, 'Check key time did not change' );
+
+               usleep( 100 );
+               $this->cache->touchCheckKey( $key );
+               $t3 = $this->cache->getCheckKeyTime( $key );
+               $this->assertGreaterThan( $t2, $t3, 'Check key time increased' );
+
+               $t4 = $this->cache->getCheckKeyTime( $key );
+               $this->assertEquals( $t3, $t4, 'Check key time did not change' );
+
+               usleep( 100 );
+               $this->cache->resetCheckKey( $key );
+               $t5 = $this->cache->getCheckKeyTime( $key );
+               $this->assertGreaterThan( $t4, $t5, 'Check key time increased' );
+
+               $t6 = $this->cache->getCheckKeyTime( $key );
+               $this->assertEquals( $t5, $t6, 'Check key time did not change' );
+       }
+}
diff --git a/tests/phpunit/includes/objectcache/BagOStuffTest.php b/tests/phpunit/includes/objectcache/BagOStuffTest.php
deleted file mode 100644 (file)
index 466b9f5..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-<?php
-/**
- * @author Matthias Mullie <mmullie@wikimedia.org>
- * @group BagOStuff
- */
-class BagOStuffTest extends MediaWikiTestCase {
-       /** @var BagOStuff */
-       private $cache;
-
-       protected function setUp() {
-               parent::setUp();
-
-               // type defined through parameter
-               if ( $this->getCliArg( 'use-bagostuff' ) ) {
-                       $name = $this->getCliArg( 'use-bagostuff' );
-
-                       $this->cache = ObjectCache::newFromId( $name );
-               } else {
-                       // no type defined - use simple hash
-                       $this->cache = new HashBagOStuff;
-               }
-
-               $this->cache->delete( wfMemcKey( 'test' ) );
-       }
-
-       /**
-        * @covers BagOStuff::makeGlobalKey
-        * @covers BagOStuff::makeKeyInternal
-        */
-       public function testMakeKey() {
-               $cache = ObjectCache::newFromId( 'hash' );
-
-               $localKey = $cache->makeKey( 'first', 'second', 'third' );
-               $globalKey = $cache->makeGlobalKey( 'first', 'second', 'third' );
-
-               $this->assertStringMatchesFormat(
-                       '%Sfirst%Ssecond%Sthird%S',
-                       $localKey,
-                       'Local key interpolates parameters'
-               );
-
-               $this->assertStringMatchesFormat(
-                       'global%Sfirst%Ssecond%Sthird%S',
-                       $globalKey,
-                       'Global key interpolates parameters and contains global prefix'
-               );
-
-               $this->assertNotEquals(
-                       $localKey,
-                       $globalKey,
-                       'Local key and global key with same parameters should not be equal'
-               );
-       }
-
-       /**
-        * @covers BagOStuff::merge
-        * @covers BagOStuff::mergeViaLock
-        */
-       public function testMerge() {
-               $key = wfMemcKey( 'test' );
-
-               $usleep = 0;
-
-               /**
-                * Callback method: append "merged" to whatever is in cache.
-                *
-                * @param BagOStuff $cache
-                * @param string $key
-                * @param int $existingValue
-                * @use int $usleep
-                * @return int
-                */
-               $callback = function ( BagOStuff $cache, $key, $existingValue ) use ( &$usleep ) {
-                       // let's pretend this is an expensive callback to test concurrent merge attempts
-                       usleep( $usleep );
-
-                       if ( $existingValue === false ) {
-                               return 'merged';
-                       }
-
-                       return $existingValue . 'merged';
-               };
-
-               // merge on non-existing value
-               $merged = $this->cache->merge( $key, $callback, 0 );
-               $this->assertTrue( $merged );
-               $this->assertEquals( $this->cache->get( $key ), 'merged' );
-
-               // merge on existing value
-               $merged = $this->cache->merge( $key, $callback, 0 );
-               $this->assertTrue( $merged );
-               $this->assertEquals( $this->cache->get( $key ), 'mergedmerged' );
-
-               /*
-                * Test concurrent merges by forking this process, if:
-                * - not manually called with --use-bagostuff
-                * - pcntl_fork is supported by the system
-                * - cache type will correctly support calls over forks
-                */
-               $fork = (bool)$this->getCliArg( 'use-bagostuff' );
-               $fork &= function_exists( 'pcntl_fork' );
-               $fork &= !$this->cache instanceof HashBagOStuff;
-               $fork &= !$this->cache instanceof EmptyBagOStuff;
-               $fork &= !$this->cache instanceof MultiWriteBagOStuff;
-               if ( $fork ) {
-                       // callback should take awhile now so that we can test concurrent merge attempts
-                       $pid = pcntl_fork();
-                       if ( $pid == -1 ) {
-                               // can't fork, ignore this test...
-                       } elseif ( $pid ) {
-                               // wait a little, making sure that the child process is calling merge
-                               usleep( 3000 );
-
-                               // attempt a merge - this should fail
-                               $merged = $this->cache->merge( $key, $callback, 0, 1 );
-
-                               // merge has failed because child process was merging (and we only attempted once)
-                               $this->assertFalse( $merged );
-
-                               // make sure the child's merge is completed and verify
-                               usleep( 3000 );
-                               $this->assertEquals( $this->cache->get( $key ), 'mergedmergedmerged' );
-                       } else {
-                               $this->cache->merge( $key, $callback, 0, 1 );
-
-                               // Note: I'm not even going to check if the merge worked, I'll
-                               // compare values in the parent process to test if this merge worked.
-                               // I'm just going to exit this child process, since I don't want the
-                               // child to output any test results (would be rather confusing to
-                               // have test output twice)
-                               exit;
-                       }
-               }
-       }
-
-       /**
-        * @covers BagOStuff::add
-        */
-       public function testAdd() {
-               $key = wfMemcKey( 'test' );
-               $this->assertTrue( $this->cache->add( $key, 'test' ) );
-       }
-
-       public function testGet() {
-               $value = array( 'this' => 'is', 'a' => 'test' );
-
-               $key = wfMemcKey( 'test' );
-               $this->cache->add( $key, $value );
-               $this->assertEquals( $this->cache->get( $key ), $value );
-       }
-
-       /**
-        * @covers BagOStuff::getWithSetCallback
-        */
-       public function testGetWithSetCallback() {
-               $key = wfMemcKey( 'test' );
-               $value = $this->cache->getWithSetCallback(
-                       $key,
-                       30,
-                       function () {
-                               return 'hello kitty';
-                       }
-               );
-
-               $this->assertEquals( 'hello kitty', $value );
-               $this->assertEquals( $value, $this->cache->get( $key ) );
-       }
-
-       /**
-        * @covers BagOStuff::incr
-        */
-       public function testIncr() {
-               $key = wfMemcKey( 'test' );
-               $this->cache->add( $key, 0 );
-               $this->cache->incr( $key );
-               $expectedValue = 1;
-               $actualValue = $this->cache->get( $key );
-               $this->assertEquals( $expectedValue, $actualValue, 'Value should be 1 after incrementing' );
-       }
-
-       /**
-        * @covers BagOStuff::getMulti
-        */
-       public function testGetMulti() {
-               $value1 = array( 'this' => 'is', 'a' => 'test' );
-               $value2 = array( 'this' => 'is', 'another' => 'test' );
-               $value3 = array( 'testing a key that may be encoded when sent to cache backend' );
-               $value4 = array( 'another test where chars in key will be encoded' );
-
-               $key1 = wfMemcKey( 'test1' );
-               $key2 = wfMemcKey( 'test2' );
-               // internally, MemcachedBagOStuffs will encode to will-%25-encode
-               $key3 = wfMemcKey( 'will-%-encode' );
-               $key4 = wfMemcKey(
-                       'flowdb:flow_ref:wiki:by-source:v3:Parser\'s_"broken"_+_(page)_&_grill:testwiki:1:4.7'
-               );
-
-               $this->cache->add( $key1, $value1 );
-               $this->cache->add( $key2, $value2 );
-               $this->cache->add( $key3, $value3 );
-               $this->cache->add( $key4, $value4 );
-
-               $this->assertEquals(
-                       array( $key1 => $value1, $key2 => $value2, $key3 => $value3, $key4 => $value4 ),
-                       $this->cache->getMulti( array( $key1, $key2, $key3, $key4 ) )
-               );
-
-               // cleanup
-               $this->cache->delete( $key1 );
-               $this->cache->delete( $key2 );
-               $this->cache->delete( $key3 );
-               $this->cache->delete( $key4 );
-       }
-
-       /**
-        * @covers BagOStuff::getScopedLock
-        */
-       public function testGetScopedLock() {
-               $key = wfMemcKey( 'test' );
-               $value1 = $this->cache->getScopedLock( $key, 0 );
-               $value2 = $this->cache->getScopedLock( $key, 0 );
-
-               $this->assertType( 'ScopedCallback', $value1, 'First call returned lock' );
-               $this->assertNull( $value2, 'Duplicate call returned no lock' );
-
-               unset( $value1 );
-
-               $value3 = $this->cache->getScopedLock( $key, 0 );
-               $this->assertType( 'ScopedCallback', $value3, 'Lock returned callback after release' );
-               unset( $value3 );
-
-               $value1 = $this->cache->getScopedLock( $key, 0, 5, 'reentry' );
-               $value2 = $this->cache->getScopedLock( $key, 0, 5, 'reentry' );
-
-               $this->assertType( 'ScopedCallback', $value1, 'First reentrant call returned lock' );
-               $this->assertType( 'ScopedCallback', $value1, 'Second reentrant call returned lock' );
-       }
-}
diff --git a/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php b/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php
new file mode 100644 (file)
index 0000000..b0c9e4f
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @group BagOStuff
+ */
+class MemcachedBagOStuffTest extends MediaWikiTestCase {
+       /** @var MemcachedBagOStuff */
+       private $cache;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->cache = new MemcachedBagOStuff( array( 'keyspace' => 'test' ) );
+       }
+
+       /**
+        * @covers MemcachedBagOStuff::makeKeyInternal
+        */
+       public function testKeyNormalization() {
+               $this->assertEquals(
+                       'test:vanilla',
+                       $this->cache->makeKey( 'vanilla' )
+               );
+
+               $this->assertEquals(
+                       'test:punctuation_marks_are_ok:!@$^&*()',
+                       $this->cache->makeKey( 'punctuation_marks_are_ok', '!@$^&*()' )
+               );
+
+               $this->assertEquals(
+                       'test:but_spaces:hashes%23:and%0Anewlines:are_not',
+                       $this->cache->makeKey( 'but spaces', 'hashes#', "and\nnewlines", 'are_not' )
+               );
+
+               $this->assertEquals(
+                       'test:this:key:contains:%F0%9D%95%9E%F0%9D%95%A6%F0%9D%95%9D%F0%9D%95%A5%F0%9' .
+                               'D%95%9A%F0%9D%95%93%F0%9D%95%AA%F0%9D%95%A5%F0%9D%95%96:characters',
+                       $this->cache->makeKey( 'this', 'key', 'contains', '𝕞𝕦𝕝𝕥𝕚𝕓𝕪𝕥𝕖', 'characters' )
+               );
+
+               $this->assertEquals(
+                       'test:this:key:contains:#c118f92685a635cb843039de50014c9c',
+                       $this->cache->makeKey( 'this', 'key', 'contains', '𝕥𝕠𝕠 𝕞𝕒𝕟𝕪 𝕞𝕦𝕝𝕥𝕚𝕓𝕪𝕥𝕖 𝕔𝕙𝕒𝕣𝕒𝕔𝕥𝕖𝕣𝕤' )
+               );
+
+               $this->assertEquals(
+                       'test:##5820ad1d105aa4dc698585c39df73e19',
+                       $this->cache->makeKey( '𝕖𝕧𝕖𝕟', '𝕚𝕗', '𝕨𝕖', '𝕄𝔻𝟝', '𝕖𝕒𝕔𝕙',
+                               '𝕒𝕣𝕘𝕦𝕞𝕖𝕟𝕥', '𝕥𝕙𝕚𝕤', '𝕜𝕖𝕪', '𝕨𝕠𝕦𝕝𝕕', '𝕤𝕥𝕚𝕝𝕝', '𝕓𝕖', '𝕥𝕠𝕠', '𝕝𝕠𝕟𝕘' )
+               );
+
+               $this->assertEquals(
+                       'test:%23%235820ad1d105aa4dc698585c39df73e19',
+                       $this->cache->makeKey( '##5820ad1d105aa4dc698585c39df73e19' )
+               );
+
+               $this->assertEquals(
+                       'test:percent_is_escaped:!@$%25^&*()',
+                       $this->cache->makeKey( 'percent_is_escaped', '!@$%^&*()' )
+               );
+
+               $this->assertEquals(
+                       'test:colon_is_escaped:!@$%3A^&*()',
+                       $this->cache->makeKey( 'colon_is_escaped', '!@$:^&*()' )
+               );
+
+               $this->assertEquals(
+                       'test:long_key_part_hashed:#0244f7b1811d982dd932dd7de01465ac',
+                       $this->cache->makeKey( 'long_key_part_hashed', str_repeat( 'y', 500 ) )
+               );
+       }
+}
diff --git a/tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php b/tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php
deleted file mode 100644 (file)
index 1d8f43a..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-/**
- * @group Database
- */
-class MultiWriteBagOStuffTest extends MediaWikiTestCase {
-       /** @var HashBagOStuff */
-       private $cache1;
-       /** @var HashBagOStuff */
-       private $cache2;
-       /** @var MultiWriteBagOStuff */
-       private $cache;
-
-       protected function setUp() {
-               parent::setUp();
-
-               $this->cache1 = new HashBagOStuff();
-               $this->cache2 = new HashBagOStuff();
-               $this->cache = new MultiWriteBagOStuff( array(
-                       'caches' => array( $this->cache1, $this->cache2 ),
-                       'replication' => 'async',
-                       'asyncHandler' => 'DeferredUpdates::addCallableUpdate'
-               ) );
-       }
-
-       public function testSetImmediate() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $this->cache->set( $key, $value );
-
-               // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
-               // Set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
-       }
-
-       public function testSyncMerge() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $func = function () use ( $value ) {
-                       return $value;
-               };
-
-               // XXX: DeferredUpdates bound to transactions in CLI mode
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin();
-               $this->cache->merge( $key, $func );
-
-               // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
-               // Not yet set in tier 2
-               $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
-
-               $dbw->commit();
-
-               // Set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
-
-               $key = wfRandomString();
-
-               $dbw->begin();
-               $this->cache->merge( $key, $func, 0, 1, BagOStuff::WRITE_SYNC );
-
-               // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
-               // Also set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
-
-               $dbw->commit();
-       }
-
-       public function testSetDelayed() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-
-               // XXX: DeferredUpdates bound to transactions in CLI mode
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin();
-               $this->cache->set( $key, $value );
-
-               // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
-               // Not yet set in tier 2
-               $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
-
-               $dbw->commit();
-
-               // Set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
-       }
-}
diff --git a/tests/phpunit/includes/objectcache/ReplicatedBagOStuffTest.php b/tests/phpunit/includes/objectcache/ReplicatedBagOStuffTest.php
deleted file mode 100644 (file)
index a419f5b..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-class ReplicatedBagOStuffTest extends MediaWikiTestCase {
-       /** @var HashBagOStuff */
-       private $writeCache;
-       /** @var HashBagOStuff */
-       private $readCache;
-       /** @var ReplicatedBagOStuff */
-       private $cache;
-
-       protected function setUp() {
-               parent::setUp();
-
-               $this->writeCache = new HashBagOStuff();
-               $this->readCache = new HashBagOStuff();
-               $this->cache = new ReplicatedBagOStuff( array(
-                       'writeFactory' => $this->writeCache,
-                       'readFactory' => $this->readCache,
-               ) );
-       }
-
-       /**
-        * @covers ReplicatedBagOStuff::set
-        */
-       public function testSet() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $this->cache->set( $key, $value );
-
-               // Write to master.
-               $this->assertEquals( $this->writeCache->get( $key ), $value );
-               // Don't write to slave. Replication is deferred to backend.
-               $this->assertEquals( $this->readCache->get( $key ), false );
-       }
-
-       /**
-        * @covers ReplicatedBagOStuff::get
-        */
-       public function testGet() {
-               $key = wfRandomString();
-
-               $write = wfRandomString();
-               $this->writeCache->set( $key, $write );
-               $read = wfRandomString();
-               $this->readCache->set( $key, $read );
-
-               // Read from slave.
-               $this->assertEquals( $this->cache->get( $key ), $read );
-       }
-
-       /**
-        * @covers ReplicatedBagOStuff::get
-        */
-       public function testGetAbsent() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $this->writeCache->set( $key, $value );
-
-               // Don't read from master. No failover if value is absent.
-               $this->assertEquals( $this->cache->get( $key ), false );
-       }
-}
diff --git a/tests/phpunit/includes/objectcache/WANObjectCacheTest.php b/tests/phpunit/includes/objectcache/WANObjectCacheTest.php
deleted file mode 100644 (file)
index c3702c5..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-<?php
-
-class WANObjectCacheTest extends MediaWikiTestCase {
-       /** @var WANObjectCache */
-       private $cache;
-       /**@var BagOStuff */
-       private $internalCache;
-
-       protected function setUp() {
-               parent::setUp();
-
-               if ( $this->getCliArg( 'use-wanobjectcache' ) ) {
-                       $name = $this->getCliArg( 'use-wanobjectcache' );
-
-                       $this->cache = ObjectCache::getWANInstance( $name );
-               } else {
-                       $this->cache = new WANObjectCache( array(
-                               'cache' => new HashBagOStuff(),
-                               'pool' => 'testcache-hash',
-                               'relayer' => new EventRelayerNull( array() )
-                       ) );
-               }
-
-               $wanCache = TestingAccessWrapper::newFromObject( $this->cache );
-               $this->internalCache = $wanCache->cache;
-       }
-
-       /**
-        * @dataProvider provider_testSetAndGet
-        * @covers WANObjectCache::set()
-        * @covers WANObjectCache::get()
-        * @param mixed $value
-        * @param integer $ttl
-        */
-       public function testSetAndGet( $value, $ttl ) {
-               $key = wfRandomString();
-               $this->cache->set( $key, $value, $ttl );
-
-               $curTTL = null;
-               $this->assertEquals( $value, $this->cache->get( $key, $curTTL ) );
-               if ( is_infinite( $ttl ) || $ttl == 0 ) {
-                       $this->assertTrue( is_infinite( $curTTL ), "Current TTL is infinite" );
-               } else {
-                       $this->assertGreaterThan( 0, $curTTL, "Current TTL > 0" );
-                       $this->assertLessThanOrEqual( $ttl, $curTTL, "Current TTL < nominal TTL" );
-               }
-       }
-
-       public static function provider_testSetAndGet() {
-               return array(
-                       array( 14141, 3 ),
-                       array( 3535.666, 3 ),
-                       array( array(), 3 ),
-                       array( null, 3 ),
-                       array( '0', 3 ),
-                       array( (object)array( 'meow' ), 3 ),
-                       array( INF, 3 ),
-                       array( '', 3 ),
-                       array( 'pizzacat', INF ),
-               );
-       }
-
-       public function testGetNotExists() {
-               $key = wfRandomString();
-               $curTTL = null;
-               $value = $this->cache->get( $key, $curTTL );
-
-               $this->assertFalse( $value, "Non-existing key has false value" );
-               $this->assertNull( $curTTL, "Non-existing key has null current TTL" );
-       }
-
-       public function testSetOver() {
-               $key = wfRandomString();
-               for ( $i = 0; $i < 3; ++$i ) {
-                       $value = wfRandomString();
-                       $this->cache->set( $key, $value, 3 );
-
-                       $this->assertEquals( $this->cache->get( $key ), $value );
-               }
-       }
-
-       public function testStaleSet() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $this->cache->set( $key, $value, 3, array( 'since' => microtime( true ) - 30 ) );
-
-               $this->assertFalse( $this->cache->get( $key ), "Stale set() value ignored" );
-       }
-
-       /**
-        * @covers WANObjectCache::getWithSetCallback()
-        */
-       public function testGetWithSetCallback() {
-               $cache = $this->cache;
-
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $cKey1 = wfRandomString();
-               $cKey2 = wfRandomString();
-
-               $wasSet = 0;
-               $func = function( $old, &$ttl ) use ( &$wasSet, $value ) {
-                       ++$wasSet;
-                       $ttl = 20; // override with another value
-                       return $value;
-               };
-
-               $wasSet = 0;
-               $v = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
-               $this->assertEquals( $value, $v, "Value returned" );
-               $this->assertEquals( 1, $wasSet, "Value regenerated" );
-
-               $curTTL = null;
-               $cache->get( $key, $curTTL );
-               $this->assertLessThanOrEqual( 20, $curTTL, 'Current TTL between 19-20 (overriden)' );
-               $this->assertGreaterThanOrEqual( 19, $curTTL, 'Current TTL between 19-20 (overriden)' );
-
-               $wasSet = 0;
-               $v = $cache->getWithSetCallback( $key, 30, $func, array(
-                       'lowTTL' => 0,
-                       'lockTSE' => 5,
-               ) );
-               $this->assertEquals( $value, $v, "Value returned" );
-               $this->assertEquals( 0, $wasSet, "Value not regenerated" );
-
-               $priorTime = microtime( true );
-               usleep( 1 );
-               $wasSet = 0;
-               $v = $cache->getWithSetCallback( $key, 30, $func,
-                       array( 'checkKeys' => array( $cKey1, $cKey2 ) ) );
-               $this->assertEquals( $value, $v, "Value returned" );
-               $this->assertEquals( 1, $wasSet, "Value regenerated due to check keys" );
-               $t1 = $cache->getCheckKeyTime( $cKey1 );
-               $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check keys generated on miss' );
-               $t2 = $cache->getCheckKeyTime( $cKey2 );
-               $this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check keys generated on miss' );
-
-               $priorTime = microtime( true );
-               $wasSet = 0;
-               $v = $cache->getWithSetCallback( $key, 30, $func,
-                       array( 'checkKeys' => array( $cKey1, $cKey2 ) ) );
-               $this->assertEquals( $value, $v, "Value returned" );
-               $this->assertEquals( 1, $wasSet, "Value regenerated due to still-recent check keys" );
-               $t1 = $cache->getCheckKeyTime( $cKey1 );
-               $this->assertLessThanOrEqual( $priorTime, $t1, 'Check keys did not change again' );
-               $t2 = $cache->getCheckKeyTime( $cKey2 );
-               $this->assertLessThanOrEqual( $priorTime, $t2, 'Check keys did not change again' );
-
-               $curTTL = null;
-               $v = $cache->get( $key, $curTTL, array( $cKey1, $cKey2 ) );
-               $this->assertEquals( $value, $v, "Value returned" );
-               $this->assertLessThanOrEqual( 0, $curTTL, "Value has current TTL < 0 due to check keys" );
-
-               $wasSet = 0;
-               $key = wfRandomString();
-               $v = $cache->getWithSetCallback( $key, 30, $func, array( 'pcTTL' => 5 ) );
-               $this->assertEquals( $value, $v, "Value returned" );
-               $cache->delete( $key );
-               $v = $cache->getWithSetCallback( $key, 30, $func, array( 'pcTTL' => 5 ) );
-               $this->assertEquals( $value, $v, "Value still returned after deleted" );
-               $this->assertEquals( 1, $wasSet, "Value process cached while deleted" );
-       }
-
-       /**
-        * @covers WANObjectCache::getWithSetCallback()
-        */
-       public function testLockTSE() {
-               $cache = $this->cache;
-               $key = wfRandomString();
-               $value = wfRandomString();
-
-               $calls = 0;
-               $func = function() use ( &$calls, $value ) {
-                       ++$calls;
-                       return $value;
-               };
-
-               $cache->delete( $key );
-               $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
-               $this->assertEquals( $value, $ret );
-               $this->assertEquals( 1, $calls, 'Value was populated' );
-
-               // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->lock( $key, 0 );
-               $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
-               $this->assertEquals( $value, $ret );
-               $this->assertEquals( 1, $calls, 'Callback was not used' );
-       }
-
-       /**
-        * @covers WANObjectCache::getMulti()
-        */
-       public function testGetMulti() {
-               $cache = $this->cache;
-
-               $value1 = array( 'this' => 'is', 'a' => 'test' );
-               $value2 = array( 'this' => 'is', 'another' => 'test' );
-
-               $key1 = wfRandomString();
-               $key2 = wfRandomString();
-               $key3 = wfRandomString();
-
-               $cache->set( $key1, $value1, 5 );
-               $cache->set( $key2, $value2, 10 );
-
-               $curTTLs = array();
-               $this->assertEquals(
-                       array( $key1 => $value1, $key2 => $value2 ),
-                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs )
-               );
-
-               $this->assertEquals( 2, count( $curTTLs ), "Two current TTLs in array" );
-               $this->assertGreaterThan( 0, $curTTLs[$key1], "Key 1 has current TTL > 0" );
-               $this->assertGreaterThan( 0, $curTTLs[$key2], "Key 2 has current TTL > 0" );
-
-               $cKey1 = wfRandomString();
-               $cKey2 = wfRandomString();
-               $curTTLs = array();
-               $this->assertEquals(
-                       array( $key1 => $value1, $key2 => $value2 ),
-                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs ),
-                       'Result array populated'
-               );
-
-               $priorTime = microtime( true );
-               usleep( 1 );
-               $curTTLs = array();
-               $this->assertEquals(
-                       array( $key1 => $value1, $key2 => $value2 ),
-                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs, array( $cKey1, $cKey2 ) ),
-                       "Result array populated even with new check keys"
-               );
-               $t1 = $cache->getCheckKeyTime( $cKey1 );
-               $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check key 1 generated on miss' );
-               $t2 = $cache->getCheckKeyTime( $cKey2 );
-               $this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check key 2 generated on miss' );
-               $this->assertEquals( 2, count( $curTTLs ), "Current TTLs array set" );
-               $this->assertLessThanOrEqual( 0, $curTTLs[$key1], 'Key 1 has current TTL <= 0' );
-               $this->assertLessThanOrEqual( 0, $curTTLs[$key2], 'Key 2 has current TTL <= 0' );
-
-               usleep( 1 );
-               $curTTLs = array();
-               $this->assertEquals(
-                       array( $key1 => $value1, $key2 => $value2 ),
-                       $cache->getMulti( array( $key1, $key2, $key3 ), $curTTLs, array( $cKey1, $cKey2 ) ),
-                       "Result array still populated even with new check keys"
-               );
-               $this->assertEquals( 2, count( $curTTLs ), "Current TTLs still array set" );
-               $this->assertLessThan( 0, $curTTLs[$key1], 'Key 1 has negative current TTL' );
-               $this->assertLessThan( 0, $curTTLs[$key2], 'Key 2 has negative current TTL' );
-       }
-
-       /**
-        * @covers WANObjectCache::delete()
-        */
-       public function testDelete() {
-               $key = wfRandomString();
-               $value = wfRandomString();
-               $this->cache->set( $key, $value );
-
-               $curTTL = null;
-               $v = $this->cache->get( $key, $curTTL );
-               $this->assertEquals( $value, $v, "Key was created with value" );
-               $this->assertGreaterThan( 0, $curTTL, "Existing key has current TTL > 0" );
-
-               $this->cache->delete( $key );
-
-               $curTTL = null;
-               $v = $this->cache->get( $key, $curTTL );
-               $this->assertFalse( $v, "Deleted key has false value" );
-               $this->assertLessThan( 0, $curTTL, "Deleted key has current TTL < 0" );
-
-               $this->cache->set( $key, $value . 'more' );
-               $this->assertFalse( $v, "Deleted key is tombstoned and has false value" );
-               $this->assertLessThan( 0, $curTTL, "Deleted key is tombstoned and has current TTL < 0" );
-       }
-
-       /**
-        * @covers WANObjectCache::touchCheckKey()
-        * @covers WANObjectCache::resetCheckKey()
-        * @covers WANObjectCache::getCheckKeyTime()
-        */
-       public function testTouchKeys() {
-               $key = wfRandomString();
-
-               $priorTime = microtime( true );
-               usleep( 100 );
-               $t0 = $this->cache->getCheckKeyTime( $key );
-               $this->assertGreaterThanOrEqual( $priorTime, $t0, 'Check key auto-created' );
-
-               $priorTime = microtime( true );
-               usleep( 100 );
-               $this->cache->touchCheckKey( $key );
-               $t1 = $this->cache->getCheckKeyTime( $key );
-               $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check key created' );
-
-               $t2 = $this->cache->getCheckKeyTime( $key );
-               $this->assertEquals( $t1, $t2, 'Check key time did not change' );
-
-               usleep( 100 );
-               $this->cache->touchCheckKey( $key );
-               $t3 = $this->cache->getCheckKeyTime( $key );
-               $this->assertGreaterThan( $t2, $t3, 'Check key time increased' );
-
-               $t4 = $this->cache->getCheckKeyTime( $key );
-               $this->assertEquals( $t3, $t4, 'Check key time did not change' );
-
-               usleep( 100 );
-               $this->cache->resetCheckKey( $key );
-               $t5 = $this->cache->getCheckKeyTime( $key );
-               $this->assertGreaterThan( $t4, $t5, 'Check key time increased' );
-
-               $t6 = $this->cache->getCheckKeyTime( $key );
-               $this->assertEquals( $t5, $t6, 'Check key time did not change' );
-       }
-}