Merge "Warn when a restricted displaytitle is ignored"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 13 May 2016 19:48:17 +0000 (19:48 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 13 May 2016 19:48:17 +0000 (19:48 +0000)
161 files changed:
RELEASE-NOTES-1.28
autoload.php
composer.json
docs/extension.schema.json
docs/hooks.txt
includes/AuthPlugin.php
includes/DefaultSettings.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/MediaWiki.php
includes/MediaWikiServices.php
includes/OutputPage.php
includes/ProtectionForm.php
includes/Revision.php
includes/ServiceWiring.php
includes/Title.php
includes/WatchedItem.php
includes/WebRequest.php
includes/api/ApiBase.php
includes/api/ApiMain.php
includes/api/ApiManageTags.php
includes/api/i18n/eo.json
includes/api/i18n/id.json
includes/api/i18n/jv.json [new file with mode: 0644]
includes/api/i18n/zh-hans.json
includes/cache/GenderCache.php
includes/cache/LinkCache.php
includes/changetags/ChangeTags.php
includes/collation/IcuCollation.php
includes/filerepo/FileRepo.php
includes/filerepo/file/File.php
includes/installer/WebInstallerPage.php
includes/installer/i18n/jv.json
includes/installer/i18n/zh-hant.json
includes/interwiki/Interwiki.php
includes/jobqueue/jobs/RecentChangesUpdateJob.php
includes/libs/Xhprof.php
includes/libs/XhprofData.php [new file with mode: 0644]
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/linker/LinkTarget.php
includes/objectcache/RedisBagOStuff.php
includes/parser/BlockLevelPass.php [new file with mode: 0644]
includes/parser/LinkHolderArray.php
includes/parser/Parser.php
includes/poolcounter/PoolCounter.php
includes/poolcounter/PoolCounterWork.php
includes/poolcounter/PoolCounterWorkViaCallback.php
includes/profiler/ProfilerXhprof.php
includes/registration/ExtensionProcessor.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderUserGroupsModule.php
includes/resourceloader/ResourceLoaderUserModule.php
includes/session/SessionInfo.php
includes/session/SessionManager.php
includes/session/SessionManagerInterface.php
includes/session/SessionProvider.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialMIMEsearch.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialSearch.php
includes/specials/SpecialTags.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialWhatlinkshere.php
includes/specials/pagers/ActiveUsersPager.php
includes/title/MediaWikiTitleCodec.php
includes/title/TitleFormatter.php
includes/title/TitleValue.php
includes/user/User.php
includes/widget/SearchInputWidget.php [changed mode: 0644->0755]
languages/i18n/ar.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/br.json
languages/i18n/ca.json
languages/i18n/cs.json
languages/i18n/cy.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/eu.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/he.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/inh.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/mk.json
languages/i18n/nds-nl.json
languages/i18n/nn.json
languages/i18n/olo.json
languages/i18n/or.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/ps.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/si.json
languages/i18n/sl.json
languages/i18n/sv.json
languages/i18n/uk.json
languages/i18n/vi.json
languages/i18n/war.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesCs.php
maintenance/updateCollation.php
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css
resources/lib/oojs-ui/oojs-ui-core-mediawiki.css
resources/lib/oojs-ui/oojs-ui-core.js
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css
resources/lib/oojs-ui/oojs-ui-toolbars.js
resources/lib/oojs-ui/oojs-ui-widgets-apex.css
resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css
resources/lib/oojs-ui/oojs-ui-widgets.js
resources/lib/oojs-ui/oojs-ui-windows-apex.css
resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css
resources/lib/oojs-ui/oojs-ui-windows.js
resources/src/mediawiki.action/mediawiki.action.view.metadata.js
resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js
resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js [changed mode: 0644->0755]
resources/src/mediawiki/api.js
resources/src/mediawiki/mediawiki.jqueryMsg.js
tests/parser/parserTest.inc
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/WatchedItemIntegrationTest.php
tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.php [new file with mode: 0644]
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/libs/XhprofDataTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/XhprofTest.php
tests/phpunit/includes/objectcache/RedisBagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/includes/poolcounter/PoolCounterTest.php
tests/phpunit/includes/session/SessionInfoTest.php
tests/phpunit/includes/session/SessionManagerTest.php
tests/phpunit/includes/session/SessionProviderTest.php
tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
tests/phpunit/includes/title/TitleValueTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js

index c950921..e365486 100644 (file)
@@ -11,6 +11,8 @@ production.
   user's language. If such access is attempted, an exception will be thrown.
 
 === New features in 1.28 ===
+* User::isBot() method for checking if an account is a bot role account.
+* Added a new hook, 'UserIsBot', to aid in determining if a user is a bot.
 
 
 === External library changes in 1.28 ===
index c7f8984..eb47300 100644 (file)
@@ -180,6 +180,7 @@ $wgAutoloadLocalClasses = [
        'BitmapMetadataHandler' => __DIR__ . '/includes/media/BitmapMetadataHandler.php',
        'Blob' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'Block' => __DIR__ . '/includes/Block.php',
+       'BlockLevelPass' => __DIR__ . '/includes/parser/BlockLevelPass.php',
        'BlockListPager' => __DIR__ . '/includes/specials/pagers/BlockListPager.php',
        'BlockLogFormatter' => __DIR__ . '/includes/logging/BlockLogFormatter.php',
        'BmpHandler' => __DIR__ . '/includes/media/BMP.php',
@@ -1464,6 +1465,7 @@ $wgAutoloadLocalClasses = [
        'XMPReader' => __DIR__ . '/includes/media/XMP.php',
        'XMPValidate' => __DIR__ . '/includes/media/XMPValidate.php',
        'Xhprof' => __DIR__ . '/includes/libs/Xhprof.php',
+       'XhprofData' => __DIR__ . '/includes/libs/XhprofData.php',
        'Xml' => __DIR__ . '/includes/Xml.php',
        'XmlDumpWriter' => __DIR__ . '/includes/export/XmlDumpWriter.php',
        'XmlJsCode' => __DIR__ . '/includes/Xml.php',
index b614a4c..ef85ec4 100644 (file)
@@ -25,7 +25,7 @@
                "ext-xml": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.17.1",
+               "oojs/oojs-ui": "0.17.2",
                "oyejorge/less.php": "1.7.0.10",
                "php": ">=5.5.9",
                "psr/log": "1.0.0",
index ed3eaa9..158cb6e 100644 (file)
                        "type": "object",
                        "description": "Registry of factory functions to create Config objects"
                },
+               "SessionProviders": {
+                       "type": "object",
+                       "description": "Session providers"
+               },
                "CentralIdLookupProviders": {
                        "type": "object",
                        "description": "Central ID lookup providers"
index a7092ec..2d5f6bc 100644 (file)
@@ -2128,6 +2128,7 @@ $sk: The Skin that called OutputPage::headElement
 since the last visit.
 &$modifiedTimes: array of timestamps.
   The following keys are set: page, user, epoch
+$out: OutputPage object (since 1.28)
 
 'OutputPageMakeCategoryLinks': Links are about to be generated for the page's
 categories. Implementations should return false if they generate the category
@@ -3216,6 +3217,10 @@ $mime: (string) The uploaded file's MIME type, as detected by MediaWiki.
   representing the problem with the file, where the first element is the message
   key and the remaining elements are used as parameters to the message.
 
+'UserIsBot': when determining whether a user is a bot account
+$user: the user
+&$isBot: whether this is user a bot or not (boolean)
+
 'User::mailPasswordInternal': before creation and mailing of a user's new
 temporary password
 &$user: the user who sent the message out
index 6449d37..add5876 100644 (file)
@@ -352,6 +352,9 @@ class AuthPluginUser {
                return false;
        }
 
+       /**
+        * @deprecated since 1.28, use SessionManager::invalidateSessionForUser() instead.
+        */
        public function resetAuthToken() {
                # Override this!
                return true;
index b7f487e..4ae050b 100644 (file)
@@ -4492,9 +4492,9 @@ $wgPasswordConfig = [
        ],
        'pbkdf2' => [
                'class' => 'Pbkdf2Password',
-               'algo' => 'sha256',
-               'cost' => '10000',
-               'length' => '128',
+               'algo' => 'sha512',
+               'cost' => '30000',
+               'length' => '64',
        ],
 ];
 
@@ -4903,6 +4903,7 @@ $wgGroupPermissions['sysop']['suppressredirect'] = true;
 # $wgGroupPermissions['sysop']['upload_by_url'] = true;
 $wgGroupPermissions['sysop']['mergehistory'] = true;
 $wgGroupPermissions['sysop']['managechangetags'] = true;
+$wgGroupPermissions['sysop']['deletechangetags'] = true;
 
 // Permission to change users' group assignments
 $wgGroupPermissions['bureaucrat']['userrights'] = true;
index 02093ff..870e2e0 100644 (file)
@@ -3860,7 +3860,7 @@ HTML
                        ],
                        $showSignature ? [
                                'id'     => 'mw-editbutton-signature',
-                               'open'   => '--~~~~',
+                               'open'   => wfMessage( 'sig-text', '~~~~' )->inContentLanguage()->text(),
                                'close'  => '',
                                'sample' => '',
                                'tip'    => wfMessage( 'sig_tip' )->text(),
index 537bdef..618fa4c 100644 (file)
@@ -2134,6 +2134,24 @@ function wfTempDir() {
                        return $tmp;
                }
        }
+
+       /**
+        * PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to it
+        * so create a directory within that called 'mwtmp' with a suffix of the user running the
+        * current process.
+        * The user is included as if various scripts are run by different users they will likely
+        * not be able to access each others temporary files.
+        */
+       if ( wfIsWindows() ) {
+               $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp' . '-' . get_current_user();
+               if ( !file_exists( $tmp ) ) {
+                       mkdir( $tmp );
+               }
+               if ( file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
+                       return $tmp;
+               }
+       }
+
        throw new MWException( 'No writable temporary directory could be found. ' .
                'Please set $wgTmpDirectory to a writable directory.' );
 }
index 3dd7420..ff469e4 100644 (file)
@@ -487,6 +487,7 @@ class MediaWiki {
                        $trxProfiler = Profiler::instance()->getTransactionProfiler();
                        if ( $request->wasPosted() && !$action->doesWrites() ) {
                                $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
+                               $request->markAsSafeRequest();
                        }
 
                        # Let CDN cache things if we can purge them.
index 6c650aa..891f426 100644 (file)
@@ -4,9 +4,11 @@ namespace MediaWiki;
 use Config;
 use ConfigFactory;
 use EventRelayerGroup;
+use GenderCache;
 use GlobalVarConfig;
 use Hooks;
 use LBFactory;
+use LinkCache;
 use Liuggio\StatsdClient\Factory\StatsdDataFactory;
 use LoadBalancer;
 use MediaWiki\Services\ServiceContainer;
@@ -19,6 +21,8 @@ use SiteLookup;
 use SiteStore;
 use WatchedItemStore;
 use SkinFactory;
+use TitleFormatter;
+use TitleParser;
 
 /**
  * Service locator for MediaWiki core services.
@@ -66,6 +70,8 @@ class MediaWikiServices extends ServiceContainer {
        /**
         * Returns the global default instance of the top level service locator.
         *
+        * @since 1.27
+        *
         * The default instance is initialized using the service instantiator functions
         * defined in ServiceWiring.php.
         *
@@ -91,6 +97,8 @@ class MediaWikiServices extends ServiceContainer {
        /**
         * Replaces the global MediaWikiServices instance.
         *
+        * @since 1.28
+        *
         * @note This is for use in PHPUnit tests only!
         *
         * @throws MWException if called outside of PHPUnit tests.
@@ -115,6 +123,8 @@ class MediaWikiServices extends ServiceContainer {
         * instance. getInstance() will return a different MediaWikiServices object
         * after every call to resetGlobalServiceLocator().
         *
+        * @since 1.28
+        *
         * @warning This should not be used during normal operation. It is intended for use
         * when the configuration has changed significantly since bootstrap time, e.g.
         * during the installation process or during testing.
@@ -194,6 +204,8 @@ class MediaWikiServices extends ServiceContainer {
         * storage layer will result in an error. Use resetGlobalInstance() to restore normal
         * operation.
         *
+        * @since 1.28
+        *
         * @warning This is intended for extreme situations only and should never be used
         * while serving normal web requests. Legitimate use cases for this method include
         * the installation process. Test fixtures may also use this, if the fixture relies
@@ -217,6 +229,8 @@ class MediaWikiServices extends ServiceContainer {
         * returns from after pcntl_fork(). It's also safe, but generally unnecessary,
         * to call this method from the parent process.
         *
+        * @since 1.28
+        *
         * @note This is intended for use in the context of process forking only!
         *
         * @see resetGlobalInstance()
@@ -235,6 +249,8 @@ class MediaWikiServices extends ServiceContainer {
        /**
         * Resets the given service for testing purposes.
         *
+        * @since 1.28
+        *
         * @warning This is generally unsafe! Other services may still retain references
         * to the stale service instance, leading to failures and inconsistencies. Subclasses
         * may use this method to reset specific services under specific instances, but
@@ -264,6 +280,8 @@ class MediaWikiServices extends ServiceContainer {
         * resetting of global services is allowed. In general, services should not be reset
         * individually, since that may introduce inconsistencies.
         *
+        * @since 1.28
+        *
         * This method will throw an exception if:
         *
         * - self::$resetInProgress is false (to allow all services to be reset together
@@ -321,6 +339,7 @@ class MediaWikiServices extends ServiceContainer {
         * when creating the MainConfig service. Application logic should
         * use getMainConfig() to get a Config instances.
         *
+        * @since 1.27
         * @return Config
         */
        public function getBootstrapConfig() {
@@ -328,6 +347,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return ConfigFactory
         */
        public function getConfigFactory() {
@@ -338,6 +358,7 @@ class MediaWikiServices extends ServiceContainer {
         * Returns the Config object that provides configuration for MediaWiki core.
         * This may or may not be the same object that is returned by getBootstrapConfig().
         *
+        * @since 1.27
         * @return Config
         */
        public function getMainConfig() {
@@ -345,6 +366,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return SiteLookup
         */
        public function getSiteLookup() {
@@ -352,6 +374,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return SiteStore
         */
        public function getSiteStore() {
@@ -359,6 +382,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return StatsdDataFactory
         */
        public function getStatsdDataFactory() {
@@ -366,6 +390,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return EventRelayerGroup
         */
        public function getEventRelayerGroup() {
@@ -373,6 +398,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return SearchEngine
         */
        public function newSearchEngine() {
@@ -381,6 +407,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return SearchEngineFactory
         */
        public function getSearchEngineFactory() {
@@ -388,6 +415,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return SearchEngineConfig
         */
        public function getSearchEngineConfig() {
@@ -395,6 +423,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.27
         * @return SkinFactory
         */
        public function getSkinFactory() {
@@ -402,6 +431,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.28
         * @return LBFactory
         */
        public function getDBLoadBalancerFactory() {
@@ -409,6 +439,7 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.28
         * @return LoadBalancer The main DB load balancer for the local wiki.
         */
        public function getDBLoadBalancer() {
@@ -416,12 +447,45 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
+        * @since 1.28
         * @return WatchedItemStore
         */
        public function getWatchedItemStore() {
                return $this->getService( 'WatchedItemStore' );
        }
 
+       /**
+        * @since 1.28
+        * @return GenderCache
+        */
+       public function getGenderCache() {
+               return $this->getService( 'GenderCache' );
+       }
+
+       /**
+        * @since 1.28
+        * @return LinkCache
+        */
+       public function getLinkCache() {
+               return $this->getService( 'LinkCache' );
+       }
+
+       /**
+        * @since 1.28
+        * @return TitleFormatter
+        */
+       public function getTitleFormatter() {
+               return $this->getService( 'TitleFormatter' );
+       }
+
+       /**
+        * @since 1.28
+        * @return TitleParser
+        */
+       public function getTitleParser() {
+               return $this->getService( 'TitleParser' );
+       }
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service getter here, don't forget to add a test
        // case for it in MediaWikiServicesTest::provideGetters() and in
index c724207..67e9a4f 100644 (file)
@@ -780,7 +780,7 @@ class OutputPage extends ContextSource {
                        // bug 44570: the core page itself may not change, but resources might
                        $modifiedTimes['sepoch'] = wfTimestamp( TS_MW, time() - $config->get( 'SquidMaxage' ) );
                }
-               Hooks::run( 'OutputPageCheckLastModified', [ &$modifiedTimes ] );
+               Hooks::run( 'OutputPageCheckLastModified', [ &$modifiedTimes, $this ] );
 
                $maxModified = max( $modifiedTimes );
                $this->mLastModified = wfTimestamp( TS_RFC2822, $maxModified );
index 70192b9..451635e 100644 (file)
@@ -70,7 +70,9 @@ class ProtectionForm {
                // Check if the form should be disabled.
                // If it is, the form will be available in read-only to show levels.
                $this->mPermErrors = $this->mTitle->getUserPermissionsErrors(
-                       'protect', $this->mContext->getUser()
+                       'protect',
+                       $this->mContext->getUser(),
+                       $this->mContext->getRequest()->wasPosted() ? 'secure' : 'full' // T92357
                );
                if ( wfReadOnly() ) {
                        $this->mPermErrors[] = [ 'readonlytext', wfReadOnlyReason() ];
index 40daf3d..0e45b25 100644 (file)
@@ -1553,7 +1553,6 @@ class Revision implements IDBAccessObject {
                        }
                        $text = $cache->get( $key );
                        if ( is_string( $text ) ) {
-                               wfDebug( __METHOD__ . ": got id $textId from cache\n" );
                                $processCache->set( $key, $text );
                                return $text;
                        }
index 8e95034..293e6eb 100644 (file)
@@ -139,6 +139,34 @@ return [
                return $store;
        },
 
+       'LinkCache' => function( MediaWikiServices $services ) {
+               return new LinkCache(
+                       $services->getTitleFormatter()
+               );
+       },
+
+       'GenderCache' => function( MediaWikiServices $services ) {
+               return new GenderCache();
+       },
+
+       '_MediaWikiTitleCodec' => function( MediaWikiServices $services ) {
+               global $wgContLang;
+
+               return new MediaWikiTitleCodec(
+                       $wgContLang,
+                       $services->getGenderCache(),
+                       $services->getMainConfig()->get( 'LocalInterwikis' )
+               );
+       },
+
+       'TitleFormatter' => function( MediaWikiServices $services ) {
+               return $services->getService( '_MediaWikiTitleCodec' );
+       },
+
+       'TitleParser' => function( MediaWikiServices $services ) {
+               return $services->getService( '_MediaWikiTitleCodec' );
+       },
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service here, don't forget to add a getter function
        // in the MediaWikiServices class. The convenience getter should just call
index 65b2d3a..876afe6 100644 (file)
@@ -22,7 +22,6 @@
  * @file
  */
 use MediaWiki\Linker\LinkTarget;
-
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -159,42 +158,6 @@ class Title implements LinkTarget {
        private $mIsBigDeletion = null;
        // @}
 
-       /**
-        * B/C kludge: provide a TitleParser for use by Title.
-        * Ideally, Title would have no methods that need this.
-        * Avoid usage of this singleton by using TitleValue
-        * and the associated services when possible.
-        *
-        * @return MediaWikiTitleCodec
-        */
-       private static function getMediaWikiTitleCodec() {
-               global $wgContLang, $wgLocalInterwikis;
-
-               static $titleCodec = null;
-               static $titleCodecFingerprint = null;
-
-               // $wgContLang and $wgLocalInterwikis may change (especially while testing),
-               // make sure we are using the right one. To detect changes over the course
-               // of a request, we remember a fingerprint of the config used to create the
-               // codec singleton, and re-create it if the fingerprint doesn't match.
-               $fingerprint = spl_object_hash( $wgContLang ) . '|' . implode( '+', $wgLocalInterwikis );
-
-               if ( $fingerprint !== $titleCodecFingerprint ) {
-                       $titleCodec = null;
-               }
-
-               if ( !$titleCodec ) {
-                       $titleCodec = new MediaWikiTitleCodec(
-                               $wgContLang,
-                               GenderCache::singleton(),
-                               $wgLocalInterwikis
-                       );
-                       $titleCodecFingerprint = $fingerprint;
-               }
-
-               return $titleCodec;
-       }
-
        /**
         * B/C kludge: provide a TitleParser for use by Title.
         * Ideally, Title would have no methods that need this.
@@ -204,11 +167,12 @@ class Title implements LinkTarget {
         * @return TitleFormatter
         */
        private static function getTitleFormatter() {
-               // NOTE: we know that getMediaWikiTitleCodec() returns a MediaWikiTitleCodec,
-               //      which implements TitleFormatter.
-               return self::getMediaWikiTitleCodec();
+               return MediaWikiServices::getInstance()->getTitleFormatter();
        }
 
+       /**
+        * @access protected
+        */
        function __construct() {
        }
 
@@ -3334,9 +3298,11 @@ class Title implements LinkTarget {
                // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
                //        the parsing code with Title, while avoiding massive refactoring.
                // @todo: get rid of secureAndSplit, refactor parsing code.
-               $titleParser = self::getMediaWikiTitleCodec();
+               // @note: getTitleParser() returns a TitleParser implementation which does not have a
+               //        splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
+               $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
                // MalformedTitleException can be thrown here
-               $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
+               $parts = $titleCodec->splitTitleString( $dbkey, $this->getDefaultNamespace() );
 
                # Fill fields
                $this->setFragment( '#' . $parts['fragment'] );
index 50c79dc..b070e1e 100644 (file)
@@ -152,7 +152,7 @@ class WatchedItem {
         *             or WatchedItemStore::loadWatchedItem()
         */
        public static function fromUserTitle( $user, $title, $checkRights = User::CHECK_USER_RIGHTS ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                return new self( $user, $title, self::DEPRECATED_USAGE_TIMESTAMP, (bool)$checkRights );
        }
 
@@ -160,7 +160,7 @@ class WatchedItem {
         * @deprecated since 1.27 Use WatchedItemStore::resetNotificationTimestamp()
         */
        public function resetNotificationTimestamp( $force = '', $oldid = 0 ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) {
                        return;
                }
@@ -176,7 +176,7 @@ class WatchedItem {
         * @deprecated since 1.27 Use WatchedItemStore::addWatchBatch()
         */
        public static function batchAddWatch( array $items ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                if ( !$items ) {
                        return false;
                }
@@ -209,7 +209,7 @@ class WatchedItem {
         * @return bool
         */
        public function addWatch() {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                $this->user->addWatch( $this->getTitle(), $this->checkRights );
                return true;
        }
@@ -219,7 +219,7 @@ class WatchedItem {
         * @return bool
         */
        public function removeWatch() {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) {
                        return false;
                }
@@ -232,7 +232,7 @@ class WatchedItem {
         * @return bool
         */
        public function isWatched() {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                return $this->user->isWatched( $this->getTitle(), $this->checkRights );
        }
 
@@ -240,7 +240,7 @@ class WatchedItem {
         * @deprecated since 1.27 Use WatchedItemStore::duplicateAllAssociatedEntries()
         */
        public static function duplicateEntries( Title $oldTitle, Title $newTitle ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                $store = MediaWikiServices::getInstance()->getWatchedItemStore();
                $store->duplicateAllAssociatedEntries( $oldTitle, $newTitle );
        }
index b159f79..2333c78 100644 (file)
@@ -80,6 +80,9 @@ class WebRequest {
         */
        protected $sessionId = null;
 
+       /** @var bool Whether this HTTP request is "safe" (even if it is an HTTP post) */
+       protected $markedAsSafe = false;
+
        public function __construct() {
                $this->requestTime = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
                        ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
@@ -318,7 +321,7 @@ class WebRequest {
         *
         * @param string $path The URL path given from the client
         * @param array $bases One or more URLs, optionally with $1 at the end
-        * @param string $key If provided, the matching key in $bases will be
+        * @param string|bool $key If provided, the matching key in $bases will be
         *    passed on as the value of this URL parameter
         * @return array Array of URL variables to interpolate; empty if no match
         */
@@ -1022,7 +1025,7 @@ class WebRequest {
         * @param mixed $data
         */
        public function setSessionData( $key, $data ) {
-               return $this->getSession()->set( $key, $data );
+               $this->getSession()->set( $key, $data );
        }
 
        /**
@@ -1245,4 +1248,50 @@ HTML;
        public function setIP( $ip ) {
                $this->ip = $ip;
        }
+
+       /**
+        * Whether this request should be identified as being "safe"
+        *
+        * This means that the client is not requesting any state changes and that database writes
+        * are not inherently required. Ideally, no visible updates would happen at all. If they
+        * must, then they should not be publically attributed to the end user.
+        *
+        * In more detail:
+        *   - Cache populations and refreshes MAY occur.
+        *   - Private user session updates and private server logging MAY occur.
+        *   - Updates to private viewing activity data MAY occur via DeferredUpdates.
+        *   - Other updates SHOULD NOT occur (e.g. modifying content assets).
+        *
+        * @return bool
+        * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+        * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+        * @since 1.28
+        */
+       public function isSafeRequest() {
+               if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
+                       return false; // CLI mode
+               }
+
+               if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
+                       return $this->markedAsSafe;
+               } elseif ( in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS' ] ) ) {
+                       return true; // HTTP "safe methods"
+               }
+
+               return false; // PUT/DELETE
+       }
+
+       /**
+        * Mark this request is identified as being nullipotent even if it is a POST request
+        *
+        * POST requests are often used due to the need for a client payload, even if the request
+        * is otherwise equivalent to a "safe method" request.
+        *
+        * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+        * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+        * @since 1.28
+        */
+       public function markAsSafeRequest() {
+               $this->markedAsSafe = true;
+       }
 }
index da64c03..7d0ae32 100644 (file)
@@ -1522,20 +1522,20 @@ abstract class ApiBase extends ContextSource {
                        throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
                }
 
-               $errors = $status->getErrorsArray();
+               $errors = $status->getErrorsByType( 'error' );
                if ( !$errors ) {
                        // No errors? Assume the warnings should be treated as errors
-                       $errors = $status->getWarningsArray();
+                       $errors = $status->getErrorsByType( 'warning' );
                }
                if ( !$errors ) {
                        // Still no errors? Punt
-                       $errors = [ [ 'unknownerror-nocode' ] ];
+                       $errors = [ [ 'message' => 'unknownerror-nocode', 'params' => [] ] ];
                }
 
                // Cannot use dieUsageMsg() because extensions might return custom
                // error messages.
-               if ( $errors[0] instanceof Message ) {
-                       $msg = $errors[0];
+               if ( $errors[0]['message'] instanceof Message ) {
+                       $msg = $errors[0]['message'];
                        if ( $msg instanceof IApiMessage ) {
                                $extraData = $msg->getApiData();
                                $code = $msg->getApiCode();
@@ -1543,8 +1543,8 @@ abstract class ApiBase extends ContextSource {
                                $code = $msg->getKey();
                        }
                } else {
-                       $code = array_shift( $errors[0] );
-                       $msg = wfMessage( $code, $errors[0] );
+                       $code = $errors[0]['message'];
+                       $msg = wfMessage( $code, $errors[0]['params'] );
                }
                if ( isset( ApiBase::$messageMap[$code] ) ) {
                        // Translate message to code, for backwards compatibility
index 07642c4..685a9ef 100644 (file)
@@ -134,7 +134,9 @@ class ApiMain extends ApiBase {
        private $mModuleMgr, $mResult, $mErrorFormatter, $mContinuationManager;
        private $mAction;
        private $mEnableWrite;
-       private $mInternalMode, $mSquidMaxage, $mModule;
+       private $mInternalMode, $mSquidMaxage;
+       /** @var ApiBase */
+       private $mModule;
 
        private $mCacheMode = 'private';
        private $mCacheControl = [];
@@ -397,13 +399,7 @@ class ApiMain extends ApiBase {
                if ( $this->mInternalMode ) {
                        $this->executeAction();
                } else {
-                       $start = microtime( true );
                        $this->executeActionWithErrorHandling();
-                       if ( $this->isWriteMode() && $this->getRequest()->wasPosted() ) {
-                               $timeMs = 1000 * max( 0, microtime( true ) - $start );
-                               $this->getStats()->timing(
-                                       'api.' . $this->getModuleName() . '.executeTiming', $timeMs );
-                       }
                }
        }
 
@@ -433,8 +429,12 @@ class ApiMain extends ApiBase {
                $isError = false;
                try {
                        $this->executeAction();
-                       $this->logRequest( microtime( true ) - $t );
-
+                       $runTime = microtime( true ) - $t;
+                       $this->logRequest( $runTime );
+                       if ( $this->mModule->isWriteMode() && $this->getRequest()->wasPosted() ) {
+                               $this->getStats()->timing(
+                                       'api.' . $this->getModuleName() . '.executeTiming', 1000 * $runTime );
+                       }
                } catch ( Exception $e ) {
                        $this->handleException( $e );
                        $this->logRequest( microtime( true ) - $t, $e );
@@ -1355,6 +1355,7 @@ class ApiMain extends ApiBase {
                                $trxProfiler->setExpectations( $limits['POST'], __METHOD__ );
                        } else {
                                $trxProfiler->setExpectations( $limits['POST-nonwrite'], __METHOD__ );
+                               $this->getRequest()->markAsSafeRequest();
                        }
                } else {
                        $trxProfiler->setExpectations( $limits['GET'], __METHOD__ );
index 60fb4dc..617db22 100644 (file)
@@ -29,8 +29,14 @@ class ApiManageTags extends ApiBase {
                $params = $this->extractRequestParams();
 
                // make sure the user is allowed
-               if ( !$this->getUser()->isAllowed( 'managechangetags' ) ) {
-                       $this->dieUsage( "You don't have permission to manage change tags", 'permissiondenied' );
+               if ( $params['operation'] !== 'delete'
+                       && !$this->getUser()->isAllowed( 'managechangetags' )
+               ) {
+                       $this->dieUsage( "You don't have permission to manage change tags",
+                               'permissiondenied' );
+               } elseif ( !$this->getUser()->isAllowed( 'deletechangetags' ) ) {
+                       $this->dieUsage( "You don't have permission to delete change tags",
+                               'permissiondenied' );
                }
 
                $result = $this->getResult();
index 45060c2..ff97cc8 100644 (file)
@@ -1,12 +1,28 @@
 {
        "@metadata": {
                "authors": [
-                       "Robin van der Vliet"
+                       "Robin van der Vliet",
+                       "Renardo"
                ]
        },
+       "apihelp-main-param-format": "La formo de la eligaĵo.",
+       "apihelp-block-description": "Forbari uzulon.",
+       "apihelp-block-param-user": "Salutnomo, IP-adreso aŭ IP-adresa intervalo forbarota.",
+       "apihelp-block-param-expiry": "Eksvalidiĝa tempo. Ĝi povas esti relativa (ekz. <kbd>5 months</kbd> aŭ <kbd>2 weeks</kbd> aŭ absoluta (ekz. <kbd>2014-09-18T12:34:56Z</kbd>). Se vi indikas <kbd>infinite</kbd> (senfine), <kbd>indefinite</kbd> (nedifinite) aŭ <kbd>never</kbd> (neniam), la forbaro neniam eksvalidiĝos.",
        "apihelp-createaccount-param-name": "Uzantnomo.",
        "apihelp-delete-description": "Forigi paĝon.",
+       "apihelp-edit-param-minor": "Redakteto.",
        "apihelp-edit-example-edit": "Redakti paĝon.",
+       "apihelp-feedrecentchanges-param-hideminor": "Kaŝi redaktetojn.",
+       "apihelp-feedrecentchanges-param-hidebots": "Kaŝi robotajn ŝanĝojn.",
+       "apihelp-feedrecentchanges-param-hideanons": "Kaŝi redaktojn de anonimuloj.",
+       "apihelp-feedrecentchanges-param-hideliu": "Kaŝi redaktojn de ensalutintaj uzantoj.",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Kaŝi reviziitajn ŝanĝojn.",
+       "apihelp-feedrecentchanges-param-hidemyself": "Kaŝi redaktojn de la nun ensalutinta uzulo (= vi).",
+       "apihelp-feedrecentchanges-param-hidecategorization": "Kaŝi ŝanĝojn de kategoria aneco.",
+       "apihelp-feedrecentchanges-example-simple": "Montri ĵusajn ŝanĝojn.",
+       "apihelp-filerevert-description": "Restarigi malnovan version de dosiero.",
+       "apihelp-filerevert-param-comment": "Alŝuta komento.",
        "apihelp-login-param-name": "Uzantnomo.",
        "apihelp-login-param-password": "Pasvorto.",
        "apihelp-login-example-login": "Ensaluti."
index b114289..dae55de 100644 (file)
@@ -3,11 +3,13 @@
                "authors": [
                        "WongKentir",
                        "Beeyan",
-                       "Rachmat.Wahidi"
+                       "Rachmat.Wahidi",
+                       "Kenrick95"
                ]
        },
        "apihelp-block-description": "Blokir pengguna.",
        "apihelp-block-param-user": "Nama pengguna, alamat IP, atau rentang alamat IP untuk diblokir.",
        "apihelp-createaccount-param-name": "Nama pengguna",
+       "apihelp-login-example-login": "Masuk log.",
        "apihelp-revisiondelete-param-ids": "Penanda untuk perubahan yang akan dihapus"
 }
diff --git a/includes/api/i18n/jv.json b/includes/api/i18n/jv.json
new file mode 100644 (file)
index 0000000..af1ca9c
--- /dev/null
@@ -0,0 +1,11 @@
+{
+       "@metadata": {
+               "authors": [
+                       "NoiX180"
+               ]
+       },
+       "apihelp-delete-example-simple": "Busak <kbd>Kaca Pokok</kbd>.",
+       "apihelp-query+backlinks-example-simple": "Tuduhaké pranala menyang <kbd>Kaca utama</kbd>.",
+       "apihelp-query+backlinks-example-generator": "Deleng pratélan bab kaca-kaca sing nggayut <kbd>Kaca utama</kbd>.",
+       "apihelp-query+contributors-example-simple": "Tuduhaké para nyumbang <kbd>Kaca Utama</kbd>."
+}
index 710da4d..71daba7 100644 (file)
        "apihelp-upload-param-sessionkey": "与$1filekey相同,基于向后兼容而维护。",
        "apihelp-upload-param-stash": "如果设置,服务器将临时藏匿文件而不是加入存储库。",
        "apihelp-upload-param-filesize": "全部上传的文件大小。",
-       "apihelp-upload-param-offset": "块的偏移量(字节)。",
+       "apihelp-upload-param-offset": "数据块的偏移量(字节)。",
        "apihelp-upload-param-chunk": "大块内容。",
        "apihelp-upload-param-async": "在可能的情况下,使潜在的大文件操作异步进行。",
        "apihelp-upload-param-checkstatus": "只检索指定文件密钥的上传状态。",
index 19695df..80f04ce 100644 (file)
@@ -21,6 +21,7 @@
  * @author Niklas Laxström
  * @ingroup Cache
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Caches user genders when needed to use correct namespace aliases.
@@ -34,18 +35,11 @@ class GenderCache {
        protected $missLimit = 1000;
 
        /**
+        * @deprecated in 1.28 see MediaWikiServices::getInstance()->getGenderCache()
         * @return GenderCache
         */
        public static function singleton() {
-               static $that = null;
-               if ( $that === null ) {
-                       $that = new self();
-               }
-
-               return $that;
-       }
-
-       protected function __construct() {
+               return MediaWikiServices::getInstance()->getGenderCache();
        }
 
        /**
index b8f2329..2c11247 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  * @ingroup Cache
  */
+use MediaWiki\Linker\LinkTarget;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Cache for article titles (prefixed DB keys) and ids linked from one source
@@ -38,56 +40,30 @@ class LinkCache {
        private $mForUpdate = false;
 
        /**
-        * How many Titles to store. There are two caches, so the amount actually
-        * stored in memory can be up to twice this.
+        * @var TitleFormatter
         */
-       const MAX_SIZE = 10000;
+       private $titleFormatter;
 
        /**
-        * @var LinkCache
+        * How many Titles to store. There are two caches, so the amount actually
+        * stored in memory can be up to twice this.
         */
-       protected static $instance;
+       const MAX_SIZE = 10000;
 
-       public function __construct() {
+       public function __construct( TitleFormatter $titleFormatter ) {
                $this->mGoodLinks = new HashBagOStuff( [ 'maxKeys' => self::MAX_SIZE ] );
                $this->mBadLinks = new HashBagOStuff( [ 'maxKeys' => self::MAX_SIZE ] );
+               $this->titleFormatter = $titleFormatter;
        }
 
        /**
         * Get an instance of this class.
         *
         * @return LinkCache
+        * @deprecated since 1.28, use MediaWikiServices instead
         */
        public static function singleton() {
-               if ( !self::$instance ) {
-                       self::$instance = new LinkCache;
-               }
-
-               return self::$instance;
-       }
-
-       /**
-        * Destroy the singleton instance
-        *
-        * A new one will be created next time singleton() is called.
-        *
-        * @since 1.22
-        */
-       public static function destroySingleton() {
-               self::$instance = null;
-       }
-
-       /**
-        * Set the singleton instance to a given object.
-        *
-        * Since we do not have an interface for LinkCache, you have to be sure the
-        * given object implements all the LinkCache public methods.
-        *
-        * @param LinkCache $instance
-        * @since 1.22
-        */
-       public static function setSingleton( LinkCache $instance ) {
-               self::$instance = $instance;
+               return MediaWikiServices::getInstance()->getLinkCache();
        }
 
        /**
@@ -119,12 +95,12 @@ class LinkCache {
        /**
         * Get a field of a title object from cache.
         * If this link is not a cached good title, it will return NULL.
-        * @param Title $title
+        * @param LinkTarget $target
         * @param string $field ('length','redirect','revision','model')
         * @return string|int|null
         */
-       public function getGoodLinkFieldObj( Title $title, $field ) {
-               $dbkey = $title->getPrefixedDBkey();
+       public function getGoodLinkFieldObj( LinkTarget $target, $field ) {
+               $dbkey = $this->titleFormatter->getPrefixedDBkey( $target );
                $info = $this->mGoodLinks->get( $dbkey );
                if ( !$info ) {
                        return null;
@@ -145,17 +121,17 @@ class LinkCache {
         * Add a link for the title to the link cache
         *
         * @param int $id Page's ID
-        * @param Title $title
+        * @param LinkTarget $target
         * @param int $len Text's length
         * @param int $redir Whether the page is a redirect
         * @param int $revision Latest revision's ID
         * @param string|null $model Latest revision's content model ID
         * @param string|null $lang Language code of the page, if not the content language
         */
-       public function addGoodLinkObj( $id, Title $title, $len = -1, $redir = null,
+       public function addGoodLinkObj( $id, LinkTarget $target, $len = -1, $redir = null,
                $revision = 0, $model = null, $lang = null
        ) {
-               $dbkey = $title->getPrefixedDBkey();
+               $dbkey = $this->titleFormatter->getPrefixedDBkey( $target );
                $this->mGoodLinks->set( $dbkey, [
                        'id' => (int)$id,
                        'length' => (int)$len,
@@ -169,12 +145,12 @@ class LinkCache {
        /**
         * Same as above with better interface.
         * @since 1.19
-        * @param Title $title
+        * @param LinkTarget $target
         * @param stdClass $row Object which has the fields page_id, page_is_redirect,
         *  page_latest and page_content_model
         */
-       public function addGoodLinkObjFromRow( Title $title, $row ) {
-               $dbkey = $title->getPrefixedDBkey();
+       public function addGoodLinkObjFromRow( LinkTarget $target, $row ) {
+               $dbkey = $this->titleFormatter->getPrefixedDBkey( $target );
                $this->mGoodLinks->set( $dbkey, [
                        'id' => intval( $row->page_id ),
                        'length' => intval( $row->page_len ),
@@ -186,10 +162,10 @@ class LinkCache {
        }
 
        /**
-        * @param Title $title
+        * @param LinkTarget $target
         */
-       public function addBadLinkObj( Title $title ) {
-               $dbkey = $title->getPrefixedDBkey();
+       public function addBadLinkObj( LinkTarget $target ) {
+               $dbkey = $this->titleFormatter->getPrefixedDBkey( $target );
                if ( !$this->isBadLink( $dbkey ) ) {
                        $this->mBadLinks->set( $dbkey, 1 );
                }
@@ -203,10 +179,10 @@ class LinkCache {
        }
 
        /**
-        * @param Title $title
+        * @param LinkTarget $target
         */
-       public function clearLink( Title $title ) {
-               $dbkey = $title->getPrefixedDBkey();
+       public function clearLink( LinkTarget $target ) {
+               $dbkey = $this->titleFormatter->getPrefixedDBkey( $target );
                $this->mBadLinks->delete( $dbkey );
                $this->mGoodLinks->delete( $dbkey );
        }
@@ -214,6 +190,7 @@ class LinkCache {
        /**
         * Add a title to the link cache, return the page_id or zero if non-existent
         *
+        * @deprecated since 1.27, unused
         * @param string $title Prefixed DB key
         * @return int Page ID or zero
         */
@@ -228,13 +205,13 @@ class LinkCache {
        /**
         * Add a title to the link cache, return the page_id or zero if non-existent
         *
-        * @param Title $nt Title object to add
+        * @param LinkTarget $nt LinkTarget object to add
         * @return int Page ID or zero
         */
-       public function addLinkObj( Title $nt ) {
+       public function addLinkObj( LinkTarget $nt ) {
                global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
 
-               $key = $nt->getPrefixedDBkey();
+               $key = $this->titleFormatter->getPrefixedDBkey( $nt );
                if ( $this->isBadLink( $key ) || $nt->isExternal() ) {
                        return 0;
                }
index 2d4d20f..a2945af 100644 (file)
@@ -1055,8 +1055,8 @@ class ChangeTags {
                $tagUsage = self::tagUsageStatistics();
 
                if ( !is_null( $user ) ) {
-                       if ( !$user->isAllowed( 'managechangetags' ) ) {
-                               return Status::newFatal( 'tags-manage-no-permission' );
+                       if ( !$user->isAllowed( 'deletechangetags' ) ) {
+                               return Status::newFatal( 'tags-delete-no-permission' );
                        } elseif ( $user->isBlocked() ) {
                                return Status::newFatal( 'tags-manage-blocked' );
                        }
index 942036b..a374b13 100644 (file)
@@ -234,32 +234,33 @@ class IcuCollation extends Collation {
 
        /**
         * @since 1.16.3
+        * @return array
         */
        public function getFirstLetterData() {
-               if ( $this->firstLetterData !== null ) {
-                       return $this->firstLetterData;
-               }
-
-               $cache = ObjectCache::getLocalServerInstance( CACHE_ANYTHING );
-               $cacheKey = $cache->makeKey(
-                       'first-letters',
-                       $this->locale,
-                       $this->digitTransformLanguage->getCode(),
-                       self::getICUVersion()
-               );
-               $cacheEntry = $cache->get( $cacheKey );
-
-               if ( $cacheEntry && isset( $cacheEntry['version'] )
-                       && $cacheEntry['version'] == self::FIRST_LETTER_VERSION
-               ) {
-                       $this->firstLetterData = $cacheEntry;
-                       return $this->firstLetterData;
+               if ( $this->firstLetterData === null ) {
+                       $cache = ObjectCache::getLocalServerInstance( CACHE_ANYTHING );
+                       $cacheKey = $cache->makeKey(
+                               'first-letters',
+                               $this->locale,
+                               $this->digitTransformLanguage->getCode(),
+                               self::getICUVersion(),
+                               self::FIRST_LETTER_VERSION
+                       );
+                       $this->firstLetterData = $cache->getWithSetCallback( $cacheKey, $cache::TTL_WEEK, function () {
+                               return $this->fetchFirstLetterData();
+                       } );
                }
+               return $this->firstLetterData;
+       }
 
+       /**
+        * @return array
+        * @throws MWException
+        */
+       private function fetchFirstLetterData() {
                // Generate data from serialized data file
-
                if ( isset( self::$tailoringFirstLetters[$this->locale] ) ) {
-                       $letters = wfGetPrecompiledData( "first-letters-root.ser" );
+                       $letters = wfGetPrecompiledData( 'first-letters-root.ser' );
                        // Append additional characters
                        $letters = array_merge( $letters, self::$tailoringFirstLetters[$this->locale] );
                        // Remove unnecessary ones, if any
@@ -374,15 +375,11 @@ class IcuCollation extends Collation {
                $data = [
                        'chars' => array_values( $letterMap ),
                        'keys' => array_keys( $letterMap ),
-                       'version' => self::FIRST_LETTER_VERSION,
                ];
 
                // Reduce memory usage before caching
                unset( $letterMap );
 
-               // Save to cache
-               $this->firstLetterData = $data;
-               $cache->set( $cacheKey, $data, $cache::TTL_WEEK );
                return $data;
        }
 
@@ -390,30 +387,21 @@ class IcuCollation extends Collation {
         * @since 1.16.3
         */
        public function getLetterByIndex( $index ) {
-               if ( $this->firstLetterData === null ) {
-                       $this->getFirstLetterData();
-               }
-               return $this->firstLetterData['chars'][$index];
+               return $this->getFirstLetterData()['chars'][$index];
        }
 
        /**
         * @since 1.16.3
         */
        public function getSortKeyByLetterIndex( $index ) {
-               if ( $this->firstLetterData === null ) {
-                       $this->getFirstLetterData();
-               }
-               return $this->firstLetterData['keys'][$index];
+               return $this->getFirstLetterData()['keys'][$index];
        }
 
        /**
         * @since 1.16.3
         */
        public function getFirstLetterCount() {
-               if ( $this->firstLetterData === null ) {
-                       $this->getFirstLetterData();
-               }
-               return count( $this->firstLetterData['chars'] );
+               return count( $this->getFirstLetterData()['chars'] );
        }
 
        /**
index 15821ea..9ad2428 100644 (file)
@@ -815,7 +815,6 @@ class FileRepo {
         * @param string $dstZone Destination zone
         * @param string $dstRel Destination relative path
         * @param int $flags Bitwise combination of the following flags:
-        *   self::DELETE_SOURCE     Delete the source file after upload
         *   self::OVERWRITE         Overwrite an existing destination file instead of failing
         *   self::OVERWRITE_SAME    Overwrite the file if the destination exists and has the
         *                           same contents as the source
@@ -838,7 +837,6 @@ class FileRepo {
         *
         * @param array $triplets (src, dest zone, dest rel) triplets as per store()
         * @param int $flags Bitwise combination of the following flags:
-        *   self::DELETE_SOURCE     Delete the source file after upload
         *   self::OVERWRITE         Overwrite an existing destination file instead of failing
         *   self::OVERWRITE_SAME    Overwrite the file if the destination exists and has the
         *                           same contents as the source
@@ -849,11 +847,14 @@ class FileRepo {
        public function storeBatch( array $triplets, $flags = 0 ) {
                $this->assertWritableRepo(); // fail out if read-only
 
+               if ( $flags & self::DELETE_SOURCE ) {
+                       throw new InvalidArgumentException( "DELETE_SOURCE not supported in " . __METHOD__ );
+               }
+
                $status = $this->newGood();
                $backend = $this->backend; // convenience
 
                $operations = [];
-               $sourceFSFilesToDelete = []; // cleanup for disk source files
                // Validate each triplet and get the store operation...
                foreach ( $triplets as $triplet ) {
                        list( $srcPath, $dstZone, $dstRel ) = $triplet;
@@ -881,12 +882,9 @@ class FileRepo {
 
                        // Get the appropriate file operation
                        if ( FileBackend::isStoragePath( $srcPath ) ) {
-                               $opName = ( $flags & self::DELETE_SOURCE ) ? 'move' : 'copy';
+                               $opName = 'copy';
                        } else {
                                $opName = 'store';
-                               if ( $flags & self::DELETE_SOURCE ) {
-                                       $sourceFSFilesToDelete[] = $srcPath;
-                               }
                        }
                        $operations[] = [
                                'op' => $opName,
@@ -903,12 +901,6 @@ class FileRepo {
                        $opts['nonLocking'] = true;
                }
                $status->merge( $backend->doOperations( $operations, $opts ) );
-               // Cleanup for disk source files...
-               foreach ( $sourceFSFilesToDelete as $file ) {
-                       MediaWiki\suppressWarnings();
-                       unlink( $file ); // FS cleanup
-                       MediaWiki\restoreWarnings();
-               }
 
                return $status;
        }
index c037516..8175b58 100644 (file)
@@ -1065,7 +1065,6 @@ abstract class File implements IDBAccessObject {
                        if ( $this->repo ) {
                                // Defer rendering if a 404 handler is set up...
                                if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) {
-                                       wfDebug( __METHOD__ . " transformation deferred.\n" );
                                        // XXX: Pass in the storage path even though we are not rendering anything
                                        // and the path is supposed to be an FS path. This is due to getScalerType()
                                        // getting called on the path and clobbering $thumb->getUrl() if it's false.
index daa429a..2ab0554 100644 (file)
@@ -98,7 +98,7 @@ abstract class WebInstallerPage {
                                wfMessage( "config-$continue" )->text(),
                                [
                                        'name' => "enter-$continue",
-                                       'style' => 'visibility:hidden;overflow:hidden;width:1px;margin:0'
+                                       'style' => 'width:0;border:0;height:0;padding:0'
                                ]
                        ) . "\n";
                }
index d33e03b..ee8ba4b 100644 (file)
@@ -1,9 +1,11 @@
 {
        "@metadata": {
                "authors": [
-                       "Anggoro"
+                       "Anggoro",
+                       "NoiX180"
                ]
        },
+       "config-install-mainpage-failed": "Ora bisa nglebokaké kaca pokok: $1",
        "mainpagetext": "'''Prangkat empuk wiki wis suksès dipasang.'''",
        "mainpagedocfooter": "Mangga maca [//meta.wikimedia.org/wiki/Help:Contents User's Guide] kanggo katrangan luwih langkung prakara panggunan prangkat empuk wiki\n== Miwiti panggunan  ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Daftar pangaturan préférènsi]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Milis rilis MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]"
 }
index 4f57b1f..4f96f4b 100644 (file)
@@ -14,7 +14,8 @@
                        "S8321414",
                        "LNDDYL",
                        "NigelSoft",
-                       "Macofe"
+                       "Macofe",
+                       "Reke"
                ]
        },
        "config-desc": "MediaWiki 安裝程式",
@@ -23,7 +24,7 @@
        "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-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-localsettings-connection-error": "使用 <code>LocalSettings.php</code> 中所指定的資料庫設定連線發生錯誤。 請修復相關設定並再試一次。\n\n$1",
index f68651b..5a0dd36 100644 (file)
@@ -171,7 +171,6 @@ class Interwiki {
                global $wgInterwikiScopes, $wgInterwikiFallbackSite;
                static $site;
 
-               wfDebug( __METHOD__ . "( $prefix )\n" );
                $value = false;
                try {
                        // Resolve site name
index 22afead..fbc1572 100644 (file)
@@ -70,7 +70,7 @@ class RecentChangesUpdateJob extends Job {
        }
 
        protected function purgeExpiredRows() {
-               global $wgRCMaxAge;
+               global $wgRCMaxAge, $wgUpdateRowsPerQuery;
 
                $lockKey = wfWikiID() . ':recentchanges-prune';
 
@@ -81,14 +81,13 @@ class RecentChangesUpdateJob extends Job {
                        return; // already in progress
                }
 
-               $batchSize = 100; // avoid slave lag
                $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
                do {
                        $rcIds = $dbw->selectFieldValues( 'recentchanges',
                                'rc_id',
                                [ 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ],
                                __METHOD__,
-                               [ 'LIMIT' => $batchSize ]
+                               [ 'LIMIT' => $wgUpdateRowsPerQuery ]
                        );
                        if ( $rcIds ) {
                                $dbw->delete( 'recentchanges', [ 'rc_id' => $rcIds ], __METHOD__ );
@@ -96,7 +95,7 @@ class RecentChangesUpdateJob extends Job {
                        // Commit in chunks to avoid slave lag
                        $dbw->commit( __METHOD__, 'flush' );
 
-                       if ( count( $rcIds ) === $batchSize ) {
+                       if ( count( $rcIds ) === $wgUpdateRowsPerQuery ) {
                                // There might be more, so try waiting for slaves
                                try {
                                        wfGetLBFactory()->waitForReplication( [ 'timeout' => 3 ] );
index d0f067f..9c1ec8e 100644 (file)
  * @file
  */
 
-use RunningStat\RunningStat;
-
 /**
  * Convenience class for working with XHProf
  * <https://github.com/phacility/xhprof>. XHProf can be installed as a PECL
  * package for use with PHP5 (Zend PHP) and is built-in to HHVM 3.3.0.
  *
- * @author Bryan Davis <bd808@wikimedia.org>
- * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
- * @since 1.25
+ * @since 1.28
  */
 class Xhprof {
-
-       /**
-        * @var array $config
-        */
-       protected $config;
-
-       /**
-        * Hierarchical profiling data returned by xhprof.
-        * @var array $hieraData
-        */
-       protected $hieraData;
-
        /**
-        * Per-function inclusive data.
-        * @var array $inclusive
+        * @var bool $enabled Whether XHProf is currently running.
         */
-       protected $inclusive;
+       protected static $enabled;
 
        /**
-        * Per-function inclusive and exclusive data.
-        * @var array $complete
+        * Start xhprof profiler
         */
-       protected $complete;
-
-       /**
-        * Configuration data can contain:
-        * - flags:   Optional flags to add additional information to the
-        *            profiling data collected.
-        *            (XHPROF_FLAGS_NO_BUILTINS, XHPROF_FLAGS_CPU,
-        *            XHPROF_FLAGS_MEMORY)
-        * - exclude: Array of function names to exclude from profiling.
-        * - include: Array of function names to include in profiling.
-        * - sort:    Key to sort per-function reports on.
-        *
-        * Note: When running under HHVM, xhprof will always behave as though the
-        * XHPROF_FLAGS_NO_BUILTINS flag has been used unless the
-        * Eval.JitEnableRenameFunction option is enabled for the HHVM process.
-        *
-        * @param array $config
-        */
-       public function __construct( array $config = [] ) {
-               $this->config = array_merge(
-                       [
-                               'flags' => 0,
-                               'exclude' => [],
-                               'include' => null,
-                               'sort' => 'wt',
-                       ],
-                       $config
-               );
-
-               xhprof_enable( $this->config['flags'], [
-                       'ignored_functions' => $this->config['exclude']
-               ] );
+       public static function isEnabled() {
+               return self::$enabled;
        }
 
        /**
-        * Stop collecting profiling data.
-        *
-        * Only the first invocation of this method will effect the internal
-        * object state. Subsequent calls will return the data collected by the
-        * initial call.
-        *
-        * @return array Collected profiling data (possibly cached)
+        * Start xhprof profiler
         */
-       public function stop() {
-               if ( $this->hieraData === null ) {
-                       $this->hieraData = $this->pruneData( xhprof_disable() );
+       public static function enable( $flags = 0, $options = [] ) {
+               if ( self::isEnabled() ) {
+                       throw new Exception( 'Xhprof profiling is already enabled.' );
                }
-               return $this->hieraData;
-       }
-
-       /**
-        * Load raw data from a prior run for analysis.
-        * Stops any existing data collection and clears internal caches.
-        *
-        * Any 'include' filters configured for this Xhprof instance will be
-        * enforced on the data as it is loaded. 'exclude' filters will however
-        * not be enforced as they are an XHProf intrinsic behavior.
-        *
-        * @param array $data
-        * @see getRawData()
-        */
-       public function loadRawData( array $data ) {
-               $this->stop();
-               $this->inclusive = null;
-               $this->complete = null;
-               $this->hieraData = $this->pruneData( $data );
-       }
-
-       /**
-        * Get raw data collected by xhprof.
-        *
-        * If data collection has not been stopped yet this method will halt
-        * collection to gather the profiling data.
-        *
-        * Each key in the returned array is an edge label for the call graph in
-        * the form "caller==>callee". There is once special case edge labled
-        * simply "main()" which represents the global scope entry point of the
-        * application.
-        *
-        * XHProf will collect different data depending on the flags that are used:
-        * - ct:    Number of matching events seen.
-        * - wt:    Inclusive elapsed wall time for this event in microseconds.
-        * - cpu:   Inclusive elapsed cpu time for this event in microseconds.
-        *          (XHPROF_FLAGS_CPU)
-        * - mu:    Delta of memory usage from start to end of callee in bytes.
-        *          (XHPROF_FLAGS_MEMORY)
-        * - pmu:   Delta of peak memory usage from start to end of callee in
-        *          bytes. (XHPROF_FLAGS_MEMORY)
-        * - alloc: Delta of amount memory requested from malloc() by the callee,
-        *          in bytes. (XHPROF_FLAGS_MALLOC)
-        * - free:  Delta of amount of memory passed to free() by the callee, in
-        *          bytes. (XHPROF_FLAGS_MALLOC)
-        *
-        * @return array
-        * @see stop()
-        * @see getInclusiveMetrics()
-        * @see getCompleteMetrics()
-        */
-       public function getRawData() {
-               return $this->stop();
+               self::$enabled = true;
+               xhprof_enable( $flags, $options );
        }
 
        /**
-        * Convert an xhprof data key into an array of ['parent', 'child']
-        * function names.
-        *
-        * The resulting array is left padded with nulls, so a key
-        * with no parent (eg 'main()') will return [null, 'function'].
+        * Stop xhprof profiler
         *
-        * @return array
+        * @return array|null xhprof data from the run, or null if xhprof was not running.
         */
-       public static function splitKey( $key ) {
-               return array_pad( explode( '==>', $key, 2 ), -2, null );
-       }
-
-       /**
-        * Remove data for functions that are not included in the 'include'
-        * configuration array.
-        *
-        * @param array $data Raw xhprof data
-        * @return array
-        */
-       protected function pruneData( $data ) {
-               if ( !$this->config['include'] ) {
-                       return $data;
-               }
-
-               $want = array_fill_keys( $this->config['include'], true );
-               $want['main()'] = true;
-
-               $keep = [];
-               foreach ( $data as $key => $stats ) {
-                       list( $parent, $child ) = self::splitKey( $key );
-                       if ( isset( $want[$parent] ) || isset( $want[$child] ) ) {
-                               $keep[$key] = $stats;
-                       }
+       public static function disable() {
+               if ( self::isEnabled() ) {
+                       self::$enabled = false;
+                       return xhprof_disable();
                }
-               return $keep;
-       }
-
-       /**
-        * Get the inclusive metrics for each function call. Inclusive metrics
-        * for given function include the metrics for all functions that were
-        * called from that function during the measurement period.
-        *
-        * If data collection has not been stopped yet this method will halt
-        * collection to gather the profiling data.
-        *
-        * See getRawData() for a description of the metric that are returned for
-        * each funcition call. The values for the wt, cpu, mu and pmu metrics are
-        * arrays with these values:
-        * - total: Cumulative value
-        * - min: Minimum value
-        * - mean: Mean (average) value
-        * - max: Maximum value
-        * - variance: Variance (spread) of the values
-        *
-        * @return array
-        * @see getRawData()
-        * @see getCompleteMetrics()
-        */
-       public function getInclusiveMetrics() {
-               if ( $this->inclusive === null ) {
-                       // Make sure we have data to work with
-                       $this->stop();
-
-                       $main = $this->hieraData['main()'];
-                       $hasCpu = isset( $main['cpu'] );
-                       $hasMu = isset( $main['mu'] );
-                       $hasAlloc = isset( $main['alloc'] );
-
-                       $this->inclusive = [];
-                       foreach ( $this->hieraData as $key => $stats ) {
-                               list( $parent, $child ) = self::splitKey( $key );
-                               if ( !isset( $this->inclusive[$child] ) ) {
-                                       $this->inclusive[$child] = [
-                                               'ct' => 0,
-                                               'wt' => new RunningStat(),
-                                       ];
-                                       if ( $hasCpu ) {
-                                               $this->inclusive[$child]['cpu'] = new RunningStat();
-                                       }
-                                       if ( $hasMu ) {
-                                               $this->inclusive[$child]['mu'] = new RunningStat();
-                                               $this->inclusive[$child]['pmu'] = new RunningStat();
-                                       }
-                                       if ( $hasAlloc ) {
-                                               $this->inclusive[$child]['alloc'] = new RunningStat();
-                                               $this->inclusive[$child]['free'] = new RunningStat();
-                                       }
-                               }
-
-                               $this->inclusive[$child]['ct'] += $stats['ct'];
-                               foreach ( $stats as $stat => $value ) {
-                                       if ( $stat === 'ct' ) {
-                                               continue;
-                                       }
-
-                                       if ( !isset( $this->inclusive[$child][$stat] ) ) {
-                                               // Ignore unknown stats
-                                               continue;
-                                       }
-
-                                       for ( $i = 0; $i < $stats['ct']; $i++ ) {
-                                               $this->inclusive[$child][$stat]->addObservation(
-                                                       $value / $stats['ct']
-                                               );
-                                       }
-                               }
-                       }
-
-                       // Convert RunningStat instances to static arrays and add
-                       // percentage stats.
-                       foreach ( $this->inclusive as $func => $stats ) {
-                               foreach ( $stats as $name => $value ) {
-                                       if ( $value instanceof RunningStat ) {
-                                               $total = $value->m1 * $value->n;
-                                               $percent = ( isset( $main[$name] ) && $main[$name] )
-                                                       ? 100 * $total / $main[$name]
-                                                       : 0;
-                                               $this->inclusive[$func][$name] = [
-                                                       'total' => $total,
-                                                       'min' => $value->min,
-                                                       'mean' => $value->m1,
-                                                       'max' => $value->max,
-                                                       'variance' => $value->m2,
-                                                       'percent' => $percent,
-                                               ];
-                                       }
-                               }
-                       }
-
-                       uasort( $this->inclusive, self::makeSortFunction(
-                               $this->config['sort'], 'total'
-                       ) );
-               }
-               return $this->inclusive;
-       }
-
-       /**
-        * Get the inclusive and exclusive metrics for each function call.
-        *
-        * If data collection has not been stopped yet this method will halt
-        * collection to gather the profiling data.
-        *
-        * In addition to the normal data contained in the inclusive metrics, the
-        * metrics have an additional 'exclusive' measurement which is the total
-        * minus the totals of all child function calls.
-        *
-        * @return array
-        * @see getRawData()
-        * @see getInclusiveMetrics()
-        */
-       public function getCompleteMetrics() {
-               if ( $this->complete === null ) {
-                       // Start with inclusive data
-                       $this->complete = $this->getInclusiveMetrics();
-
-                       foreach ( $this->complete as $func => $stats ) {
-                               foreach ( $stats as $stat => $value ) {
-                                       if ( $stat === 'ct' ) {
-                                               continue;
-                                       }
-                                       // Initialize exclusive data with inclusive totals
-                                       $this->complete[$func][$stat]['exclusive'] = $value['total'];
-                               }
-                               // Add sapce for call tree information to be filled in later
-                               $this->complete[$func]['calls'] = [];
-                               $this->complete[$func]['subcalls'] = [];
-                       }
-
-                       foreach ( $this->hieraData as $key => $stats ) {
-                               list( $parent, $child ) = self::splitKey( $key );
-                               if ( $parent !== null ) {
-                                       // Track call tree information
-                                       $this->complete[$child]['calls'][$parent] = $stats;
-                                       $this->complete[$parent]['subcalls'][$child] = $stats;
-                               }
-
-                               if ( isset( $this->complete[$parent] ) ) {
-                                       // Deduct child inclusive data from exclusive data
-                                       foreach ( $stats as $stat => $value ) {
-                                               if ( $stat === 'ct' ) {
-                                                       continue;
-                                               }
-
-                                               if ( !isset( $this->complete[$parent][$stat] ) ) {
-                                                       // Ignore unknown stats
-                                                       continue;
-                                               }
-
-                                               $this->complete[$parent][$stat]['exclusive'] -= $value;
-                                       }
-                               }
-                       }
-
-                       uasort( $this->complete, self::makeSortFunction(
-                               $this->config['sort'], 'exclusive'
-                       ) );
-               }
-               return $this->complete;
-       }
-
-       /**
-        * Get a list of all callers of a given function.
-        *
-        * @param string $function Function name
-        * @return array
-        * @see getEdges()
-        */
-       public function getCallers( $function ) {
-               $edges = $this->getCompleteMetrics();
-               if ( isset( $edges[$function]['calls'] ) ) {
-                       return array_keys( $edges[$function]['calls'] );
-               } else {
-                       return [];
-               }
-       }
-
-       /**
-        * Get a list of all callees from a given function.
-        *
-        * @param string $function Function name
-        * @return array
-        * @see getEdges()
-        */
-       public function getCallees( $function ) {
-               $edges = $this->getCompleteMetrics();
-               if ( isset( $edges[$function]['subcalls'] ) ) {
-                       return array_keys( $edges[$function]['subcalls'] );
-               } else {
-                       return [];
-               }
-       }
-
-       /**
-        * Find the critical path for the given metric.
-        *
-        * @param string $metric Metric to find critical path for
-        * @return array
-        */
-       public function getCriticalPath( $metric = 'wt' ) {
-               $this->stop();
-               $func = 'main()';
-               $path = [
-                       $func => $this->hieraData[$func],
-               ];
-               while ( $func ) {
-                       $callees = $this->getCallees( $func );
-                       $maxCallee = null;
-                       $maxCall = null;
-                       foreach ( $callees as $callee ) {
-                               $call = "{$func}==>{$callee}";
-                               if ( $maxCall === null ||
-                                       $this->hieraData[$call][$metric] >
-                                               $this->hieraData[$maxCall][$metric]
-                               ) {
-                                       $maxCallee = $callee;
-                                       $maxCall = $call;
-                               }
-                       }
-                       if ( $maxCall !== null ) {
-                               $path[$maxCall] = $this->hieraData[$maxCall];
-                       }
-                       $func = $maxCallee;
-               }
-               return $path;
-       }
-
-       /**
-        * Make a closure to use as a sort function. The resulting function will
-        * sort by descending numeric values (largest value first).
-        *
-        * @param string $key Data key to sort on
-        * @param string $sub Sub key to sort array values on
-        * @return Closure
-        */
-       public static function makeSortFunction( $key, $sub ) {
-               return function ( $a, $b ) use ( $key, $sub ) {
-                       if ( isset( $a[$key] ) && isset( $b[$key] ) ) {
-                               // Descending sort: larger values will be first in result.
-                               // Assumes all values are numeric.
-                               // Values for 'main()' will not have sub keys
-                               $valA = is_array( $a[$key] ) ? $a[$key][$sub] : $a[$key];
-                               $valB = is_array( $b[$key] ) ? $b[$key][$sub] : $b[$key];
-                               return $valB - $valA;
-                       } else {
-                               // Sort datum with the key before those without
-                               return isset( $a[$key] ) ? -1 : 1;
-                       }
-               };
        }
 }
diff --git a/includes/libs/XhprofData.php b/includes/libs/XhprofData.php
new file mode 100644 (file)
index 0000000..c6da432
--- /dev/null
@@ -0,0 +1,384 @@
+<?php
+/**
+ * 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
+ */
+
+use RunningStat\RunningStat;
+
+/**
+ * Convenience class for working with XHProf profiling data
+ * <https://github.com/phacility/xhprof>. XHProf can be installed as a PECL
+ * package for use with PHP5 (Zend PHP) and is built-in to HHVM 3.3.0.
+ *
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
+ * @since 1.28
+ */
+class XhprofData {
+
+       /**
+        * @var array $config
+        */
+       protected $config;
+
+       /**
+        * Hierarchical profiling data returned by xhprof.
+        * @var array $hieraData
+        */
+       protected $hieraData;
+
+       /**
+        * Per-function inclusive data.
+        * @var array $inclusive
+        */
+       protected $inclusive;
+
+       /**
+        * Per-function inclusive and exclusive data.
+        * @var array $complete
+        */
+       protected $complete;
+
+       /**
+        * Configuration data can contain:
+        * - include: Array of function names to include in profiling.
+        * - sort:    Key to sort per-function reports on.
+        *
+        * @param array $data Xhprof profiling data, as returned by xhprof_disable()
+        * @param array $config
+        */
+       public function __construct( array $data, array $config = [] ) {
+               $this->config = array_merge( [
+                       'include' => null,
+                       'sort' => 'wt',
+               ], $config );
+
+               $this->hieraData = $this->pruneData( $data );
+       }
+
+       /**
+        * Get raw data collected by xhprof.
+        *
+        * Each key in the returned array is an edge label for the call graph in
+        * the form "caller==>callee". There is once special case edge labled
+        * simply "main()" which represents the global scope entry point of the
+        * application.
+        *
+        * XHProf will collect different data depending on the flags that are used:
+        * - ct:    Number of matching events seen.
+        * - wt:    Inclusive elapsed wall time for this event in microseconds.
+        * - cpu:   Inclusive elapsed cpu time for this event in microseconds.
+        *          (XHPROF_FLAGS_CPU)
+        * - mu:    Delta of memory usage from start to end of callee in bytes.
+        *          (XHPROF_FLAGS_MEMORY)
+        * - pmu:   Delta of peak memory usage from start to end of callee in
+        *          bytes. (XHPROF_FLAGS_MEMORY)
+        * - alloc: Delta of amount memory requested from malloc() by the callee,
+        *          in bytes. (XHPROF_FLAGS_MALLOC)
+        * - free:  Delta of amount of memory passed to free() by the callee, in
+        *          bytes. (XHPROF_FLAGS_MALLOC)
+        *
+        * @return array
+        * @see getInclusiveMetrics()
+        * @see getCompleteMetrics()
+        */
+       public function getRawData() {
+               return $this->hieraData;
+       }
+
+       /**
+        * Convert an xhprof data key into an array of ['parent', 'child']
+        * function names.
+        *
+        * The resulting array is left padded with nulls, so a key
+        * with no parent (eg 'main()') will return [null, 'function'].
+        *
+        * @return array
+        */
+       public static function splitKey( $key ) {
+               return array_pad( explode( '==>', $key, 2 ), -2, null );
+       }
+
+       /**
+        * Remove data for functions that are not included in the 'include'
+        * configuration array.
+        *
+        * @param array $data Raw xhprof data
+        * @return array
+        */
+       protected function pruneData( $data ) {
+               if ( !$this->config['include'] ) {
+                       return $data;
+               }
+
+               $want = array_fill_keys( $this->config['include'], true );
+               $want['main()'] = true;
+
+               $keep = [];
+               foreach ( $data as $key => $stats ) {
+                       list( $parent, $child ) = self::splitKey( $key );
+                       if ( isset( $want[$parent] ) || isset( $want[$child] ) ) {
+                               $keep[$key] = $stats;
+                       }
+               }
+               return $keep;
+       }
+
+       /**
+        * Get the inclusive metrics for each function call. Inclusive metrics
+        * for given function include the metrics for all functions that were
+        * called from that function during the measurement period.
+        *
+        * See getRawData() for a description of the metric that are returned for
+        * each funcition call. The values for the wt, cpu, mu and pmu metrics are
+        * arrays with these values:
+        * - total: Cumulative value
+        * - min: Minimum value
+        * - mean: Mean (average) value
+        * - max: Maximum value
+        * - variance: Variance (spread) of the values
+        *
+        * @return array
+        * @see getRawData()
+        * @see getCompleteMetrics()
+        */
+       public function getInclusiveMetrics() {
+               if ( $this->inclusive === null ) {
+                       $main = $this->hieraData['main()'];
+                       $hasCpu = isset( $main['cpu'] );
+                       $hasMu = isset( $main['mu'] );
+                       $hasAlloc = isset( $main['alloc'] );
+
+                       $this->inclusive = [];
+                       foreach ( $this->hieraData as $key => $stats ) {
+                               list( $parent, $child ) = self::splitKey( $key );
+                               if ( !isset( $this->inclusive[$child] ) ) {
+                                       $this->inclusive[$child] = [
+                                               'ct' => 0,
+                                               'wt' => new RunningStat(),
+                                       ];
+                                       if ( $hasCpu ) {
+                                               $this->inclusive[$child]['cpu'] = new RunningStat();
+                                       }
+                                       if ( $hasMu ) {
+                                               $this->inclusive[$child]['mu'] = new RunningStat();
+                                               $this->inclusive[$child]['pmu'] = new RunningStat();
+                                       }
+                                       if ( $hasAlloc ) {
+                                               $this->inclusive[$child]['alloc'] = new RunningStat();
+                                               $this->inclusive[$child]['free'] = new RunningStat();
+                                       }
+                               }
+
+                               $this->inclusive[$child]['ct'] += $stats['ct'];
+                               foreach ( $stats as $stat => $value ) {
+                                       if ( $stat === 'ct' ) {
+                                               continue;
+                                       }
+
+                                       if ( !isset( $this->inclusive[$child][$stat] ) ) {
+                                               // Ignore unknown stats
+                                               continue;
+                                       }
+
+                                       for ( $i = 0; $i < $stats['ct']; $i++ ) {
+                                               $this->inclusive[$child][$stat]->addObservation(
+                                                       $value / $stats['ct']
+                                               );
+                                       }
+                               }
+                       }
+
+                       // Convert RunningStat instances to static arrays and add
+                       // percentage stats.
+                       foreach ( $this->inclusive as $func => $stats ) {
+                               foreach ( $stats as $name => $value ) {
+                                       if ( $value instanceof RunningStat ) {
+                                               $total = $value->m1 * $value->n;
+                                               $percent = ( isset( $main[$name] ) && $main[$name] )
+                                                       ? 100 * $total / $main[$name]
+                                                       : 0;
+                                               $this->inclusive[$func][$name] = [
+                                                       'total' => $total,
+                                                       'min' => $value->min,
+                                                       'mean' => $value->m1,
+                                                       'max' => $value->max,
+                                                       'variance' => $value->m2,
+                                                       'percent' => $percent,
+                                               ];
+                                       }
+                               }
+                       }
+
+                       uasort( $this->inclusive, self::makeSortFunction(
+                               $this->config['sort'], 'total'
+                       ) );
+               }
+               return $this->inclusive;
+       }
+
+       /**
+        * Get the inclusive and exclusive metrics for each function call.
+        *
+        * In addition to the normal data contained in the inclusive metrics, the
+        * metrics have an additional 'exclusive' measurement which is the total
+        * minus the totals of all child function calls.
+        *
+        * @return array
+        * @see getRawData()
+        * @see getInclusiveMetrics()
+        */
+       public function getCompleteMetrics() {
+               if ( $this->complete === null ) {
+                       // Start with inclusive data
+                       $this->complete = $this->getInclusiveMetrics();
+
+                       foreach ( $this->complete as $func => $stats ) {
+                               foreach ( $stats as $stat => $value ) {
+                                       if ( $stat === 'ct' ) {
+                                               continue;
+                                       }
+                                       // Initialize exclusive data with inclusive totals
+                                       $this->complete[$func][$stat]['exclusive'] = $value['total'];
+                               }
+                               // Add sapce for call tree information to be filled in later
+                               $this->complete[$func]['calls'] = [];
+                               $this->complete[$func]['subcalls'] = [];
+                       }
+
+                       foreach ( $this->hieraData as $key => $stats ) {
+                               list( $parent, $child ) = self::splitKey( $key );
+                               if ( $parent !== null ) {
+                                       // Track call tree information
+                                       $this->complete[$child]['calls'][$parent] = $stats;
+                                       $this->complete[$parent]['subcalls'][$child] = $stats;
+                               }
+
+                               if ( isset( $this->complete[$parent] ) ) {
+                                       // Deduct child inclusive data from exclusive data
+                                       foreach ( $stats as $stat => $value ) {
+                                               if ( $stat === 'ct' ) {
+                                                       continue;
+                                               }
+
+                                               if ( !isset( $this->complete[$parent][$stat] ) ) {
+                                                       // Ignore unknown stats
+                                                       continue;
+                                               }
+
+                                               $this->complete[$parent][$stat]['exclusive'] -= $value;
+                                       }
+                               }
+                       }
+
+                       uasort( $this->complete, self::makeSortFunction(
+                               $this->config['sort'], 'exclusive'
+                       ) );
+               }
+               return $this->complete;
+       }
+
+       /**
+        * Get a list of all callers of a given function.
+        *
+        * @param string $function Function name
+        * @return array
+        * @see getEdges()
+        */
+       public function getCallers( $function ) {
+               $edges = $this->getCompleteMetrics();
+               if ( isset( $edges[$function]['calls'] ) ) {
+                       return array_keys( $edges[$function]['calls'] );
+               } else {
+                       return [];
+               }
+       }
+
+       /**
+        * Get a list of all callees from a given function.
+        *
+        * @param string $function Function name
+        * @return array
+        * @see getEdges()
+        */
+       public function getCallees( $function ) {
+               $edges = $this->getCompleteMetrics();
+               if ( isset( $edges[$function]['subcalls'] ) ) {
+                       return array_keys( $edges[$function]['subcalls'] );
+               } else {
+                       return [];
+               }
+       }
+
+       /**
+        * Find the critical path for the given metric.
+        *
+        * @param string $metric Metric to find critical path for
+        * @return array
+        */
+       public function getCriticalPath( $metric = 'wt' ) {
+               $func = 'main()';
+               $path = [
+                       $func => $this->hieraData[$func],
+               ];
+               while ( $func ) {
+                       $callees = $this->getCallees( $func );
+                       $maxCallee = null;
+                       $maxCall = null;
+                       foreach ( $callees as $callee ) {
+                               $call = "{$func}==>{$callee}";
+                               if ( $maxCall === null ||
+                                       $this->hieraData[$call][$metric] >
+                                               $this->hieraData[$maxCall][$metric]
+                               ) {
+                                       $maxCallee = $callee;
+                                       $maxCall = $call;
+                               }
+                       }
+                       if ( $maxCall !== null ) {
+                               $path[$maxCall] = $this->hieraData[$maxCall];
+                       }
+                       $func = $maxCallee;
+               }
+               return $path;
+       }
+
+       /**
+        * Make a closure to use as a sort function. The resulting function will
+        * sort by descending numeric values (largest value first).
+        *
+        * @param string $key Data key to sort on
+        * @param string $sub Sub key to sort array values on
+        * @return Closure
+        */
+       public static function makeSortFunction( $key, $sub ) {
+               return function ( $a, $b ) use ( $key, $sub ) {
+                       if ( isset( $a[$key] ) && isset( $b[$key] ) ) {
+                               // Descending sort: larger values will be first in result.
+                               // Assumes all values are numeric.
+                               // Values for 'main()' will not have sub keys
+                               $valA = is_array( $a[$key] ) ? $a[$key][$sub] : $a[$key];
+                               $valB = is_array( $b[$key] ) ? $b[$key][$sub] : $b[$key];
+                               return $valB - $valA;
+                       } else {
+                               // Sort datum with the key before those without
+                               return isset( $a[$key] ) ? -1 : 1;
+                       }
+               };
+       }
+}
index 8e3c0a5..bf46ce1 100644 (file)
@@ -285,8 +285,12 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        protected function mergeViaCas( $key, $callback, $exptime = 0, $attempts = 10 ) {
                do {
                        $this->clearLastError();
+                       $reportDupes = $this->reportDupes;
+                       $this->reportDupes = false;
                        $casToken = null; // passed by reference
                        $currentValue = $this->getWithToken( $key, $casToken, self::READ_LATEST );
+                       $this->reportDupes = $reportDupes;
+
                        if ( $this->getLastError() ) {
                                return false; // don't spam retries (retry only on races)
                        }
@@ -342,7 +346,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                }
 
                $this->clearLastError();
+               $reportDupes = $this->reportDupes;
+               $this->reportDupes = false;
                $currentValue = $this->get( $key, self::READ_LATEST );
+               $this->reportDupes = $reportDupes;
+
                if ( $this->getLastError() ) {
                        $success = false;
                } else {
index 18cc10e..470a38c 100644 (file)
@@ -110,12 +110,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** Cache format version number */
        const VERSION = 1;
 
-       const FLD_VERSION = 0;
-       const FLD_VALUE = 1;
-       const FLD_TTL = 2;
-       const FLD_TIME = 3;
-       const FLD_FLAGS = 4;
-       const FLD_HOLDOFF = 5;
+       const FLD_VERSION = 0; // key to cache version number
+       const FLD_VALUE = 1; // key to the cached value
+       const FLD_TTL = 2; // key to the original TTL
+       const FLD_TIME = 3; // key to the cache time
+       const FLD_FLAGS = 4; // key to the flags bitfield
+       const FLD_HOLDOFF = 5; // key to any hold-off TTL
 
        /** @var integer Treat this value as expired-on-arrival */
        const FLG_STALE = 1;
@@ -377,8 +377,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
+               $now = microtime( true );
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
-               $age = isset( $opts['since'] ) ? max( 0, microtime( true ) - $opts['since'] ) : 0;
+               $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
                $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
 
                // Do not cache potentially uncommitted data as it might get rolled back
@@ -413,7 +414,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                }
 
                // Wrap that value with time/TTL/version metadata
-               $wrapped = $this->wrap( $value, $ttl ) + $wrapExtra;
+               $wrapped = $this->wrap( $value, $ttl, $now ) + $wrapExtra;
 
                $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
                        return ( is_string( $cWrapped ) )
@@ -1009,14 +1010,15 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * @param mixed $value
         * @param integer $ttl [0=forever]
+        * @param float $now Unix Current timestamp just before calling set()
         * @return array
         */
-       protected function wrap( $value, $ttl ) {
+       protected function wrap( $value, $ttl, $now ) {
                return [
                        self::FLD_VERSION => self::VERSION,
                        self::FLD_VALUE => $value,
                        self::FLD_TTL => $ttl,
-                       self::FLD_TIME => microtime( true )
+                       self::FLD_TIME => $now
                ];
        }
 
@@ -1024,7 +1026,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * Do not use this method outside WANObjectCache
         *
         * @param array|string|bool $wrapped
-        * @param float $now Unix Current timestamp (preferrable pre-query)
+        * @param float $now Unix Current timestamp (preferrably pre-query)
         * @return array (mixed; false if absent/invalid, current time left)
         */
        protected function unwrap( $wrapped, $now ) {
index 7b59751..da48e00 100644 (file)
@@ -33,6 +33,14 @@ interface LinkTarget {
         */
        public function getNamespace();
 
+       /**
+        * Convenience function to test if it is in the namespace
+        *
+        * @param int $ns
+        * @return bool
+        */
+       public function inNamespace( $ns );
+
        /**
         * Get the link fragment (i.e. the bit after the #) in text form.
         *
index 61e6926..90508da 100644 (file)
@@ -310,7 +310,8 @@ class RedisBagOStuff extends BagOStuff {
         * @return mixed
         */
        protected function unserialize( $data ) {
-               return ctype_digit( $data ) ? intval( $data ) : unserialize( $data );
+               $int = intval( $data );
+               return $data === (string)$int ? $int : unserialize( $data );
        }
 
        /**
diff --git a/includes/parser/BlockLevelPass.php b/includes/parser/BlockLevelPass.php
new file mode 100644 (file)
index 0000000..cbacd34
--- /dev/null
@@ -0,0 +1,535 @@
+<?php
+
+/**
+ * This is the part of the wikitext parser which handles automatic paragraphs
+ * and conversion of start-of-line prefixes to HTML lists.
+ *
+ * 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 Parser
+ */
+class BlockLevelPass {
+       private $DTopen = false;
+       private $inPre = false;
+       private $lastSection = '';
+       private $linestart;
+       private $text;
+
+       # State constants for the definition list colon extraction
+       const COLON_STATE_TEXT = 0;
+       const COLON_STATE_TAG = 1;
+       const COLON_STATE_TAGSTART = 2;
+       const COLON_STATE_CLOSETAG = 3;
+       const COLON_STATE_TAGSLASH = 4;
+       const COLON_STATE_COMMENT = 5;
+       const COLON_STATE_COMMENTDASH = 6;
+       const COLON_STATE_COMMENTDASHDASH = 7;
+
+       /**
+        * Make lists from lines starting with ':', '*', '#', etc.
+        *
+        * @param string $text
+        * @param bool $lineStart Whether or not this is at the start of a line.
+        * @return string The lists rendered as HTML
+        */
+       public static function doBlockLevels( $text, $lineStart ) {
+               $pass = new self( $text, $lineStart );
+               return $pass->execute();
+       }
+
+       /**
+        * Private constructor
+        */
+       private function __construct( $text, $lineStart ) {
+               $this->text = $text;
+               $this->lineStart = $lineStart;
+       }
+
+       /**
+        * If a pre or p is open, return the corresponding close tag and update
+        * the state. If no tag is open, return an empty string.
+        * @return string
+        */
+       private function closeParagraph() {
+               $result = '';
+               if ( $this->lastSection !== '' ) {
+                       $result = '</' . $this->lastSection . ">\n";
+               }
+               $this->inPre = false;
+               $this->lastSection = '';
+               return $result;
+       }
+
+       /**
+        * getCommon() returns the length of the longest common substring
+        * of both arguments, starting at the beginning of both.
+        *
+        * @param string $st1
+        * @param string $st2
+        *
+        * @return int
+        */
+       private function getCommon( $st1, $st2 ) {
+               $shorter = min( strlen( $st1 ), strlen( $st2 ) );
+
+               for ( $i = 0; $i < $shorter; ++$i ) {
+                       if ( $st1[$i] !== $st2[$i] ) {
+                               break;
+                       }
+               }
+               return $i;
+       }
+
+       /**
+        * Open the list item element identified by the prefix character.
+        *
+        * @param string $char
+        *
+        * @return string
+        */
+       private function openList( $char ) {
+               $result = $this->closeParagraph();
+
+               if ( '*' === $char ) {
+                       $result .= "<ul><li>";
+               } elseif ( '#' === $char ) {
+                       $result .= "<ol><li>";
+               } elseif ( ':' === $char ) {
+                       $result .= "<dl><dd>";
+               } elseif ( ';' === $char ) {
+                       $result .= "<dl><dt>";
+                       $this->DTopen = true;
+               } else {
+                       $result = '<!-- ERR 1 -->';
+               }
+
+               return $result;
+       }
+
+       /**
+        * Close the current list item and open the next one.
+        * @param string $char
+        *
+        * @return string
+        */
+       private function nextItem( $char ) {
+               if ( '*' === $char || '#' === $char ) {
+                       return "</li>\n<li>";
+               } elseif ( ':' === $char || ';' === $char ) {
+                       $close = "</dd>\n";
+                       if ( $this->DTopen ) {
+                               $close = "</dt>\n";
+                       }
+                       if ( ';' === $char ) {
+                               $this->DTopen = true;
+                               return $close . '<dt>';
+                       } else {
+                               $this->DTopen = false;
+                               return $close . '<dd>';
+                       }
+               }
+               return '<!-- ERR 2 -->';
+       }
+
+       /**
+        * Close the current list item identified by the prefix character.
+        * @param string $char
+        *
+        * @return string
+        */
+       private function closeList( $char ) {
+               if ( '*' === $char ) {
+                       $text = "</li></ul>";
+               } elseif ( '#' === $char ) {
+                       $text = "</li></ol>";
+               } elseif ( ':' === $char ) {
+                       if ( $this->DTopen ) {
+                               $this->DTopen = false;
+                               $text = "</dt></dl>";
+                       } else {
+                               $text = "</dd></dl>";
+                       }
+               } else {
+                       return '<!-- ERR 3 -->';
+               }
+               return $text;
+       }
+
+       /**
+        * Execute the pass.
+        * @return string
+        */
+       private function execute() {
+               $text = $this->text;
+               # Parsing through the text line by line.  The main thing
+               # happening here is handling of block-level elements p, pre,
+               # and making lists from lines starting with * # : etc.
+               $textLines = StringUtils::explode( "\n", $text );
+
+               $lastPrefix = $output = '';
+               $this->DTopen = $inBlockElem = false;
+               $prefixLength = 0;
+               $pendingPTag = false;
+               $inBlockquote = false;
+
+               foreach ( $textLines as $inputLine ) {
+                       # Fix up $lineStart
+                       if ( !$this->lineStart ) {
+                               $output .= $inputLine;
+                               $this->lineStart = true;
+                               continue;
+                       }
+                       # * = ul
+                       # # = ol
+                       # ; = dt
+                       # : = dd
+
+                       $lastPrefixLength = strlen( $lastPrefix );
+                       $preCloseMatch = preg_match( '/<\\/pre/i', $inputLine );
+                       $preOpenMatch = preg_match( '/<pre/i', $inputLine );
+                       # If not in a <pre> element, scan for and figure out what prefixes are there.
+                       if ( !$this->inPre ) {
+                               # Multiple prefixes may abut each other for nested lists.
+                               $prefixLength = strspn( $inputLine, '*#:;' );
+                               $prefix = substr( $inputLine, 0, $prefixLength );
+
+                               # eh?
+                               # ; and : are both from definition-lists, so they're equivalent
+                               #  for the purposes of determining whether or not we need to open/close
+                               #  elements.
+                               $prefix2 = str_replace( ';', ':', $prefix );
+                               $t = substr( $inputLine, $prefixLength );
+                               $this->inPre = (bool)$preOpenMatch;
+                       } else {
+                               # Don't interpret any other prefixes in preformatted text
+                               $prefixLength = 0;
+                               $prefix = $prefix2 = '';
+                               $t = $inputLine;
+                       }
+
+                       # List generation
+                       if ( $prefixLength && $lastPrefix === $prefix2 ) {
+                               # Same as the last item, so no need to deal with nesting or opening stuff
+                               $output .= $this->nextItem( substr( $prefix, -1 ) );
+                               $pendingPTag = false;
+
+                               if ( substr( $prefix, -1 ) === ';' ) {
+                                       # The one nasty exception: definition lists work like this:
+                                       # ; title : definition text
+                                       # So we check for : in the remainder text to split up the
+                                       # title and definition, without b0rking links.
+                                       $term = $t2 = '';
+                                       if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
+                                               $t = $t2;
+                                               $output .= $term . $this->nextItem( ':' );
+                                       }
+                               }
+                       } elseif ( $prefixLength || $lastPrefixLength ) {
+                               # We need to open or close prefixes, or both.
+
+                               # Either open or close a level...
+                               $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix );
+                               $pendingPTag = false;
+
+                               # Close all the prefixes which aren't shared.
+                               while ( $commonPrefixLength < $lastPrefixLength ) {
+                                       $output .= $this->closeList( $lastPrefix[$lastPrefixLength - 1] );
+                                       --$lastPrefixLength;
+                               }
+
+                               # Continue the current prefix if appropriate.
+                               if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
+                                       $output .= $this->nextItem( $prefix[$commonPrefixLength - 1] );
+                               }
+
+                               # Open prefixes where appropriate.
+                               if ( $lastPrefix && $prefixLength > $commonPrefixLength ) {
+                                       $output .= "\n";
+                               }
+                               while ( $prefixLength > $commonPrefixLength ) {
+                                       $char = substr( $prefix, $commonPrefixLength, 1 );
+                                       $output .= $this->openList( $char );
+
+                                       if ( ';' === $char ) {
+                                               # @todo FIXME: This is dupe of code above
+                                               if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
+                                                       $t = $t2;
+                                                       $output .= $term . $this->nextItem( ':' );
+                                               }
+                                       }
+                                       ++$commonPrefixLength;
+                               }
+                               if ( !$prefixLength && $lastPrefix ) {
+                                       $output .= "\n";
+                               }
+                               $lastPrefix = $prefix2;
+                       }
+
+                       # If we have no prefixes, go to paragraph mode.
+                       if ( 0 == $prefixLength ) {
+                               # No prefix (not in list)--go to paragraph mode
+                               # @todo consider using a stack for nestable elements like span, table and div
+                               $openMatch = preg_match(
+                                       '/(?:<table|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|'
+                                               . '<p|<ul|<ol|<dl|<li|<\\/tr|<\\/td|<\\/th)/iS',
+                                       $t
+                               );
+                               $closeMatch = preg_match(
+                                       '/(?:<\\/table|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'
+                                               . '<td|<th|<\\/?blockquote|<\\/?div|<hr|<\\/pre|<\\/p|<\\/mw:|'
+                                               . Parser::MARKER_PREFIX
+                                               . '-pre|<\\/li|<\\/ul|<\\/ol|<\\/dl|<\\/?center)/iS',
+                                       $t
+                               );
+
+                               if ( $openMatch || $closeMatch ) {
+                                       $pendingPTag = false;
+                                       # @todo bug 5718: paragraph closed
+                                       $output .= $this->closeParagraph();
+                                       if ( $preOpenMatch && !$preCloseMatch ) {
+                                               $this->inPre = true;
+                                       }
+                                       $bqOffset = 0;
+                                       while ( preg_match( '/<(\\/?)blockquote[\s>]/i', $t,
+                                               $bqMatch, PREG_OFFSET_CAPTURE, $bqOffset )
+                                       ) {
+                                               $inBlockquote = !$bqMatch[1][0]; // is this a close tag?
+                                               $bqOffset = $bqMatch[0][1] + strlen( $bqMatch[0][0] );
+                                       }
+                                       $inBlockElem = !$closeMatch;
+                               } elseif ( !$inBlockElem && !$this->inPre ) {
+                                       if ( ' ' == substr( $t, 0, 1 )
+                                               && ( $this->lastSection === 'pre' || trim( $t ) != '' )
+                                               && !$inBlockquote
+                                       ) {
+                                               # pre
+                                               if ( $this->lastSection !== 'pre' ) {
+                                                       $pendingPTag = false;
+                                                       $output .= $this->closeParagraph() . '<pre>';
+                                                       $this->lastSection = 'pre';
+                                               }
+                                               $t = substr( $t, 1 );
+                                       } else {
+                                               # paragraph
+                                               if ( trim( $t ) === '' ) {
+                                                       if ( $pendingPTag ) {
+                                                               $output .= $pendingPTag . '<br />';
+                                                               $pendingPTag = false;
+                                                               $this->lastSection = 'p';
+                                                       } else {
+                                                               if ( $this->lastSection !== 'p' ) {
+                                                                       $output .= $this->closeParagraph();
+                                                                       $this->lastSection = '';
+                                                                       $pendingPTag = '<p>';
+                                                               } else {
+                                                                       $pendingPTag = '</p><p>';
+                                                               }
+                                                       }
+                                               } else {
+                                                       if ( $pendingPTag ) {
+                                                               $output .= $pendingPTag;
+                                                               $pendingPTag = false;
+                                                               $this->lastSection = 'p';
+                                                       } elseif ( $this->lastSection !== 'p' ) {
+                                                               $output .= $this->closeParagraph() . '<p>';
+                                                               $this->lastSection = 'p';
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       # somewhere above we forget to get out of pre block (bug 785)
+                       if ( $preCloseMatch && $this->inPre ) {
+                               $this->inPre = false;
+                       }
+                       if ( $pendingPTag === false ) {
+                               $output .= $t;
+                               if ( $prefixLength === 0 ) {
+                                       $output .= "\n";
+                               }
+                       }
+               }
+               while ( $prefixLength ) {
+                       $output .= $this->closeList( $prefix2[$prefixLength - 1] );
+                       --$prefixLength;
+                       if ( !$prefixLength ) {
+                               $output .= "\n";
+                       }
+               }
+               if ( $this->lastSection !== '' ) {
+                       $output .= '</' . $this->lastSection . '>';
+                       $this->lastSection = '';
+               }
+
+               return $output;
+       }
+
+       /**
+        * Split up a string on ':', ignoring any occurrences inside tags
+        * to prevent illegal overlapping.
+        *
+        * @param string $str The string to split
+        * @param string &$before Set to everything before the ':'
+        * @param string &$after Set to everything after the ':'
+        * @throws MWException
+        * @return string The position of the ':', or false if none found
+        */
+       private function findColonNoLinks( $str, &$before, &$after ) {
+               $colonPos = strpos( $str, ':' );
+               if ( $colonPos === false ) {
+                       # Nothing to find!
+                       return false;
+               }
+
+               $ltPos = strpos( $str, '<' );
+               if ( $ltPos === false || $ltPos > $colonPos ) {
+                       # Easy; no tag nesting to worry about
+                       $before = substr( $str, 0, $colonPos );
+                       $after = substr( $str, $colonPos + 1 );
+                       return $colonPos;
+               }
+
+               # Ugly state machine to walk through avoiding tags.
+               $state = self::COLON_STATE_TEXT;
+               $level = 0;
+               $len = strlen( $str );
+               for ( $i = 0; $i < $len; $i++ ) {
+                       $c = $str[$i];
+
+                       switch ( $state ) {
+                       case self::COLON_STATE_TEXT:
+                               switch ( $c ) {
+                               case "<":
+                                       # Could be either a <start> tag or an </end> tag
+                                       $state = self::COLON_STATE_TAGSTART;
+                                       break;
+                               case ":":
+                                       if ( $level === 0 ) {
+                                               # We found it!
+                                               $before = substr( $str, 0, $i );
+                                               $after = substr( $str, $i + 1 );
+                                               return $i;
+                                       }
+                                       # Embedded in a tag; don't break it.
+                                       break;
+                               default:
+                                       # Skip ahead looking for something interesting
+                                       $colonPos = strpos( $str, ':', $i );
+                                       if ( $colonPos === false ) {
+                                               # Nothing else interesting
+                                               return false;
+                                       }
+                                       $ltPos = strpos( $str, '<', $i );
+                                       if ( $level === 0 ) {
+                                               if ( $ltPos === false || $colonPos < $ltPos ) {
+                                                       # We found it!
+                                                       $before = substr( $str, 0, $colonPos );
+                                                       $after = substr( $str, $colonPos + 1 );
+                                                       return $i;
+                                               }
+                                       }
+                                       if ( $ltPos === false ) {
+                                               # Nothing else interesting to find; abort!
+                                               # We're nested, but there's no close tags left. Abort!
+                                               break 2;
+                                       }
+                                       # Skip ahead to next tag start
+                                       $i = $ltPos;
+                                       $state = self::COLON_STATE_TAGSTART;
+                               }
+                               break;
+                       case self::COLON_STATE_TAG:
+                               # In a <tag>
+                               switch ( $c ) {
+                               case ">":
+                                       $level++;
+                                       $state = self::COLON_STATE_TEXT;
+                                       break;
+                               case "/":
+                                       # Slash may be followed by >?
+                                       $state = self::COLON_STATE_TAGSLASH;
+                                       break;
+                               default:
+                                       # ignore
+                               }
+                               break;
+                       case self::COLON_STATE_TAGSTART:
+                               switch ( $c ) {
+                               case "/":
+                                       $state = self::COLON_STATE_CLOSETAG;
+                                       break;
+                               case "!":
+                                       $state = self::COLON_STATE_COMMENT;
+                                       break;
+                               case ">":
+                                       # Illegal early close? This shouldn't happen D:
+                                       $state = self::COLON_STATE_TEXT;
+                                       break;
+                               default:
+                                       $state = self::COLON_STATE_TAG;
+                               }
+                               break;
+                       case self::COLON_STATE_CLOSETAG:
+                               # In a </tag>
+                               if ( $c === ">" ) {
+                                       $level--;
+                                       if ( $level < 0 ) {
+                                               wfDebug( __METHOD__ . ": Invalid input; too many close tags\n" );
+                                               return false;
+                                       }
+                                       $state = self::COLON_STATE_TEXT;
+                               }
+                               break;
+                       case self::COLON_STATE_TAGSLASH:
+                               if ( $c === ">" ) {
+                                       # Yes, a self-closed tag <blah/>
+                                       $state = self::COLON_STATE_TEXT;
+                               } else {
+                                       # Probably we're jumping the gun, and this is an attribute
+                                       $state = self::COLON_STATE_TAG;
+                               }
+                               break;
+                       case self::COLON_STATE_COMMENT:
+                               if ( $c === "-" ) {
+                                       $state = self::COLON_STATE_COMMENTDASH;
+                               }
+                               break;
+                       case self::COLON_STATE_COMMENTDASH:
+                               if ( $c === "-" ) {
+                                       $state = self::COLON_STATE_COMMENTDASHDASH;
+                               } else {
+                                       $state = self::COLON_STATE_COMMENT;
+                               }
+                               break;
+                       case self::COLON_STATE_COMMENTDASHDASH:
+                               if ( $c === ">" ) {
+                                       $state = self::COLON_STATE_TEXT;
+                               } else {
+                                       $state = self::COLON_STATE_COMMENT;
+                               }
+                               break;
+                       default:
+                               throw new MWException( "State machine error in " . __METHOD__ );
+                       }
+               }
+               if ( $level > 0 ) {
+                       wfDebug( __METHOD__ . ": Invalid input; not enough close tags (level $level, state $state)\n" );
+                       return false;
+               }
+               return false;
+       }
+}
index 04b5614..bd969a5 100644 (file)
@@ -297,7 +297,9 @@ class LinkHolderArray {
                $linkcolour_ids = [];
 
                # Generate query
-               $queries = [];
+               $lb = new LinkBatch();
+               $lb->setCaller( __METHOD__ );
+
                foreach ( $this->internals as $ns => $entries ) {
                        foreach ( $entries as $entry ) {
                                /** @var Title $title */
@@ -325,23 +327,12 @@ class LinkHolderArray {
                                                $colours[$pdbk] = 'new';
                                        } else {
                                                # Not in the link cache, add it to the query
-                                               $queries[$ns][] = $title->getDBkey();
+                                               $lb->addObj( $title );
                                        }
                                }
                        }
                }
-               if ( $queries ) {
-                       $where = [];
-                       foreach ( $queries as $ns => $pages ) {
-                               $where[] = $dbr->makeList(
-                                       [
-                                               'page_namespace' => $ns,
-                                               'page_title' => array_unique( $pages ),
-                                       ],
-                                       LIST_AND
-                               );
-                       }
-
+               if ( !$lb->isEmpty() ) {
                        $fields = [ 'page_id', 'page_namespace', 'page_title',
                                'page_is_redirect', 'page_len', 'page_latest' ];
 
@@ -355,7 +346,7 @@ class LinkHolderArray {
                        $res = $dbr->select(
                                'page',
                                $fields,
-                               $dbr->makeList( $where, LIST_OR ),
+                               $lb->constructSet( 'page', $dbr ),
                                __METHOD__
                        );
 
@@ -513,9 +504,6 @@ class LinkHolderArray {
                                }
 
                                $variantTitle = Title::makeTitle( $ns, $textVariant );
-                               if ( is_null( $variantTitle ) ) {
-                                       continue;
-                               }
 
                                // Self-link checking for mixed/different variant titles. At this point, we
                                // already know the exact title does not exist, so the link cannot be to a
index a1d62e5..96674be 100644 (file)
@@ -99,16 +99,6 @@ class Parser {
        # Regular expression for a non-newline space
        const SPACE_NOT_NL = '(?:\t|&nbsp;|&\#0*160;|&\#[Xx]0*[Aa]0;|\p{Zs})';
 
-       # State constants for the definition list colon extraction
-       const COLON_STATE_TEXT = 0;
-       const COLON_STATE_TAG = 1;
-       const COLON_STATE_TAGSTART = 2;
-       const COLON_STATE_CLOSETAG = 3;
-       const COLON_STATE_TAGSLASH = 4;
-       const COLON_STATE_COMMENT = 5;
-       const COLON_STATE_COMMENTDASH = 6;
-       const COLON_STATE_COMMENTDASHDASH = 7;
-
        # Flags for preprocessToDom
        const PTD_FOR_INCLUSION = 1;
 
@@ -176,14 +166,14 @@ class Parser {
         * @var ParserOutput
         */
        public $mOutput;
-       public $mAutonumber, $mDTopen;
+       public $mAutonumber;
 
        /**
         * @var StripState
         */
        public $mStripState;
 
-       public $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
+       public $mIncludeCount;
        /**
         * @var LinkHolderArray
         */
@@ -342,11 +332,7 @@ class Parser {
                $this->mOutput = new ParserOutput;
                $this->mOptions->registerWatcher( [ $this->mOutput, 'recordOption' ] );
                $this->mAutonumber = 0;
-               $this->mLastSection = '';
-               $this->mDTopen = false;
                $this->mIncludeCount = [];
-               $this->mArgStack = false;
-               $this->mInPre = false;
                $this->mLinkHolders = new LinkHolderArray( $this );
                $this->mLinkID = 0;
                $this->mRevisionObject = $this->mRevisionTimestamp =
@@ -2401,127 +2387,6 @@ class Parser {
                return Linker::normalizeSubpageLink( $this->mTitle, $target, $text );
        }
 
-       /**#@+
-        * Used by doBlockLevels()
-        * @private
-        *
-        * @return string
-        */
-       public function closeParagraph() {
-               $result = '';
-               if ( $this->mLastSection != '' ) {
-                       $result = '</' . $this->mLastSection . ">\n";
-               }
-               $this->mInPre = false;
-               $this->mLastSection = '';
-               return $result;
-       }
-
-       /**
-        * getCommon() returns the length of the longest common substring
-        * of both arguments, starting at the beginning of both.
-        * @private
-        *
-        * @param string $st1
-        * @param string $st2
-        *
-        * @return int
-        */
-       public function getCommon( $st1, $st2 ) {
-               $fl = strlen( $st1 );
-               $shorter = strlen( $st2 );
-               if ( $fl < $shorter ) {
-                       $shorter = $fl;
-               }
-
-               for ( $i = 0; $i < $shorter; ++$i ) {
-                       if ( $st1[$i] != $st2[$i] ) {
-                               break;
-                       }
-               }
-               return $i;
-       }
-
-       /**
-        * These next three functions open, continue, and close the list
-        * element appropriate to the prefix character passed into them.
-        * @private
-        *
-        * @param string $char
-        *
-        * @return string
-        */
-       public function openList( $char ) {
-               $result = $this->closeParagraph();
-
-               if ( '*' === $char ) {
-                       $result .= "<ul><li>";
-               } elseif ( '#' === $char ) {
-                       $result .= "<ol><li>";
-               } elseif ( ':' === $char ) {
-                       $result .= "<dl><dd>";
-               } elseif ( ';' === $char ) {
-                       $result .= "<dl><dt>";
-                       $this->mDTopen = true;
-               } else {
-                       $result = '<!-- ERR 1 -->';
-               }
-
-               return $result;
-       }
-
-       /**
-        * TODO: document
-        * @param string $char
-        * @private
-        *
-        * @return string
-        */
-       public function nextItem( $char ) {
-               if ( '*' === $char || '#' === $char ) {
-                       return "</li>\n<li>";
-               } elseif ( ':' === $char || ';' === $char ) {
-                       $close = "</dd>\n";
-                       if ( $this->mDTopen ) {
-                               $close = "</dt>\n";
-                       }
-                       if ( ';' === $char ) {
-                               $this->mDTopen = true;
-                               return $close . '<dt>';
-                       } else {
-                               $this->mDTopen = false;
-                               return $close . '<dd>';
-                       }
-               }
-               return '<!-- ERR 2 -->';
-       }
-
-       /**
-        * @todo Document
-        * @param string $char
-        * @private
-        *
-        * @return string
-        */
-       public function closeList( $char ) {
-               if ( '*' === $char ) {
-                       $text = "</li></ul>";
-               } elseif ( '#' === $char ) {
-                       $text = "</li></ol>";
-               } elseif ( ':' === $char ) {
-                       if ( $this->mDTopen ) {
-                               $this->mDTopen = false;
-                               $text = "</dt></dl>";
-                       } else {
-                               $text = "</dd></dl>";
-                       }
-               } else {
-                       return '<!-- ERR 3 -->';
-               }
-               return $text;
-       }
-       /**#@-*/
-
        /**
         * Make lists from lines starting with ':', '*', '#', etc. (DBL)
         *
@@ -2531,365 +2396,7 @@ class Parser {
         * @return string The lists rendered as HTML
         */
        public function doBlockLevels( $text, $linestart ) {
-
-               # Parsing through the text line by line.  The main thing
-               # happening here is handling of block-level elements p, pre,
-               # and making lists from lines starting with * # : etc.
-               $textLines = StringUtils::explode( "\n", $text );
-
-               $lastPrefix = $output = '';
-               $this->mDTopen = $inBlockElem = false;
-               $prefixLength = 0;
-               $paragraphStack = false;
-               $inBlockquote = false;
-
-               foreach ( $textLines as $oLine ) {
-                       # Fix up $linestart
-                       if ( !$linestart ) {
-                               $output .= $oLine;
-                               $linestart = true;
-                               continue;
-                       }
-                       # * = ul
-                       # # = ol
-                       # ; = dt
-                       # : = dd
-
-                       $lastPrefixLength = strlen( $lastPrefix );
-                       $preCloseMatch = preg_match( '/<\\/pre/i', $oLine );
-                       $preOpenMatch = preg_match( '/<pre/i', $oLine );
-                       # If not in a <pre> element, scan for and figure out what prefixes are there.
-                       if ( !$this->mInPre ) {
-                               # Multiple prefixes may abut each other for nested lists.
-                               $prefixLength = strspn( $oLine, '*#:;' );
-                               $prefix = substr( $oLine, 0, $prefixLength );
-
-                               # eh?
-                               # ; and : are both from definition-lists, so they're equivalent
-                               #  for the purposes of determining whether or not we need to open/close
-                               #  elements.
-                               $prefix2 = str_replace( ';', ':', $prefix );
-                               $t = substr( $oLine, $prefixLength );
-                               $this->mInPre = (bool)$preOpenMatch;
-                       } else {
-                               # Don't interpret any other prefixes in preformatted text
-                               $prefixLength = 0;
-                               $prefix = $prefix2 = '';
-                               $t = $oLine;
-                       }
-
-                       # List generation
-                       if ( $prefixLength && $lastPrefix === $prefix2 ) {
-                               # Same as the last item, so no need to deal with nesting or opening stuff
-                               $output .= $this->nextItem( substr( $prefix, -1 ) );
-                               $paragraphStack = false;
-
-                               if ( substr( $prefix, -1 ) === ';' ) {
-                                       # The one nasty exception: definition lists work like this:
-                                       # ; title : definition text
-                                       # So we check for : in the remainder text to split up the
-                                       # title and definition, without b0rking links.
-                                       $term = $t2 = '';
-                                       if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
-                                               $t = $t2;
-                                               $output .= $term . $this->nextItem( ':' );
-                                       }
-                               }
-                       } elseif ( $prefixLength || $lastPrefixLength ) {
-                               # We need to open or close prefixes, or both.
-
-                               # Either open or close a level...
-                               $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix );
-                               $paragraphStack = false;
-
-                               # Close all the prefixes which aren't shared.
-                               while ( $commonPrefixLength < $lastPrefixLength ) {
-                                       $output .= $this->closeList( $lastPrefix[$lastPrefixLength - 1] );
-                                       --$lastPrefixLength;
-                               }
-
-                               # Continue the current prefix if appropriate.
-                               if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
-                                       $output .= $this->nextItem( $prefix[$commonPrefixLength - 1] );
-                               }
-
-                               # Open prefixes where appropriate.
-                               if ( $lastPrefix && $prefixLength > $commonPrefixLength ) {
-                                       $output .= "\n";
-                               }
-                               while ( $prefixLength > $commonPrefixLength ) {
-                                       $char = substr( $prefix, $commonPrefixLength, 1 );
-                                       $output .= $this->openList( $char );
-
-                                       if ( ';' === $char ) {
-                                               # @todo FIXME: This is dupe of code above
-                                               if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
-                                                       $t = $t2;
-                                                       $output .= $term . $this->nextItem( ':' );
-                                               }
-                                       }
-                                       ++$commonPrefixLength;
-                               }
-                               if ( !$prefixLength && $lastPrefix ) {
-                                       $output .= "\n";
-                               }
-                               $lastPrefix = $prefix2;
-                       }
-
-                       # If we have no prefixes, go to paragraph mode.
-                       if ( 0 == $prefixLength ) {
-                               # No prefix (not in list)--go to paragraph mode
-                               # XXX: use a stack for nestable elements like span, table and div
-                               $openmatch = preg_match(
-                                       '/(?:<table|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|'
-                                               . '<p|<ul|<ol|<dl|<li|<\\/tr|<\\/td|<\\/th)/iS',
-                                       $t
-                               );
-                               $closematch = preg_match(
-                                       '/(?:<\\/table|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'
-                                               . '<td|<th|<\\/?blockquote|<\\/?div|<hr|<\\/pre|<\\/p|<\\/mw:|'
-                                               . self::MARKER_PREFIX
-                                               . '-pre|<\\/li|<\\/ul|<\\/ol|<\\/dl|<\\/?center)/iS',
-                                       $t
-                               );
-
-                               if ( $openmatch || $closematch ) {
-                                       $paragraphStack = false;
-                                       # @todo bug 5718: paragraph closed
-                                       $output .= $this->closeParagraph();
-                                       if ( $preOpenMatch && !$preCloseMatch ) {
-                                               $this->mInPre = true;
-                                       }
-                                       $bqOffset = 0;
-                                       while ( preg_match( '/<(\\/?)blockquote[\s>]/i', $t,
-                                               $bqMatch, PREG_OFFSET_CAPTURE, $bqOffset )
-                                       ) {
-                                               $inBlockquote = !$bqMatch[1][0]; // is this a close tag?
-                                               $bqOffset = $bqMatch[0][1] + strlen( $bqMatch[0][0] );
-                                       }
-                                       $inBlockElem = !$closematch;
-                               } elseif ( !$inBlockElem && !$this->mInPre ) {
-                                       if ( ' ' == substr( $t, 0, 1 )
-                                               && ( $this->mLastSection === 'pre' || trim( $t ) != '' )
-                                               && !$inBlockquote
-                                       ) {
-                                               # pre
-                                               if ( $this->mLastSection !== 'pre' ) {
-                                                       $paragraphStack = false;
-                                                       $output .= $this->closeParagraph() . '<pre>';
-                                                       $this->mLastSection = 'pre';
-                                               }
-                                               $t = substr( $t, 1 );
-                                       } else {
-                                               # paragraph
-                                               if ( trim( $t ) === '' ) {
-                                                       if ( $paragraphStack ) {
-                                                               $output .= $paragraphStack . '<br />';
-                                                               $paragraphStack = false;
-                                                               $this->mLastSection = 'p';
-                                                       } else {
-                                                               if ( $this->mLastSection !== 'p' ) {
-                                                                       $output .= $this->closeParagraph();
-                                                                       $this->mLastSection = '';
-                                                                       $paragraphStack = '<p>';
-                                                               } else {
-                                                                       $paragraphStack = '</p><p>';
-                                                               }
-                                                       }
-                                               } else {
-                                                       if ( $paragraphStack ) {
-                                                               $output .= $paragraphStack;
-                                                               $paragraphStack = false;
-                                                               $this->mLastSection = 'p';
-                                                       } elseif ( $this->mLastSection !== 'p' ) {
-                                                               $output .= $this->closeParagraph() . '<p>';
-                                                               $this->mLastSection = 'p';
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-                       # somewhere above we forget to get out of pre block (bug 785)
-                       if ( $preCloseMatch && $this->mInPre ) {
-                               $this->mInPre = false;
-                       }
-                       if ( $paragraphStack === false ) {
-                               $output .= $t;
-                               if ( $prefixLength === 0 ) {
-                                       $output .= "\n";
-                               }
-                       }
-               }
-               while ( $prefixLength ) {
-                       $output .= $this->closeList( $prefix2[$prefixLength - 1] );
-                       --$prefixLength;
-                       if ( !$prefixLength ) {
-                               $output .= "\n";
-                       }
-               }
-               if ( $this->mLastSection != '' ) {
-                       $output .= '</' . $this->mLastSection . '>';
-                       $this->mLastSection = '';
-               }
-
-               return $output;
-       }
-
-       /**
-        * Split up a string on ':', ignoring any occurrences inside tags
-        * to prevent illegal overlapping.
-        *
-        * @param string $str The string to split
-        * @param string &$before Set to everything before the ':'
-        * @param string &$after Set to everything after the ':'
-        * @throws MWException
-        * @return string The position of the ':', or false if none found
-        */
-       public function findColonNoLinks( $str, &$before, &$after ) {
-
-               $pos = strpos( $str, ':' );
-               if ( $pos === false ) {
-                       # Nothing to find!
-                       return false;
-               }
-
-               $lt = strpos( $str, '<' );
-               if ( $lt === false || $lt > $pos ) {
-                       # Easy; no tag nesting to worry about
-                       $before = substr( $str, 0, $pos );
-                       $after = substr( $str, $pos + 1 );
-                       return $pos;
-               }
-
-               # Ugly state machine to walk through avoiding tags.
-               $state = self::COLON_STATE_TEXT;
-               $stack = 0;
-               $len = strlen( $str );
-               for ( $i = 0; $i < $len; $i++ ) {
-                       $c = $str[$i];
-
-                       switch ( $state ) {
-                       # (Using the number is a performance hack for common cases)
-                       case 0: # self::COLON_STATE_TEXT:
-                               switch ( $c ) {
-                               case "<":
-                                       # Could be either a <start> tag or an </end> tag
-                                       $state = self::COLON_STATE_TAGSTART;
-                                       break;
-                               case ":":
-                                       if ( $stack == 0 ) {
-                                               # We found it!
-                                               $before = substr( $str, 0, $i );
-                                               $after = substr( $str, $i + 1 );
-                                               return $i;
-                                       }
-                                       # Embedded in a tag; don't break it.
-                                       break;
-                               default:
-                                       # Skip ahead looking for something interesting
-                                       $colon = strpos( $str, ':', $i );
-                                       if ( $colon === false ) {
-                                               # Nothing else interesting
-                                               return false;
-                                       }
-                                       $lt = strpos( $str, '<', $i );
-                                       if ( $stack === 0 ) {
-                                               if ( $lt === false || $colon < $lt ) {
-                                                       # We found it!
-                                                       $before = substr( $str, 0, $colon );
-                                                       $after = substr( $str, $colon + 1 );
-                                                       return $i;
-                                               }
-                                       }
-                                       if ( $lt === false ) {
-                                               # Nothing else interesting to find; abort!
-                                               # We're nested, but there's no close tags left. Abort!
-                                               break 2;
-                                       }
-                                       # Skip ahead to next tag start
-                                       $i = $lt;
-                                       $state = self::COLON_STATE_TAGSTART;
-                               }
-                               break;
-                       case 1: # self::COLON_STATE_TAG:
-                               # In a <tag>
-                               switch ( $c ) {
-                               case ">":
-                                       $stack++;
-                                       $state = self::COLON_STATE_TEXT;
-                                       break;
-                               case "/":
-                                       # Slash may be followed by >?
-                                       $state = self::COLON_STATE_TAGSLASH;
-                                       break;
-                               default:
-                                       # ignore
-                               }
-                               break;
-                       case 2: # self::COLON_STATE_TAGSTART:
-                               switch ( $c ) {
-                               case "/":
-                                       $state = self::COLON_STATE_CLOSETAG;
-                                       break;
-                               case "!":
-                                       $state = self::COLON_STATE_COMMENT;
-                                       break;
-                               case ">":
-                                       # Illegal early close? This shouldn't happen D:
-                                       $state = self::COLON_STATE_TEXT;
-                                       break;
-                               default:
-                                       $state = self::COLON_STATE_TAG;
-                               }
-                               break;
-                       case 3: # self::COLON_STATE_CLOSETAG:
-                               # In a </tag>
-                               if ( $c === ">" ) {
-                                       $stack--;
-                                       if ( $stack < 0 ) {
-                                               wfDebug( __METHOD__ . ": Invalid input; too many close tags\n" );
-                                               return false;
-                                       }
-                                       $state = self::COLON_STATE_TEXT;
-                               }
-                               break;
-                       case self::COLON_STATE_TAGSLASH:
-                               if ( $c === ">" ) {
-                                       # Yes, a self-closed tag <blah/>
-                                       $state = self::COLON_STATE_TEXT;
-                               } else {
-                                       # Probably we're jumping the gun, and this is an attribute
-                                       $state = self::COLON_STATE_TAG;
-                               }
-                               break;
-                       case 5: # self::COLON_STATE_COMMENT:
-                               if ( $c === "-" ) {
-                                       $state = self::COLON_STATE_COMMENTDASH;
-                               }
-                               break;
-                       case self::COLON_STATE_COMMENTDASH:
-                               if ( $c === "-" ) {
-                                       $state = self::COLON_STATE_COMMENTDASHDASH;
-                               } else {
-                                       $state = self::COLON_STATE_COMMENT;
-                               }
-                               break;
-                       case self::COLON_STATE_COMMENTDASHDASH:
-                               if ( $c === ">" ) {
-                                       $state = self::COLON_STATE_TEXT;
-                               } else {
-                                       $state = self::COLON_STATE_COMMENT;
-                               }
-                               break;
-                       default:
-                               throw new MWException( "State machine error in " . __METHOD__ );
-                       }
-               }
-               if ( $stack > 0 ) {
-                       wfDebug( __METHOD__ . ": Invalid input; not enough close tags (stack $stack, state $state)\n" );
-                       return false;
-               }
-               return false;
+               return BlockLevelPass::doBlockLevels( $text, $linestart );
        }
 
        /**
index acdbd81..bd7072a 100644 (file)
@@ -81,7 +81,7 @@ abstract class PoolCounter {
 
        /**
         * @param array $conf
-        * @param string $type
+        * @param string $type The class of actions to limit concurrency for (task type)
         * @param string $key
         */
        protected function __construct( $conf, $type, $key ) {
@@ -93,8 +93,9 @@ abstract class PoolCounter {
                }
 
                if ( $this->slots ) {
-                       $key = $this->hashKeyIntoSlots( $key, $this->slots );
+                       $key = $this->hashKeyIntoSlots( $type, $key, $this->slots );
                }
+
                $this->key = $key;
                $this->isMightWaitKey = !preg_match( '/^nowait:/', $this->key );
        }
@@ -102,7 +103,7 @@ abstract class PoolCounter {
        /**
         * Create a Pool counter. This should only be called from the PoolWorks.
         *
-        * @param string $type
+        * @param string $type The class of actions to limit concurrency for (task type)
         * @param string $key
         *
         * @return PoolCounter
@@ -192,18 +193,19 @@ abstract class PoolCounter {
        }
 
        /**
-        * Given a key (any string) and the number of lots, returns a slot number (an integer from
-        * the [0..($slots-1)] range). This is used for a global limit on the number of instances of
-        * a given type that can acquire a lock. The hashing is deterministic so that
+        * Given a key (any string) and the number of lots, returns a slot key (a prefix with a suffix
+        * integer from the [0..($slots-1)] range). This is used for a global limit on the number of
+        * instances of a given type that can acquire a lock. The hashing is deterministic so that
         * PoolCounter::$workers is always an upper limit of how many instances with the same key
         * can acquire a lock.
         *
+        * @param string $type The class of actions to limit concurrency for (task type)
         * @param string $key PoolCounter instance key (any string)
         * @param int $slots The number of slots (max allowed value is 65536)
-        * @return int
+        * @return string Slot key with the type and slot number
         */
-       protected function hashKeyIntoSlots( $key, $slots ) {
-               return hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots;
+       protected function hashKeyIntoSlots( $type, $key, $slots ) {
+               return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots );
        }
 }
 
index e61b65a..a570d78 100644 (file)
@@ -31,7 +31,7 @@ abstract class PoolCounterWork {
        protected $cacheable = false; // does this override getCachedWork() ?
 
        /**
-        * @param string $type The type of PoolCounter to use
+        * @param string $type The class of actions to limit concurrency for (task type)
         * @param string $key Key that identifies the queue this work is placed on
         */
        public function __construct( $type, $key ) {
index 85a7cef..834b8b1 100644 (file)
@@ -44,7 +44,7 @@ class PoolCounterWorkViaCallback extends PoolCounterWork {
         * If a 'doCachedWork' callback is provided, then execute() may wait for any prior
         * process in the pool to finish and reuse its cached result.
         *
-        * @param string $type
+        * @param string $type The class of actions to limit concurrency for
         * @param string $key
         * @param array $callbacks Map of callbacks
         * @throws MWException
index 7c4fde4..8fc0b77 100644 (file)
@@ -52,9 +52,9 @@
  */
 class ProfilerXhprof extends Profiler {
        /**
-        * @var Xhprof $xhprof
+        * @var XhprofData|null $xhprofData
         */
-       protected $xhprof;
+       protected $xhprofData;
 
        /**
         * Profiler for explicit, arbitrary, frame labels
@@ -68,10 +68,24 @@ class ProfilerXhprof extends Profiler {
         */
        public function __construct( array $params = [] ) {
                parent::__construct( $params );
-               $this->xhprof = new Xhprof( $params );
+
+               $flags = isset( $params['flags'] ) ? $params['flags'] : 0;
+               $options = isset( $params['exclude'] )
+                       ? [ 'ignored_functions' => $params['exclude'] ] : [];
+               Xhprof::enable( $flags, $options );
                $this->sprofiler = new SectionProfiler();
        }
 
+       /**
+        * @return XhprofData
+        */
+       public function getXhprofData() {
+               if ( !$this->xhprofData ) {
+                       $this->xhprofData = new XhprofData( Xhprof::disable(), $this->params );
+               }
+               return $this->xhprofData;
+       }
+
        public function scopedProfileIn( $section ) {
                $key = 'section.' . ltrim( $section, '.' );
                return $this->sprofiler->scopedProfileIn( $key );
@@ -112,7 +126,7 @@ class ProfilerXhprof extends Profiler {
        }
 
        public function getFunctionStats() {
-               $metrics = $this->xhprof->getCompleteMetrics();
+               $metrics = $this->getXhprofData()->getCompleteMetrics();
                $profile = [];
 
                $main = null; // units in ms
@@ -216,6 +230,6 @@ class ProfilerXhprof extends Profiler {
         * @return array
         */
        public function getRawData() {
-               return $this->xhprof->getRawData();
+               return $this->getXhprofData()->getRawData();
        }
 }
index 415e664..26058c9 100644 (file)
@@ -23,6 +23,7 @@ class ExtensionProcessor implements Processor {
                'AvailableRights',
                'ContentHandlers',
                'ConfigRegistry',
+               'SessionProviders',
                'CentralIdLookupProviders',
                'RateLimits',
                'RecentChangesFlags',
index 8e0239a..85fc53d 100644 (file)
@@ -227,15 +227,17 @@ class ResourceLoaderContext {
         * Get the possibly-cached User object for the specified username
         *
         * @since 1.25
-        * @return User|bool false if a valid object cannot be created
+        * @return User
         */
        public function getUserObj() {
                if ( $this->userObj === null ) {
                        $username = $this->getUser();
                        if ( $username ) {
-                               $this->userObj = User::newFromName( $username );
+                               // Use provided username if valid, fallback to anonymous user
+                               $this->userObj = User::newFromName( $username ) ?: new User;
                        } else {
-                               $this->userObj = new User; // Anonymous user
+                               // Anonymous user
+                               $this->userObj = new User;
                        }
                }
 
index 13f13e6..121a6c9 100644 (file)
@@ -114,16 +114,6 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                return $this->origin;
        }
 
-       /**
-        * Set this module's origin. This is called by ResourceLoader::register()
-        * when registering the module. Other code should not call this.
-        *
-        * @param int $origin Origin
-        */
-       public function setOrigin( $origin ) {
-               $this->origin = $origin;
-       }
-
        /**
         * @param ResourceLoaderContext $context
         * @return bool
index e2a8e41..b225185 100644 (file)
@@ -40,7 +40,7 @@ class ResourceLoaderUserGroupsModule extends ResourceLoaderWikiModule {
                }
 
                $user = $context->getUserObj();
-               if ( !$user || $user->isAnon() ) {
+               if ( $user->isAnon() ) {
                        return [];
                }
 
index d584165..c38f8d8 100644 (file)
@@ -43,7 +43,7 @@ class ResourceLoaderUserModule extends ResourceLoaderWikiModule {
                }
 
                $user = $context->getUserObj();
-               if ( !$user || $user->isAnon() ) {
+               if ( $user->isAnon() ) {
                        return [];
                }
 
index 1b5a834..c235861 100644 (file)
@@ -54,6 +54,7 @@ class SessionInfo {
        private $remembered = false;
        private $forceHTTPS = false;
        private $idIsSafe = false;
+       private $forceUse = false;
 
        /** @var array|null */
        private $providerMetadata = null;
@@ -76,6 +77,10 @@ class SessionInfo {
         *  - idIsSafe: (bool) Set true if the 'id' did not come from the user.
         *    Generally you'll use this from SessionProvider::newEmptySession(),
         *    and not from any other method.
+        *  - forceUse: (bool) Set true if the 'id' is from
+        *    SessionProvider::hashToSessionId() to delete conflicting session
+        *    store data instead of discarding this SessionInfo. Ignored unless
+        *    both 'provider' and 'id' are given.
         *  - copyFrom: (SessionInfo) SessionInfo to copy other data items from.
         */
        public function __construct( $priority, array $data ) {
@@ -97,6 +102,7 @@ class SessionInfo {
                                'forceHTTPS' => $from->forceHTTPS,
                                'metadata' => $from->providerMetadata,
                                'idIsSafe' => $from->idIsSafe,
+                               'forceUse' => $from->forceUse,
                                // @codeCoverageIgnoreStart
                        ];
                        // @codeCoverageIgnoreEnd
@@ -110,6 +116,7 @@ class SessionInfo {
                                'forceHTTPS' => false,
                                'metadata' => null,
                                'idIsSafe' => false,
+                               'forceUse' => false,
                                // @codeCoverageIgnoreStart
                        ];
                        // @codeCoverageIgnoreEnd
@@ -137,9 +144,11 @@ class SessionInfo {
                if ( $data['id'] !== null ) {
                        $this->id = $data['id'];
                        $this->idIsSafe = $data['idIsSafe'];
+                       $this->forceUse = $data['forceUse'] && $this->provider;
                } else {
                        $this->id = $this->provider->getManager()->generateSessionId();
                        $this->idIsSafe = true;
+                       $this->forceUse = false;
                }
                $this->priority = (int)$priority;
                $this->userInfo = $data['userInfo'];
@@ -185,6 +194,20 @@ class SessionInfo {
                return $this->idIsSafe;
        }
 
+       /**
+        * Force use of this SessionInfo if validation fails
+        *
+        * The normal behavior is to discard the SessionInfo if validation against
+        * the data stored in the session store fails. If this returns true,
+        * SessionManager will instead delete the session store data so this
+        * SessionInfo may still be used.
+        *
+        * @return bool
+        */
+       final public function forceUse() {
+               return $this->forceUse;
+       }
+
        /**
         * Return the priority
         * @return int
index a364045..777d3d6 100644 (file)
@@ -301,6 +301,19 @@ final class SessionManager implements SessionManagerInterface {
                return $this->getSessionFromInfo( $infos[0], $request );
        }
 
+       public function invalidateSessionsForUser( User $user ) {
+               global $wgAuth;
+
+               $user->setToken();
+               $user->saveSettings();
+
+               $wgAuth->getUserInstance( $user )->resetAuthToken();
+
+               foreach ( $this->getProviders() as $provider ) {
+                       $provider->invalidateSessionsForUser( $user );
+               }
+       }
+
        public function getVaryHeaders() {
                // @codeCoverageIgnoreStart
                if ( defined( 'MW_NO_SESSION' ) && MW_NO_SESSION !== 'warn' ) {
@@ -704,6 +717,20 @@ final class SessionManager implements SessionManagerInterface {
                $key = wfMemcKey( 'MWSession', $info->getId() );
                $blob = $this->store->get( $key );
 
+               // If we got data from the store and the SessionInfo says to force use,
+               // "fail" means to delete the data from the store and retry. Otherwise,
+               // "fail" is just return false.
+               if ( $info->forceUse() && $blob !== false ) {
+                       $failHandler = function () use ( $key, &$info, $request ) {
+                               $this->store->delete( $key );
+                               return $this->loadSessionInfoFromStore( $info, $request );
+                       };
+               } else {
+                       $failHandler = function () {
+                               return false;
+                       };
+               }
+
                $newParams = [];
 
                if ( $blob !== false ) {
@@ -713,7 +740,7 @@ final class SessionManager implements SessionManagerInterface {
                                        'session' => $info,
                                ] );
                                $this->store->delete( $key );
-                               return false;
+                               return $failHandler();
                        }
 
                        // Sanity check: blob has data and metadata arrays
@@ -724,7 +751,7 @@ final class SessionManager implements SessionManagerInterface {
                                        'session' => $info,
                                ] );
                                $this->store->delete( $key );
-                               return false;
+                               return $failHandler();
                        }
 
                        $data = $blob['data'];
@@ -741,7 +768,7 @@ final class SessionManager implements SessionManagerInterface {
                                        'session' => $info,
                                ] );
                                $this->store->delete( $key );
-                               return false;
+                               return $failHandler();
                        }
 
                        // First, load the provider from metadata, or validate it against the metadata.
@@ -756,7 +783,7 @@ final class SessionManager implements SessionManagerInterface {
                                                ]
                                        );
                                        $this->store->delete( $key );
-                                       return false;
+                                       return $failHandler();
                                }
                        } elseif ( $metadata['provider'] !== (string)$provider ) {
                                $this->logger->warning( 'Session "{session}": Wrong provider ' .
@@ -764,7 +791,7 @@ final class SessionManager implements SessionManagerInterface {
                                        [
                                                'session' => $info,
                                ] );
-                               return false;
+                               return $failHandler();
                        }
 
                        // Load provider metadata from metadata, or validate it against the metadata
@@ -788,7 +815,7 @@ final class SessionManager implements SessionManagerInterface {
                                                                'exception' => $ex,
                                                        ] + $ex->getContext()
                                                );
-                                               return false;
+                                               return $failHandler();
                                        }
                                }
                        }
@@ -810,7 +837,7 @@ final class SessionManager implements SessionManagerInterface {
                                                'session' => $info,
                                                'exception' => $ex,
                                        ] );
-                                       return false;
+                                       return $failHandler();
                                }
                                $newParams['userInfo'] = $userInfo;
                        } else {
@@ -825,7 +852,7 @@ final class SessionManager implements SessionManagerInterface {
                                                                'uid_a' => $metadata['userId'],
                                                                'uid_b' => $userInfo->getId(),
                                                ] );
-                                               return false;
+                                               return $failHandler();
                                        }
 
                                        // If the user was renamed, probably best to fail here.
@@ -839,7 +866,7 @@ final class SessionManager implements SessionManagerInterface {
                                                                'uname_a' => $metadata['userName'],
                                                                'uname_b' => $userInfo->getName(),
                                                ] );
-                                               return false;
+                                               return $failHandler();
                                        }
 
                                } elseif ( $metadata['userName'] !== null ) { // Shouldn't happen, but just in case
@@ -851,7 +878,7 @@ final class SessionManager implements SessionManagerInterface {
                                                                'uname_a' => $metadata['userName'],
                                                                'uname_b' => $userInfo->getName(),
                                                ] );
-                                               return false;
+                                               return $failHandler();
                                        }
                                } elseif ( !$userInfo->isAnon() ) {
                                        // Metadata specifies an anonymous user, but the passed-in
@@ -861,7 +888,7 @@ final class SessionManager implements SessionManagerInterface {
                                                [
                                                        'session' => $info,
                                        ] );
-                                       return false;
+                                       return $failHandler();
                                }
                        }
 
@@ -872,7 +899,7 @@ final class SessionManager implements SessionManagerInterface {
                                $this->logger->warning( 'Session "{session}": User token mismatch', [
                                        'session' => $info,
                                ] );
-                               return false;
+                               return $failHandler();
                        }
                        if ( !$userInfo->isVerified() ) {
                                $newParams['userInfo'] = $userInfo->verified();
@@ -899,7 +926,7 @@ final class SessionManager implements SessionManagerInterface {
                                        [
                                                'session' => $info,
                                ] );
-                               return false;
+                               return $failHandler();
                        }
 
                        // If no user was provided and no metadata, it must be anon.
@@ -912,7 +939,7 @@ final class SessionManager implements SessionManagerInterface {
                                                [
                                                        'session' => $info,
                                        ] );
-                                       return false;
+                                       return $failHandler();
                                }
                        } elseif ( !$info->getUserInfo()->isVerified() ) {
                                $this->logger->warning(
@@ -920,7 +947,7 @@ final class SessionManager implements SessionManagerInterface {
                                        [
                                                'session' => $info,
                                ] );
-                               return false;
+                               return $failHandler();
                        }
 
                        $data = false;
@@ -942,7 +969,7 @@ final class SessionManager implements SessionManagerInterface {
                // Allow the provider to check the loaded SessionInfo
                $providerMetadata = $info->getProviderMetadata();
                if ( !$info->getProvider()->refreshSessionInfo( $info, $request, $providerMetadata ) ) {
-                       return false;
+                       return $failHandler();
                }
                if ( $providerMetadata !== $info->getProviderMetadata() ) {
                        $info = new SessionInfo( $info->getPriority(), [
@@ -962,7 +989,7 @@ final class SessionManager implements SessionManagerInterface {
                        $this->logger->warning( 'Session "{session}": ' . $reason, [
                                'session' => $info,
                        ] );
-                       return false;
+                       return $failHandler();
                }
 
                return true;
@@ -1074,7 +1101,7 @@ final class SessionManager implements SessionManagerInterface {
         */
        public function generateSessionId() {
                do {
-                       $id = wfBaseConvert( \MWCryptRand::generateHex( 40 ), 16, 32, 32 );
+                       $id = \Wikimedia\base_convert( \MWCryptRand::generateHex( 40 ), 16, 32, 32 );
                        $key = wfMemcKey( 'MWSession', $id );
                } while ( isset( $this->allSessionIds[$id] ) || is_array( $this->store->get( $key ) ) );
                return $id;
index b3e28fe..d4e52c7 100644 (file)
@@ -24,6 +24,7 @@
 namespace MediaWiki\Session;
 
 use Psr\Log\LoggerAwareInterface;
+use User;
 use WebRequest;
 
 /**
@@ -72,6 +73,17 @@ interface SessionManagerInterface extends LoggerAwareInterface {
         */
        public function getEmptySession( WebRequest $request = null );
 
+       /**
+        * Invalidate sessions for a user
+        *
+        * After calling this, existing sessions should be invalid. For mutable
+        * session providers, this generally means the user has to log in again;
+        * for immutable providers, it generally means the loss of session data.
+        *
+        * @param User $user
+        */
+       public function invalidateSessionsForUser( User $user );
+
        /**
         * Return the HTTP headers that need varying on.
         *
index 8ee1272..ed113b7 100644 (file)
@@ -27,6 +27,7 @@ use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 use Config;
 use Language;
+use User;
 use WebRequest;
 
 /**
@@ -358,6 +359,19 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
                }
        }
 
+       /**
+        * Invalidate existing sessions for a user
+        *
+        * If the provider has its own equivalent of CookieSessionProvider's Token
+        * cookie (and doesn't use User::getToken() to implement it), it should
+        * reset whatever token it does use here.
+        *
+        * @protected For use by \MediaWiki\Session\SessionManager only
+        * @param User $user;
+        */
+       public function invalidateSessionsForUser( User $user ) {
+       }
+
        /**
         * Return the HTTP headers that need varying on.
         *
@@ -458,7 +472,9 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
         *
         * Generally this will only be used when self::persistsSessionId() is false and
         * the provider has to base the session ID on the verified user's identity
-        * or other static data.
+        * or other static data. The SessionInfo should then typically have the
+        * 'forceUse' flag set to avoid persistent session failure if validation of
+        * the stored data fails.
         *
         * @param string $data
         * @param string|null $key Defaults to $this->config->get( 'SecretKey' )
@@ -484,7 +500,7 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
                        // @codeCoverageIgnoreEnd
                }
                if ( strlen( $hash ) >= 40 ) {
-                       $hash = wfBaseConvert( $hash, 16, 32, 32 );
+                       $hash = \Wikimedia\base_convert( $hash, 16, 32, 32 );
                }
                return substr( $hash, -32 );
        }
index 725c4fc..4c869f9 100644 (file)
@@ -86,6 +86,7 @@ class SpecialPageFactory {
                'CreateAccount' => 'SpecialCreateAccount',
 
                // Users and rights
+               'Activeusers' => 'SpecialActiveUsers',
                'Block' => 'SpecialBlock',
                'Unblock' => 'SpecialUnblock',
                'BlockList' => 'SpecialBlockList',
@@ -254,8 +255,6 @@ class SpecialPageFactory {
                                self::$list['ChangeContentModel'] = 'SpecialChangeContentModel';
                        }
 
-                       self::$list['Activeusers'] = 'SpecialActiveUsers';
-
                        // Add extension special pages
                        self::$list = array_merge( self::$list, $wgSpecialPages );
 
@@ -539,6 +538,7 @@ class SpecialPageFactory {
                        $trxProfiler = Profiler::instance()->getTransactionProfiler();
                        if ( $context->getRequest()->wasPosted() && !$page->doesWrites() ) {
                                $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
+                               $context->getRequest()->markAsSafeRequest();
                        }
                }
 
index d6d4500..c697ca7 100644 (file)
@@ -24,6 +24,8 @@
  */
 
 /**
+ * Implements Special:Activeusers
+ *
  * @ingroup SpecialPage
  */
 class SpecialActiveUsers extends SpecialPage {
@@ -41,16 +43,25 @@ class SpecialActiveUsers extends SpecialPage {
         * @param string $par Parameter passed to the page or null
         */
        public function execute( $par ) {
-               $days = $this->getConfig()->get( 'ActiveUserDays' );
+               $out = $this->getOutput();
 
                $this->setHeaders();
                $this->outputHeader();
 
-               $out = $this->getOutput();
-               $out->wrapWikiMsg( "<div class='mw-activeusers-intro'>\n$1\n</div>",
-                       [ 'activeusers-intro', $this->getLanguage()->formatNum( $days ) ] );
+               $opts = new FormOptions();
+
+               $opts->add( 'username', '' );
+               $opts->add( 'hidebots', false, FormOptions::BOOL );
+               $opts->add( 'hidesysops', false, FormOptions::BOOL );
+
+               $opts->fetchValuesFromRequest( $this->getRequest() );
+
+               if ( $par !== null ) {
+                       $opts->setValue( 'username', $par );
+               }
 
                // Mention the level of cache staleness...
+               $cacheText = '';
                $dbr = wfGetDB( DB_SLAVE, 'recentchanges' );
                $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
                if ( $rcMax ) {
@@ -66,22 +77,51 @@ class SpecialActiveUsers extends SpecialPage {
                                $secondsOld = time() - wfTimestamp( TS_UNIX, $rcMin );
                        }
                        if ( $secondsOld > 0 ) {
-                               $out->addWikiMsg( 'cachedspecial-viewing-cached-ttl',
-                               $this->getLanguage()->formatDuration( $secondsOld ) );
+                               $cacheTxt = '<br>' . $this->msg( 'cachedspecial-viewing-cached-ttl' )
+                                       ->durationParams( $secondsOld );
                        }
                }
 
-               $up = new ActiveUsersPager( $this->getContext(), null, $par );
+               $pager = new ActiveUsersPager( $this->getContext(), $opts );
+               $usersBody = $pager->getBody();
+
+               $days = $this->getConfig()->get( 'ActiveUserDays' );
+
+               $formDescriptor = [
+                       'username' => [
+                               'type' => 'user',
+                               'name' => 'username',
+                               'label-message' => 'activeusers-from',
+                       ],
+
+                       'hidebots' => [
+                               'type' => 'check',
+                               'name' => 'hidebots',
+                               'label-message' => 'activeusers-hidebots',
+                               'default' => false,
+                       ],
+
+                       'hidesysops' => [
+                               'type' => 'check',
+                               'name' => 'hidesysops',
+                               'label-message' => 'activeusers-hidesysops',
+                               'default' => false,
+                       ],
+               ];
 
-               # getBody() first to check, if empty
-               $usersbody = $up->getBody();
+               $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+                       ->setIntro( $this->msg( 'activeusers-intro' )->numParams( $days ) . $cacheText )
+                       ->setWrapperLegendMsg( 'activeusers' )
+                       ->setSubmitTextMsg( 'activeusers-submit' )
+                       ->setMethod( 'get' )
+                       ->prepareForm()
+                       ->displayForm( false );
 
-               $out->addHTML( $up->getPageHeader() );
-               if ( $usersbody ) {
+               if ( $usersBody ) {
                        $out->addHTML(
-                               $up->getNavigationBar() .
-                               Html::rawElement( 'ul', [], $usersbody ) .
-                               $up->getNavigationBar()
+                               $pager->getNavigationBar() .
+                               Html::rawElement( 'ul', [], $usersBody ) .
+                               $pager->getNavigationBar()
                        );
                } else {
                        $out->addWikiMsg( 'activeusers-noresult' );
index defca7d..e3be225 100644 (file)
@@ -106,21 +106,26 @@ class MIMEsearchPage extends QueryPage {
        }
 
        /**
-        * Return HTML to put just before the results.
+        * Generate and output the form
         */
        function getPageHeader() {
-               return Xml::openElement(
-                               'form',
-                               [ 'id' => 'specialmimesearch', 'method' => 'get', 'action' => wfScript() ]
-                       ) .
-                       Xml::openElement( 'fieldset' ) .
-                       Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) .
-                       Xml::element( 'legend', null, $this->msg( 'mimesearch' )->text() ) .
-                       Xml::inputLabel( $this->msg( 'mimetype' )->text(), 'mime', 'mime', 20, $this->mime ) .
-                       ' ' .
-                       Xml::submitButton( $this->msg( 'ilsubmit' )->text() ) .
-                                       Xml::closeElement( 'fieldset' ) .
-                                       Xml::closeElement( 'form' );
+               $formDescriptor = [
+                       'mime' => [
+                               'type' => 'text',
+                               'name' => 'mime',
+                               'label-message' => 'mimetype',
+                               'required' => true,
+                               'default' => $this->mime,
+                       ],
+               ];
+
+               $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+                       ->setWrapperLegendMsg( 'mimesearch' )
+                       ->setSubmitTextMsg( 'ilsubmit' )
+                       ->setAction( $this->getPageTitle()->getLocalURL() )
+                       ->setMethod( 'get' )
+                       ->prepareForm()
+                       ->displayForm( false );
        }
 
        public function execute( $par ) {
@@ -133,7 +138,7 @@ class MIMEsearchPage extends QueryPage {
                ) {
                        $this->setHeaders();
                        $this->outputHeader();
-                       $this->getOutput()->addHTML( $this->getPageHeader() );
+                       $this->getPageHeader();
                        return;
                }
 
index b916c1f..162ef60 100644 (file)
  * @ingroup SpecialPage
  */
 class SpecialMergeHistory extends SpecialPage {
-       /** @var string */
-       protected $mAction;
+       /** @var FormOptions */
+       protected $mOpts;
 
-       /** @var string */
-       protected $mTarget;
+       /** @var Status */
+       protected $mStatus;
 
-       /** @var string */
-       protected $mDest;
-
-       /** @var string */
-       protected $mTimestamp;
-
-       /** @var int */
-       protected $mTargetID;
-
-       /** @var int */
-       protected $mDestID;
-
-       /** @var string */
-       protected $mComment;
-
-       /** @var bool Was posted? */
-       protected $mMerge;
-
-       /** @var bool Was submitted? */
-       protected $mSubmitted;
-
-       /** @var Title */
-       protected $mTargetObj;
-
-       /** @var Title */
-       protected $mDestObj;
+       /** @var Title|null */
+       protected $mTargetObj, $mDestObj;
 
        /** @var int[] */
        public $prevId;
@@ -72,124 +48,107 @@ class SpecialMergeHistory extends SpecialPage {
                return true;
        }
 
-       /**
-        * @return void
-        */
-       private function loadRequestParams() {
-               $request = $this->getRequest();
-               $this->mAction = $request->getVal( 'action' );
-               $this->mTarget = $request->getVal( 'target' );
-               $this->mDest = $request->getVal( 'dest' );
-               $this->mSubmitted = $request->getBool( 'submitted' );
-
-               $this->mTargetID = intval( $request->getVal( 'targetID' ) );
-               $this->mDestID = intval( $request->getVal( 'destID' ) );
-               $this->mTimestamp = $request->getVal( 'mergepoint' );
-               if ( !preg_match( '/[0-9]{14}/', $this->mTimestamp ) ) {
-                       $this->mTimestamp = '';
-               }
-               $this->mComment = $request->getText( 'wpComment' );
-
-               $this->mMerge = $request->wasPosted()
-                       && $this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) );
-
-               // target page
-               if ( $this->mSubmitted ) {
-                       $this->mTargetObj = Title::newFromText( $this->mTarget );
-                       $this->mDestObj = Title::newFromText( $this->mDest );
-               } else {
-                       $this->mTargetObj = null;
-                       $this->mDestObj = null;
-               }
-       }
-
        public function execute( $par ) {
                $this->useTransactionalTimeLimit();
 
                $this->checkPermissions();
                $this->checkReadOnly();
 
-               $this->loadRequestParams();
-
                $this->setHeaders();
                $this->outputHeader();
 
-               if ( $this->mTargetID && $this->mDestID && $this->mAction == 'submit' && $this->mMerge ) {
+               $this->addHelpLink( 'Help:Merge history' );
+
+               $opts = new FormOptions();
+
+               $opts->add( 'target', '' );
+               $opts->add( 'dest', '' );
+               $opts->add( 'target', '' );
+               $opts->add( 'mergepoint', '' );
+               $opts->add( 'reason', '' );
+               $opts->add( 'merge', false );
+
+               $opts->fetchValuesFromRequest( $this->getRequest() );
+
+               $target = $opts->getValue( 'target' );
+               $dest = $opts->getValue( 'dest' );
+               $targetObj = Title::newFromText( $target );
+               $destObj = Title::newFromText( $dest );
+               $status = Status::newGood();
+
+               $this->mOpts = $opts;
+               $this->mTargetObj = $targetObj;
+               $this->mDestObj = $destObj;
+
+               if ( $opts->getValue( 'merge' ) && $targetObj &&
+                       $destObj && $opts->getValue( 'mergepoint' ) !== '' ) {
                        $this->merge();
 
                        return;
                }
 
-               if ( !$this->mSubmitted ) {
+               if ( $target === '' && $dest === '' ) {
                        $this->showMergeForm();
 
                        return;
                }
 
-               $errors = [];
-               if ( !$this->mTargetObj instanceof Title ) {
-                       $errors[] = $this->msg( 'mergehistory-invalid-source' )->parseAsBlock();
-               } elseif ( !$this->mTargetObj->exists() ) {
-                       $errors[] = $this->msg( 'mergehistory-no-source',
-                               wfEscapeWikiText( $this->mTargetObj->getPrefixedText() )
-                       )->parseAsBlock();
+               if ( !$targetObj instanceof Title ) {
+                       $status->merge( Status::newFatal( 'mergehistory-invalid-source' ) );
+               } elseif ( !$targetObj->exists() ) {
+                       $status->merge( Status::newFatal( 'mergehistory-no-source',
+                               wfEscapeWikiText( $targetObj->getPrefixedText() )
+                       ) );
                }
 
-               if ( !$this->mDestObj instanceof Title ) {
-                       $errors[] = $this->msg( 'mergehistory-invalid-destination' )->parseAsBlock();
-               } elseif ( !$this->mDestObj->exists() ) {
-                       $errors[] = $this->msg( 'mergehistory-no-destination',
-                               wfEscapeWikiText( $this->mDestObj->getPrefixedText() )
-                       )->parseAsBlock();
+               if ( !$destObj instanceof Title ) {
+                       $status->merge( Status::newFatal( 'mergehistory-invalid-destination' ) );
+               } elseif ( !$destObj->exists() ) {
+                       $status->merge( Status::newFatal( 'mergehistory-no-destination',
+                               wfEscapeWikiText( $destObj->getPrefixedText() )
+                       ) );
                }
 
-               if ( $this->mTargetObj && $this->mDestObj && $this->mTargetObj->equals( $this->mDestObj ) ) {
-                       $errors[] = $this->msg( 'mergehistory-same-destination' )->parseAsBlock();
+               if ( $targetObj && $destObj && $targetObj->equals( $destObj ) ) {
+                       $status->merge( Status::newFatal( 'mergehistory-same-destination' ) );
                }
 
-               if ( count( $errors ) ) {
-                       $this->showMergeForm();
-                       $this->getOutput()->addHTML( implode( "\n", $errors ) );
-               } else {
+               $this->mStatus = $status;
+
+               $this->showMergeForm();
+
+               if ( $status->isOK() ) {
                        $this->showHistory();
                }
        }
 
        function showMergeForm() {
-               $out = $this->getOutput();
-               $out->addWikiMsg( 'mergehistory-header' );
-
-               $out->addHTML(
-                       Xml::openElement( 'form', [
-                               'method' => 'get',
-                               'action' => wfScript() ] ) .
-                               '<fieldset>' .
-                               Xml::element( 'legend', [],
-                                       $this->msg( 'mergehistory-box' )->text() ) .
-                               Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) .
-                               Html::hidden( 'submitted', '1' ) .
-                               Html::hidden( 'mergepoint', $this->mTimestamp ) .
-                               Xml::openElement( 'table' ) .
-                               '<tr>
-                               <td>' . Xml::label( $this->msg( 'mergehistory-from' )->text(), 'target' ) . '</td>
-                               <td>' . Xml::input( 'target', 30, $this->mTarget, [ 'id' => 'target' ] ) . '</td>
-                       </tr><tr>
-                               <td>' . Xml::label( $this->msg( 'mergehistory-into' )->text(), 'dest' ) . '</td>
-                               <td>' . Xml::input( 'dest', 30, $this->mDest, [ 'id' => 'dest' ] ) . '</td>
-                       </tr><tr><td>' .
-                               Xml::submitButton( $this->msg( 'mergehistory-go' )->text() ) .
-                               '</td></tr>' .
-                               Xml::closeElement( 'table' ) .
-                               '</fieldset>' .
-                               '</form>'
-               );
-
-               $this->addHelpLink( 'Help:Merge history' );
+               $formDescriptor = [
+                       'target' => [
+                               'type' => 'title',
+                               'name' => 'target',
+                               'label-message' => 'mergehistory-from',
+                               'required' => true,
+                       ],
+
+                       'dest' => [
+                               'type' => 'title',
+                               'name' => 'dest',
+                               'label-message' => 'mergehistory-into',
+                               'required' => true,
+                       ],
+               ];
+
+               $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+                       ->setIntro( $this->msg( 'mergehistory-header' ) )
+                       ->setWrapperLegendMsg( 'mergehistory-box' )
+                       ->setSubmitTextMsg( 'mergehistory-go' )
+                       ->setMethod( 'post' )
+                       ->prepareForm()
+                       ->displayForm( $this->mStatus );
        }
 
        private function showHistory() {
-               $this->showMergeForm();
-
                # List all stored revisions
                $revisions = new MergeHistoryPager(
                        $this, [], $this->mTargetObj, $this->mDestObj
@@ -197,62 +156,46 @@ class SpecialMergeHistory extends SpecialPage {
                $haveRevisions = $revisions && $revisions->getNumRows() > 0;
 
                $out = $this->getOutput();
-               $titleObj = $this->getPageTitle();
-               $action = $titleObj->getLocalURL( [ 'action' => 'submit' ] );
-               # Start the form here
-               $top = Xml::openElement(
-                       'form',
-                       [
-                               'method' => 'post',
-                               'action' => $action,
-                               'id' => 'merge'
-                       ]
-               );
-               $out->addHTML( $top );
-
-               if ( $haveRevisions ) {
-                       # Format the user-visible controls (comment field, submission button)
-                       # in a nice little table
-                       $table =
-                               Xml::openElement( 'fieldset' ) .
-                                       $this->msg( 'mergehistory-merge', $this->mTargetObj->getPrefixedText(),
-                                               $this->mDestObj->getPrefixedText() )->parse() .
-                                       Xml::openElement( 'table', [ 'id' => 'mw-mergehistory-table' ] ) .
-                                       '<tr>
-                                               <td class="mw-label">' .
-                                       Xml::label( $this->msg( 'mergehistory-reason' )->text(), 'wpComment' ) .
-                                       '</td>
-                                       <td class="mw-input">' .
-                                       Xml::input( 'wpComment', 50, $this->mComment, [ 'id' => 'wpComment' ] ) .
-                                       '</td>
-                                       </tr>
-                                       <tr>
-                                               <td>&#160;</td>
-                                               <td class="mw-submit">' .
-                                       Xml::submitButton(
-                                               $this->msg( 'mergehistory-submit' )->text(),
-                                               [ 'name' => 'merge', 'id' => 'mw-merge-submit' ]
-                                       ) .
-                                       '</td>
-                                       </tr>' .
-                                       Xml::closeElement( 'table' ) .
-                                       Xml::closeElement( 'fieldset' );
-
-                       $out->addHTML( $table );
-               }
-
-               $out->addHTML(
-                       '<h2 id="mw-mergehistory">' .
-                               $this->msg( 'mergehistory-list' )->escaped() . "</h2>\n"
-               );
+               $header = '<h2 id="mw-mergehistory">' .
+                       $this->msg( 'mergehistory-list' )->escaped() . "</h2>\n";
 
                if ( $haveRevisions ) {
-                       $out->addHTML( $revisions->getNavigationBar() );
-                       $out->addHTML( '<ul>' );
-                       $out->addHTML( $revisions->getBody() );
-                       $out->addHTML( '</ul>' );
-                       $out->addHTML( $revisions->getNavigationBar() );
+                       $hiddenFields = [
+                               'merge' => true,
+                               'target' => $this->mOpts->getValue( 'target' ),
+                               'dest' => $this->mOpts->getValue( 'dest' ),
+                       ];
+
+                       $formDescriptor = [
+                               'reason' => [
+                                       'type' => 'text',
+                                       'name' => 'reason',
+                                       'label-message' => 'mergehistory-reason',
+                               ],
+                       ];
+
+                       $mergeText = $this->msg( 'mergehistory-merge',
+                               $this->mTargetObj->getPrefixedText(),
+                               $this->mDestObj->getPrefixedText()
+                       )->parse();
+
+                       $history = $header .
+                               $revisions->getNavigationBar() .
+                               '<ul>' .
+                               $revisions->getBody() .
+                               '</ul>' .
+                               $revisions->getNavigationBar();
+
+                       $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+                               ->addHiddenFields( $hiddenFields )
+                               ->setPreText( $mergeText )
+                               ->setFooterText( $history )
+                               ->setSubmitTextMsg( 'mergehistory-submit' )
+                               ->setMethod( 'post' )
+                               ->prepareForm()
+                               ->displayForm( false );
                } else {
+                       $out->addHTML( $header );
                        $out->addWikiMsg( 'mergehistory-empty' );
                }
 
@@ -260,18 +203,6 @@ class SpecialMergeHistory extends SpecialPage {
                $mergeLogPage = new LogPage( 'merge' );
                $out->addHTML( '<h2>' . $mergeLogPage->getName()->escaped() . "</h2>\n" );
                LogEventsList::showLogExtract( $out, 'merge', $this->mTargetObj );
-
-               # When we submit, go by page ID to avoid some nasty but unlikely collisions.
-               # Such would happen if a page was renamed after the form loaded, but before submit
-               $misc = Html::hidden( 'targetID', $this->mTargetObj->getArticleID() );
-               $misc .= Html::hidden( 'destID', $this->mDestObj->getArticleID() );
-               $misc .= Html::hidden( 'target', $this->mTarget );
-               $misc .= Html::hidden( 'dest', $this->mDest );
-               $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
-               $misc .= Xml::closeElement( 'form' );
-               $out->addHTML( $misc );
-
-               return true;
        }
 
        function formatRevisionRow( $row ) {
@@ -281,7 +212,7 @@ class SpecialMergeHistory extends SpecialPage {
                $last = $this->msg( 'last' )->escaped();
 
                $ts = wfTimestamp( TS_MW, $row->rev_timestamp );
-               $checkBox = Xml::radio( 'mergepoint', $ts, ( $this->mTimestamp === $ts ) );
+               $checkBox = Xml::radio( 'mergepoint', $ts, ( $this->mOpts->getValue( 'mergepoint' ) === $ts ) );
 
                $user = $this->getUser();
 
@@ -336,23 +267,24 @@ class SpecialMergeHistory extends SpecialPage {
         * @return bool Success
         */
        function merge() {
+               $opts = $this->mOpts;
+
                # Get the titles directly from the IDs, in case the target page params
                # were spoofed. The queries are done based on the IDs, so it's best to
                # keep it consistent...
-               $targetTitle = Title::newFromID( $this->mTargetID );
-               $destTitle = Title::newFromID( $this->mDestID );
-               if ( is_null( $targetTitle ) || is_null( $destTitle ) ) {
-                       return false; // validate these
-               }
-               if ( $targetTitle->getArticleID() == $destTitle->getArticleID() ) {
+               $targetObj = $this->mTargetObj;
+               $destObj = $this->mDestObj;
+
+               if ( is_null( $targetObj ) || is_null( $destObj ) ||
+                       $targetObj->getArticleID() == $destObj->getArticleID() ) {
                        return false;
                }
 
                // MergeHistory object
-               $mh = new MergeHistory( $targetTitle, $destTitle, $this->mTimestamp );
+               $mh = new MergeHistory( $targetObj, $destObj, $opts->getValue( 'mergepoint' ) );
 
                // Merge!
-               $mergeStatus = $mh->merge( $this->getUser(), $this->mComment );
+               $mergeStatus = $mh->merge( $this->getUser(), $opts->getValue( 'reason' ) );
                if ( !$mergeStatus->isOK() ) {
                        // Failed merge
                        $this->getOutput()->addWikiMsg( $mergeStatus->getMessage() );
@@ -360,7 +292,7 @@ class SpecialMergeHistory extends SpecialPage {
                }
 
                $targetLink = Linker::link(
-                       $targetTitle,
+                       $targetObj,
                        null,
                        [],
                        [ 'redirect' => 'no' ]
@@ -368,7 +300,7 @@ class SpecialMergeHistory extends SpecialPage {
 
                $this->getOutput()->addWikiMsg( $this->msg( 'mergehistory-done' )
                        ->rawParams( $targetLink )
-                       ->params( $destTitle->getPrefixedText() )
+                       ->params( $destObj->getPrefixedText() )
                        ->numParams( $mh->getMergedRevisionCount() )
                );
 
index 15bf39b..d474ba5 100644 (file)
@@ -1238,6 +1238,7 @@ class SpecialSearch extends SpecialPage {
                        'name' => 'search',
                        'autofocus' => trim( $term ) === '',
                        'value' => $term,
+                       'dataLocation' => 'content',
                ] );
 
                $out =
index e79fd6e..521f0ce 100644 (file)
@@ -77,6 +77,7 @@ class SpecialTags extends SpecialPage {
 
                $user = $this->getUser();
                $userCanManage = $user->isAllowed( 'managechangetags' );
+               $userCanDelete = $user->isAllowed( 'deletechangetags' );
                $userCanEditInterface = $user->isAllowed( 'editinterface' );
 
                // Show form to create a tag
@@ -154,12 +155,13 @@ class SpecialTags extends SpecialPage {
 
                // Insert tags that have been applied at least once
                foreach ( $tagStats as $tag => $hitcount ) {
-                       $html .= $this->doTagRow( $tag, $hitcount, $userCanManage, $userCanEditInterface );
+                       $html .= $this->doTagRow( $tag, $hitcount, $userCanManage,
+                               $userCanDelete, $userCanEditInterface );
                }
                // Insert tags defined somewhere but never applied
                foreach ( $definedTags as $tag ) {
                        if ( !isset( $tagStats[$tag] ) ) {
-                               $html .= $this->doTagRow( $tag, 0, $userCanManage, $userCanEditInterface );
+                               $html .= $this->doTagRow( $tag, 0, $userCanManage, $userCanDelete, $userCanEditInterface );
                        }
                }
 
@@ -170,7 +172,7 @@ class SpecialTags extends SpecialPage {
                ) );
        }
 
-       function doTagRow( $tag, $hitcount, $showActions, $showEditLinks ) {
+       function doTagRow( $tag, $hitcount, $showManageActions, $showDeleteActions, $showEditLinks ) {
                $newRow = '';
                $newRow .= Xml::tags( 'td', null, Xml::element( 'code', null, $tag ) );
 
@@ -229,16 +231,17 @@ class SpecialTags extends SpecialPage {
                $newRow .= Xml::tags( 'td', [ 'data-sort-value' => $hitcount ], $hitcountLabel );
 
                // actions
-               if ( $showActions ) { // we've already checked that the user had the requisite userright
-                       $actionLinks = [];
+               $actionLinks = [];
 
-                       // delete
-                       if ( ChangeTags::canDeleteTag( $tag )->isOK() ) {
-                               $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'delete' ),
-                                       $this->msg( 'tags-delete' )->escaped(),
-                                       [],
-                                       [ 'tag' => $tag ] );
-                       }
+               // delete
+               if ( $showDeleteActions && ChangeTags::canDeleteTag( $tag )->isOK() ) {
+                       $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'delete' ),
+                               $this->msg( 'tags-delete' )->escaped(),
+                               [],
+                               [ 'tag' => $tag ] );
+               }
+
+               if ( $showManageActions ) { // we've already checked that the user had the requisite userright
 
                        // activate
                        if ( ChangeTags::canActivateTag( $tag )->isOK() ) {
@@ -319,8 +322,8 @@ class SpecialTags extends SpecialPage {
 
        protected function showDeleteTagForm( $tag ) {
                $user = $this->getUser();
-               if ( !$user->isAllowed( 'managechangetags' ) ) {
-                       throw new PermissionsError( 'managechangetags' );
+               if ( !$user->isAllowed( 'deletechangetags' ) ) {
+                       throw new PermissionsError( 'deletechangetags' );
                }
 
                $out = $this->getOutput();
index a77c79e..45315a7 100644 (file)
@@ -699,7 +699,7 @@ class LoginForm extends SpecialPage {
 
                $u->setEmail( $this->mEmail );
                $u->setRealName( $this->mRealName );
-               $u->setToken();
+               SessionManager::singleton()->invalidateSessionsForUser( $u );
 
                Hooks::run( 'LocalUserCreated', [ $u, $autocreate ] );
                $oldUser = $u;
index baa55f0..b4ea732 100644 (file)
@@ -75,7 +75,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
                $this->target = Title::newFromText( $opts->getValue( 'target' ) );
                if ( !$this->target ) {
                        if ( !$this->including() ) {
-                               $out->addHTML( $this->whatlinkshereForm() );
+                               $this->buildForm();
                        }
 
                        return;
@@ -200,12 +200,8 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
                ) {
                        if ( 0 == $level ) {
                                if ( !$this->including() ) {
-                                       $out->addHTML( $this->whatlinkshereForm() );
+                                       $this->buildForm();
 
-                                       // Show filters only if there are links
-                                       if ( $hidelinks || $hidetrans || $hideredirs || $hideimages ) {
-                                               $out->addHTML( $this->getFilterPanel() );
-                                       }
                                        $errMsg = is_int( $namespace ) ? 'nolinkshere-ns' : 'nolinkshere';
                                        $out->addWikiMsg( $errMsg, $this->target->getPrefixedText() );
                                        $out->setStatusCode( 404 );
@@ -269,8 +265,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
 
                if ( $level == 0 ) {
                        if ( !$this->including() ) {
-                               $out->addHTML( $this->whatlinkshereForm() );
-                               $out->addHTML( $this->getFilterPanel() );
+                               $this->buildForm();
                                $out->addWikiMsg( 'linkshere', $this->target->getPrefixedText() );
 
                                $prevnext = $this->getPrevNext( $prevId, $nextId );
@@ -444,7 +439,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
                return $this->msg( 'viewprevnext' )->rawParams( $prev, $next, $nums )->escaped();
        }
 
-       function whatlinkshereForm() {
+       protected function buildForm() {
                // We get nicer value from the title object
                $this->opts->consumeValue( 'target' );
                // Reset these for new requests
@@ -455,88 +450,57 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
                $nsinvert = $this->opts->consumeValue( 'invert' );
 
                # Build up the form
-               $f = Xml::openElement( 'form', [ 'action' => wfScript() ] );
 
-               # Values that should not be forgotten
-               $f .= Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() );
-               foreach ( $this->opts->getUnconsumedValues() as $name => $value ) {
-                       $f .= Html::hidden( $name, $value );
-               }
-
-               $f .= Xml::fieldset( $this->msg( 'whatlinkshere' )->text() );
-
-               # Target input (.mw-searchInput enables suggestions)
-               $f .= Xml::inputLabel( $this->msg( 'whatlinkshere-page' )->text(), 'target',
-                       'mw-whatlinkshere-target', 40, $target, [ 'class' => 'mw-searchInput' ] );
+               $hiddenFields = [
+                       'title' => $this->getPageTitle()->getPrefixedDBkey(),
+               ];
 
-               $f .= ' ';
+               $formDescriptor = [
+                       'target' => [
+                               'type' => 'title',
+                               'name' => 'target',
+                               'label-message' => 'whatlinkshere-page',
+                               'default' => $this->opts->getValue( 'target' ),
+                       ],
 
-               # Namespace selector
-               $f .= Html::namespaceSelector(
-                       [
-                               'selected' => $namespace,
-                               'all' => '',
-                               'label' => $this->msg( 'namespace' )->text()
-                       ], [
+                       'namespace' => [
+                               'type' => 'namespaceselect',
                                'name' => 'namespace',
-                               'id' => 'namespace',
-                               'class' => 'namespaceselector',
-                       ]
-               );
-
-               $f .= '&#160;' .
-                       Xml::checkLabel(
-                               $this->msg( 'invert' )->text(),
-                               'invert',
-                               'nsinvert',
-                               $nsinvert,
-                               [ 'title' => $this->msg( 'tooltip-whatlinkshere-invert' )->text() ]
-                       );
-
-               $f .= ' ';
-
-               # Submit
-               $f .= Xml::submitButton( $this->msg( 'whatlinkshere-submit' )->text() );
-
-               # Close
-               $f .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n";
-
-               return $f;
-       }
-
-       /**
-        * Create filter panel
-        *
-        * @return string HTML fieldset and filter panel with the show/hide links
-        */
-       function getFilterPanel() {
-               $show = $this->msg( 'show' )->escaped();
-               $hide = $this->msg( 'hide' )->escaped();
-
-               $changed = $this->opts->getChangedValues();
-               unset( $changed['target'] ); // Already in the request title
+                               'label-message' => 'namespace',
+                               'all' => '',
+                       ],
+
+                       'invert' => [
+                               'type' => 'check',
+                               'name' => 'invert',
+                               'label-message' => 'invert',
+                               'default' => false,
+                       ],
+               ];
 
-               $links = [];
-               $types = [ 'hidetrans', 'hidelinks', 'hideredirs' ];
-               if ( $this->target->getNamespace() == NS_FILE ) {
-                       $types[] = 'hideimages';
+               $filters = [ 'hidetrans', 'hidelinks', 'hideredirs' ];
+               if ( $this->target instanceof Title &&
+                       $this->target->getNamespace() == NS_FILE ) {
+                       $filters[] = 'hideimages';
                }
 
-               // Combined message keys: 'whatlinkshere-hideredirs', 'whatlinkshere-hidetrans',
-               // 'whatlinkshere-hidelinks', 'whatlinkshere-hideimages'
-               // To be sure they will be found by grep
-               foreach ( $types as $type ) {
-                       $chosen = $this->opts->getValue( $type );
-                       $msg = $chosen ? $show : $hide;
-                       $overrides = [ $type => !$chosen ];
-                       $links[] = $this->msg( "whatlinkshere-{$type}" )->rawParams(
-                               $this->makeSelfLink( $msg, array_merge( $changed, $overrides ) ) )->escaped();
+               foreach ( $filters as $filter ) {
+                       $formDescriptor[$filter] = [
+                               'type' => 'check',
+                               'name' => $filter,
+                               'label' => $this->msg( 'whatlinkshere-' . $filter ),
+                               'value' => false,
+                       ];
                }
 
-               return Xml::fieldset(
-                       $this->msg( 'whatlinkshere-filters' )->text(),
-                       $this->getLanguage()->pipeList( $links )
-               );
+               $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+                       ->addHiddenFields( $hiddenFields )
+                       ->setWrapperLegendMsg( 'whatlinkshere' )
+                       ->setSubmitTextMsg( 'whatlinkshere-submit' )
+                       ->setAction( $this->getPageTitle()->getLocalURL() )
+                       ->setMethod( 'get' )
+                       ->prepareForm()
+                       ->displayForm( false );
        }
 
        /**
index 0d3bc9a..73ab0ad 100644 (file)
@@ -52,15 +52,15 @@ class ActiveUsersPager extends UsersPager {
 
        /**
         * @param IContextSource $context
-        * @param null $group Unused
-        * @param string $par Parameter passed to the page
+        * @param FormOptions $opts
         */
-       function __construct( IContextSource $context = null, $group = null, $par = null ) {
+       function __construct( IContextSource $context = null, FormOptions $opts ) {
                parent::__construct( $context );
 
                $this->RCMaxAge = $this->getConfig()->get( 'ActiveUserDays' );
-               $un = $this->getRequest()->getText( 'username', $par );
                $this->requestedUser = '';
+
+               $un = $opts->getValue( 'username' );
                if ( $un != '' ) {
                        $username = Title::makeTitleSafe( NS_USER, $un );
                        if ( !is_null( $username ) ) {
@@ -68,21 +68,10 @@ class ActiveUsersPager extends UsersPager {
                        }
                }
 
-               $this->setupOptions();
-       }
-
-       public function setupOptions() {
-               $this->opts = new FormOptions();
-
-               $this->opts->add( 'hidebots', false, FormOptions::BOOL );
-               $this->opts->add( 'hidesysops', false, FormOptions::BOOL );
-
-               $this->opts->fetchValuesFromRequest( $this->getRequest() );
-
-               if ( $this->opts->getValue( 'hidebots' ) == 1 ) {
+               if ( $opts->getValue( 'hidebots' ) == 1 ) {
                        $this->hideRights[] = 'bot';
                }
-               if ( $this->opts->getValue( 'hidesysops' ) == 1 ) {
+               if ( $opts->getValue( 'hidesysops' ) == 1 ) {
                        $this->hideGroups[] = 'sysop';
                }
        }
@@ -203,52 +192,4 @@ class ActiveUsersPager extends UsersPager {
                return Html::rawElement( 'li', [], "{$item} [{$count}]{$blocked}" );
        }
 
-       function getPageHeader() {
-               $self = $this->getTitle();
-               $limit = $this->mLimit ? Html::hidden( 'limit', $this->mLimit ) : '';
-
-               # Form tag
-               $out = Xml::openElement( 'form', [ 'method' => 'get', 'action' => wfScript() ] );
-               $out .= Xml::fieldset( $this->msg( 'activeusers' )->text() ) . "\n";
-               $out .= Html::hidden( 'title', $self->getPrefixedDBkey() ) . $limit . "\n";
-
-               # Username field (with autocompletion support)
-               $this->getOutput()->addModules( 'mediawiki.userSuggest' );
-               $out .= Xml::inputLabel(
-                               $this->msg( 'activeusers-from' )->text(),
-                               'username',
-                               'offset',
-                               20,
-                               $this->requestedUser,
-                               [
-                                       'class' => 'mw-ui-input-inline mw-autocomplete-user',
-                                       'tabindex' => 1,
-                               ] + (
-                                       // Set autofocus on blank input
-                               $this->requestedUser === '' ? [ 'autofocus' => '' ] : []
-                               )
-                       ) . '<br />';
-
-               $out .= Xml::checkLabel( $this->msg( 'activeusers-hidebots' )->text(),
-                       'hidebots', 'hidebots', $this->opts->getValue( 'hidebots' ), [ 'tabindex' => 2 ] );
-
-               $out .= Xml::checkLabel(
-                               $this->msg( 'activeusers-hidesysops' )->text(),
-                               'hidesysops',
-                               'hidesysops',
-                               $this->opts->getValue( 'hidesysops' ),
-                               [ 'tabindex' => 3 ]
-                       ) . '<br />';
-
-               # Submit button and form bottom
-               $out .= Xml::submitButton(
-                               $this->msg( 'activeusers-submit' )->text(),
-                               [ 'tabindex' => 4 ]
-                       ) . "\n";
-               $out .= Xml::closeElement( 'fieldset' );
-               $out .= Xml::closeElement( 'form' );
-
-               return $out;
-       }
-
 }
index 0e291ed..38e9ecd 100644 (file)
@@ -181,6 +181,37 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser {
                );
        }
 
+       /**
+        * @since 1.27
+        * @see TitleFormatter::getPrefixedDBkey()
+        * @param LinkTarget $target
+        * @return string
+        */
+       public function getPrefixedDBkey( LinkTarget $target ) {
+               $key = '';
+               if ( $target->isExternal() ) {
+                       $key .= $target->getInterwiki() . ':';
+               }
+               // Try to get a namespace name, but fallback
+               // to empty string if it doesn't exist
+               try {
+                       $nsName = $this->getNamespaceName(
+                               $target->getNamespace(),
+                               $target->getText()
+                       );
+               } catch ( InvalidArgumentException $e ) {
+                       $nsName = '';
+               }
+
+               if ( $target->getNamespace() !== 0 ) {
+                       $key .= $nsName . ':';
+               }
+
+               $key .= $target->getText();
+
+               return strtr( $key, ' ', '_' );
+       }
+
        /**
         * @see TitleFormatter::getText()
         *
index c081129..5177606 100644 (file)
@@ -68,6 +68,18 @@ interface TitleFormatter {
         */
        public function getPrefixedText( LinkTarget $title );
 
+       /**
+        * Return the title in prefixed database key form, with interwiki
+        * and namespace.
+        *
+        * @since 1.27
+        *
+        * @param LinkTarget $target
+        *
+        * @return string
+        */
+       public function getPrefixedDBkey( LinkTarget $target );
+
        /**
         * Returns the title formatted for display, with namespace and fragment.
         *
index 63c075f..597bf2f 100644 (file)
@@ -94,6 +94,15 @@ class TitleValue implements LinkTarget {
                return $this->namespace;
        }
 
+       /**
+        * @since 1.27
+        * @param int $ns
+        * @return bool
+        */
+       public function inNamespace( $ns ) {
+               return $this->namespace == $ns;
+       }
+
        /**
         * @return string
         */
index ee617a2..b5384bc 100644 (file)
@@ -127,6 +127,7 @@ class User implements IDBAccessObject {
                'createpage',
                'createtalk',
                'delete',
+               'deletechangetags',
                'deletedhistory',
                'deletedtext',
                'deletelogentry',
@@ -2489,9 +2490,9 @@ class User implements IDBAccessObject {
                        throw new PasswordError( wfMessage( 'externaldberror' )->text() );
                }
 
-               $this->setToken();
                $this->setOption( 'watchlisttoken', false );
                $this->setPasswordInternal( $str );
+               SessionManager::singleton()->invalidateSessionsForUser( $this );
 
                return true;
        }
@@ -2508,9 +2509,9 @@ class User implements IDBAccessObject {
                global $wgAuth;
 
                if ( $wgAuth->allowSetLocalPassword() ) {
-                       $this->setToken();
                        $this->setOption( 'watchlisttoken', false );
                        $this->setPasswordInternal( $str );
+                       SessionManager::singleton()->invalidateSessionsForUser( $this );
                }
        }
 
@@ -3398,6 +3399,21 @@ class User implements IDBAccessObject {
                return !$this->isLoggedIn();
        }
 
+       /**
+        * @return bool Whether this user is flagged as being a bot role account
+        * @since 1.28
+        */
+       public function isBot() {
+               if ( in_array( 'bot', $this->getGroups() ) && $this->isAllowed( 'bot' ) ) {
+                       return true;
+               }
+
+               $isBot = false;
+               Hooks::run( "UserIsBot", [ $this, &$isBot ] );
+
+               return $isBot;
+       }
+
        /**
         * Check if user is allowed to access a feature / make an action
         *
old mode 100644 (file)
new mode 100755 (executable)
index 5ff411d..6baaff0
@@ -16,6 +16,7 @@ class SearchInputWidget extends TitleInputWidget {
        protected $performSearchOnClick = true;
        protected $validateTitle = false;
        protected $highlightFirst = false;
+       protected $dataLocation = 'header';
 
        /**
         * @param array $config Configuration options
@@ -24,6 +25,8 @@ class SearchInputWidget extends TitleInputWidget {
         * @param boolean|null $config['performSearchOnClick'] If true, the script will start a search
         *  whenever a user hits a suggestion. If false, the text of the suggestion is inserted into the
         *  text field only (default: true)
+        * @param string $config['dataLocation'] Where the search input field will be
+        *  used (header or content, default: header)
         */
        public function __construct( array $config = [] ) {
                $config = array_merge( [
@@ -31,7 +34,6 @@ class SearchInputWidget extends TitleInputWidget {
                        'maxLength' => null,
                        'type' => 'search',
                        'icon' => 'search',
-                       'dataLocation' => 'content',
                ], $config );
 
                // Parent constructor
index 29de535..0f46528 100644 (file)
        "newarticle": "(جديد)",
        "newarticletext": "لقد تبعت وصلة لصفحة لم يتم إنشائها بعد.\nلإنشاء هذه الصفحة ابدأ الكتابة في الصندوق بالأسفل (انظر في [$1 صفحة المساعدة] للمزيد من المعلومات).\nإذا كانت زيارتك لهذه الصفحة بالخطأ، اضغط على زر ''رجوع'' في متصفح الإنترنت لديك.",
        "anontalkpagetext": "----''هذه صفحة نقاش لمستخدم مجهول لم يقم بإنشاء حساب بعد أو لا يستعمل ذلك الحساب.\nلذا فيجب علينا استعمال رقم الأيبي للتعرف عليه/عليها.\nمثل هذا العنوان يمكن أن يشترك فيه عدة مستخدمين.\nلو كنت مستخدما مجهولا وتشعر بأن تعليقات لا تخصك تم توجيهها إليك، من فضلك [[Special:UserLogin/signup|أنشئ حسابا]] أو [[Special:UserLogin|سجل الدخول]] لتجنب الارتباك المستقبلي مع مستخدمين مجهولين آخرين.''",
-       "noarticletext": "لا يوجد حاليا أي نص في هذه الصفحة.\nيمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوان هذه الصفحة]] في الصفحات الأخرى،\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات المتعلقة]،\nأو [{{fullurl:{{FULLPAGENAME}}|action=edit}} تعديل هذه الصفحة]</span>.",
+       "noarticletext": "الصفحة خالية. يمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوانها]] في الصفحات الأخرى أو\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات] (لتعرف إذا حذفت)،\nأو '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} إنشاؤها بنفسك]'''</span>.",
        "noarticletext-nopermission": "لا يوجد حاليا أي نص في هذه الصفحة.\nيمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوان هذه الصفحة]] في الصفحات الأخرى، أو <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات المتعلقة بها]</span>، لكنك لست مخولاً لإنشاء هذه الصفحة.",
        "missing-revision": "المراجعة #$1 من الصفحة المسماة \"{{FULLPAGENAME}}\" غير موجودة.\n\nهذا يحدث عادة عن طريق اتباع وصلة تاريخ قديمة لصفحة تم حذفها.\nالتفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "userpage-userdoesnotexist": "حساب المستخدم \"<nowiki>$1</nowiki>\" غير مسجل.\nمن فضلك تأكد أنك تريد إنشاء/تعديل هذه الصفحة.",
        "watchlistedit-raw-done": "قائمة مراقبتك تم تحديثها.",
        "watchlistedit-raw-added": "تمت إضافة {{PLURAL:$1||عنوان واحد|عنوانين|$1 عناوين|$1 عنوانا|$1 عنوان}}:",
        "watchlistedit-raw-removed": "تمت إزالة {{PLURAL:$1||عنوان واحد|عنوانين|$1 عناوين|$1 عنوانا|$1 عنوان}}:",
-       "watchlistedit-clear-title": "قائمة مراقبة ممسوحة",
+       "watchlistedit-clear-title": "امسح قائمة المراقبة",
        "watchlistedit-clear-legend": "امسح قائمة المراقبة",
        "watchlistedit-clear-explain": "ستحذف جميع الصفحات من قائمة مراقبتك",
        "watchlistedit-clear-titles": "العناوين:",
index 7807207..ad623cb 100644 (file)
        "noemail": "«$1» ایستیفاده‌چی‌یه ایمیل آدرسی قئید اولماییب‌دیر.",
        "noemailcreate": "دوزگون بیر ایمیل آدرسی وئرمه‌لیسینیز",
        "passwordsent": "«$1»-نا قئید اولونان ایمیل آدرسینه، یئنی بیر رمز گؤندریلدی.\nاونا آلان‌دان سونرا یئنی‌دن گیرین.",
-       "blocked-mailpassword": "سیزین آی‌پی آدرسینیزین دییشدیرمه ائده بیلمه‌سی کسیلیبدیر. سوءایستیفاده قارشی‌سینی آلماق اۆچون، رمزی یئنی‌دن اله گتیرمک ایمکانینا ایجازه‌نیز یوْخدور.",
+       "blocked-mailpassword": "سیزین آی‌پی آدرسینیزین دییشدیرمه ائده بیلمه‌سی باغلانمیشدیر. سوءایستیفاده قارشی‌سینی آلماق اۆچون، رمزی یئنی‌دن اله گتیرمک ایمکانینا ایجازه‌نیز یوْخدور.",
        "eauthentsent": "سیزین سئچیلمیش ایمیل آدرسینه، دوغرولاماق اوچون بیر ایمیل گؤندریلدی.\nهر یئنی بیر ایمیل گؤندرمک‌دن اؤنجه، بو حسابین دوغرودان سیزین اولدوغونو گؤسترمک اوچون، او ایمیل‌ده‌کی ایشلری گؤرمه‌لیسینیز.",
        "throttled-mailpassword": "سون {{PLURAL:$1|ساعات|$1 ساعات}}‌دا سیزه بیر رمز یئنیله‌مه ایمیلی گؤندریلیب‌دیر.\nسوءاستفاده قاباغین آلماق اوچون، هر {{PLURAL:$1|ساعات|$1 ساعات}}‌دا یالنیز بیر رمز یئنیله‌مه ایمیلی گؤندریلر.",
        "mailerror": "ایمیل گؤندرمه خطاسی: $1",
        "uploadstash-summary": "بو صحیفه‌‌ يوکلنمیش(و يا يوکلنمکده دیر) آمما هله ویکیده نشر اولونمامیش فايللارا چاتماغی تعمین ائدر. بو فايللار يالنیز يوکله‌يه‌نی طرفیندن گؤروله بیلر.",
        "uploadstash-clear": "مووققتی فايللاری تمیزله",
        "uploadstash-nofiles": "سیزین هئچ آمبار اولموش فایلینیز یوخدور.",
-       "uploadstash-badtoken": "Ú\86اÙ\84Û\8cØ´Ù\85اÙ\86Û\8cÙ\86 Ø­Û\8cاتا Ú©Ø¦Ú\86Û\8cرÛ\8cÙ\84Ù\85Ù\87â\80\8cسÛ\8c Ø¨Ø§Ø´Ø§Ø±Û\8cسÛ\8cز Ø§Ù\88Ù\84دÙ\88Ø\8c Ø§Ø­ØªÛ\8cÙ\85اÙ\84Ù\84ا ØªÙ\86زیمله‌مه قايدالاری زامان آشیمینا معروض قالدی. يئنیدن چالیشین.",
+       "uploadstash-badtoken": "Ú\86اÙ\84Û\8cØ´Ù\85اÙ\86Û\8cÙ\86 Ø­Û\8cاتا Ú©Ø¦Ú\86Û\8cرÛ\8cÙ\84Ù\85Ù\87â\80\8cسÛ\8c Ø¨Ø§Ø´Ø§Ø±Û\8cسÛ\8cز Ø§Ù\88Ù\84دÙ\88Ø\8c Ø§Ø­ØªÛ\8cÙ\85اÙ\84Ù\84ا ØªÙ\86ظیمله‌مه قايدالاری زامان آشیمینا معروض قالدی. يئنیدن چالیشین.",
        "uploadstash-errclear": "فايللارین سیلینمه‌سی باشاریسیز اولدو.",
        "uploadstash-refresh": "فايل سیياهیسینی يئنیله",
        "invalid-chunk-offset": "اعتبارسیز یئربه‌یئر",
        "unblock": "ایستیفاده‌چی‌نین باغلانماسین گؤتور",
        "blockip": " {{GENDER:$1|ایشلدن}}ی باغلا",
        "blockip-legend": "ایستیفادچی نی باغلا",
-       "blockiptext": "آشاغی‌داکی فورمو ایستیفاده ائده‌رک مویین بیر ایپنین و یا قئیدیات‌دان کئچمیش ایستیفاده‌چی‌نین دییشیک‌لیک ائتمه‌سینی مانعه تؤره‌ده بیلرسینیز. بو یالنیز واندالیزمی قارشی‌سینی آلماق اوچون و [[{{MediaWiki:Policy-url}}|قایدا‌لارا]] اویغون اولا‌راق ائدیلمه‌لی. آشاغییا موتلق قاداغا ایله علاقه‌دار بیر شرح یازین. (نومونه:-بو-صحیفه‌لرده واندالیزم ائتمیش‌دیر).",
+       "blockiptext": "آشاغی‌داکی فورمو ایستیفاده ائده‌رک مۆعیّن بیر آی‌پی‌نین و یا قئیدیات‌دان کئچمیش ایستیفاده‌چی‌نین دییشیک‌لیک ائتمه‌سینی مانعه تؤره‌ده بیلرسینیز. بۇ یالنیز واندالیزمین قارشی‌سینی آلماق اۆچون و [[{{MediaWiki:Policy-url}}|قایدا‌لارا]] اۇیغون اوْلا‌راق ائدیلمه‌لی. آشاغی‌یا مۆطلق قاداغا ایله علاقه‌دار بیر شرح یازین. (اؤرنک:-بۇ-صفحه‌لرده واندالیزم ائتمیشدیر).",
        "ipaddressorusername": "آی-پی عونوانی و یا ایستیفاده‌چی آدی",
        "ipbexpiry": "بیتمه مدتی:",
        "ipbreason": "ندن:",
        "unblock-hideuser": "ایستیفاده‌چی آدی گیزلی اولدوغو اوچون، بی باغلامانی گؤتوره بیلمزسینیز.",
        "ipb_cant_unblock": "ختا: باغلاما آی دی سی $1 تاپیلمادی. باغلامانین گؤتورولمه‌سی مومکون‌دور.",
        "ipb_blocked_as_range": "خطا: $1 ای پی عنوانی بیرباشا مانعه و مانعه تؤرتمه آزالدیلماسینا یول وئریلمیر.\nآنجاق، بو عنوان $2 آرا‌لیغینین پارچاسی اولا‌راق مانعه تؤردیلمیش، دئکابر مانعه تؤرتمه‌سینی قال‌دیرا.",
-       "ip_range_invalid": "یانلیش ای پی",
+       "ip_range_invalid": "گئچرسیز آی‌پی آرالیغی.",
        "ip_range_toolarge": "/ $1 بلوک‌دان داها بؤیوک بازه باغلانمالارینا ایجازه وئریلمیر.",
        "proxyblocker": "پروکسی باغلییان",
        "proxyblockreason": "ای پی آدرئسینیز آچیق بیر پروکسی اولدوغو اوچون مانعه تؤردیلدی.\nخاهیش ائدیریک اینتئرنئت سئویش تعمین ایله یا دا تئکنیکی دستک ایله علاقه قورون و بو جدی تهلوکه‌سیزلیک پروبلئمین‌دن خبردار ائدین.",
index aaf7ca8..1cb4117 100644 (file)
        "parser-unstrip-loop-warning": "Ябылмаған pre табылды",
        "parser-unstrip-recursion-limit": "($1) рекурсия сиге үтеп кителгән",
        "converter-manual-rule-error": "Тел әйлендереү ҡағиҙәһендә хата табылды",
-       "undo-success": "Был үҙгәртеүҙе кире алып була. Зинһар, улар һеҙҙе ҡыҙыҡһындырған үҙгәртеүҙәр булыуынан шикләнмәҫ өсөн версияларҙы сағыштырыуҙы ҡарағыҙ һәм үҙгәртеүҙәрҙе ғәмәлғә керетер өсөн «Битте һаҡларға» төймәһенә баҫығыҙ.",
+       "undo-success": "Был үҙгәртеүҙе кире алып була. Үҙгәртеүҙәр нәҡ һеҙ теләгәнсә булһа, версияларҙы сағыштырып ҡарағыҙ, ғәмәлгә индерер өсөн, «Битте һаҡларға» төймәһенә баҫығыҙ.",
        "undo-failure": "Ара үҙгәртеүҙәр тура килмәү сәбәпле төҙәтеүҙе кире алып булмай.",
        "undo-norev": "Үҙгәртеүҙе кире алып булмай, сөнки юҡ йәки юйылған.",
        "undo-nochange": "Төҙәтеү кире ҡайтарылған.",
index 7b4842d..5f93c6f 100644 (file)
@@ -58,7 +58,7 @@
        "tog-ccmeonemails": "Адпраўляць мне копіі лістоў, якія я дасылаю іншым удзельнікам",
        "tog-diffonly": "Не паказваць зьмест старонкі пад параўнаньнем зьменаў",
        "tog-showhiddencats": "Паказваць схаваныя катэгорыі",
-       "tog-norollbackdiff": "Не паказваць зьмены пасьля выкарыстаньня функцыі адкату",
+       "tog-norollbackdiff": "Не паказваць зьмены пасьля выкананьня адкату",
        "tog-useeditwarning": "Папярэджваць мяне, калі я буду пакідаць старонку рэдагаваньня зь незахаванымі зьменамі",
        "tog-prefershttps": "Заўсёды карыстацца бясьпечным злучэньнем па ўваходзе ў сыстэму",
        "underline-always": "Заўсёды",
        "mailmypassword": "Скінуць пароль",
        "passwordremindertitle": "Новы часовы пароль для {{GRAMMAR:родны|{{SITENAME}}}}",
        "passwordremindertext": "Нехта (магчыма Вы, з IP-адрасу $1) запытаў нас даслаць новы пароль для {{GRAMMAR:родны|{{SITENAME}}}} ($4). Для ўдзельніка «$2» быў створаны часовы пароль і ён цяпер «$3». Калі гэта была Вашая ініцыятыва, Вам трэба ўвайсьці ў сыстэму і адразу зьмяніць пароль. Тэрмін дзеяньня Вашага часовага паролю — $5 {{PLURAL:$5|дзень|дні|дзён}}.\n\nКалі гэты запыт адправіў нехта іншы, альбо Вы ўзгадалі свой пароль і ўжо не жадаеце яго зьмяніць, Вы можаце праігнараваць гэты ліст і працягваць карыстацца старым паролем.",
-       "noemail": "{{GENDER:$1|УдзелÑ\8cнÑ\96к Â«$1» Ð½Ðµ Ð¿Ð°Ð·Ð½Ð°Ñ\87Ñ\8bÑ\9e|УдзелÑ\8cнÑ\96Ñ\86а Â«$1» Ð½Ðµ Ð¿Ð°Ð·Ð½Ð°Ñ\87Ñ\8bла}} Ð½Ñ\96Ñ\8fкага Ð°Ð´Ñ\80аÑ\81Ñ\83 электроннай пошты.",
+       "noemail": "{{GENDER:$1|УдзелÑ\8cнÑ\96к Â«$1» Ð½Ðµ Ð¿Ð°Ð·Ð½Ð°Ñ\87Ñ\8bÑ\9e|УдзелÑ\8cнÑ\96Ñ\86а Â«$1» Ð½Ðµ Ð¿Ð°Ð·Ð½Ð°Ñ\87Ñ\8bла}} Ð°Ð´Ñ\80аÑ\81 электроннай пошты.",
        "noemailcreate": "Вы павінны пазначыць слушны адрас электроннай пошты",
        "passwordsent": "Новы пароль быў дасланы на адрас электроннай пошты ўдзельніка «$1».\nКалі ласка, увайдзіце ў сыстэму пасьля яго атрыманьня.",
        "blocked-mailpassword": "З Вашага IP-адрасу забароненыя рэдагаваньні. Каб пазьбегнуць злоўжываньняў, з гэтага IP-адрасу забаронена аднаўляць пароль.",
        "userpage-userdoesnotexist": "Рахунак удзельніка «<nowiki>$1</nowiki>» не зарэгістраваны. Калі ласка, удакладніце, ці жадаеце Вы стварыць/рэдагаваць гэтую старонку.",
        "userpage-userdoesnotexist-view": "Рахунак «$1» ня створаны.",
        "blocked-notice-logextract": "Гэты ўдзельнік у дадзены момант заблякаваны.\nАпошні запіс з журналу блякаваньняў пададзены ніжэй для даведкі:",
-       "clearyourcache": "'''Заўвага:''' Каб пабачыць зьмены пасьля захаваньня, Вам можа спатрэбіцца ачысьціць кэш Вашага браўзэра. \n* '''Firefox / Safari:''' Трымайце ''Shift'' і націсьніце ''Reload'', ці націсьніце ''Ctrl-F5'' ці ''Ctrl-R'' (''⌘-R'' на Mac)\n* '''Google Chrome:''' Націсьніце ''Ctrl-Shift-R'' (''⌘-Shift-R'' на Mac)\n* '''Internet Explorer:''' Трымайце ''Ctrl'' і націсьніце ''Refresh'', ці націсьніце ''Ctrl-F5''\n* '''Opera:''' Ачысьціце кэш праз ''Tools → Preferences''",
+       "clearyourcache": "<strong>Заўвага:</strong> каб пабачыць зьмены пасьля захаваньня, Вам можа спатрэбіцца ачысьціць кэш Вашага браўзэра. \n* <strong>Firefox / Safari:</strong> трымайце <em>Shift</em> і націсьніце <em>Reload</em>, ці націсьніце <em>Ctrl-F5</em> ці <em>Ctrl-R</em> (<em>⌘-R</em> на Mac)\n* <strong>Google Chrome:</strong> націсьніце <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Mac)\n* <strong>Internet Explorer:</strong> трымайце <em>Ctrl</em> і націсьніце <em>Refresh</em>, ці націсьніце <em>Ctrl-F5</em>\n* <strong>Opera:</strong> перайдзіце ў <em>Menu → Settings</em> (<em>Opera → Preferences</em> на Mac), а потым у <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''Падказка:''' выкарыстоўвайце кнопку «{{int:showpreview}}», каб паспрабаваць новы код CSS перад тым як яго запісаць.",
        "userjsyoucanpreview": "'''Падказка:''' выкарыстоўвайце кнопку «{{int:showpreview}}», каб паспрабаваць новы код JavaScript перад тым, як яго запісаць.",
        "usercsspreview": "'''Памятайце, што гэта толькі папярэдні прагляд Вашага CSS. Ён яшчэ не запісаны!'''",
        "apisandbox-request-url-label": "URL-адрас запыту:",
        "apisandbox-request-time": "Час запыту: {{PLURAL:$1|$1 мс}}",
        "apisandbox-results-fixtoken": "Выпраўце токен і паўтарыце адпраўку",
+       "apisandbox-results-fixtoken-fail": "Памылка пры атрыманьні токену «$1».",
        "apisandbox-alert-page": "Палі на гэтай старонцы няслушныя.",
        "apisandbox-alert-field": "Значэньне гэтага поля зьяўляецца няслушным.",
        "booksources": "Крыніцы кніг",
        "changecontentmodel-title-label": "Назва старонкі",
        "changecontentmodel-model-label": "Новая мадэль зьместу",
        "changecontentmodel-reason-label": "Прычына:",
+       "changecontentmodel-submit": "Зьмяніць",
        "changecontentmodel-success-title": "Мадэль зьместу была зьмененая",
        "changecontentmodel-success-text": "Тып зьместу [[:$1]] быў зьменены.",
        "changecontentmodel-cannot-convert": "Зьмест [[:$1]] ня можа быць ператвораны ў тып $2.",
        "whatlinkshere-prev": "{{PLURAL:$1|папярэдняя|папярэднія}} $1",
        "whatlinkshere-next": "{{PLURAL:$1|наступная|наступныя}} $1",
        "whatlinkshere-links": "← спасылкі",
-       "whatlinkshere-hideredirs": "$1 перанакіраваньні",
-       "whatlinkshere-hidetrans": "$1 уключэньні",
-       "whatlinkshere-hidelinks": "$1 спасылкі",
+       "whatlinkshere-hideredirs": "Схаваць перанакіраваньні",
+       "whatlinkshere-hidetrans": "Схаваць уключэньні",
+       "whatlinkshere-hidelinks": "Схаваць спасылкі",
        "whatlinkshere-hideimages": "$1 спасылкі на выявы",
        "whatlinkshere-filters": "Фільтры",
        "whatlinkshere-submit": "Перайсьці",
index ad81c32..828033c 100644 (file)
        "revdelete-show-file-submit": "Да",
        "revdelete-selected-text": "{{PLURAL:$1|Избрана версия|Избрани версии}} от [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Избрано събитие|Избрани събития}}:",
+       "revdelete-text-text": "Изтритите редакции ще продължат да се виждат в историята на страницата, но части от съдържанието ще бъдат публично недостъпни.",
+       "revdelete-text-others": "Другите администратори ще продължат да имат достъп до скритото съдържание и могат да го възстановят, освен ако не бъдат наложени допълнителни ограничения.",
        "revdelete-confirm": "Необходимо е да потвърдите, че желаете да извършите действието, разбирате последствията и го правите според [[{{MediaWiki:Policy-url}}|политиката]].",
        "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n* Потенциално уязвима в правно отношение информация\n* Неподходяща лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
        "revdelete-legend": "Задаване на ограничения:",
        "logempty": "Дневникът не съдържа записи, отговарящи на избрания критерий.",
        "log-title-wildcard": "Търсене на заглавия, започващи със",
        "showhideselectedlogentries": "Промяна на видимостта на избраните записи",
-       "checkbox-all": "Всички",
-       "checkbox-none": "Никои",
+       "checkbox-select": "Избери: $1",
+       "checkbox-all": "всички",
+       "checkbox-none": "никои",
+       "checkbox-invert": "обърни избора",
        "allpages": "Всички страници",
        "nextpage": "Следваща страница ($1)",
        "prevpage": "Предходна страница ($1)",
        "protect-othertime": "Друг срок:",
        "protect-othertime-op": "друг срок",
        "protect-existing-expiry": "Оставащо време: $2, $3",
+       "protect-existing-expiry-infinity": "Existing expiration time: безсрочно",
        "protect-otherreason": "Друга/допълнителна причина:",
        "protect-otherreason-op": "Друга причина",
        "protect-dropdown": "* Стандартни причини за защита на страници\n** Чест обект на вандализъм\n** Чест обект на спам\n** Редакторска война\n** Страница, изискваща много сървърни ресурси",
        "sqlite-no-fts": "$1 без поддръжка на пълнотекстово търсене",
        "logentry-delete-delete": "$1 {{GENDER:$2|изтри}} страницата $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|възстанови}} страницата $3",
+       "logentry-delete-revision": "$1 {{GENDER:$2|промени}} видимостта на {{PLURAL:$5|една редакция|$5 редакции}} в страница $3: $4",
+       "logentry-delete-event-legacy": "$1 {{GENDER:$2|промени}} видимостта на събитията от дневниците за страница $3",
        "logentry-delete-revision-legacy": "$1 {{GENDER:$2|промени}} видимостта на версиите на страница $3",
        "logentry-suppress-revision": "$1 тайно {{GENDER:$2|промени}} видимостта на {{PLURAL:$5|една версия|$5 версии}} на страницата $3: $4",
        "logentry-suppress-revision-legacy": "$1 тайно {{GENDER:$2|промени}} видимостта на версиите на страница $3",
index 41f4923..00e296a 100644 (file)
@@ -45,6 +45,7 @@
        "tog-watchdefault": "আমার সম্পাদিত পাতা এবং ফাইলগুলো আমার নজরতালিকায় যোগ করা হোক",
        "tog-watchmoves": "আমার স্থানান্তরিত পাতা এবং ফাইলগুলো আমার নজরতালিকায় যোগ করা হোক",
        "tog-watchdeletion": "আমার অপসারিত পাতা এবং ফাইলগুলো আমার নজর তালিকায় যোগ করা হোক",
+       "tog-watchuploads": "আমার নজরতালিকায় আমার আপলোড করা নতুন ফাইল যোগ কর",
        "tog-watchrollback": "আমার দ্বারা রোলব্যাক করা পাতা আমার নজরতালিকায় যোগ করা হোক",
        "tog-minordefault": "শুরুতেই সব সম্পাদনাকে অনুল্লেখ্য বলে চিহ্নিত করা হোক",
        "tog-previewontop": "সম্পাদনা বাক্সের আগে প্রাকদর্শন দেখানো হোক",
        "minoredit": "এটি একটি অনুল্লেখ্য সম্পাদনা",
        "watchthis": "এই পাতাটি নজরে রাখুন",
        "savearticle": "সংরক্ষণ",
+       "publishpage": "পাতা প্রকাশ করুন",
        "preview": "প্রাকদর্শন",
        "showpreview": "প্রাকদর্শন",
        "showdiff": "পরিবর্তনসমূহ",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" নামের কোন ব্যবহারকারী অ্যাকাউন্ট নিবন্ধিত হয়নি। অনুগ্রহ করে পরীক্ষা করে দেখুন আপনি এই পাতাটি সৃষ্টি/সম্পাদনা করতে চান কি না।",
        "userpage-userdoesnotexist-view": "ব্যবহারকারী অ্যাকাউন্ট \"$1\" অনিবন্ধিত।",
        "blocked-notice-logextract": "এই ব্যবহারকারী বর্তমানে ব্লক রয়েছে।\nরেফারেন্সের জন্য সাম্প্রতিক ব্লক লগ ভুক্তি নিচে দেওয়া হল:",
-       "clearyourcache": "<strong>লà¦\95à§\8dষà§\8dয à¦\95রà§\81ন:</strong> à¦¸à¦\82রà¦\95à§\8dষণà§\87র à¦ªà¦°, à¦ªà¦°à¦¿à¦¬à¦°à§\8dতনà¦\97à§\81লà§\8b à¦¦à§\87à¦\96তà§\87 à¦\86পনাà¦\95à§\87 à¦\86পনার à¦¬à§\8dরাà¦\89à¦\9cারà§\87র à¦\95à§\8dযাশà§\87 à¦ªà¦°à¦¿à¦·à§\8dà¦\95ার à¦\95রার à¦ªà§\8dরয়à§\8bà¦\9cন à¦¹à¦¤à§\87 à¦ªà¦¾à¦°à§\87।\n* <strong>ফায়ারফà¦\95à§\8dস / à¦¸à¦¾à¦«à¦¾à¦°à¦¿:</strong> <em>Shift</em> à¦§à¦°à§\87 à¦°à¦¾à¦\96া à¦\85বসà§\8dথায়<em>পà§\81নà¦\83লà§\8bড à¦\95রà§\81ন</em>-à¦\8f à¦\95à§\8dলিà¦\95 à¦\95রà§\81ন, à¦\85থবা <em>Ctrl-F5</em> à¦¬à¦¾ <em>Ctrl-R</em> (মà§\8dযাà¦\95-à¦\8f <em>â\8c\98-R</em>) à¦\9aাপà§\81ন\n* <strong>à¦\97à§\81à¦\97ল à¦\95à§\8dরà§\8bম:</strong> <em>Ctrl-Shift-R</em> (মà§\8dযাà¦\95-à¦\8f <em>â\8c\98-Shift-R</em>) à¦\9aাপà§\81ন\n* <strong>à¦\87নà§\8dà¦\9fারনà§\87à¦\9f à¦\8fà¦\95à§\8dসপà§\8dলà§\8bরার:</strong> <em>Ctrl</em> à¦§à¦°à§\87 à¦°à¦¾à¦\96া à¦\85বসà§\8dথায় <em>Refresh</em>-à¦\8f à¦\95à§\8dলিà¦\95 à¦\95রà§\81ন, à¦\85থবা <em>Ctrl-F5</em> à¦\9aাপà§\81ন\n* <strong>à¦\85পà§\87রা:</strong> <em>সরà¦\9eà§\8dà¦\9cাম â\86\92 à¦ªà¦\9bনà§\8dদসমà§\82হ</em>-à¦\8f à¦\97িয়à§\87 à¦\95à§\8dযাশà§\87 à¦ªà¦°à¦¿à¦·à§\8dà¦\95ার à¦\95রà§\87 à¦¨à¦¿à¦¨",
+       "clearyourcache": "<strong>লà¦\95à§\8dষà§\8dয à¦\95রà§\81ন:</strong> à¦¸à¦\82রà¦\95à§\8dষণà§\87র à¦ªà¦°, à¦ªà¦°à¦¿à¦¬à¦°à§\8dতনà¦\97à§\81লà§\8b à¦¦à§\87à¦\96তà§\87 à¦\86পনাà¦\95à§\87 à¦\86পনার à¦¬à§\8dরাà¦\89à¦\9cারà§\87র à¦\95à§\8dযাশà§\87 à¦ªà¦°à¦¿à¦·à§\8dà¦\95ার à¦\95রার à¦ªà§\8dরয়à§\8bà¦\9cন à¦¹à¦¤à§\87 à¦ªà¦¾à¦°à§\87।\n* <strong>ফায়ারফà¦\95à§\8dস / à¦¸à¦¾à¦«à¦¾à¦°à¦¿:</strong> <em>Shift</em> à¦§à¦°à§\87 à¦°à¦¾à¦\96া à¦\85বসà§\8dথায়<em>পà§\81নà¦\83লà§\8bড à¦\95রà§\81ন</em>-à¦\8f à¦\95à§\8dলিà¦\95 à¦\95রà§\81ন, à¦\85থবা <em>Ctrl-F5</em> à¦¬à¦¾ <em>Ctrl-R</em> (মà§\8dযাà¦\95-à¦\8f <em>â\8c\98-R</em>) à¦\9aাপà§\81ন\n* <strong>à¦\97à§\81à¦\97ল à¦\95à§\8dরà§\8bম:</strong> <em>Ctrl-Shift-R</em> (মà§\8dযাà¦\95-à¦\8f <em>â\8c\98-Shift-R</em>) à¦\9aাপà§\81ন\n* <strong>à¦\87নà§\8dà¦\9fারনà§\87à¦\9f à¦\8fà¦\95à§\8dসপà§\8dলà§\8bরার:</strong> <em>Ctrl</em> à¦§à¦°à§\87 à¦°à¦¾à¦\96া à¦\85বসà§\8dথায় <em>Refresh</em>-à¦\8f à¦\95à§\8dলিà¦\95 à¦\95রà§\81ন, à¦\85থবা <em>Ctrl-F5</em> à¦\9aাপà§\81ন\n* <strong>à¦\85পà§\87রা:</strong> <em>মà§\87নà§\81 â\86\92 à¦¬à§\8dযবসà§\8dথাপনাসমà§\82হ</em>-à¦\8f à¦¯à¦¾à¦¨ (মà§\8dযাà¦\95à§\87 <em>à¦\85পà§\87রা â\86\92 à¦ªà¦\9bনà§\8dদসমà§\82হ</em>) à¦\8fবà¦\82 à¦\8fরপর <em>à¦\97à§\8bপনà§\80য়তা à¦\93 à¦¸à§\81রà¦\95à§\8dষা â\86\92 à¦¬à§\8dরাà¦\89à¦\9cিà¦\82-à¦\8fর à¦¤à¦¥à§\8dয à¦ªà¦°à¦¿à¦·à§\8dà¦\95ার à¦\95রà§\81ন â\86\92 à¦\95à§\8dযাশà§\87 à¦\95রা à¦\9bবি à¦\93 à¦«à¦¾à¦\87লà¦\97à§\81লি</em>।",
        "usercssyoucanpreview": "'''পরামর্শ:''' \"{{int:showpreview}}\" বোতাম ব্যবহার করে সংরক্ষণের আগে আপনার নতুন CSS পরীক্ষা করুন।",
        "userjsyoucanpreview": "'''পরামর্শ:''' \"{{int:showpreview}}\" বোতাম ব্যবহার করে সংরক্ষণের আগে আপনার নতুন JavaScript পরীক্ষা করুন।",
        "usercsspreview": "'''মনে রাখবেন আপনি আপনার জন্য বরাদ্ধকৃত সিএসএস প্রাকদর্শন করছেন।\nএটা এখনও সংরক্ষণ করা হয়নি!'''",
        "continue-editing": "সম্পাদনা করুন",
        "previewconflict": "এই প্রাকদর্শনটি সম্পাদনা ক্ষেত্রের উপরের অংশটির টেক্সট সংরক্ষণ করলে যেরকম দেখাবে, তা দেখাচ্ছে।",
        "session_fail_preview": "দুঃখিত! সেশন ডাটা হারিয়ে যাওয়ার কারণে আপনার সম্পাদনাটি সংরক্ষণ করা সম্ভব হয়নি।\n\nআপনি সম্ভবত সংযোগ হারিয়েছন। <strong>দয়া করে যাচাই করুন যে আপনি এখনও প্রবেশরত রয়েছেন এবং আবার চেষ্টা করুন</strong>। যদি এটি এখনও কাজ না করে, তাহলে দয়া করে [[Special:UserLogout|অ্যাকাউন্ট থেকে প্রস্থান করুন]] এবং আবার অ্যাকাউন্টে প্রবেশ করে চেষ্টা করুন এবং এবং পরীক্ষা করুন যে আপনার ব্রাউজার এই সাইটে কুকি ব্যবহারের অনুমতি দেয়।",
-       "session_fail_preview_html": "'''দুঃখিত! সেশন উপাত্ত হারিয়ে যাওয়ার কারণে আমরা আপনার সম্পাদনাটি প্রক্রিয়া করতে পারিনি।'''\n\n''{{SITENAME}}-এ raw HTML সক্রিয় আছে বলে জাভাস্ক্রিপ্টভিত্তিক আক্রমণ থেকে প্রতিরক্ষার জন্য প্রাকদর্শনটি দেখানো হচ্ছে না।''\n\n'''যদি এটি সম্পাদনার একটি বৈধ প্রচেষ্টা হয়, তবে অনুগ্রহ করে আবার চেষ্টা করুন। যদি তারপরেও কাজ না হয়, তবে অ্যাকাউন্ট থেকে বেরিয়ে গিয়ে আবার প্রবেশ করে চেষ্টা করুন।'''",
+       "session_fail_preview_html": "দুঃখিত! সেশন উপাত্ত হারিয়ে যাওয়ার কারণে আমরা আপনার সম্পাদনাটি প্রক্রিয়া করতে পারিনি।\n\n<em>{{SITENAME}}-এ raw HTML সক্রিয় আছে বলে জাভাস্ক্রিপ্ট ভিত্তিক আক্রমণ থেকে প্রতিরক্ষার জন্য প্রাকদর্শনটি দেখানো হচ্ছে না।</em>\n\n<strong>যদি এটি সম্পাদনার একটি বৈধ প্রচেষ্টা হয়, তবে অনুগ্রহ করে আবার চেষ্টা করুন।</strong>\nযদি তারপরেও কাজ না হয়, তবে অ্যাকাউন্ট থেকে [[Special:UserLogout|বেরিয়ে গিয়ে]] আবার প্রবেশ করুন, এবং পরীক্ষা করে দেখুন যে আপনার ব্রাউজারে এই সাইট থেকে কুকি অনুমতি দেয়।",
        "token_suffix_mismatch": "'''আপনার সম্পাদনাটি প্রত্যাখ্যান করা হয়েছে, কারণ আপনার ক্লায়েন্ট প্রোগ্রামটি সম্পাদনা টেক্সটের বিরামচিহ্নগুলি গুলিয়ে ফেলেছে। পাতাটির টেক্সটে যাতে ক্ষতি না হয় সেজন্য সম্পাদনাটি প্রত্যাখ্যান করা হয়েছে। আপনি কোন ত্রুটিপূর্ণ ওয়েব-ভিত্তিক বেনামী প্রক্সি সেবা ব্যবহার করলে এরকম হতে পারে।'''",
        "edit_form_incomplete": "'''আপনার সম্পাদনার কিছু অংশ সার্ভারে পৌছায় নি; আপনার সম্পাদনা সম্পূর্ণরুপে আছে কিনা নিশ্চিত হয়ে আবার চেষ্টা করুন'''",
        "editing": "সম্পাদনা করছেন: $1",
        "undo-summary-username-hidden": "একজন লুকানো ব্যবহারকারী $1 সংশোধন পুনরায় ফিরিয়ে এনেছেন",
        "cantcreateaccounttitle": "অ্যাকাউন্ট তৈরি করা যাবে না",
        "cantcreateaccount-text": "[[User:$3|$3]] এই আইপি ঠিকানা('''$1''') থেকে অ্যাকাউন্ট সৃষ্টিতে বাধা দিয়েছেন।\n\n$3-এর দেয়া কারণ হল ''$2''",
-       "cantcreateaccount-range-text": "[[User:$3|$3]] কর্তৃক আইপি ঠিকানা <strong>$1</strong> ব্যাপ্তির মধ্য থেকে অ্যাকাউন্ট তৈরি করা অবরুদ্ধ করা হয়েছে। যাতে আপনার আইপি ঠিকানা (<strong>$4</strong>) রয়েছে। \n\n$3 কর্তৃক <em>$2</em> কারণ দেখানো হয়েছে।",
+       "cantcreateaccount-range-text": "[[User:$3|$3]] কর্তৃক আইপি ঠিকানার ব্যাপ্তি <strong>$1</strong>-এর মধ্যে অ্যাকাউন্ট তৈরি করা অবরুদ্ধ করা হয়েছে। যাতে আপনার আইপি ঠিকানাও (<strong>$4</strong>) রয়েছে। \n\n$3 কর্তৃক <em>$2</em> কারণ দেখানো হয়েছে।",
        "viewpagelogs": "এই পাতার জন্য লগগুলো দেখুন",
        "nohistory": "এই পাতার কোন সম্পাদনার ইতিহাস নেই।",
        "currentrev": "সর্বশেষ সংস্করণ",
        "lockdbsuccesstext": "ডাটাবেজ বন্ধ করা হয়েছে\n<br />আপনার রক্ষণাবেক্ষণ সম্পন্ন হবার পর [[Special:UnlockDB|ডাটাবেজ খুলে দিতে]] ভুলবেন না।",
        "unlockdbsuccesstext": "ডাটাবেজ খুলে দেওয়া হয়েছে।",
        "lockfilenotwritable": "ডাটাবেজ বন্ধ করার ফাইলটি লিখনযোগ্য নয়। ডাটাবেজ বন্ধ করতে বা খুলতে চাইলে ফাইলটিকে ওয়েব সার্ভার কর্তৃক লিখনযোগ্য হতে হবে।",
+       "databaselocked": "ডাটাবেসটি ইতিমধ্যেই তালাবদ্ধ।",
        "databasenotlocked": "ডাটাবেজ বন্ধ নয়।",
        "lockedbyandtime": "({{GENDER:$1|$1}} $2 এর $3 সময়ে)",
        "move-page": "$1 স্থানান্তর",
        "tooltip-ca-nstab-category": "বিষয়শ্রেণী পাতাটি দেখুন",
        "tooltip-minoredit": "এটিকে অনুল্লেখ্য সম্পাদনা হিসেবে চিহ্নিত করা হোক",
        "tooltip-save": "আপনার পরিবর্তনগুলি সংরক্ষিত হোক",
+       "tooltip-publish": "আপনার পরিবর্তন প্রকাশ করুন",
        "tooltip-preview": "অনুগ্রহ করে সংরক্ষণের আগে আপনার পরিবর্তনগুলি প্রাকদর্শন করুন!",
        "tooltip-diff": "আপনি টেক্সটে কী কী পরিবর্তন করেছেন, তা দেখানো হোক।",
        "tooltip-compareselectedversions": "এই পাতার দুইটি নির্বাচিত সংস্করণের মধ্যে তুলনা দেখুন।",
        "watchlistedit-raw-done": "আপনার নজর তালিকা হালনাগাদ করা হয়েছে।",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 শিরোনাম|$1 শিরোনামসমূহ}} যোগ করা হয়েছে:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 শিরোনাম|$1 শিরোনামসমূহ}} মুছে ফেলা হয়েছে:",
-       "watchlistedit-clear-title": "নà¦\9cরতালিà¦\95া à¦ªà¦°à¦¿à¦¸à§\8dà¦\95ার à¦\95রা à¦¹à¦¯à¦¼à§\87à¦\9bà§\87",
+       "watchlistedit-clear-title": "নà¦\9cরতালিà¦\95া à¦ªà¦°à¦¿à¦·à§\8dà¦\95ার à¦\95রà§\81ন",
        "watchlistedit-clear-legend": "নজরতালিকা পরিস্কার",
        "watchlistedit-clear-explain": "সকল শিরোনামসমূহ আপনার নজরতালিকা থেকে সরিয়ে নেয়া হয়েছে।",
        "watchlistedit-clear-titles": "শিরোনামসমূহ:",
        "logentry-protect-protect-cascade": "$1 $3 {{GENDER:$2|সুরক্ষিত করেছেন}} $4 [প্রপাতাকার]",
        "logentry-protect-modify": "$1 $3-এর জন্য সুরক্ষা স্তর {{GENDER:$2|পরিবর্তন করেছেন}} $4",
        "logentry-protect-modify-cascade": "$1 $3-এর জন্য সুরক্ষা স্তর {{GENDER:$2|পরিবর্তন করেছেন}} $4 [প্রপাতাকার]",
-       "logentry-rights-rights": "$1 ব্যবহারকারী, $3 এর দলগত সদস্যপদ $4 থেকে $5 এ {{GENDER:$2|পরিবর্তন}} করেছেন",
+       "logentry-rights-rights": "$1 ব্যবহারকারী, {{GENDER:$6|$3}}-এর দলগত সদস্যপদ $4 থেকে $5 এ {{GENDER:$2|পরিবর্তন}} করেছেন",
        "logentry-rights-rights-legacy": "$1 দলের সদস্যপদ পরিবর্তন করেছেন {{GENDER:$2|changed}} এর জন্য $3",
        "logentry-rights-autopromote": "$1 স্বয়ংক্রিয়ভাবে $4 থেকে $5-এ {{GENDER:$2|উন্নীত}} হয়েছেন",
        "logentry-upload-upload": "$1 $3 {{GENDER:$2|আপলোড করেছেন}}",
        "randomrootpage": "অজানা মূল পাতা",
        "log-action-filter-block": "বাধাদানের ধরন:",
        "log-action-filter-delete": "অপসারণের ধরন:",
+       "log-action-filter-import": "আমদানির ধরন:",
        "log-action-filter-patrol": "টহলের ধরন:",
        "log-action-filter-protect": "সুরক্ষার ধরন:",
        "log-action-filter-upload": "আপলোডের ধরন:",
        "log-action-filter-protect-protect": "সুরক্ষা",
        "log-action-filter-protect-modify": "সুরক্ষা পরিমার্জন",
        "log-action-filter-protect-unprotect": "অসুরক্ষা",
+       "log-action-filter-rights-autopromote": "স্বয়ংক্রিয় পরিবর্তন",
        "log-action-filter-upload-upload": "নতুন আপলোড",
        "log-action-filter-upload-overwrite": "পুনঃআপলোড"
 }
index 3fbd79e..4a459fa 100644 (file)
@@ -24,6 +24,7 @@
        "tog-hideminor": "Kuzhat ar c'hemmoù nevez dister",
        "tog-hidepatrolled": "Kuzhat ar c'hemmoù evezhiet e-touez ar c'hemmoù diwezhañ",
        "tog-newpageshidepatrolled": "Kuzhat ar pajennoù evezhiet diouzh roll ar pajennoù nevez",
+       "tog-hidecategorization": "Kuzhat rummatadur ar pajennoù",
        "tog-extendwatchlist": "Astenn ar roll evezhiañ a-benn diskouez an holl gemmoù ha neket ar re ziwezhañ hepken.",
        "tog-usenewrc": "Diskouez ar c'hemmoù nevez en ur feson kempennoc'h",
        "tog-numberheadings": "Niverenniñ emgefre an titloù",
        "rows": "Linennoù :",
        "columns": "Bannoù",
        "searchresultshead": "Klask",
-       "stub-threshold": "Bevenn uhelañ evit al <a href=\"#\" class=\"stub\">liammoù war-du an danvez pennadoù</a> (okted) :",
+       "stub-threshold": "Bevenn uhelañ evit al liammoù war-du an danvez pennadoù ($1) :",
        "stub-threshold-disabled": "Diweredekaet",
        "recentchangesdays": "Niver a zevezhioù da ziskouez er c'hemmoù diwezhañ :",
        "recentchangesdays-max": "D'ar muiañ $1 {{PLURAL:$1|deiz|deiz}}",
        "prefs-emailconfirm-label": "Kadarnaat ar postel :",
        "youremail": "Postel :",
        "username": "{{GENDER:$1|Anv implijer|Anv implijerez}}:",
-       "prefs-memberingroups": "{{GENDER:$2|Ezel}} eus {{PLURAL:$1|ar strollad|ar strolladoù}} :",
+       "prefs-memberingroups": "{{GENDER:$2|Ezel}} eus {{PLURAL:$1|ar strollad|ar strolladoù}}:",
        "prefs-registration": "Deiziad enskrivañ :",
        "yourrealname": "Anv gwir*",
        "yourlanguage": "Yezh an etrefas&nbsp;",
        "delete-confirm": "Diverkañ \"$1\"",
        "delete-legend": "Diverkañ",
        "historywarning": "<strong>Diwallit :</strong> Emaoc'h war-nes diverkañ ur bajenn dezhi un istor gant {{PLURAL:$1|adweladenn}} :",
+       "historyaction-submit": "Diskouez",
        "confirmdeletetext": "War-nes diverkañ da viken ur bajenn pe ur skeudenn eus ar bank roadennoù emaoc'h. Diverket e vo ivez an holl stummoù kozh stag outi.\nKadarnait, mar plij, eo mat an dra-se hoc'h eus c'hoant da ober, e komprenit mat an heuliadoù, hag e rit se diouzh ar [[{{MediaWiki:Policy-url}}]].",
        "actioncomplete": "Diverkadenn kaset da benn",
        "actionfailed": "Ober c'hwitet",
index cfe09b6..86f481e 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|anterior|anteriors $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|següent|següents $1}}",
        "whatlinkshere-links": "← enllaços",
-       "whatlinkshere-hideredirs": "$1 redireccions",
-       "whatlinkshere-hidetrans": "$1 inclusions",
-       "whatlinkshere-hidelinks": "$1 enllaços",
+       "whatlinkshere-hideredirs": "Amaga les redireccions",
+       "whatlinkshere-hidetrans": "Amagar transclusions",
+       "whatlinkshere-hidelinks": "Amagar enllaços",
        "whatlinkshere-hideimages": "$1 enllaços de fitxers",
        "whatlinkshere-filters": "Filtres",
        "whatlinkshere-submit": "Vés-hi",
index 5cf3198..b260279 100644 (file)
        "changepassword-success": "Vaše heslo bylo změněno!",
        "changepassword-throttled": "Provedli jste příliš mnoho pokusů o přihlášení.\nČekejte prosím $1 a zkuste to znovu.",
        "botpasswords": "Hesla pro boty",
-       "botpasswords-summary": "<em>Hesla pro boty</em> umožňují přistupovat k uživatelskému účtu prostřednictví API bez použití hlavních přihlašovacích údajů účtu. Uživatelská oprávnění dostupná po přihlášení pomocí hesla pro boty mohou být omezena.\n\nPokud nevíte, k čemu byste to {{GENDER:|chtěl|chtěla|chtěli}} použít, pravděpodobně byste to používat {{GENDER:|neměl|neměla|neměli}}. Nikdo by vás nikdy neměl žádat, abyste si zde vygenerovali heslo a dali mu ho.",
+       "botpasswords-summary": "<em>Hesla pro boty</em> umožňují přistupovat k uživatelskému účtu prostřednictví API bez použití hlavních přihlašovacích údajů účtu. Uživatelská oprávnění dostupná po přihlášení pomocí hesla pro boty mohou být omezena.\n\nPokud nevíte, k čemu byste to {{GENDER:|chtěl|chtěla|chtěli}} použít, pravděpodobně byste to používat {{GENDER:|neměl|neměla|neměli}}. Nikdo by vás nikdy neměl žádat, abyste si zde {{GENDER:|vygeneroval heslo a dal|vygenerovala heslo a dala|vygenerovali heslo a dali}} mu ho.",
        "botpasswords-disabled": "Hesla pro boty jsou zakázána.",
        "botpasswords-no-central-id": "Abyste {{GENDER:|mohl|mohla|mohl(a)}} použít hesla pro boty, musíte být {{GENDER:|přihlášen|přihlášena|přihlášen(a)}} k centrálnímu účtu.",
        "botpasswords-existing": "Stávající hesla pro boty",
        "changecontentmodel-success-text": "Model obsahu stránky [[:$1]] byl změněn.",
        "changecontentmodel-cannot-convert": "Obsah stránky [[:$1]] nelze zkonvertovat na typ $2.",
        "changecontentmodel-nodirectediting": "Model obsahu $1 nepodporuje přímou editaci",
+       "changecontentmodel-emptymodels-title": "Nejsou k dispozici žádné modely obsahu",
+       "changecontentmodel-emptymodels-text": "Obsah stránky [[:$1]] nelze zkonvertovat na žádný typ.",
        "log-name-contentmodel": "Kniha změny modelů obsahu",
        "log-description-contentmodel": "Události týkající se modelů obsahu stránek",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|založil|založila}} stránku $3 za použití nestandardního modelu obsahu „$5“",
        "whatlinkshere-prev": "{{PLURAL:$1|předchozí|předchozí $1|předchozích $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|následující|následující $1|následujících $1}}",
        "whatlinkshere-links": "← odkazy",
-       "whatlinkshere-hideredirs": "$1 přesměrování",
-       "whatlinkshere-hidetrans": "$1 vložení",
-       "whatlinkshere-hidelinks": "$1 odkazy",
-       "whatlinkshere-hideimages": "$1 vložení souboru",
+       "whatlinkshere-hideredirs": "Skrýt přesměrování",
+       "whatlinkshere-hidetrans": "Skrýt vložení",
+       "whatlinkshere-hidelinks": "Skrýt odkazy",
+       "whatlinkshere-hideimages": "Skrýt vložení souboru",
        "whatlinkshere-filters": "Filtry",
        "whatlinkshere-submit": "Přejít",
        "autoblockid": "Autoblok #$1",
        "lockdbsuccesstext": "Databáze {{grammar:2sg|{{SITENAME}}}} byla úspěšně uzamčena.\n<br />Nezapomeňte ji po dokončení údržby [[Special:UnlockDB|odemknout]].",
        "unlockdbsuccesstext": "Databáze {{grammar:2sg|{{SITENAME}}}} je odemčena.",
        "lockfilenotwritable": "Do souboru zámku databáze nelze zapisovat. Pro zamčení či odemčení databáze musí mít webový server právo zápisu do tohoto souboru.",
+       "databaselocked": "Databáze je již zamčená.",
        "databasenotlocked": "Databáze není uzamčena.",
        "lockedbyandtime": "({{gender:$1|zamkl|zamkla|zamkl}} $1 $2 v $3)",
        "move-page": "Přesunout „$1“",
index 6ed4fad..4cbca02 100644 (file)
@@ -24,7 +24,7 @@
        "tog-hideminor": "Cuddio golygiadau bychain yn rhestr y newidiadau diweddar",
        "tog-hidepatrolled": "Cuddio golygiadau sydd wedi derbyn ymweliad patrôl rhag y rhestr newidiadau diweddar",
        "tog-newpageshidepatrolled": "Cuddio tudalennau a batroliwyd o'r rhestr y tudalennau newydd",
-       "tog-hidecategorization": "Cuddiwych y categoriau",
+       "tog-hidecategorization": "Cuddiwch y categoriau",
        "tog-extendwatchlist": "Ehangu'r rhestr wylio i ddangos pob golygiad yn hytrach na'r diweddaraf yn unig",
        "tog-usenewrc": "Grwpio'r newidiadau bob yn ddalen yn y 'newidiadau diweddar' a'r 'rhestr wylio'",
        "tog-numberheadings": "Rhifo penawdau'n awtomatig",
@@ -55,7 +55,7 @@
        "tog-watchlistreloadautomatically": "Ail-lwyther y Rhestr wylio yn otomatigpan newider ffiltr (angen JavaScript)",
        "tog-watchlisthideanons": "Cuddio golygiadau gan ddefnyddwyr anhysbys rhag y rhestr wylio",
        "tog-watchlisthidepatrolled": "Cuddio golygiadau sydd wedi derbyn ymweliad patrôl rhag y rhestr wylio",
-       "tog-watchlisthidecategorization": "Cuddiwych y categoriau",
+       "tog-watchlisthidecategorization": "Cuddiwch y categoriau",
        "tog-ccmeonemails": "Anfon copi ataf pan anfonaf e-bost at ddefnyddiwr arall",
        "tog-diffonly": "Peidio â dangos cynnwys y dudalen islaw'r gymhariaeth ar dudalennau cymharu",
        "tog-showhiddencats": "Dangos categorïau cuddiedig",
        "rcshowhidemine": "$1 fy ngolygiadau",
        "rcshowhidemine-show": "Dangoser",
        "rcshowhidemine-hide": "Cuddier",
-       "rcshowhidecategorization": "Categorieiddio tudalen $1",
+       "rcshowhidecategorization": "Categoreiddio tudalen $1",
        "rcshowhidecategorization-show": "Dangos",
        "rcshowhidecategorization-hide": "Cuddio",
        "rclinks": "Dangos y $1 newid diweddaraf yn ystod y(r) $2 diwrnod diwethaf<br />$3",
index 8c40276..05c4418 100644 (file)
        "revdelete-restricted": "tilføjede begrænsninger for administratorer",
        "revdelete-unrestricted": "fjernede begrænsninger for administratorer",
        "logentry-block-block": "$1 {{GENDER:$2|blokerede}} {{GENDER:$4|$3}} med en udløbstid på $5 $6",
+       "logentry-block-unblock": "$1 {{GENDER:$2|ophævede blokering af}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|ændrede}} blokeringsindstillinger for {{GENDER:$4|$3}} med en udløbstid på $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|ændrede}} blokeringsindstillinger for {{GENDER:$4|$3}} med en udløbstid på $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|flyttede}} siden $3 til $4",
index 99607c5..be7ed04 100644 (file)
        "blankarticle": "<strong>Warnung:</strong> Die Seite, die du erstellst, ist leer.\nWenn du erneut auf „{{int:savearticle}}“ klickst, wird die Seite ohne Inhalt erstellt.",
        "anoneditwarning": "<strong>Warnung:</strong> Du bist nicht angemeldet. Deine IP-Adresse wird öffentlich sichtbar, falls du Bearbeitungen durchführst. Sofern du dich <strong>[$1 anmeldest]</strong> oder <strong>[$2 ein Benutzerkonto erstellst]</strong>, werden deine Bearbeitungen zusammen mit anderen Beiträgen deinem Benutzernamen zugeordnet.",
        "anonpreviewwarning": "''Du bist nicht angemeldet. Beim Speichern wird deine IP-Adresse in der Versionsgeschichte aufgezeichnet.''",
-       "missingsummary": "'''Hinweis:''' Du hast keine Zusammenfassung angegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Änderung ohne Zusammenfassung übernommen.",
+       "missingsummary": "<strong>Hinweis:</strong> Du hast keine Zusammenfassung angegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Änderung ohne Zusammenfassung übernommen.",
        "selfredirect": "<strong>Warnung:</strong> Du leitest auf diese Seite selbst weiter.\nDu hast vermutlich das falsche Weiterleitungsziel angegeben oder du bearbeitest die falsche Seite.\nWenn du erneut auf „{{int:savearticle}}“ klickst, wird die Weiterleitung dennoch erstellt.",
        "missingcommenttext": "Bitte gib unten einen Kommentar ein.",
        "missingcommentheader": "<strong>Achtung:</strong> Du hast keinen Betreff eingegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Bearbeitung ohne Überschrift gespeichert.",
        "editpage-cannot-use-custom-model": "Das Inhaltsmodell dieser Seite kann nicht geändert werden.",
        "longpageerror": "'''Fehler: Der Text, den du zu speichern versuchst, ist {{PLURAL:$1|ein Kilobyte|$1 Kilobyte}} groß. Dies ist größer als das erlaubte Maximum von {{PLURAL:$2|ein Kilobyte|$2 Kilobyte}}.'''\nEr kann nicht gespeichert werden.",
        "readonlywarning": "<strong>Achtung: Die Datenbank wurde für Wartungsarbeiten gesperrt, so dass deine Änderungen derzeit nicht gespeichert werden können.\nSichere den Text bitte lokal auf deinem Computer und versuche zu einem späteren Zeitpunkt, die Änderungen zu übertragen.</strong>\n\nGrund für die Sperre: $1",
-       "protectedpagewarning": "'''Achtung: Diese Seite wurde geschützt. Nur Benutzer mit Administratorrechten können die Seite bearbeiten.'''\nZur Information folgt der aktuelle Logbucheintrag:",
+       "protectedpagewarning": "<strong>Achtung: Diese Seite wurde geschützt. Nur Benutzer mit Administratorrechten können die Seite bearbeiten.</strong>\nZur Information folgt der aktuelle Logbucheintrag:",
        "semiprotectedpagewarning": "'''Halbsperrung:''' Die Seite wurde so geschützt, dass nur registrierte Benutzer diese ändern können.\nZur Information folgt der aktuelle Logbucheintrag:",
        "cascadeprotectedwarning": "<strong>Achtung:</strong> Diese Seite wurde so geschützt, dass sie nur durch Benutzer mit Administratorrechten bearbeitet werden kann. Sie ist in die {{PLURAL:$1|folgende Seite|folgenden Seiten}} eingebunden, die mittels der Kaskadensperroption geschützt {{PLURAL:$1|ist|sind}}:",
        "titleprotectedwarning": "'''Achtung: Die Seitenerstellung wurde so geschützt, dass nur Benutzer mit [[Special:ListGroupRights|speziellen Rechten]] diese Seite erstellen können.'''\nZur Information folgt der aktuelle Logbucheintrag:",
        "permissionserrorstext": "Du bist nicht berechtigt, die Aktion auszuführen. {{PLURAL:$1|Grund|Gründe}}:",
        "permissionserrorstext-withaction": "Du bist aus {{PLURAL:$1|dem folgenden Grund|den folgenden Gründen}} nicht berechtigt, $2:",
        "contentmodelediterror": "Du kannst diese Version nicht bearbeiten, da das Inhaltsmodell <code>$1</code> vom aktuellen Inhaltsmodell der Seite <code>$2</code> abweicht.",
-       "recreate-moveddeleted-warn": "'''Achtung: Du erstellst eine Seite, die bereits früher gelöscht wurde.'''\n\nBitte prüfe sorgfältig, ob die erneute Seitenerstellung den Richtlinien entspricht.\nZu deiner Information folgt das Lösch- und Verschiebungs-Logbuch mit der Begründung für die vorhergehende Löschung:",
+       "recreate-moveddeleted-warn": "<strong>Achtung: Du erstellst eine Seite, die bereits früher gelöscht wurde.</strong>\n\nBitte prüfe sorgfältig, ob die erneute Seitenerstellung den Richtlinien entspricht.\nZu deiner Information folgt das Lösch- und Verschiebungs-Logbuch mit der Begründung für die vorhergehende Löschung:",
        "moveddeleted-notice": "Diese Seite wurde gelöscht. Zur Information folgt das Lösch- und Verschiebungs-Logbuch dieser Seite.",
        "moveddeleted-notice-recent": "Leider wurde diese Seite kürzlich gelöscht (innerhalb der letzten 24 Stunden).\nZur Information wird das Lösch- und Verschiebungs-Logbuch für die Seite unten angezeigt.",
        "log-fulllog": "Alle Logbucheinträge ansehen",
        "whatlinkshere-prev": "{{PLURAL:$1|vorheriger|vorherige $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|nächster|nächste $1}}",
        "whatlinkshere-links": "← Links",
-       "whatlinkshere-hideredirs": "Weiterleitungen $1",
-       "whatlinkshere-hidetrans": "Vorlageneinbindungen $1",
-       "whatlinkshere-hidelinks": "Links $1",
-       "whatlinkshere-hideimages": "Dateilinks $1",
+       "whatlinkshere-hideredirs": "Weiterleitungen ausblenden",
+       "whatlinkshere-hidetrans": "Vorlageneinbindungen ausblenden",
+       "whatlinkshere-hidelinks": "Links ausblenden",
+       "whatlinkshere-hideimages": "Dateilinks ausblenden",
        "whatlinkshere-filters": "Filter",
        "whatlinkshere-submit": "Los",
        "autoblockid": "Automatische Sperrung #$1",
        "lockdbsuccesstext": "Die {{SITENAME}}-Datenbank wurde gesperrt.<br />Bitte gib die Datenbank [[Special:UnlockDB|wieder frei]], sobald die Wartung abgeschlossen ist.",
        "unlockdbsuccesstext": "Die {{SITENAME}}-Datenbank wurde freigegeben.",
        "lockfilenotwritable": "Die Datenbank-Sperrdatei ist nicht beschreibbar. Zum Sperren oder Freigeben der Datenbank muss diese für den Webserver beschreibbar sein.",
+       "databaselocked": "Die Datenbank ist bereits gesperrt.",
        "databasenotlocked": "Die Datenbank ist nicht gesperrt.",
        "lockedbyandtime": "(von $1 am $2 um $3 Uhr)",
        "move-page": "Verschieben von „$1“",
        "movepagetext": "Mit untenstehendem Formular kannst du eine Seite umbenennen, indem du sie mitsamt allen Versionen auf einen neuen Titel verschiebst.\nDer alte Titel wird danach zum neuen weiterleiten.\nDu kannst Weiterleitungen, die auf den Originaltitel verlinken, automatisch korrigieren lassen.\nStelle sicher, dass du im Anschluss alle [[Special:DoubleRedirects|doppelten]] oder [[Special:BrokenRedirects|defekten Weiterleitungen]] überprüfst.\nDu bist dafür verantwortlich, dass Links weiterhin auf das korrekte Ziel verweisen.\n\nDie Seite wird <strong>nicht</strong> verschoben, sofern es bereits eine Seite mit dem vorgesehenen Titel gibt, es sei denn, letztere ist eine Weiterleitung ohne Versionsgeschichte.\nDies bedeutet, dass du die Umbenennung rückgängig machen kannst, sofern du einen Fehler gemacht hast. Du kannst hingegen keine existierende Seite überschreiben.\n\n<strong>Hinweis:</strong>\nDie Verschiebung kann weitreichende und unerwartete Folgen für häufig besuchte Seiten haben.\nDu solltest daher die Konsequenzen verstanden haben, bevor du jetzt fortfährst.",
        "movepagetext-noredirectfixer": "Mit untenstehendem Formular kannst du eine Seite umbenennen, indem du sie mitsamt allen Versionen auf einen neuen Titel verschiebst.\nDer alte Titel wird danach zum neuen weiterleiten.\nStelle sicher, dass du im Anschluss alle [[Special:DoubleRedirects|doppelten]] oder [[Special:BrokenRedirects|defekten Weiterleitungen]] überprüfst.\nDu bist dafür verantwortlich, dass Links weiterhin auf das korrekte Ziel verweisen.\n\nDie Seite wird <strong>nicht</strong> verschoben, sofern es bereits eine Seite mit dem vorgesehenen Titel gibt, es sei denn, diese ist eine Weiterleitung ohne Versionsgeschichte.\nDies bedeutet, dass du die Umbenennung rückgängig machen kannst, sofern du einen Fehler gemacht hast. Du kannst hingegen keine existierende Seite überschreiben.\n\n<strong>Hinweis:</strong>\nDie Verschiebung kann weitreichende und unerwartete Folgen für häufig besuchte Seiten haben.\nDu solltest daher die Konsequenzen verstanden haben, bevor du jetzt fortfährst.",
        "movepagetalktext": "Falls du dieses Kästchen aktivierst, wird die dazugehörige Diskussionsseite automatisch auf den neuen Titel verschoben, sofern nicht bereits eine nicht-leere Diskussionsseite dort vorhanden ist.\n\nIn diesem Fall musst du die Seite manuell verschieben oder zusammenführen, falls erforderlich.",
-       "moveuserpage-warning": "'''Warnung:''' Du bist dabei, eine Benutzerseite zu verschieben. Bitte bedenke, dass dadurch nur die Benutzerseite verschoben, '''nicht''' aber der Benutzer umbenannt wird.",
+       "moveuserpage-warning": "<strong>Warnung:</strong> Du bist dabei, eine Benutzerseite zu verschieben. Bitte bedenke, dass dadurch nur die Benutzerseite verschoben, <em>nicht</em> aber der Benutzer umbenannt wird.",
        "movecategorypage-warning": "<strong>Warnung:</strong> Du bist gerade dabei, eine Kategorieseite zu verschieben. Bitte sei dir bewusst, dass nur die Seite verschoben wird. Alle der alten Kategorie zugeordneten Seiten werden <em>nicht</em> neu kategorisiert.",
        "movenologintext": "Du musst ein registrierter Benutzer und [[Special:UserLogin|angemeldet]] sein, um eine Seite zu verschieben.",
        "movenotallowed": "Du hast nicht die erforderliche Berechtigung, um Seiten verschieben zu können.",
        "imageinvalidfilename": "Der Ziel-Dateiname ist ungültig",
        "fix-double-redirects": "Nach dem Verschieben alle Weiterleitungen auf die Ursprungsseite bereinigen",
        "move-leave-redirect": "Weiterleitung erstellen",
-       "protectedpagemovewarning": "'''Warnung:''' Diese Seite wurde so geschützt, dass sie nur von Benutzern mit Administratorenrechten verschoben werden kann.\nZur Information folgt der aktuelle Logbucheintrag:",
+       "protectedpagemovewarning": "<strong>Warnung:</strong> Diese Seite wurde so geschützt, dass sie nur von Benutzern mit Administratorenrechten verschoben werden kann.\nZur Information folgt der aktuelle Logbucheintrag:",
        "semiprotectedpagemovewarning": "'''Hinweis:''' Diese Seite wurde so geschützt, dass sie nur von angemeldeten Benutzern verschoben werden kann.\nZur Information folgt der aktuelle Logbucheintrag:",
        "move-over-sharedrepo": "[[:$1]] existiert in einem gemeinsam genutzten Repositorium. Das Verschieben einer Datei zu diesem Titel überschreibt die gemeinsam genutzte Datei.",
        "file-exists-sharedrepo": "Der gewählte Dateiname wird bereits in einem gemeinsam genutzten Repositorium verwendet.\nBitte wähle einen anderen Namen.",
        "filedelete-archive-read-only": "Das Archiv-Verzeichnis „$1“ ist für den Webserver nicht beschreibbar.",
        "previousdiff": "← Zum vorherigen Versionsunterschied",
        "nextdiff": "Zum nächsten Versionsunterschied →",
-       "mediawarning": "'''Warnung:''' Dieser Dateityp kann böswilligen Programmcode enthalten.\nDurch das Herunterladen und Öffnen der Datei kann dein Computer beschädigt werden.",
+       "mediawarning": "<strong>Warnung:</strong> Dieser Dateityp kann böswilligen Programmcode enthalten.\nDurch das Herunterladen und Öffnen der Datei kann dein Computer beschädigt werden.",
        "imagemaxsize": "Maximale Bildgröße:<br />''(für Dateibeschreibungsseiten)''",
        "thumbsize": "Standardgröße der Vorschaubilder:",
        "widthheightpage": "$1 × $2, {{PLURAL:$3|1 Seite|$3 Seiten}}",
index ba9a6e0..3578658 100644 (file)
@@ -22,7 +22,8 @@
                        "아라",
                        "Calak",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Kumkumuk"
                ]
        },
        "tog-underline": "Bınê gırey de xete bance:",
        "whatlinkshere-prev": "{{PLURAL:$1|veror|veror $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|verni|verni $1}}",
        "whatlinkshere-links": "← gırey",
-       "whatlinkshere-hideredirs": "Hetenayışê $1",
-       "whatlinkshere-hidetrans": "Açarnayışê $1",
-       "whatlinkshere-hidelinks": "Greyê $1",
+       "whatlinkshere-hideredirs": "Hetenayışan bınımne",
+       "whatlinkshere-hidetrans": "Gırêyanê açarnayışan bınımne",
+       "whatlinkshere-hidelinks": "Gırêyan bınımne",
        "whatlinkshere-hideimages": "Gıreyê dosya $1",
        "whatlinkshere-filters": "Avrêci",
        "autoblockid": "Otomatik vındarnayış #$1",
index 379bbec..6d198c2 100644 (file)
        "apisandbox-dynamic-parameters-add-placeholder": "Ονομασία παραμέτρου",
        "apisandbox-dynamic-error-exists": "Η παράμετρος με την ονομασία \"$1\" υπάρχει ήδη",
        "apisandbox-submit-invalid-fields-title": "Κάποια από τα πεδία δεν είναι έγκυρα",
-       "apisandbox-submit-invalid-fields-message": "ΠαÏ\81ακαλÏ\8e Î´Î¹Î¿Ï\81θÏ\8eÏ\83Ï\84ε Ï\84α Ï\83ημειÏ\89μένα Ï\80εδία ÎºÎ±Î¹ Ï\80Ï\81οÏ\83Ï\80αθείστε ξανά.",
+       "apisandbox-submit-invalid-fields-message": "ΠαÏ\81ακαλÏ\8e Î´Î¹Î¿Ï\81θÏ\8eÏ\83Ï\84ε Ï\84α Ï\83ημειÏ\89μένα Ï\80εδία ÎºÎ±Î¹ Ï\80Ï\81οÏ\83Ï\80αθήστε ξανά.",
        "apisandbox-results": "Αποτελέσματα",
        "apisandbox-sending-request": "Αποστολή αιτήματος API...",
        "apisandbox-loading-results": "Λήψη αποτελεσμάτων API...",
index 10bf2c9..08d95b9 100644 (file)
        "image_tip": "Embedded file",
        "media_sample": "Example.ogg",
        "media_tip": "File link",
+       "sig-text": "--$1",
        "sig_tip": "Your signature with timestamp",
        "hr_tip": "Horizontal line (use sparingly)",
        "summary": "Summary:",
        "right-override-export-depth": "Export pages including linked pages up to a depth of 5",
        "right-sendemail": "Send email to other users",
        "right-passwordreset": "View password reset emails",
-       "right-managechangetags": "Create and delete [[Special:Tags|tags]] from the database",
+       "right-managechangetags": "Create and (de)activate [[Special:Tags|tags]]",
        "right-applychangetags": "Apply [[Special:Tags|tags]] along with one's changes",
        "right-changetags": "Add and remove arbitrary [[Special:Tags|tags]] on individual revisions and log entries",
+       "right-deletechangetags": "Delete [[Special:Tags|tags]] from the database",
        "grant-generic": "\"$1\" rights bundle",
        "grant-group-page-interaction": "Interact with pages",
        "grant-group-file-interaction": "Interact with media",
        "action-viewmyprivateinfo": "view your private information",
        "action-editmyprivateinfo": "edit your private information",
        "action-editcontentmodel": "edit the content model of a page",
-       "action-managechangetags": "create and delete tags from the database",
+       "action-managechangetags": "create and (de)activate tags",
        "action-applychangetags": "apply tags along with your changes",
        "action-changetags": "add and remove arbitrary tags on individual revisions and log entries",
+       "action-deletechangetags": "delete tags from the database",
        "nchanges": "$1 {{PLURAL:$1|change|changes}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|since last visit}}",
        "enhancedrc-history": "history",
        "whatlinkshere-prev": "{{PLURAL:$1|previous|previous $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|next|next $1}}",
        "whatlinkshere-links": "← links",
-       "whatlinkshere-hideredirs": "$1 redirects",
-       "whatlinkshere-hidetrans": "$1 transclusions",
-       "whatlinkshere-hidelinks": "$1 links",
-       "whatlinkshere-hideimages": "$1 file links",
+       "whatlinkshere-hideredirs": "Hide redirects",
+       "whatlinkshere-hidetrans": "Hide transclusions",
+       "whatlinkshere-hidelinks": "Hide links",
+       "whatlinkshere-hideimages": "Hide file links",
        "whatlinkshere-filters": "Filters",
        "whatlinkshere-submit": "Go",
        "autoblockid": "Autoblock #$1",
        "tags-delete-not-found": "The tag \"$1\" does not exist.",
        "tags-delete-too-many-uses": "The tag \"$1\" is applied to more than $2 {{PLURAL:$2|revision|revisions}}, which means it cannot be deleted.",
        "tags-delete-warnings-after-delete": "The tag \"$1\" was deleted, but the following {{PLURAL:$2|warning was|warnings were}} encountered:",
+       "tags-delete-no-permission": "You do not have permission to delete change tags.",
        "tags-activate-title": "Activate tag",
        "tags-activate-question": "You are about to activate the tag \"$1\".",
        "tags-activate-reason": "Reason:",
index b9ef105..a481bf4 100644 (file)
@@ -89,7 +89,7 @@
        "tog-ccmeonemails": "Sendi al mi kopiojn de retpoŝtaĵoj, kiujn mi sendis al aliaj uzantoj.",
        "tog-diffonly": "Ne montri paĝan enhavon sub la ŝanĝmontrilo",
        "tog-showhiddencats": "Montri kaŝitajn kategoriojn",
-       "tog-norollbackdiff": "Preterlasi ŝanĝoelmontron post malfaro",
+       "tog-norollbackdiff": "Nemontri diferencon post plenumado de ŝanĝomalfaro",
        "tog-useeditwarning": "Averti min kiam mi forlasas redaktan paĝon kun nekonservitaj ŝanĝoj",
        "tog-prefershttps": "Ĉiam uzu sekuran konekton ensalutite",
        "underline-always": "Ĉiam",
        "talk": "Diskuto",
        "views": "Vidoj",
        "toolbox": "Iloj",
-       "userpage": "Vidi uzantan paĝon",
+       "userpage": "Vidi uzulan paĝon",
        "projectpage": "Rigardi projektopaĝon",
        "imagepage": "Vidi dosieropaĝon",
        "mediawikipage": "Vidi mesaĝopaĝon",
        "missingarticle-rev": "(versio#: $1)",
        "missingarticle-diff": "(Diferenco inter versioj: $1, $2)",
        "readonly_lag": "La datumbazo estis aŭtomate ŝlosita dum la subdatumbazo atingas la ĉefan datumbazon.",
+       "nonwrite-api-promise-error": "La 'Promeso-Ne-Skribi-API-Ago' HTTPa titolo estis sendita sed la peto estis al API skriba modulo.",
        "internalerror": "Interna eraro",
        "internalerror_info": "Interna eraro: $1",
        "internalerror-fatal-exception": "Neriparebla escepto de la tipo \"$1\"",
        "noemail": "Retpoŝtadreso ne estas registrita por uzanto \"$1\".",
        "noemailcreate": "Vi devas provizi validan retadreson",
        "passwordsent": "Oni sendis novan pasvorton al la retpoŝtadreso\nregistrita por \"$1\".\nBonvolu ensaluti denove ricevinte ĝin.",
-       "blocked-mailpassword": "Via IP-adreso estas forbarita de redaktado. Por preventi fiuzojn, la pasvorto-restaŭron estas malpermesita per tiu IP-adreso.",
+       "blocked-mailpassword": "Via IP-adreso estas forbarita de redaktado. Por preventi fiuzojn, la pasvorto-restaŭro per tiu IP-adreso estas malpermesita.",
        "eauthentsent": "Konfirma retmesaĝo estis sendita al la nomita retadreso. Antaŭ ol iu ajn alia mesaĝo estos sendita al la konto, vi devos sekvi la instrukciojn en la mesaĝo por konfirmi ke la konto ja estas via.",
        "throttled-mailpassword": "Retpoŝto kun reŝargita pasvorto estis jam sendita ene de la {{PLURAL:$1|lasta horo|lastaj $1 horoj}}.\nPor preventi misuzon, nur unu reŝargita pasvorto estos sendita dum {{PLURAL:$1|horo|$1 horoj}}.",
        "mailerror": "Okazis eraro sendante retpoŝtaĵon: $1",
        "botpasswords-label-grants": "Uzeblaj permesdonoj:",
        "botpasswords-help-grants": "Ĉiu permesdono provizas aliron al listitaj uzantaj permisoj, kiujn uzantkonto jam havas. Vidu la [[Special:ListGrants|tabelon de permisdonoj]] por pli da informo.",
        "botpasswords-label-restrictions": "Limigoj de uzado:",
-       "botpasswords-label-grants-column": "Permisdonita",
+       "botpasswords-label-grants-column": "Permeso donita",
        "botpasswords-bad-appid": "La robota nomo \"$1\" estas malvalida.",
        "botpasswords-insert-failed": "Aldono de la robota nomo \"$1\" ne sukcesis. Ĉu ĝi jam estis aldonita?",
        "botpasswords-update-failed": "Ĝisdatigo de la robota nomo \"$1\" ne sukcesis. Ĉu ĝi estis forigita?",
        "botpasswords-updated-body": "La robota pasvorto por robota nomo \"$1\" de la uzanto \"$2\" estis ĝisdatigita.",
        "botpasswords-deleted-title": "Robota pasvorto forigita",
        "botpasswords-deleted-body": "La robota pasvorto por robota nomo \"$1\" de la uzanto \"$2\" estis forigita.",
-       "botpasswords-newpassword": "La nova pasvorto por ensaluti kun <strong>$1</strong> estas <strong>$2</strong>. <em>Bonvolu registri tiun por referenconto.",
+       "botpasswords-newpassword": "La nova pasvorto por ensaluti per <strong>$1</strong> estas <strong>$2</strong>. <em>Bonvolu noti ĝin por estonta konsultado.",
        "botpasswords-no-provider": "Robotopasvortensalutoprovizilo (''BotPasswordsSessionProvider'') maldisponeblas.",
-       "botpasswords-restriction-failed": "Limigoj pri robota pasvorto maleblas tiun uzantonomon.",
+       "botpasswords-restriction-failed": "Limigoj pri robota pasvorto malebligas tiun ensalutadon.",
        "botpasswords-invalid-name": "La difinita uzantnomo malenhavas la robotopasvortan disigilon (\"$1\").",
        "botpasswords-not-exist": "Uzanto \"$1\" ne havas robotopasvorton, kiu nomiĝas \"$2\".",
        "resetpass_forbidden": "Pasvortoj ne estas ŝanĝeblaj",
        "passwordreset-emailtext-user": "Uzanto $1 de {{SITENAME}} petis restarigo de via pasvorto por {{SITENAME}}\n($4). La {{PLURAL:$3|jena uzanto-konto estas asociita|jenaj uzanto-kontoj estas asociitaj}} kun ĉi tiu retpoŝtadreso:\n\n$2\n\nĈi {{PLURAL:$3|tiu provizora pasvorto|tiuj provizoraj pasvortoj}} findatiĝos {{PLURAL:$5|unu tagon|$5 tagojn}}.\nVi devas ensaluti kaj elekti novan pasvorton nun. Se iu alia petis ĉi tion,\naŭ se vi memoris vian originalan pasvorton, kaj vi ne plu volas ŝanĝi\nĝin, vi povas ignori ĉi tiun mesaĝon kaj uzi vian malnovan pasvorton.",
        "passwordreset-emailelement": "Salutnomo: \n$1\n\nProvizora pasvorto: \n$2",
        "passwordreset-emailsentemail": "Se tiu ĉu retpoŝta adreso estas kunligita kun via konto, tiam al ĉi tiu adreso estos sendita retpoŝto por renovigi pasvorton.",
-       "passwordreset-emailsentusername": "Se estas retpoŝta adreso, kiu estas asocigita kun tiu uzantnomo, tiam sendos retpôstan mesaĝon pri reasigno de pasvorto.",
+       "passwordreset-emailsentusername": "Se estas retpoŝta adreso, kiu estas asociita kun tiu uzantnomo, tiam ni sendos retpoŝtan mesaĝon pri reagordado de la pasvorto.",
        "passwordreset-emailsent-capture": "Retpoŝto kun renovigita pasvorto estis sendita, kiu estas montrata malsupre.",
        "passwordreset-emailerror-capture": "Retpoŝto kun renovigita pasvorto estis generita, montrata sube, sed sendado al la {{GENDER:$2|uzanto}} malsukcesis: $1",
        "changeemail": "Ŝanĝi aŭ forigi retpoŝtadreson",
        "permissionserrors": "Eraro pri permeso",
        "permissionserrorstext": "Vi ne rajtas fari tion pro la {{PLURAL:$1|sekva kialo|sekvaj kialoj}}:",
        "permissionserrorstext-withaction": "Vi ne rajtas $2, pro la {{PLURAL:$1|jena kialo|jenaj kialoj}}:",
-       "contentmodelediterror": "Vi ne povas prilabori ĉi tiun reviziaĵo, ĉar ĝia enhavoŝablono estas <code>$1</code>, kiu malsamas la aktualan enhavoŝablonon de la paĝo <code>$2</code>.",
+       "contentmodelediterror": "Vi ne povas prilabori ĉi tiun version, ĉar ĝia enhavomodelo estas <code>$1</code>, kiu malsamas la aktualan enhavomodelon de la paĝo <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Averto: Vi rekreas paĝon, kiu estis antaŭe forigita.'''\n\nVi konsideru, ĉu konvenas daŭre redakti ĉi tiun paĝon.\nJen la protokolo de forigoj kaj alinomigado por via oportuno:",
        "moveddeleted-notice": "Ĉi tiu paĝo estis forigita.\nPliaj detaloj estas en protokolo pri forigado kaj alinomado de tiu ĉi paĝo.",
-       "moveddeleted-notice-recent": "Pardonon, tiu paĝo freŝdate estis forigita (en la dauro de la lasta 24 horoj).\nLa forigo kaj la movprotokolo pri la paĝo estas provizitaj sube por referenco.",
+       "moveddeleted-notice-recent": "Pardonon, tiu ĉi paĝo freŝdate estis forigita (en la daŭro de la lastaj 24 horoj).\nLa foriga kaj la nomŝanĝa protokoloj pri la paĝo estas provizitaj sube por referenco.",
        "log-fulllog": "Vidi kompletan protokolon",
        "edit-hook-aborted": "Redakto estis ĉesigita per etendaĵo de la Vikia softvaro.\nĜi ne donis eksplikon.",
        "edit-gone-missing": "Ne eblis ĝisdatigi la paĝon.\nVerŝajne ĝi estis forigita.",
        "content-model-css": "CSS",
        "content-json-empty-object": "Malplena objeto",
        "content-json-empty-array": "Malplena tabelo",
-       "duplicate-args-warning": "'''Averto:''' [[:$1]] vokas je [[:$2]] kun pli ol unu valoro por la parametro \"$3\". Nur la lasta liverita valoro estas uzonta.",
+       "duplicate-args-warning": "'''Averto:''' [[:$1]] vokas al [[:$2]] kun pli ol unu valoro por la parametro \"$3\". Nur la lasta liverita valoro estos uzata.",
        "duplicate-args-category": "Paĝoj kun pluroblaj argumentoj en ŝablonvokoj",
        "duplicate-args-category-desc": "La paĝo enhavas uzon de ŝablono kun pluroble uzitaj argumentoj, kiel ekzemple <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> aŭ <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "Averto: Ĉi tiu paĝo enhavas tro da multekostaj sintaksaj funkcio-vokoj.\n\nĜi havu malpli ol $2 {{PLURAL:$2|vokon|vokojn}}, sed nun estas $1 {{PLURAL:$1|voko|vokoj}}.",
        "revdelete-submit": "Apliki al {{PLURAL:$1|elektita revizio|elektitaj revizioj}}",
        "revdelete-success": "'''Revizivideblecon ĝisdatigis.'''",
        "revdelete-failure": "'''Videblecon de revizio ne eblis ĝisdatigi:'''\n$1",
-       "logdelete-success": "'''Protokolovideblecon ensignitan.'''",
+       "logdelete-success": "'''Protokolovidebleco agordita.'''",
        "logdelete-failure": "'''Protokola videbleco ne estis akordebla:'''\n$1",
        "revdel-restore": "Ŝanĝi videblecon",
        "pagehist": "Paĝa historio",
        "mergehistory-fail-invalid-source": "Fonta paĝo estas malvalida.",
        "mergehistory-fail-invalid-dest": "Cela paĝo estas malvalida.",
        "mergehistory-fail-no-change": "Historio-kunfandado kunfandis neniun revizion. Bonvolu rekontroli la paĝon kaj la tempo-parametrojn.",
-       "mergehistory-fail-permission": "Nesufiĉa permesoj por kunfadi historion.",
+       "mergehistory-fail-permission": "Nesufiĉaj permesoj por kunfandi historion.",
        "mergehistory-fail-self-merge": "Fonta kaj cela paĝoj samas.",
-       "mergehistory-fail-timestamps-overlap": "Fonta revizio surkunigas aŭ postokuras la celan revizion.",
+       "mergehistory-fail-timestamps-overlap": "Fontaj modifoj okazis dum aŭ post la celaj modifoj.",
        "mergehistory-fail-toobig": "Ne eblas kunigi historiojn ĉar pli ol sojlo de $1 {{PLURAL:$1|revizio|revizioj}} estus {{PLURAL:$1|movita|movitaj}}.",
        "mergehistory-no-source": "Fontpaĝo $1 ne ekzistas.",
        "mergehistory-no-destination": "Celpaĝo $1 ne ekzistas.",
        "search-category": "(kategorio $1)",
        "search-file-match": "(kongruas kun dosiera enhavo)",
        "search-suggest": "Ĉu vi intenciis: $1",
-       "search-rewritten": "Montru rezultojn por $1. Serĉita anstataŭ $2.",
+       "search-rewritten": "Ni montras rezultojn por $1. Serĉi anstataŭe pri $2.",
        "search-interwiki-caption": "Kunprojektoj",
        "search-interwiki-default": "Rezultoj de $1:",
        "search-interwiki-more": "(plu)",
        "prefs-help-recentchangescount": "Ĉi tiu inkluzivas lastajn ŝanĝojn, paĝajn historiojn, kaj protokolojn.",
        "prefs-help-watchlist-token2": "Tio estas la sekreta ŝlosilo al la retfluo de via atentaro.\nĈiu, kiu konas ĝin, povas legi vian atentaron. Do, ne kunhavigu ĝin.\nSe vi devas, [[Special:ResetTokens|vi povas rekomencigi ĝin]].",
        "savedprefs": "Viaj preferoj estas konservitaj.",
-       "savedrights": "La uzanto-rajtojn de {{GENDER:$1|$1}} konservigis.",
+       "savedrights": "La uzanto-rajtoj de {{GENDER:$1|$1}} estis konservitaj.",
        "timezonelegend": "Horzono:",
        "localtime": "Loka tempo:",
        "timezoneuseserverdefault": "Uzi defaŭlton de servilo ($1)",
        "right-createpage": "Kreu paĝojn (kiuj ne estas diskuto-paĝoj)",
        "right-createtalk": "Krei diskuto-paĝojn",
        "right-createaccount": "Krei novajn uzanto-kontojn",
-       "right-autocreateaccount": "Aŭtomate ensaluti eksteruzantan konton",
+       "right-autocreateaccount": "Aŭtomate ensaluti per ekstera uzokonto",
        "right-minoredit": "Marki redaktojn kiel etajn",
        "right-move": "Movi paĝojn",
        "right-move-subpages": "Alinomigi paĝojn kun ĝiaj subpaĝoj",
        "right-bigdelete": "Forigi paĝojn kun grandaj historioj",
        "right-deletelogentry": "Forigi kaj malforigi specifajn enmetojn en la registro.",
        "right-deleterevision": "Forigi kaj malforigi specifajn versiojn de paĝoj",
-       "right-deletedhistory": "Vidi forigitajn historieroj, sen ties asociaj tekstoj",
+       "right-deletedhistory": "Vidi forigitajn historierojn, sen iliaj ligita teksto",
        "right-deletedtext": "Rigardi forigitan tekston kaj ŝanĝojn inter forigitaj revizioj.",
        "right-browsearchive": "Serĉi forigitajn paĝojn",
        "right-undelete": "Restarigi paĝon",
        "grant-group-file-interaction": "Interagi aŭdvidaĵajn dosierojn",
        "grant-group-watchlist-interaction": "Interagi vian atentaron",
        "grant-group-email": "Sendi retpoŝton",
-       "grant-group-high-volume": "Efektivigi ampleksege aktivecon",
+       "grant-group-high-volume": "Efektivigi ampleksegajn agojn",
        "grant-group-customization": "Personecigoj kaj preferoj",
        "grant-group-administration": "Efektivigi administrajn agojn",
        "grant-group-other": "Diversaj aktivecoj",
        "grant-createeditmovepage": "Krei, redakti kaj alinomi paĝojn",
        "grant-delete": "Forigi paĝojn, reviziaĵojn kaj protokolerojn",
        "grant-editinterface": "Redakti la MediaVikian nomspacon kaj la CSS/Ĝavoskripto de uzanto",
-       "grant-editmycssjs": "Redakti vian uzantan CSS/Ĝavoskripton",
-       "grant-editmyoptions": "Redakti vian uzantan preferojn",
+       "grant-editmycssjs": "Redakti viajn personajn CSS-kodon / Ĝavoskripton",
+       "grant-editmyoptions": "Redakti viajn personajn agordojn",
        "grant-editmywatchlist": "Redakti vian atentaron",
        "grant-editpage": "Redakti ekzistantajn paĝojn",
        "grant-editprotected": "Redakti protektitajn paĝojn",
-       "grant-highvolume": "Ampleksegaj redaktado",
+       "grant-highvolume": "Ampleksega redaktado",
        "grant-oversight": "Kaŝi uzantojn kaj forigi reviziaĵojn",
        "grant-patrol": "Patroli ŝanĝojn al pâgoj",
        "grant-protect": "Protekti kaj malprotekti paĝojn",
-       "grant-rollback": "Malvalidi ŝanĝojn al paĝoj",
+       "grant-rollback": "Malfari ŝanĝojn de paĝoj",
        "grant-sendemail": "Retpoŝti al aliaj uzantoj",
        "grant-uploadeditmovefile": "Alŝuti, anstataŭigi kaj alinomi dosierojn",
        "grant-uploadfile": "Alŝuti novajn dosierojn",
        "rcshowhidemine": "$1 miajn redaktojn",
        "rcshowhidemine-show": "Montri",
        "rcshowhidemine-hide": "Kaŝi",
-       "rcshowhidecategorization": "$1 paĝokategoriadon",
+       "rcshowhidecategorization": "$1 paĝa enkategoriigo",
        "rcshowhidecategorization-show": "Montri",
        "rcshowhidecategorization-hide": "Kaŝi",
        "rclinks": "Montri $1 lastajn ŝanĝojn dum la $2 lastaj tagoj.<br />$3",
        "recentchangeslinked-summary": "Jen listo de ŝanĝoj faritaj lastatempe al paĝoj ligitaj el specifa paĝo (aŭ al membroj de specifa kategorio).\nPaĝoj en [[Special:Watchlist|via atentaro]] estas '''grasaj'''.",
        "recentchangeslinked-page": "Nomo de paĝo:",
        "recentchangeslinked-to": "Montru ŝanĝojn al paĝoj ligitaj al la specifa paĝo anstataŭe.",
-       "recentchanges-page-added-to-category": "[[:$1]] kategorialdonita",
+       "recentchanges-page-added-to-category": "[[:$1]] aldonita al la kategorio",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] kategorialdonita, [[Special:WhatLinksHere/$1|tiu paĝo estas inkluzivita ene de aliaj paĝoj]]",
        "recentchanges-page-removed-from-category": "[[:$1]] kategoriforigita",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] kategoriforigita, [[Special:WhatLinksHere/$1|tiu paĝo estas inkluzivita ene de aliaj paĝoj]]",
        "uploadscripted": "HTML-aĵo aŭ skriptokodaĵo troviĝas en tiu ĉi tiu dosiero, kiun TTT-foliumilo eble interpretus erare.",
        "upload-scripted-pi-callback": "Malalŝuteblas dosieron, kiu enhavas instrukcion de XML-stilfolia traktado",
        "uploaded-script-svg": "Trovis skriptelbero \"$1\" en la alŝutita SVGa dosiero.",
-       "uploaded-hostile-svg": "Trovis malsekura CSS en la stilero de alŝutita SVGa dosiero.",
+       "uploaded-hostile-svg": "Trovis malsekuran CSS-kodon en la stila elemento de alŝutita SVG-a dosiero.",
        "uploaded-event-handler-on-svg": "Ensigni eventotraktilajn atributojn <code>$1=\"$2\"</code> estas malpermisita en SVGaj dosieroj.",
        "uploaded-href-attribute-svg": "Atributoj je \"href\" en SVGaj dosieroj nur povas ligi al \"http://\" aŭ \"https://\" celoj, trovis  <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-href-unsafe-target-svg": "Trovis je \"href\" ligita al malsekuraj datenoj: URIa celo <code>&lt;$1 $2=\"$3\"&gt;</code> en la alŝuta SVGa dosiero.",
        "uploaded-setting-href-svg": "Uzi la markon je \"set\" por aldoni atributon je \"href\" al ujero estas blokita.",
        "uploaded-wrong-setting-svg": "Uzi la ''ensigni'' (''<span lang=\"en\">set</span>'') markon por aldoni foran/datenan/skriptan celon al ajn atributon estas blokita. Trovis <code>&lt;set to=\"$1\"&gt;</code> en la alŝutita SVGan dosieron.",
        "uploaded-setting-handler-svg": "SVGa dosiero kiu ensignas la traktilan atributon (''<span lang=\"en\">handler</span>'') kun fora/datena/skripta estas blokita. Trovis <code>$1=\"$2\"</code> en la alsûtita SVGa dosiero.",
-       "uploaded-remote-url-svg": "SVGa dosiero kiu ensignas ajn stilan atributon kun fora URL estas blokita. Trovis <code>$1=\"$2\"</code> en la alsûtita SVGa dosiero.",
+       "uploaded-remote-url-svg": "SVG-a dosiero kiu asignas ajnan stilan atributon kun fora URL estas blokita. Ni trovis <code>$1=\"$2\"</code> en la alsûtita SVG-a dosiero.",
        "uploaded-image-filter-svg": "Trovis bildan filtrilon kun URL:  <code>&lt;$1 $2=\"$3\"&gt;</code> en la alŝutita SVGa dosiero.",
        "uploadscriptednamespace": "Ĉi tiu SVG-dosiero enhavas nevalidan nomspacon \"$1\"",
        "uploadinvalidxml": "Ne eblas interpreti la XML-sintakson en la alŝutita dosiero",
        "upload-options": "Alŝutaj agordoj",
        "watchthisupload": "Atenti ĉi tiun dosieron",
        "filewasdeleted": "Dosiero de ĉi tiu nomo estis antaŭe alŝutita kaj poste forigita. Bonvolu kontroli en la $1 antaŭ alŝuti ĝin denove.",
-       "filename-thumb-name": "Ĉi tiu aspektas kiel titolo de etigita versio de plena bildo. Bonvolu ne alŝuti etigitajn versiojn de bildoj en la sama vikio. Alimaniere, bonvolu modifi la dosiernomon pli signife, kaj ne plu havas la eta-versian prefikson.",
+       "filename-thumb-name": "Ĉi tiu aspektas kiel titolo de etigita versio de plena bildo. Bonvolu ne alŝuti etigitajn versiojn de bildoj al la sama vikio. Alimaniere, bonvolu modifi la dosiernomon al pli signifa, kiu ne havas la eta-versian prefikson.",
        "filename-bad-prefix": "La nomo de la dosiero kiun vi alŝutas komencas kun '''\"$1\"''', kiu estas nepriskriba nomo ofte aŭtomate donata de ciferecaj fotiloj. Bonvolu elekti pli priskriban nomon por via bildo.",
        "upload-proto-error": "Malvalida protokolo",
        "upload-proto-error-text": "Fora alŝuto devas URL-on komence de <code>http://</code> aŭ <code>ftp://</code>.",
        "upload-form-label-infoform-name": "Nomo",
        "upload-form-label-infoform-name-tooltip": "Unika priskriba titolo por tiu ĉi dosiero, kiu servos kiel dosiernomo. Eblas uzi normalan lingvaĵon kun interspacoj. Ne aldonu la dosieran aldonaĵon.",
        "upload-form-label-infoform-description": "Priskribo",
-       "upload-form-label-infoform-description-tooltip": "Bonvolu koncize priskribi ĉion noteblan pri la verko.\nPor foto, menciu la esencaj aĵoj bildigitaj, la okazon, aŭ la lokon.",
+       "upload-form-label-infoform-description-tooltip": "Bonvolu koncize priskribi ĉion notindan pri la verko.\nPri foto, menciu la esencajn aĵojn bildigitajn, la okazon, aŭ la lokon.",
        "upload-form-label-usage-title": "Uzo",
        "upload-form-label-usage-filename": "Dosiernomo",
        "upload-form-label-own-work": "Tio estas mia propra laboro",
        "upload-form-label-not-own-work-message-local": "Se vi ne eblas alŝuti tiun dosieron respektante de politikoj de {{SITENAME}}, bonvolu fermi tiun dialogon kaj provi denove kun alia metodo.",
        "upload-form-label-not-own-work-local-local": "Vi eble ŝatu egale pravi [[Special:Upload|la defaŭltan paĝon]].",
        "upload-form-label-own-work-message-default": "Mi komprenas ke mi alŝutas tiun dosieron al komunigita deponejo. Mi konfirmas ke mi faras tiun respektante de la uzadtermoj kaj de la permisilopolitikoj tie.",
+       "upload-form-label-not-own-work-message-default": "Se vi ne eblas alŝuti tiun dosieron respektante de politikoj de komuna deponejo, bonvolu fermi tiun dialogon kaj provi denove kun alia metodo.",
+       "upload-form-label-not-own-work-local-default": "Vi ankaŭ eble dezirus provi per uzi [[Special:Upload|la alŝutan paĝon sur {{SITENAME}}]], se ĉi tiu dosiero povas esti alŝutita tie respektante de iliaj politikoj.",
+       "upload-form-label-own-work-message-shared": "Mi atestas ke mi posedas la kopirajton sur ĉi tiu dosiero kaj konsenti al neeksvalidiĝebla liberigo de ĉi tiu dosiero al Vikimedia Komunejo sub la [https://creativecommons.Org/licencoj/de-sa/4.0/ Krea Komunaĵo Atribuite Samkondiĉe 4.0] permisilo kaj mi konsentas al la [https://wikimediafoundation.Org/vikiaj/Terminoj_de_Uzaj kondiĉoj de uzo].",
+       "upload-form-label-not-own-work-message-shared": "Se vi ne posedas la kopirajton sur ĉi tiu dosiero aŭ vi deziras liberigi ĝin sub malsama licenco, konsideru uzi la [https://commons.Wikimedia.Org/vikia/Specialaĵo:UploadWizard asistanto de  alŝutado al komunejo].",
+       "upload-form-label-not-own-work-local-shared": "Vi ankaŭ eble dezirus provi per uzi [[Special:Upload|la paĝon de alŝutado sur {{SITENAME}}]], se ĉi tiu dosiero povas esti alŝutita tie respektante de iliaj politikoj.",
        "backend-fail-stream": "Ne povis fluigi dosieron $1.",
        "backend-fail-backup": "Ne povis enarkivigi dosieron $1.",
        "backend-fail-notexists": "La dosiero $1 ne ekzistas.",
        "uploadstash-summary": "Tiu ĉi paĝo alirebligas la dosierojn alŝutitajn (aŭ alŝutatajn), kiuj ne jam estas publikigitaj per la vikio. Tiujn ĉi dosierojn ne povas vidi  iu ajn, krom la alŝutinto mem.",
        "uploadstash-clear": "Malplenigi la dosierkonversejon.",
        "uploadstash-nofiles": "Mankas dosieroj en la konservejo.",
-       "uploadstash-badtoken": "Malsukcesis tiu ago, eble pro tio ke viaj ensalutiloj senvalidiĝis. Bonvolu reprovi.",
+       "uploadstash-badtoken": "Malsukcesis tiu ago, eble pro tio ke viaj ensalutiloj eksvalidiĝis. Bonvolu reprovi.",
        "uploadstash-errclear": "Malsukcesis la forigo de la dosieroj.",
        "uploadstash-refresh": "Aktualigi la dosierliston.",
-       "uploadstash-thumbnail": "Vidi bildetigon",
+       "uploadstash-thumbnail": "Vidi bildeton",
        "invalid-chunk-offset": "Malvalida deŝovo de dosierpeco",
        "img-auth-accessdenied": "Atingo malpermisita",
        "img-auth-nopathinfo": "Mankas PATH_INFO (informo pri dosiervojo).\nVia servilo ne estas konfigurita por sendi ĉi tiun informon.\nEble ĝi estas CGI-bazita kaj ne subtenas img_auth.\nVidu https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization (angle).",
        "listfiles-latestversion-no": "Ne",
        "file-anchor-link": "Dosiero",
        "filehist": "Dosiera historio",
-       "filehist-help": "Klaku daton/tempon por vidi la dosieron kiel ĝin ŝajnitan tiame.",
+       "filehist-help": "Klaku daton/tempon por vidi la dosieron kia ĝi aspektis tiam.",
        "filehist-deleteall": "forigi ĉiujn",
        "filehist-deleteone": "forigi",
        "filehist-revert": "restarigi",
        "apihelp": "Helpo pri API",
        "apihelp-no-such-module": "Modulo \"$1\" ne estis trovita.",
        "apisandbox": "API testejo",
+       "apisandbox-jsonly": "JavaScript estas postulita por uzi la API provejon.",
        "apisandbox-api-disabled": "API estas malŝalta en ĉi tiu retejo.",
        "apisandbox-intro": "Uzu tiun ĉi paĝon por eksperimenti kun '''MediaWiki API'''.\nVidu [//www.mediawiki.org/wiki/API:Main_page la API-dokumentadon] por pli da detaloj pri la uzo de API. Ekz-e: [//www.mediawiki.org/wiki/API#A_simple_example atingi la enhavon de la Ĉefpaĝo]. Elektu agon por vidi pliajn ekzemplojn.\n\nNotu ke, kvankam ĉi tiu estas provejo, agoj kiun vi faros en ĉi tiu paĝo povas modifi la vikion.",
+       "apisandbox-fullscreen": "Etendi panelon",
+       "apisandbox-fullscreen-tooltip": "Etendi la proveja panelo por plenigi la retumilan fenestron.",
        "apisandbox-unfullscreen": "Montri paĝon",
+       "apisandbox-unfullscreen-tooltip": "Maletendi la provejan panelon, tiel Mediavikiaj navigadaj ligoj estas haveblaj.",
        "apisandbox-submit": "Fari mendon",
        "apisandbox-reset": "Nuligi",
        "apisandbox-retry": "Reprovi",
+       "apisandbox-loading": "Ŝutas informon de la APIa modulo je \"$1\"…",
+       "apisandbox-load-error": "Eraro okazis dum ŝutis informon por APIa modulo je \"$1\": $2",
+       "apisandbox-no-parameters": "Ĉi tiu APIa modulo ne havas parametron.",
        "apisandbox-helpurls": "Ligiloj pri helpo",
        "apisandbox-examples": "Ekzemploj",
        "apisandbox-dynamic-parameters": "Aldonaj parametroj",
        "apisandbox-dynamic-parameters-add-label": "Aldoni parametron:",
        "apisandbox-dynamic-parameters-add-placeholder": "Nomo de parametro",
        "apisandbox-dynamic-error-exists": "Parametro nomata \"$1\" jam ekzistas.",
+       "apisandbox-deprecated-parameters": "Evitindajn parametrojn",
+       "apisandbox-fetch-token": "Aŭtoplenigu ĵetonon",
        "apisandbox-submit-invalid-fields-title": "Iuj kampoj estas malvalidaj.",
+       "apisandbox-submit-invalid-fields-message": "Bonvolu ĝustigi la markitajn kampojn kaj provi denove.",
        "apisandbox-results": "Rezultoj",
+       "apisandbox-sending-request": "Sendanta aplikprograminterfacan peton…",
+       "apisandbox-loading-results": "Ricevas APIajn rezultojn…",
+       "apisandbox-results-error": "Eraro okazis dum ŝutis la APIan petan respondon: $1.",
        "apisandbox-request-url-label": "Mendi URL-on.",
        "apisandbox-request-time": "Tempo de peto:{{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Korekti ĵetonon kaj resendi",
+       "apisandbox-results-fixtoken-fail": "Malsukcese venigis ĵetonon je \"$1\".",
+       "apisandbox-alert-page": "Kampoj de ĉi tiu paĝo ne estas validaj.",
+       "apisandbox-alert-field": "La valoro de ĉi tiu kampo ne estas valida.",
        "booksources": "Librofontoj",
        "booksources-search-legend": "Serĉi librofontojn",
        "booksources-search": "Serĉi",
        "activeusers-hidebots": "kaŝi robotojn",
        "activeusers-hidesysops": "Kaŝi administrantojn",
        "activeusers-noresult": "Neniuj uzantoj trovitaj.",
+       "activeusers-submit": "Montri la agemajn uzantojn",
        "listgrouprights": "Gruprajtoj de uzantoj",
        "listgrouprights-summary": "Jen listo de uzanto-grupoj difinitaj en ĉi tiu vikio, kun ties asociaj atingrajtoj.\nEstas [[{{MediaWiki:Listgrouprights-helppage}}|aldona informo]] pri individuaj rajtoj.",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Donita rajto</span>\n* <span class=\"listgrouprights-revoked\">Forigita rajto</span>",
        "listgrouprights-namespaceprotection-header": "Nomspacaj restriktoj",
        "listgrouprights-namespaceprotection-namespace": "Nomspaco",
        "listgrouprights-namespaceprotection-restrictedto": "Rajtoj, kiuj permesas al uzanto redakti",
-       "listgrants": "Rajdonaro",
+       "listgrants": "Rajtoj donitaj",
        "trackingcategories": "Kategorioj por kontrolado",
        "trackingcategories-summary": "Ĉi tiu paĝo listigas kategoriojn por kontrolado, aŭtomate farita de la Mediavikia programaro. Ties nomoj estas ŝanĝebla, ŝanĝante la paran sistemmesaĝon en la nomspaco {{ns:8}}.",
        "trackingcategories-msg": "Kategorio pri kontrolado",
        "wlshowhideanons": "anonimaj uzantoj",
        "wlshowhidepatr": "patrolitaj redaktoj",
        "wlshowhidemine": "miaj redaktoj",
-       "wlshowhidecategorization": "paĝokategoriado",
+       "wlshowhidecategorization": "paĝa enkategoriigo.",
        "watchlist-options": "Opcioj por atentaro",
        "watching": "Aldonata al la atentaro...",
        "unwatching": "Malatentante...",
        "deleteprotected": "Vi ne povas forigi ĉi tiun paĝon ĉar ĝi estis protektita.",
        "deleting-backlinks-warning": "<strong>Atentigo:</strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Aliaj paĝoj]] ligas al aŭ transkludas tiun ĉi forigotan paĝon.",
        "rollback": "Restarigi antaŭan redakton",
-       "rollbacklink": "malvalidi",
+       "rollbacklink": "malfari",
        "rollbacklinkcount": "nuligi $1 {{PLURAL:$1|redakton|redaktojn}}",
        "rollbacklinkcount-morethan": "nuligi pli ol $1 {{PLURAL:$1|redakton|redaktojn}}",
        "rollbackfailed": "Malfaro malsukcesis",
        "tooltip-watchlistedit-raw-submit": "Ĝisdatigi atentaron",
        "tooltip-recreate": "Rekrei la paĝon malgraŭ ĝi estis forigita",
        "tooltip-upload": "Ekalŝuti",
-       "tooltip-rollback": "\"Malvalidi\" malfaras redakto(j)n al ĉi tiu paĝo de la lasta kontribuanto per unu klako.",
+       "tooltip-rollback": "\"Malfari\" per unu klako nuligas redakto(j)n de la lasta kontribuanto al ĉi tiu paĝo.",
        "tooltip-undo": "\"Malfari\" malfaris ĉi tiun redakton kaj malfermas la redakto-paĝon en antaŭvida reĝimo. Permesas aldoni kialon en la resumo.",
        "tooltip-preferences-save": "Konservi preferojn",
        "tooltip-summary": "Enigu mallongan resumon",
        "mw-widgets-dateinput-placeholder-day": "JJJJ-MM-TT",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "paĝo ankoraŭ ne ekzistas",
-       "mw-widgets-titleinput-description-redirect": "alidirektas al $1",
+       "mw-widgets-titleinput-description-redirect": "alidirekti al $1",
        "api-error-blacklisted": "Bonvolu elekti alian, priskriban titolon.",
        "sessionprovider-generic": "$1 seancoj",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "kuketaj seancoj",
index 8173ca9..29ede6c 100644 (file)
                        "Transonlohk",
                        "Eloy",
                        "Lemondoge",
-                       "Jdforrester"
+                       "Jdforrester",
+                       "Indiralena",
+                       "Rubentl134",
+                       "Codynguyen1116"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "tog-watchdefault": "Añadir las páginas y archivos que edite a mi lista de seguimiento",
        "tog-watchmoves": "Añadir las páginas y archivos que mueva a mi lista de seguimiento",
        "tog-watchdeletion": "Añadir las páginas y archivos que borre a mi lista de seguimiento",
-       "tog-watchuploads": "Agregar nuevos archivos puedo subir a mi lista de favoritos",
+       "tog-watchuploads": "Agregar los archivos nuevos que suba a mi lista de seguimiento",
        "tog-watchrollback": "Añadir las páginas donde haya realizado una reversión a mi lista de seguimiento",
        "tog-minordefault": "Marcar todas las ediciones como menores de manera predeterminada",
        "tog-previewontop": "Mostrar previsualización antes del cuadro de edición",
        "changecontentmodel-success-text": "Se ha cambiado el tipo de contenido de [[:$1]].",
        "changecontentmodel-cannot-convert": "El contenido de [[:$1]] no se puede convertir a un tipo de $2.",
        "changecontentmodel-nodirectediting": "El modelo de contenido $1 no admite la edición directa",
+       "changecontentmodel-emptymodels-title": "No hay modelos de contenido disponibles",
        "log-name-contentmodel": "Registro de cambios del modelo de contenido",
        "log-description-contentmodel": "Eventos relacionados con los modelos de contenido de una página",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|creó}} la página $3 usando un modelo de contenido no predeterminado \"$5\"",
        "whatlinkshere-prev": "{{PLURAL:$1|previa|previas $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|siguiente|siguientes $1}}",
        "whatlinkshere-links": "← enlaces",
-       "whatlinkshere-hideredirs": "$1 redirecciones",
-       "whatlinkshere-hidetrans": "$1 inclusiones",
-       "whatlinkshere-hidelinks": "$1 enlaces",
-       "whatlinkshere-hideimages": "$1 enlaces a archivos",
+       "whatlinkshere-hideredirs": "Ocultar redirecciones",
+       "whatlinkshere-hidetrans": "Ocultar transclusiones",
+       "whatlinkshere-hidelinks": "Ocultar enlaces",
+       "whatlinkshere-hideimages": "Ocultar los vínculos de archivo",
        "whatlinkshere-filters": "Filtros",
        "whatlinkshere-submit": "Ir",
        "autoblockid": "Bloqueo automático #$1",
        "lockdbsuccesstext": "La base de datos de {{SITENAME}} ha sido bloqueada.\n<br />Recuerde retirar el bloqueo después de completar las tareas de mantenimiento.",
        "unlockdbsuccesstext": "La base de datos de {{SITENAME}} ha sido desbloqueada.",
        "lockfilenotwritable": "El archivo-cerrojo de la base de datos no tiene permiso de escritura. Para bloquear o desbloquear la base de datos, este archivo tiene que ser escribible por el servidor web.",
+       "databaselocked": "La base de datos ya está bloqueada.",
        "databasenotlocked": "La base de datos no está bloqueada.",
        "lockedbyandtime": "(por {{GENDER:$1|$1}} el $2 a las $3)",
        "move-page": "Trasladar $1",
index 4926942..7f68ff5 100644 (file)
        "whatlinkshere-links": "← loturak",
        "whatlinkshere-hideredirs": "$1 birzuzenketak",
        "whatlinkshere-hidetrans": "$1 transklusioak",
-       "whatlinkshere-hidelinks": "$1 loturak",
+       "whatlinkshere-hidelinks": "Ezkutatu loturak",
        "whatlinkshere-hideimages": "$1 irudi loturak",
        "whatlinkshere-filters": "Iragazleak",
        "whatlinkshere-submit": "Joan",
index e5985c0..672b071 100644 (file)
                        "Gnangbade",
                        "Frigory",
                        "Lemondoge",
-                       "Jdforrester"
+                       "Jdforrester",
+                       "Yasten"
                ]
        },
        "tog-underline": "Soulignement des liens :",
        "tog-editsectiononrightclick": "Activer la modification des sections par un clic droit sur les titres de section",
        "tog-watchcreations": "Ajouter à ma liste de suivi les pages que je crée et les fichiers que j’importe",
        "tog-watchdefault": "Ajouter à ma liste de suivi les pages et les fichiers que je modifie",
-       "tog-watchmoves": "Ajouter à ma liste de suivi les pages et les fichiers que je renomme",
+       "tog-watchmoves": "Ajouter les pages et les fichiers que je déplace dans ma liste de suivi",
        "tog-watchdeletion": "Ajouter à ma liste de suivi les pages et les fichiers que je supprime",
        "tog-watchuploads": "Ajouter les nouveaux fichiers que j’importe à ma liste de suivi",
        "tog-watchrollback": "Ajouter à ma liste de suivi les pages sur lesquelles j’ai effectué une révocation",
        "tog-enotifusertalkpages": "M’avertir par courriel si ma page de discussion est modifiée",
        "tog-enotifminoredits": "M’avertir par courriel également lors des modifications mineures des pages ou des fichiers",
        "tog-enotifrevealaddr": "Afficher mon adresse électronique dans les courriels de notification",
-       "tog-shownumberswatching": "Afficher le nombre d’utilisateurs qui suivent la page",
+       "tog-shownumberswatching": "Afficher le nombre d’utilisateurs en cours",
        "tog-oldsig": "Signature existante :",
        "tog-fancysig": "Traiter la signature comme du wikitexte (sans lien automatique)",
        "tog-uselivepreview": "Utiliser l’aperçu rapide",
        "viewhelppage": "Voir la page d'aide",
        "categorypage": "Voir la page de catégorie",
        "viewtalkpage": "Voir la page de discussion",
-       "otherlanguages": "Autres langues",
+       "otherlanguages": "Dans d'autres langues",
        "redirectedfrom": "(Redirigé depuis $1)",
        "redirectpagesub": "Page de redirection",
        "redirectto": "Rediriger vers :",
        "confirmable-no": "Non",
        "thisisdeleted": "Désirez-vous afficher ou restaurer $1 ?",
        "viewdeleted": "Voir $1 ?",
-       "restorelink": "{{PLURAL:$1|la modification effacée|les $1 modifications effacées}}",
+       "restorelink": "{{PLURAL:$1|une modification effacée|$1 modifications effacées}}",
        "feedlinks": "Flux :",
-       "feed-invalid": "Type de flux invalide.",
+       "feed-invalid": "Type de flux invalide pour abonnement.",
        "feed-unavailable": "Les flux de syndication ne sont pas disponibles",
        "site-rss-feed": "Flux RSS de $1",
        "site-atom-feed": "Flux Atom de $1",
        "noemail": "Aucune adresse de courriel n’a été enregistrée pour l'utilisateur « $1 ».",
        "noemailcreate": "Vous devez fournir une adresse de courriel valide",
        "passwordsent": "Un nouveau mot de passe a été envoyé à l’adresse de courriel de l’utilisateur « $1 ».\nVeuillez vous reconnecter après l’avoir reçu.",
-       "blocked-mailpassword": "Votre adresse IP est bloquée pour la modification. Pour éviter les abus, il n’est pas autorisé d’utiliser la récupération de mot de passe à partir de cette adresse IP.",
+       "blocked-mailpassword": "Votre adresse IP est bloquée en modification. Pour éviter les abus, il n’est pas autorisé d’utiliser la récupération de mot de passe à partir de cette adresse IP.",
        "eauthentsent": "Un courriel de confirmation a été envoyé à l’adresse indiquée.\nAvant qu’un autre courriel ne soit envoyé à ce compte, vous devrez suivre les instructions du courriel et confirmer que le compte est bien le vôtre.",
        "throttled-mailpassword": "Un courriel de réinitialisation de votre mot de passe a déjà été envoyé durant {{PLURAL:$1|la dernière heure|les $1 dernières heures}}. \nAfin d’éviter les abus, un seul courriel de réinitialisation de votre mot de passe sera envoyé par {{PLURAL:$1|heure|intervalle de $1 heures}}.",
        "mailerror": "Erreur lors de l’envoi du courriel : $1",
        "changecontentmodel-success-text": "Le modèle de contenu de [[:$1]] a été modifié.",
        "changecontentmodel-cannot-convert": "Le contenu sur [[:$1]] n’a pas pu être converti en un type de $2.",
        "changecontentmodel-nodirectediting": "Le modèle de contenu $1 ne permet pas la modification directe",
+       "changecontentmodel-emptymodels-title": "Aucun modèle de contenu disponible",
+       "changecontentmodel-emptymodels-text": "Le contenu sur [[:$1]] ne peut être converti en aucun type.",
        "log-name-contentmodel": "Journal de modification de modèle de contenu",
        "log-description-contentmodel": "Événements relatifs aux modèles de contenu d’une page",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|a créé}} la page $3 en utilisant un modèle de contenu « $5 » autre que celui par défaut",
        "whatlinkshere-prev": "{{PLURAL:$1|précédente|$1 précédentes}}",
        "whatlinkshere-next": "{{PLURAL:$1|suivante|$1 suivantes}}",
        "whatlinkshere-links": "← liens",
-       "whatlinkshere-hideredirs": "$1 les redirections",
-       "whatlinkshere-hidetrans": "$1 les inclusions",
-       "whatlinkshere-hidelinks": "$1 les liens",
-       "whatlinkshere-hideimages": "$1 les liens vers le fichier",
+       "whatlinkshere-hideredirs": "Masquer les redirections",
+       "whatlinkshere-hidetrans": "Masquer les inclusions",
+       "whatlinkshere-hidelinks": "Masquer les liens",
+       "whatlinkshere-hideimages": "Masquer les liens vers le fichier",
        "whatlinkshere-filters": "Filtres",
        "whatlinkshere-submit": "Lister",
        "autoblockid": "Blocage automatique #$1",
        "lockdbsuccesstext": "La base de données a été verrouillée.<br />\nN'oubliez pas de la [[Special:UnlockDB|déverrouiller]] lorsque vous aurez terminé votre opération de maintenance.",
        "unlockdbsuccesstext": "La base de données a été déverrouillée.",
        "lockfilenotwritable": "Le fichier de verrouillage de la base de données n'est pas inscriptible.\nPour bloquer ou débloquer la base de données, il doit être accessible par le serveur web.",
+       "databaselocked": "La base de données est déjà verrouillée.",
        "databasenotlocked": "La base de données n'est pas verrouillée.",
        "lockedbyandtime": "(par $1 le $2 à $3)",
        "move-page": "Renommer $1",
        "invalidateemail": "Annuler la confirmation de l'adresse de courriel",
        "notificationemail_subject_changed": "L’adresse courriel enregistrée sur {{SITENAME}} a été changée",
        "notificationemail_subject_removed": "L’adresse courriel enregistrée sur {{SITENAME}} a été supprimée",
-       "notificationemail_body_changed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1,\na changé l’adresse courriel associée au compte « $2 » sur {{SITENAME}}\nen « $3 ».\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
+       "notificationemail_body_changed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1, a changé l’adresse courriel\nassociée au compte « $2 » sur {{SITENAME}} en « $3 ».\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
        "notificationemail_body_removed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1,\na suprrimé l’adresse courriel associée au compte « $2 » sur {{SITENAME}}.\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
        "scarytranscludedisabled": "[La transclusion interwiki est désactivée]",
        "scarytranscludefailed": "[La récupération de modèle a échoué pour $1]",
        "api-error-nomodule": "Erreur interne : aucun module de versement défini.",
        "api-error-ok-but-empty": "Erreur interne : Le serveur n'a pas répondu.",
        "api-error-overwrite": "Écraser un fichier existant n'est pas autorisé.",
-       "api-error-ratelimited": "Vous essayez de télécharger plus de fichiers dans un court espace de temps que ce que ce wiki autorise.\nVeuillez réessayer dans quelques minutes.",
+       "api-error-ratelimited": "Vous essayez de téléverser plus de fichiers dans un court espace de temps que ce que ce wiki peut accepter.\nVeuillez réessayer dans quelques minutes.",
        "api-error-stashfailed": "Erreur interne : le serveur n'a pas pu enregistrer le fichier temporaire.",
        "api-error-publishfailed": "Erreur interne : Le serveur n'a pas pu publier le fichier temporaire.",
        "api-error-stasherror": "Une erreur s'est produite lors du téléchargement du fichier pour le dissimuler.",
index 1d60303..ba7e524 100644 (file)
        "changecontentmodel-success-text": "O tipo de contido de [[:$1]] foi modificado.",
        "changecontentmodel-cannot-convert": "O contido en [[:$1]] non pode converterse ó tipo de $2.",
        "changecontentmodel-nodirectediting": "O modelo de contido $1 non permite a modificación directa",
+       "changecontentmodel-emptymodels-title": "Non hai modelos de contido dispoñibles",
+       "changecontentmodel-emptymodels-text": "O contido de [[:$1]] non pode converterse a ningún tipo.",
        "log-name-contentmodel": "Rexistro de cambios de modelo de contido",
        "log-description-contentmodel": "Eventos relacinados cos modelos de contido dunha páxina",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|creou}} a páxina $3 usando un modelo de contido non predeterminado \"$5\"",
        "whatlinkshere-prev": "{{PLURAL:$1|anterior|$1 anteriores}}",
        "whatlinkshere-next": "{{PLURAL:$1|seguinte|$1 seguintes}}",
        "whatlinkshere-links": "← ligazóns",
-       "whatlinkshere-hideredirs": "$1 as redireccións",
-       "whatlinkshere-hidetrans": "$1 as inclusións",
-       "whatlinkshere-hidelinks": "$1 as ligazóns",
-       "whatlinkshere-hideimages": "$1 as ligazóns ao ficheiro",
+       "whatlinkshere-hideredirs": "Ocultar as redireccións",
+       "whatlinkshere-hidetrans": "Ocultar as inclusións",
+       "whatlinkshere-hidelinks": "Ocultar as ligazóns",
+       "whatlinkshere-hideimages": "Ocultar as ligazóns ao ficheiro",
        "whatlinkshere-filters": "Filtros",
        "whatlinkshere-submit": "Ir",
        "autoblockid": "Bloqueo automático nº$1",
        "lockdbsuccesstext": "Pechouse a base de datos.<br />\nLembre [[Special:UnlockDB|eliminar o bloqueo]] unha vez completado o seu mantemento.",
        "unlockdbsuccesstext": "A base de datos foi desbloqueada.",
        "lockfilenotwritable": "Non se pode escribir no ficheiro de bloqueo da base de datos. Para bloquear ou desbloquear a base de datos, o servidor web ten que poder escribir neste ficheiro.",
+       "databaselocked": "A base de datos xa está bloqueada.",
        "databasenotlocked": "A base de datos non está bloqueada.",
        "lockedbyandtime": "(por $1 o $2 ás $3)",
        "move-page": "Mover \"$1\"",
index fd346d7..2377488 100644 (file)
        "category_header": "דפים בקטגוריה \"$1\"",
        "subcategories": "קטגוריות משנה",
        "category-media-header": "קובצי מדיה בקטגוריה \"$1\"",
-       "category-empty": "'''קטגוריה זו אינה כוללת דפים או קובצי מדיה.'''",
+       "category-empty": "<em>קטגוריה זו אינה כוללת דפים או קובצי מדיה.</em>",
        "hidden-categories": "{{PLURAL:$1|קטגוריה מוסתרת|קטגוריות מוסתרות}}",
        "hidden-category-category": "קטגוריות מוסתרות",
        "category-subcat-count": "{{PLURAL:$2|קטגוריה זו כוללת את קטגוריית המשנה הבאה בלבד.|קטגוריה זו כוללת את {{PLURAL:$1|קטגוריית המשנה המוצגת להלן|$1 קטגוריות המשנה המוצגות להלן}}, וכוללת בסך־הכול $2 קטגוריות משנה.}}",
        "redirectedfrom": "(הופנה מהדף $1)",
        "redirectpagesub": "דף הפניה",
        "redirectto": "הפניה ל:",
-       "lastmodifiedat": "×\93×£ ×\96×\94 ×©×\81×\95Ö¼× ×\94 ×\9c×\90×\97ר×\95× ×\94 ×\91{{GRAMMAR:ת×\97×\99×\9c×\99ת|$1}}, בשעה $2.",
+       "lastmodifiedat": "×\93×£ ×\96×\94 ×©×\95Ö¼× ×\94 ×\9c×\90×\97ר×\95× ×\94 ×\91Ö¾$1, בשעה $2.",
        "viewcount": "דף זה נצפה {{PLURAL:$1|פעם אחת|פעמיים|$1 פעמים}}.",
        "protectedpage": "דף מוגן",
        "jumpto": "קפיצה אל:",
        "perfcachedts": "המידע הבא הוא עותק שמור בזיכרון המטמון של המידע, שעודכן לאחרונה ב־$1. לכל היותר {{PLURAL:$4|תוצאה אחת נשמרת|$4 תוצאות נשמרות}} בזיכרון המטמון.",
        "querypage-no-updates": "העדכונים לדף זה כרגע מופסקים, והמידע לא יעודכן באופן שוטף.",
        "viewsource": "הצגת מקור",
-       "viewsource-title": "הצגת המקור של $1",
+       "viewsource-title": "הצגת המקור של הדף \"$1\"",
        "actionthrottled": "הפעולה הוגבלה",
        "actionthrottledtext": "כאמצעי נגד שימוש לרעה, קיימת מגבלה על ביצוע פעולה זו פעמים רבות מדי בזמן קצר, וחרגת מהמגבלה הזאת.\nנא לנסות שוב בעוד מספר דקות.",
        "protectedpagetext": "דף זה מוגן כדי למנוע עריכה ופעולות אחרות.",
        "subject": "נושא:",
        "minoredit": "זוהי עריכה משנית",
        "watchthis": "מעקב אחרי דף זה",
-       "savearticle": "ש×\9e×\99ר×\94",
+       "savearticle": "ש×\9e×\99רת ×\94×\93×£",
        "publishpage": "פרסום הדף",
        "preview": "תצוגה מקדימה",
        "showpreview": "תצוגה מקדימה",
        "edit-no-change": "המערכת התעלמה מעריכתך כיוון שלא נעשה שינוי בטקסט.",
        "postedit-confirmation-created": "הדף נוצר.",
        "postedit-confirmation-restored": "הדף שוחזר.",
-       "postedit-confirmation-saved": "ער×\99×\9bתך נשמרה.",
+       "postedit-confirmation-saved": "×\94ער×\99×\9b×\94 ×©×\9cך נשמרה.",
        "edit-already-exists": "לא ניתן ליצור דף חדש.\nהוא כבר קיים.",
        "defaultmessagetext": "טקסט ההודעה המקורי",
        "content-failed-to-parse": "פענוח $2 כתוכן מסוג $1 נכשל: $3",
        "revdelete-unsuppress": "הסרת הגבלות בגרסאות המשוחזרות",
        "revdelete-log": "סיבה:",
        "revdelete-submit": "ביצוע על {{PLURAL:$1|הגרסה שנבחרה|הגרסאות שנבחרו}}",
-       "revdelete-success": "×\9eצ×\91 ×\94תצ×\95×\92×\94 ×©×\9c ×\94×\92רס×\94 ×©×\81×\95Ö¼× ×\94.",
+       "revdelete-success": "מצב התצוגה של הגרסה שוּנה.",
        "revdelete-failure": "לא ניתן היה לשנות את מצב התצוגה של הגרסה:\n$1",
-       "logdelete-success": "×\9eצ×\91 ×\94תצ×\95×\92×\94 ×©×\9c ×¤×¢×\95×\9cת ×\94×\99×\95×\9e×\9f ×©×\81×\95Ö¼× ×\94.",
+       "logdelete-success": "מצב התצוגה של פעולת היומן שוּנה.",
        "logdelete-failure": "לא ניתן היה לשנות את מצב התצוגה של היומן:\n$1",
        "revdel-restore": "שינוי מצב התצוגה",
        "pagehist": "היסטוריית הדף",
        "recentchangeslinked": "שינויים בדפים המקושרים",
        "recentchangeslinked-feed": "שינויים בדפים המקושרים",
        "recentchangeslinked-toolbox": "שינויים בדפים המקושרים",
-       "recentchangeslinked-title": "שינויים בדפים המקושרים מהדף $1",
+       "recentchangeslinked-title": "שינויים בדפים המקושרים מהדף \"$1\"",
        "recentchangeslinked-summary": "בדף מיוחד זה רשומים השינויים האחרונים בדפים המקושרים מתוך הדף (או בדפים הכלולים בקטגוריה).\nדפים ב[[Special:Watchlist|רשימת המעקב שלכם]] מוצגים ב'''הדגשה'''.",
        "recentchangeslinked-page": "שם הדף:",
        "recentchangeslinked-to": "הצגת השינויים בדפים המקשרים לדף הנתון במקום זאת",
        "recentchanges-page-removed-from-category": "הדף [[:$1]] הוסר מקטגוריה",
        "recentchanges-page-removed-from-category-bundled": "הדף [[:$1]] הוסר מקטגוריה, [[Special:WhatLinksHere/$1|והוא מוכלל בדפים אחרים]]",
        "autochange-username": "שינוי אוטומטי של מדיה־ויקי",
-       "upload": "העלאת קובץ לשרת",
-       "uploadbtn": "×\94×¢×\9c×\90×\94",
+       "upload": "העלאת קובץ",
+       "uploadbtn": "×\94×¢×\9c×\90ת ×\94ק×\95×\91×¥",
        "reuploaddesc": "ביטול ההעלאה וחזרה לטופס העלאת קבצים לשרת",
        "upload-tryagain": "שליחת התיאור החדש של הקובץ",
        "uploadnologin": "לא נכנסת לחשבון",
        "ntransclusions": "בשימוש {{PLURAL:$1|בדף אחד|ב־$1 דפים}}",
        "specialpage-empty": "אין תוצאות.",
        "lonelypages": "דפים יתומים",
-       "lonelypagestext": "×\94×\93פ×\99×\9d ×\94×\91×\90×\99×\9d ×\90×\99× ×\9d ×\9eק×\95שר×\99×\9d ×\9e×\93פ×\99×\9d ×\90×\97ר×\99×\9d ×\91×\90תר ×\96×\94 ×\95×\90×\99× ×\9d ×\9e×\95×\9b×\9c×\9c×\99×\9d ×\91×\94×\9d.",
+       "lonelypagestext": "×\94×\93פ×\99×\9d ×\94×\91×\90×\99×\9d ×\90×\99× ×\9d ×\9eק×\95שר×\99×\9d ×\95×\90×\99× ×\9d ×\9e×\95×\9b×\9c×\9c×\99×\9d ×\91×\93פ×\99×\9d ×\90×\97ר×\99×\9d ×\91×\90תר {{SITENAME}}.",
        "uncategorizedpages": "דפים חסרי קטגוריה",
        "uncategorizedcategories": "קטגוריות חסרות קטגוריה",
        "uncategorizedimages": "קבצים חסרי קטגוריה",
        "deadendpages": "דפים ללא קישורים",
        "deadendpagestext": "הדפים הבאים אינם מקשרים לדפים אחרים באתר {{SITENAME}}.",
        "protectedpages": "דפים מוגנים",
-       "protectedpages-indef": "×\94×\92× ×\95ת ×\9c×\96×\9e×\9f ×\91×\9cת×\99 ×\9e×\95×\92×\91×\9c בלבד",
+       "protectedpages-indef": "×\94×\92× ×\95ת ×\9c×\9c×\90 ×\94×\92×\91×\9cת ×\96×\9e×\9f בלבד",
        "protectedpages-summary": "בדף זה רשומים הדפים הקיימים שמוגנים כרגע. לרשימת הכותרות שמוגנות מפני יצירה, ראו [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "הגנות מדורגות בלבד",
        "protectedpages-noredirect": "הסתרת הפניות",
        "protectedpages-page": "דף",
        "protectedpages-expiry": "זמן פקיעה",
        "protectedpages-performer": "הוגן על־ידי",
-       "protectedpages-params": "פר×\9e×\98ר×\99×\9d ×\9cהגנה",
+       "protectedpages-params": "ר×\9eת ×\94הגנה",
        "protectedpages-reason": "סיבה",
        "protectedpages-submit": "הצגת דפים",
        "protectedpages-unknown-timestamp": "לא ידוע",
        "emailccsubject": "העתק של הודעתך למשתמש $1: $2",
        "emailsent": "הדואר נשלח",
        "emailsenttext": "הודעת הדואר האלקטרוני שלך נשלחה.",
-       "emailuserfooter": "$1 {{GENDER:$1|ש×\9c×\97|ש×\9c×\97×\94}} ×\90ת ×\94×\93×\95×\90\"×\9c ×\94×\96×\94 ×\9c{{GRAMMAR:ת×\97×\99×\9c×\99ת|$2}} ×\91×\90×\9eצע×\95ת ×¤×¢×\95×\9cת \"{{int:emailuser}}\" ב{{GRAMMAR:תחילית|{{SITENAME}}}}.",
+       "emailuserfooter": "$1 {{GENDER:$1|ש×\9c×\97|ש×\9c×\97×\94}} ×\90ת ×\94×\93×\95×\90\"×\9c ×\94×\96×\94 ×\9c{{GRAMMAR:ת×\97×\99×\9c×\99ת|$2}} ×\91×\90×\9eצע×\95ת ×\94ת×\9b×\95× ×\94 \"{{int:emailuser}}\" ב{{GRAMMAR:תחילית|{{SITENAME}}}}.",
        "usermessage-summary": "השארת הודעת מערכת.",
        "usermessage-editor": "שולח הודעות המערכת",
        "watchlist": "רשימת המעקב",
        "enotif_body": "לכבוד $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nתקציר העריכה: $PAGESUMMARY $PAGEMINOREDIT\n\nבאפשרותכם ליצור קשר עם העורך:\nבדואר האלקטרוני: $PAGEEDITOR_EMAIL\nבאתר: $PAGEEDITOR_WIKI\n\nלא תהיינה הודעות על פעולות נוספות עד שתבקרו בדף כשאתם מחוברים לחשבון. באפשרותכם גם לאפס את דגלי ההודעות בכל הדפים שברשימת המעקב.\n\nמערכת ההודעות של {{SITENAME}}\n\n--\nכדי לשנות את ההגדרות של הודעות הדוא\"ל הנשלחות אליכם, בקרו בדף\n{{canonicalurl:{{#special:Preferences}}}}\n\nכדי לשנות את הגדרות רשימת המעקב, בקרו בדף\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nכדי למחוק את הדף מרשימת המעקב שלכם, בקרו בדף\n$UNWATCHURL\n\nלמשוב ולעזרה נוספת:\n$HELPPAGE",
        "created": "נוצר",
        "changed": "שונה",
-       "deletepage": "×\9e×\97×\99ק×\94",
+       "deletepage": "×\9e×\97×\99קת ×\94×\93×£",
        "confirm": "אישור",
        "excontent": "התוכן היה: \"$1\"",
        "excontentauthor": "התוכן היה: \"$1\", {{GENDER:$2|והתורם היחיד היה|והתורמת היחידה הייתה}} \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|שיחה]])",
        "exbeforeblank": "התוכן לפני שרוקן היה: \"$1\"",
-       "delete-confirm": "מחיקת \"$1\"",
+       "delete-confirm": "מחיקת הדף \"$1\"",
        "delete-legend": "מחיקה",
        "historywarning": "<strong>אזהרה:</strong> לדף שאתם עומדים למחוק יש היסטוריית שינויים של {{PLURAL:$1|גרסה אחת|$1 גרסאות}}:",
        "historyaction-submit": "הצגה",
        "protect-level-autoconfirmed": "רק משתמשים ותיקים מורשים",
        "protect-level-sysop": "רק מפעילי מערכת מורשים",
        "protect-summary-cascade": "מדורג",
-       "protect-expiring": "פוקעת $1 (UTC)",
-       "protect-expiring-local": "פוקעת $1",
+       "protect-expiring": "פוקעת ב{{GRAMMAR:תחילית|$1}} (UTC)",
+       "protect-expiring-local": "פוקעת ב{{GRAMMAR:תחילית|$1}}",
        "protect-expiry-indefinite": "בלתי מוגבלת בזמן",
        "protect-cascade": "הגנה על כל הדפים המוכללים בדף זה (הגנה מדורגת)",
        "protect-cantedit": "אין באפשרותך לשנות את רמת ההגנה על דף זה כיוון שאין לך הרשאה לערוך אותו.",
        "protect-edit-reasonlist": "עריכת סיבות ההגנה",
        "protect-expiry-options": "שעה:1 hour,יום:1 day,שבוע:1 week,שבועיים:2 weeks,חודש:1 month,שלושה חודשים:3 months,שישה חודשים:6 months,שנה:1 year,זמן בלתי מוגבל:infinite",
        "restriction-type": "הרשאה:",
-       "restriction-level": "ר×\9eת ×\94×\94×\92×\91×\9cה:",
+       "restriction-level": "ר×\9eת ×\94×\94×\92× ה:",
        "minimum-size": "גודל מינימלי",
        "maximum-size": "גודל מרבי:",
        "pagesize": "(בבתים)",
        "sp-contributions-newonly": "הצגת עריכות שהן יצירות של דפים בלבד",
        "sp-contributions-submit": "חיפוש",
        "whatlinkshere": "דפים המקושרים לכאן",
-       "whatlinkshere-title": "דפים המקשרים לדף $1",
+       "whatlinkshere-title": "דפים המקשרים לדף \"$1\"",
        "whatlinkshere-page": "דף:",
        "linkshere": "הדפים שלהלן מקושרים לדף '''[[:$1]]''':",
        "nolinkshere": "אין דפים המקושרים לדף '''[[:$1]]'''.",
        "whatlinkshere-prev": "{{PLURAL:$1|הקודם|$1 הקודמים}}",
        "whatlinkshere-next": "{{PLURAL:$1|הבא|$1 הבאים}}",
        "whatlinkshere-links": "→ קישורים",
-       "whatlinkshere-hideredirs": "$1 הפניות",
-       "whatlinkshere-hidetrans": "$1 הכללות",
-       "whatlinkshere-hidelinks": "$1 קישורים",
-       "whatlinkshere-hideimages": "$1 קישורים לקובץ",
+       "whatlinkshere-hideredirs": "הסתרת הפניות",
+       "whatlinkshere-hidetrans": "הסתרת הכללות",
+       "whatlinkshere-hidelinks": "הסתרת קישורים",
+       "whatlinkshere-hideimages": "הסתרת קישורי קבצים",
        "whatlinkshere-filters": "מסננים",
        "whatlinkshere-submit": "הצגה",
        "autoblockid": "חסימה אוטומטית #$1",
        "blocklist-rangeblocks": "הסתרת חסימות טווחים",
        "blocklist-timestamp": "זמן",
        "blocklist-target": "יעד",
-       "blocklist-expiry": "פקיעה",
+       "blocklist-expiry": "×\96×\9e×\9f ×¤×§×\99×¢×\94",
        "blocklist-by": "מפעיל חוסם",
        "blocklist-params": "הגדרות חסימה",
        "blocklist-reason": "סיבה",
        "ipblocklist-submit": "חיפוש",
        "ipblocklist-localblock": "חסימה מקומית",
        "ipblocklist-otherblocks": "{{PLURAL:$1|חסימה אחרת|חסימות אחרות}}",
-       "infiniteblock": "×\91×\9cת×\99 ×\9e×\95×\92×\91×\9c ×\91זמן",
-       "expiringblock": "פ×\95קע ×\91Ö¾$2, $1",
+       "infiniteblock": "×\9c×\9c×\90 ×\94×\92×\91×\9cת זמן",
+       "expiringblock": "×\94×\97ס×\99×\9e×\94 ×¤×\95קעת ×\91{{GRAMMAR:ת×\97×\99×\9c×\99ת|$1}} ×\91שע×\94 $2",
        "anononlyblock": "משתמשים אנונימיים בלבד",
        "noautoblockblock": "חסימה אוטומטית מבוטלת",
        "createaccountblock": "יצירת חשבונות נחסמה",
        "lockdbsuccesstext": "בסיס הנתונים ננעל.<br />\nיש לזכור [[Special:UnlockDB|לשחרר את הנעילה]] לאחר שפעולת התחזוקה תסתיים.",
        "unlockdbsuccesstext": "שוחררה הנעילה של בסיס הנתונים",
        "lockfilenotwritable": "קובץ נעילת בסיס הנתונים אינו ניתן לכתיבה. כדי שאפשר יהיה לנעול את בסיס הנתונים או לבטל את נעילתו, שרת האינטרנט צריך לקבל הרשאות לכתוב אליו.",
+       "databaselocked": "בסיס הנתונים כבר נעול.",
        "databasenotlocked": "בסיס הנתונים אינו נעול.",
        "lockedbyandtime": "(על־ידי $1 ב־$3, $2)",
-       "move-page": "העברת $1",
+       "move-page": "העברת הדף \"$1\"",
        "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אנא ודאו שאתם מבינים את השלכות המעשה לפני שאתם ממשיכים.",
        "cant-move-to-category-page": "אין לך הרשאה להעביר דף לדף קטגוריה.",
        "newtitle": "השם החדש:",
        "move-watch": "מעקב אחרי דף המקור ואחרי דף היעד",
-       "movepagebtn": "×\94×¢×\91ר×\94",
+       "movepagebtn": "×\94×¢×\91רת ×\94×\93×£",
        "pagemovedsub": "ההעברה הושלמה בהצלחה",
        "movepage-moved": "הדף \"$1\" הועבר לשם \"$2\".",
        "movepage-moved-redirect": "נוצרה הפניה.",
        "tooltip-pt-mytalk": "דף השיחה שלך",
        "tooltip-pt-anontalk": "שיחה על תרומות המשתמש האנונימי",
        "tooltip-pt-preferences": "ההעדפות שלך",
-       "tooltip-pt-watchlist": "רשימת הדפים שאתם עוקבים אחרי השינויים בהם",
+       "tooltip-pt-watchlist": "רשימת הדפים ש{{GENDER:|אתה עוקב|את עוקבת}} אחרי השינויים בהם",
        "tooltip-pt-mycontris": "רשימת התרומות שלך",
        "tooltip-pt-anoncontribs": "רשימת העריכות שנעשו מכתובת ה־IP הזאת",
        "tooltip-pt-login": "מומלץ להיכנס לחשבון; עם זאת, אין חובה לעשות זאת",
        "tooltip-save": "שמירת השינויים שלך",
        "tooltip-publish": "פרסום השינויים שלך",
        "tooltip-preview": "תצוגה מקדימה של השינויים שלך. נא להשתמש באפשרות זו לפני השמירה.",
-       "tooltip-diff": "צפ×\99×\99×\94 ×\91ש×\99× ×\95×\99×\99×\9d ×©×¢×¨×\9bת×\9d בטקסט",
+       "tooltip-diff": "×\94צ×\92ת ×\94ש×\99× ×\95×\99×\99×\9d ×©×\91×\99צעת בטקסט",
        "tooltip-compareselectedversions": "צפייה בהשוואת שתי גרסאות של דף זה",
        "tooltip-watch": "הוספת דף זה לרשימת המעקב שלך",
        "tooltip-watchlistedit-normal-submit": "הסרת הדפים",
        "anonymous": "{{PLURAL:$1|משתמש אנונימי|משתמשים אנונימיים}} של {{SITENAME}}",
        "siteuser": "משתמש {{SITENAME}} $1",
        "anonuser": "משתמש אנונימי של {{SITENAME}} $1",
-       "lastmodifiedatby": "דף זה שונה לאחרונה ב־$2, $1 על־ידי $3.",
+       "lastmodifiedatby": "דף זה שוּנה לאחרונה ב־$2, $1 על־ידי $3.",
        "othercontribs": "מבוסס על העבודה של $1.",
        "others": "אחרים",
        "siteusers": "{{PLURAL:$2|{{GENDER:$1|משתמש}}|משתמשי}} {{SITENAME}} $1",
        "spam_blanking": "כל הגרסאות כוללות קישורים ל־$1, מרוקן את הדף",
        "spam_deleting": "כל הגרסאות כוללות קישורים ל־$1, מוחק את הדף",
        "simpleantispam-label": "בדיקת אנטי־ספאם.\n<strong>אל</strong> תמלאו שדה זה!",
-       "pageinfo-title": "מידע על \"$1\"",
+       "pageinfo-title": "מידע על הדף \"$1\"",
        "pageinfo-not-current": "מצטערים, לא ניתן להציג את המידע הזה לגרסאות ישנות.",
        "pageinfo-header-basic": "מידע בסיסי",
        "pageinfo-header-edits": "היסטוריית עריכות",
index 0f60d82..56efb62 100644 (file)
@@ -32,6 +32,7 @@
        "tog-watchdefault": "Adder le paginas e files que io modifica a mi observatorio",
        "tog-watchmoves": "Adder le paginas e files que io renomina a mi observatorio",
        "tog-watchdeletion": "Adder le paginas e files que io dele a mi observatorio",
+       "tog-watchuploads": "Adder le nove files que io incarga a mi observatorio",
        "tog-watchrollback": "Adder a mi observatorio le paginas in que io ha effectuate un revocation",
        "tog-minordefault": "Marcar omne modificationes initialmente como minor",
        "tog-previewontop": "Monstrar previsualisation ante le quadro de modification",
@@ -56,7 +57,7 @@
        "tog-ccmeonemails": "Inviar me copias del messages de e-mail que io invia a altere usatores",
        "tog-diffonly": "Non monstrar le contento del pagina sub le comparation de duo versiones",
        "tog-showhiddencats": "Monstrar categorias celate",
-       "tog-norollbackdiff": "Omitter le diff post le execution de un revocation",
+       "tog-norollbackdiff": "Non monstrar differentias post exequer un revocation",
        "tog-useeditwarning": "Advertir me quando io quita un pagina de modification sin publicar le cambiamentos",
        "tog-prefershttps": "Sempre usar un connexion secur in session aperte",
        "underline-always": "Sempre",
        "minoredit": "Isto es un modification minor",
        "watchthis": "Observar iste pagina",
        "savearticle": "Publicar pagina",
+       "publishpage": "Publicar pagina",
        "preview": "Previsualisation",
        "showpreview": "Monstrar previsualisation",
        "showdiff": "Detaliar modificationes",
        "userpage-userdoesnotexist": "Le conto de usator \"<nowiki>$1</nowiki>\" non es registrate. Per favor verifica que tu vole crear/modificar iste pagina.",
        "userpage-userdoesnotexist-view": "Le conto de usator \"$1\" non es registrate.",
        "blocked-notice-logextract": "Iste usator es actualmente blocate.\nLe ultime entrata del registro de blocadas es reproducite ci infra pro information:",
-       "clearyourcache": "'''Nota:''' Post confirmar, il pote esser necessari refrescar le ''cache'' de tu navigator pro vider le cambiamentos.\n* '''Firefox / Safari:''' Tenente ''Shift'' clicca ''Reload (Recargar)'', o preme ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' sur Mac)\n* '''Google Chrome:''' Preme ''Ctrl-Shift-R'' (''⌘-Shift-R'' sur Mac)\n* '''Internet Explorer:''' Tenente ''Ctrl'' clicca ''Refresh (Refrescar)'', o preme ''Ctrl-F5'' \n* '''Opera:''' Vacua le ''cache'' in ''Tools → Preferences (Utensiles → Preferentias)''",
+       "clearyourcache": "<strong>Nota:</strong> Post confirmar, il pote esser necessari refrescar le <em>cache</em> de tu navigator pro vider le cambiamentos.\n* <strong>Firefox / Safari:</strong> Tenente <em>Shift</em> clicca <em>Reload (Recargar)</em>, o preme <em>Ctrl-F5</em> o <em>Ctrl-R</em> (<em>⌘-R</em> sur Mac)\n* <strong>Google Chrome:</strong> Preme <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> sur Mac)\n* <strong>Internet Explorer:</strong> Tenente <em>Ctrl</em> clicca <em>Refresh (Refrescar)</em>, o preme <em>Ctrl-F5</em> \n* <strong>Opera:</strong> Vade a <em>Menu → Configurationes</em> (<em>Opera → Preferentias</em> sur un Mac) e alora a <em>Privacy & securitate → Rader datos de navigation → Files e imagines in cache</em>.",
        "usercssyoucanpreview": "'''Consilio:''' Usa le button \"{{int:showpreview}}\" pro testar tu nove CSS ante de salveguardar lo.",
        "userjsyoucanpreview": "'''Consilio:''' Usa le button \"{{int:showpreview}}\" pro testar tu nove JavaScript ante de salveguardar lo.",
        "usercsspreview": "'''Non oblida que isto es solmente un previsualisation de tu CSS personalisate.'''\n'''Le modificationes non ha ancora essite salveguardate!'''",
        "recentchangeslinked-page": "Nomine del pagina:",
        "recentchangeslinked-to": "Monstrar modificationes in paginas con ligamines al pagina specificate",
        "recentchanges-page-added-to-category": "[[:$1]] addite al categoria",
-       "recentchanges-page-added-to-category-bundled": "[[:$1]] e [[Special:WhatLinksHere/$1|{{PLURAL:$2|un pagina|$2 paginas}}]] addite al categoria",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] addite al categoria, [[Special:WhatLinksHere/$1|iste pagina es includite in altere paginas]]",
        "recentchanges-page-removed-from-category": "[[:$1]] removite del categoria",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] e [[Special:WhatLinksHere/$1|{{PLURAL:$2|un pagina|$2 paginas}}]] removite del categoria",
        "autochange-username": "Cambiamento automatic de MediaWiki",
        "changecontentmodel-success-text": "Le typo de contento de [[:$1]] ha essite cambiate.",
        "changecontentmodel-cannot-convert": "Le contento de [[:$1]] non pote esser convertite a un typo de $2.",
        "changecontentmodel-nodirectediting": "Le modello de contento $1 non supporta le modification directe",
+       "changecontentmodel-emptymodels-title": "Nulle modello de contento disponibile",
+       "changecontentmodel-emptymodels-text": "Le contento in [[:$1]] non pote esser convertite in alcun typo.",
        "log-name-contentmodel": "Registro de cambiamentos de modello de contento",
        "log-description-contentmodel": "Eventos relative al modellos de contento de un pagina",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|creava}} le pagina $3 con le modello de contento non predefinite \"$5\"",
        "whatlinkshere-prev": "{{PLURAL:$1|precedente|precedente $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|sequente|sequente $1}}",
        "whatlinkshere-links": "← ligamines",
-       "whatlinkshere-hideredirs": "$1 redirectiones",
-       "whatlinkshere-hidetrans": "$1 transclusiones",
-       "whatlinkshere-hidelinks": "$1 ligamines",
-       "whatlinkshere-hideimages": "$1 le ligamines a files",
+       "whatlinkshere-hideredirs": "Celar redirectiones",
+       "whatlinkshere-hidetrans": "Celar transclusiones",
+       "whatlinkshere-hidelinks": "Celar ligamines",
+       "whatlinkshere-hideimages": "Celar le ligamines a files",
        "whatlinkshere-filters": "Filtros",
        "whatlinkshere-submit": "Va",
        "autoblockid": "Auto-blocada №$1",
        "lockdbsuccesstext": "Le base de datos de {{SITENAME}} ha essite blocate.\n<br />Rememora te de disblocar lo post completar tu mantenentia.",
        "unlockdbsuccesstext": "Le base de datos de {{SITENAME}} ha essite disblocate.",
        "lockfilenotwritable": "Impossibile scriber al file de blocada del base de datos.\nPro blocar o disblocar le base de datos, le servitor web debe poter scriber a iste file.",
+       "databaselocked": "Le base de datos es jam blocate.",
        "databasenotlocked": "Le base de datos non es blocate.",
        "lockedbyandtime": "(per $1 le $2 a $3)",
        "move-page": "Renominar $1",
        "tooltip-ca-nstab-category": "Vider le pagina del categoria",
        "tooltip-minoredit": "Marcar iste modification como minor",
        "tooltip-save": "Confirmar tu modificationes",
+       "tooltip-publish": "Publicar tu cambiamentos",
        "tooltip-preview": "Per favor verifica tu modificationes ante que tu los publica!",
        "tooltip-diff": "Detaliar le modificationes que tu ha facite in le texto.",
        "tooltip-compareselectedversions": "Vider le differentias inter le seligite duo versiones de iste pagina.",
        "confirmemail_body_set": "Un persona, probabilemente tu, usante le adresse IP $1,\nha specificate que iste adresse de e-mail pertine al conto \"$2\" in {{SITENAME}}.\n\nPro confirmar que iste conto vermente pertine a te, e pro activar le functionalitate\nde e-mail in {{SITENAME}}, visita iste ligamine in tu navigator:\n\n$3\n\nSi le conto *non* pertine a te, seque iste ligamine\npro cancellar le confirmation del adresse de e-mail:\n\n$5\n\nIste codice de confirmation expirara le $6 a $7.",
        "confirmemail_invalidated": "Confirmation del adresse de e-mail cancellate",
        "invalidateemail": "Cancellar confirmation del adresse de e-mail",
+       "notificationemail_subject_changed": "Le adresse de e-mail registrate sur {{SITENAME}} ha essite cambiate",
+       "notificationemail_subject_removed": "Le adresse de e-mail registrate sur {{SITENAME}} ha essite removite",
+       "notificationemail_body_changed": "Qualcuno, probabilemente tu, ab le adresse IP $1, ha cambiate le adresse de e-mail del conto \"$2\" in \"$3\" sur {{SITENAME}}.\n\nSi isto non esseva tu, contacta immediatemente un administrator del sito.",
+       "notificationemail_body_removed": "Qualcuno, probabilemente tu, ab le adresse IP $1, ha removite le adresse de e-mail del conto \"$2\" sur {{SITENAME}}.\n\nSi isto non esseva tu, contacta immediatemente un administrator del sito.",
        "scarytranscludedisabled": "[Le transclusion interwiki es disactivate]",
        "scarytranscludefailed": "[Falleva de obtener le patrono pro $1]",
        "scarytranscludefailed-httpstatus": "[Obtention de patrono fallite pro $1: HTTP $2]",
        "watchlistedit-raw-done": "Tu observatorio ha essite actualisate.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 titulo|$1 titulos}} ha essite addite:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 titulo|$1 titulos}} ha essite removite:",
-       "watchlistedit-clear-title": "Observatorio radite",
+       "watchlistedit-clear-title": "Rader observatorio",
        "watchlistedit-clear-legend": "Rader observatorio",
        "watchlistedit-clear-explain": "Tote le titulos essera removite de tu observatorio",
        "watchlistedit-clear-titles": "Titulos:",
        "version-libraries-description": "Description",
        "version-libraries-authors": "Autores",
        "redirect": "Rediriger per ID de file, usator, pagina, version o registro",
-       "redirect-summary": "Iste pagina special redirige a un file (si es date le nomine de un file), a un pagina (si es date un ID de version o ID de pagina) o a un pagina de usator (si es date un ID de usator numeric). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] o [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Iste pagina special redirige a un file (si es date le nomine de un file), a un pagina (si es date un ID de version o ID de pagina), a un pagina de usator (si es date un ID de usator numeric) o a un entrata de registro (si es date le ID de un registro). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] o [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Va",
        "redirect-lookup": "Cercar:",
        "redirect-value": "Valor:",
        "redirect-page": "ID del pagina",
        "redirect-revision": "Version de pagina",
        "redirect-file": "Nomine de file",
+       "redirect-logid": "ID de registro",
        "redirect-not-exists": "Valor non trovate",
        "fileduplicatesearch": "Cercar files duplicate",
        "fileduplicatesearch-summary": "Cercar files duplicate a base de lor summas de verification ''(hash).''",
        "tags-deactivate": "disactivar",
        "tags-hitcount": "$1 {{PLURAL:$1|modification|modificationes}}",
        "tags-manage-no-permission": "Tu non ha le permission de gerer le etiquettas de modification.",
+       "tags-manage-blocked": "Tu non pote gerer etiquettas de cambiamento durante que tu es blocate.",
        "tags-create-heading": "Crear un nove etiquetta",
        "tags-create-explanation": "Per configuration predefinite, le etiquettas novemente create essera disponibile pro le uso per usatores e robots.",
        "tags-create-tag-name": "Nomine del etiquetta:",
        "tags-deactivate-not-allowed": "Non es possibile disactivar le etiquetta \"$1\".",
        "tags-deactivate-submit": "Disactivar",
        "tags-apply-no-permission": "Tu non ha le permission de adjunger etiquettas de cambiamento a tu cambiamentos.",
+       "tags-apply-blocked": "Tu non pote applicar etiquettas de cambiamento con tu cambiamentos durante que tu es blocate.",
        "tags-apply-not-allowed-one": "Non es permittite applicar le etiquetta \"$1\" manualmente.",
        "tags-apply-not-allowed-multi": "Le sequente {{PLURAL:$2|etiquetta|etiquettas}} non es autorisate a esser manualmente applicate: $1",
        "tags-update-no-permission": "Tu non ha le permission de adder o remover etiquettas de cambiamento sur individual versiones o entratas de registro.",
+       "tags-update-blocked": "Tu non pote adder o remover etiquettas de cambiamento durante que tu es blocate.",
        "tags-update-add-not-allowed-one": "Non es permittite adjunger le etiquetta \"$1\" manualmente.",
        "tags-update-add-not-allowed-multi": "Le sequente {{PLURAL:$2|etiquetta|etiquettas}} non es autorisate a esser manualmente adjungite: $1",
        "tags-update-remove-not-allowed-one": "Non es permittite remover le etiquetta \"$1\".",
        "tags-edit-revision-legend": "Adder o remover etiquettas de {{PLURAL:$1|iste version|tote le $1 versiones}}",
        "tags-edit-logentry-legend": "Adder o remover etiquettas de {{PLURAL:$1|iste entrata|tote le $1 entratas}} de registro",
        "tags-edit-existing-tags": "Etiquettas existente:",
-       "tags-edit-existing-tags-none": "\"Nulle\"",
+       "tags-edit-existing-tags-none": "<em>Nulle</em>",
        "tags-edit-new-tags": "Nove etiquettas:",
        "tags-edit-add": "Adder iste etiquettas:",
        "tags-edit-remove": "Remover iste etiquettas:",
index 8b0af46..a58b482 100644 (file)
        "changepassword-success": "Kata sandi Anda telah diubah!",
        "changepassword-throttled": "Anda terlalu sering mencoba masuk log.\nMohon tunggu $1 sebelum mencoba lagi.",
        "botpasswords": "Kata sandi bot",
+       "botpasswords-summary": "<em>Kata sandi bot</em> memungkinkan akses ke akun pengguna menggunakan API tanpa menggunakan kredensial masuk log utama akun tersebut. Hak pengguna yang tersedia ketika masuk log dengan kata sandi bot mungkin akan dibatasi.\n\nJika Anda tidak tahu kenapa Anda ingin melakukan hal ini, sebaiknya jangan lakukan. Semestinya tidak ada orang lain yang boleh meminta Anda untuk menciptakan dan menyerahkan kata sandi bot ini kepadanya.",
        "botpasswords-disabled": "Kata sandi bot dinonaktifkan.",
        "botpasswords-no-central-id": "Untuk menggunakan kata sandi bot, Anda harus masuk log ke akun yang telah tersentralisasi.",
        "botpasswords-existing": "Kata sandi bot tersedia",
        "botpasswords-label-delete": "Hapus",
        "botpasswords-label-resetpassword": "Setel ulang kata sandi",
        "botpasswords-label-grants": "Akses yang dapat diberikan:",
+       "botpasswords-help-grants": "Tiap izin memberikan akses ke hak-hak pengguna yang telah dimiliki suatu akun pengguna. Lihat [[Special:ListGrants|tabel izin]] untuk informasi lebih lanjut.",
        "botpasswords-label-restrictions": "Batasan penggunaan:",
        "botpasswords-label-grants-column": "Izin diberikan",
        "botpasswords-bad-appid": "Nama bot \"$1\" tidak valid.",
        "botpasswords-insert-failed": "Gagal menambah nama bot \"$1\". Apakah sudah ditambahkan sebelum ini?",
        "botpasswords-update-failed": "Gagal memperbarui nama bot \"$1\". Apakah sebelumnya sudah pernah dihapus?",
        "botpasswords-created-title": "Kata sandi bot dibuat",
-       "botpasswords-created-body": "Kata sandi bot \"$1\" sukses dibuat.",
+       "botpasswords-created-body": "Kata sandi bot \"$1\" berhasil dibuat.",
        "botpasswords-updated-title": "Kata sandi bot diperbarui",
-       "botpasswords-updated-body": "Kata sandi bot \"$1\" sukses diperbarui.",
+       "botpasswords-updated-body": "Kata sandi bot \"$1\" berhasil diperbarui.",
        "botpasswords-deleted-title": "Kata sandi bot dihapus",
        "botpasswords-deleted-body": "Kata sandi bot \"$1\" telah dihapus.",
        "botpasswords-newpassword": "Kata sandi baru untuk masuk log dengan '''$1''' adalah '''$2'''. ''Mohon simpan untuk referensi di kemudian hari.''",
        "resetpass-no-info": "Anda harus masuk log untuk mengakses halaman ini secara langsung.",
        "resetpass-submit-loggedin": "Ganti kata sandi",
        "resetpass-submit-cancel": "Batalkan",
-       "resetpass-wrong-oldpass": "Kata sandi tidak sah.\nAnda mungkin telah berhasil mengganti kata sandi Anda atau telah meminta kata sandi sementara yang baru.",
+       "resetpass-wrong-oldpass": "Kata sandi tidak sah.\nAnda mungkin telah mengganti kata sandi Anda atau telah meminta kata sandi sementara yang baru.",
        "resetpass-recycled": "Mohon menyetel ulang kata sandi Anda ke sesuatu yang berbeda dari kata sandi Anda sekarang.",
        "resetpass-temp-emailed": "Anda masuk log dengan kode sementara yang disurel.\nUntuk menyelesaikan masuk log, Anda harus mengatur sandi baru di sini:",
        "resetpass-temp-password": "Kata sandi sementara:",
        "minoredit": "Ini adalah suntingan kecil.",
        "watchthis": "Pantau halaman ini",
        "savearticle": "Simpan halaman",
+       "publishpage": "Terbitkan halaman",
        "preview": "Pratayang",
        "showpreview": "Lihat pratayang",
        "showdiff": "Lihat perubahan",
        "userpage-userdoesnotexist": "Akun pengguna \"<nowiki>$1</nowiki>\" tidak terdaftar.",
        "userpage-userdoesnotexist-view": "Pengguna \"$1\" tidak terdaftar.",
        "blocked-notice-logextract": "Pengguna ini sedang diblokir.\nEntri log pemblokiran terakhir tersedia di bawah ini sebagai rujukan:",
-       "clearyourcache": "'''Catatan:''' Setelah menyimpan, Anda mungkin harus memintas singgahan peramban Anda untuk melihat perubahan.\n* '''Firefox / Safari:''' Tahan ''Shift'' sambil mengeklik ''Reload'', atau tekan ''Ctrl-F5'' atau ''Ctrl-R'' (''⌘-R'' di Mac)\n* '''Google Chrome:''' Tekan ''Ctrl-Shift-R'' (''⌘-Shift-R'' di Mac)\n* '''Internet Explorer:''' Tahan ''Ctrl'' sambl mengeklik ''Refresh'', atau tekan ''Ctrl-F5''\n* '''Opera:''' Bersihkan tembolok di ''Tools → Preferences''",
+       "clearyourcache": "'''Catatan:''' Setelah menyimpan, Anda mungkin harus memintas isi singgahan peramban Anda untuk melihat perubahan.\n* '''Firefox / Safari:''' Tahan ''Shift'' sambil mengeklik ''Reload'', atau tekan ''Ctrl-F5'' atau ''Ctrl-R'' (''⌘-R'' di Mac)\n* '''Google Chrome:''' Tekan ''Ctrl-Shift-R'' (''⌘-Shift-R'' di Mac)\n* '''Internet Explorer:''' Tahan ''Ctrl'' sambl mengeklik ''Refresh'', atau tekan ''Ctrl-F5''\n* '''Opera:''' Buka <em> Menu → Settings</em> (<em>Opera → Preferences</em>Privacy & Security  → Clear browsing data  → Cached images and files</em>.",
        "usercssyoucanpreview": "'''Tips:''' Gunakan tombol \"{{int:showpreview}}\" untuk menguji CSS baru Anda sebelum menyimpannya.",
        "userjsyoucanpreview": "'''Tips:''' Gunakan tombol \"{{int:showpreview}}\" untuk menguji JS baru Anda sebelum menyimpannya.",
        "usercsspreview": "'''Ingatlah bahwa Anda sedang menampilkan pratayang dari CSS Anda.\nPratayang ini belum disimpan!'''",
        "userrights-unchangeable-col": "Kelompok yang tidak dapat Anda ubah",
        "userrights-irreversible-marker": "$1*",
        "userrights-conflict": "Konflik perubahan hak pengguna! Silakan tinjau ulang dan konfirmasi perubahan Anda.",
-       "userrights-removed-self": "Anda berhasil mencabut hak-hak Anda. Anda tidak bisa lagi mengakses halaman ini.",
+       "userrights-removed-self": "Anda telah mencabut hak-hak Anda sendiri. Anda tidak bisa lagi mengakses halaman ini.",
        "group": "Kelompok:",
        "group-user": "Pengguna",
        "group-autoconfirmed": "Pengguna terkonfirmasi otomatis",
        "uploaded-script-svg": "Terdapat elemen terskrip \"$1\" dalam berkas SVG yang diunggah.",
        "uploaded-hostile-svg": "Terdapat CSS yang tidak aman dalam elemen gaya berkas SVG yang diunggah.",
        "uploaded-event-handler-on-svg": "Penetapan atribut <i>event-handler</i> $1=\"$2\" tidak diizinkan dalam berkas SVG.",
+       "uploaded-href-attribute-svg": "Atribut href pada berkas SVG hanya dibolehkan untuk ditautkan ke target http:// atau https://, ditemukan <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Menemukan href ke data tidak aman: URI menarget <code>&lt;$1 $2=\"$3\"&gt;</code> dalam berkas SVG yang diunggah.",
        "uploaded-setting-event-handler-svg": "Penyetelan atribut event-handler diblokir, menemukan <code>&lt;$1 $2=\"$3\"&gt;</code> dalam berkas SVG yang diunggah.",
        "uploadscriptednamespace": "Berkas SVG ini memuat ruang nama ilegal \"$1\"",
        "uploadinvalidxml": "XML dalam berkas yang diunggah tidak bisa diuraikan.",
        "upload-options": "Opsi pengunggahan",
        "watchthisupload": "Pantau berkas ini",
        "filewasdeleted": "Suatu berkas dengan nama ini pernah dimuat dan selanjutnya dihapus. Harap cek $1 sebelum memuat lagi berkas tersebut.",
+       "filename-thumb-name": "Tampaknya ini adalah judul gambar mini. Mohon jangan mengunggah gambar mini kembali ke wiki yang sama. Bila tidak, silakan perbaiki nama berkas sehingga lebih bermakna, dan tidak memiliki awalan seperti judul gambar mini.",
        "filename-bad-prefix": "Nama berkas yang Anda muat diawali dengan '''\"$1\"''', yang merupakan nama non-deskriptif yang biasanya diberikan secara otomatis oleh kamera digital. Harap pilih nama lain yang lebih deskriptif untuk berkas Anda.",
        "filename-prefix-blacklist": " #<!-- biarkan baris ini seperti adanya --> <pre>\n# Contohnya sebagai berikut:\n#   * Semuanya dari karekter \"#\" sampai akhir baris ini adalah komentar\n#   * Setiap garis \"_\" adalah awalan untuk nama file khas yang diberikan secara otomatis oleh kamera digital\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # beberapa model telpon seluler\nIMG # generik\nJD # Jenoptik\nMGP # Pentax\nPICT # lainnya.\n #</pre> <!-- biarkan baris ini seperti adanya -->",
        "upload-proto-error": "Protokol tak tepat",
        "upload-form-label-own-work": "Ini adalah karya saya sendiri",
        "upload-form-label-infoform-categories": "Kategori",
        "upload-form-label-infoform-date": "Tanggal",
+       "upload-form-label-own-work-message-shared": "Saya menegaskan bahwa saya memiliki hak cipta atas berkas ini, dan setuju untuk melepas berkas ini menurut lisensi [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Atribusi-BerbagiSerupa 4.0], dan saya menyetujui [https://wikimediafoundation.org/wiki/Terms_of_Use ketentuan pemakaian dari Yayasan Wikimedia].",
+       "upload-form-label-not-own-work-message-shared": "Jika Anda tidak memiliki hak cipta atas berkas ini, atau Anda ingin melepasnya dengan lisensi berbeda, pertimbangkan untuk menggunakan [https://commons.wikimedia.org/wiki/Special:UploadWizard Wisaya penggunggah Commons].",
+       "upload-form-label-not-own-work-local-shared": "Anda juga mungkin ingin mencoba menggunakan  [[Special:Upload|laman pengunggahan pada{{SITENAME}}]],jika situs tersebut membolehkan pengunggahan berkas ini menurut kebijakannya.",
        "backend-fail-stream": "Tidak bisa mengalikan berkas $1.",
        "backend-fail-backup": "Tidak dapat mencadangkan berkas $1.",
        "backend-fail-notexists": "Berkas $1 tidak ada.",
        "uploadstash-summary": "Halaman ini memberikan akses terhadap berkas yang diunggah (atau dalam proses pengunggahan), namun belum diterbitkan ke wiki. Berkas-berkas ini tidak dapat dilihat oleh siapa pun kecuali pengunggahnya.",
        "uploadstash-clear": "Hapus berkas simpanan",
        "uploadstash-nofiles": "Anda tidak memiliki berkas simpanan.",
-       "uploadstash-badtoken": "Pelaksanaan tindakan tersebut gagal. Mungkin karena hak penyuntingan Anda telah kedaluwarsa. Coba lagi.",
+       "uploadstash-badtoken": "Pelaksanaan tindakan tersebut gagal. Mungkin karena hak penyuntingan Anda telah kedaluwarsa. Silakan coba lagi.",
        "uploadstash-errclear": "Penghapusan berkas gagal.",
        "uploadstash-refresh": "Segarkan daftar berkas.",
+       "uploadstash-thumbnail": "lihat miniatur",
        "invalid-chunk-offset": "Ofset potongan tidak valid",
        "img-auth-accessdenied": "Akses ditolak",
        "img-auth-nopathinfo": "PATH_INFO hilang.\nServer Anda tidak diatur untuk melewatkan informasi ini.\nServer tersebut mungkin berbasis CGI dan tidak dapat mendukung img_auth.\nLihat https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "changecontentmodel-title-label": "Judul halaman",
        "changecontentmodel-model-label": "Model konten baru",
        "changecontentmodel-reason-label": "Alasan:",
+       "changecontentmodel-submit": "Ubah",
        "changecontentmodel-success-title": "Model konten ini telah diubah",
        "changecontentmodel-success-text": "Jenis konten [[:$1]] telah diubah",
        "changecontentmodel-cannot-convert": "Isi pada [[:$1]] tidak dapat ditukar kepada jenis $2.",
        "whatlinkshere-prev": "{{PLURAL:$1|sebelumnya $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|selanjutnya $1}}",
        "whatlinkshere-links": "← pranala",
-       "whatlinkshere-hideredirs": "$1 pengalihan",
-       "whatlinkshere-hidetrans": "$1 transklusi",
-       "whatlinkshere-hidelinks": "$1 pranala",
-       "whatlinkshere-hideimages": "$1 pranala berkas",
+       "whatlinkshere-hideredirs": "Sembunyikan pengalihan",
+       "whatlinkshere-hidetrans": "Sembunyikan transklusi",
+       "whatlinkshere-hidelinks": "Sembunyikan pranala",
+       "whatlinkshere-hideimages": "Sembunyikan pranala berkas",
        "whatlinkshere-filters": "Penyaring",
        "whatlinkshere-submit": "Tuju ke",
        "autoblockid": "Blokir otomatis #$1",
        "ipb-unblock": "Hilangkan blokir seorang pengguna atau suatu alamat IP",
        "ipb-blocklist": "Lihat blokir yang diterapkan",
        "ipb-blocklist-contribs": "Kontribusi untuk {{GENDER:$1|$1}}",
+       "ipb-blocklist-duration-left": "Tersisa $1",
        "unblockip": "Buka blokir pengguna",
        "unblockiptext": "Gunakan formulir di bawah untuk mengembalikan kemampuan menulis sebuah alamat IP atau pengguna yang sebelumnya telah diblokir.",
        "ipusubmit": "Hilangkan blokir ini",
        "lockdbsuccesstext": "Basis data telah dikunci.<br />\nPastikan Anda [[Special:UnlockDB|membuka kuncinya]] setelah pemeliharaan selesai.",
        "unlockdbsuccesstext": "Kunci basis data telah dibuka.",
        "lockfilenotwritable": "Berkas kunci basis data tidak dapat ditulis. Untuk mengunci atau membuka basis data, berkas ini harus dapat ditulis oleh server web.",
+       "databaselocked": "Basis data telah terkunci.",
        "databasenotlocked": "Basis data tidak terkunci.",
        "lockedbyandtime": "(oleh $1 pada $2 $3)",
        "move-page": "Pindahkan $1",
        "tooltip-ca-nstab-category": "Lihat halaman kategori",
        "tooltip-minoredit": "Tandai ini sebagai suntingan kecil",
        "tooltip-save": "Simpan perubahan Anda",
+       "tooltip-publish": "Terbitkan perubahan Anda",
        "tooltip-preview": "Pratayang perubahan Anda, harap gunakan ini sebelum menyimpan!",
        "tooltip-diff": "Lihat perubahan yang telah Anda lakukan.",
        "tooltip-compareselectedversions": "Lihat perbedaan antara dua versi halaman yang dipilih.",
        "watchlistedit-raw-done": "Daftar pantauan Anda telah diperbarui.",
        "watchlistedit-raw-added": "{{PLURAL:$1|$1 judul telah}} ditambahkan:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|$1 judul telah}} dikeluarkan:",
-       "watchlistedit-clear-title": "Daftar pantauan dihapus",
+       "watchlistedit-clear-title": "Hapus daftar pantauan",
        "watchlistedit-clear-legend": "Hapus daftar pantauan",
        "watchlistedit-clear-explain": "Semua judul akan dihapus dari daftar pantauan Anda",
        "watchlistedit-clear-titles": "Judul:",
        "tags-create-invalid-chars": "Nama tag tidak boleh mengandung koma (<code>,</code>) atau garis miring (<code>/</code>).",
        "tags-create-invalid-title-chars": "Nama tag tidak boleh mengandung karakter yang tidak bisa digunakan dalam judul halaman.",
        "tags-create-already-exists": "Tag \"$1\" sudah ada.",
+       "tags-create-warnings-below": "Apakah Anda ingin melanjutkan pembuatan tanda ini?",
        "tags-delete-reason": "Alasan:",
        "tags-activate-reason": "Alasan:",
        "tags-activate-submit": "Aktifkan",
        "api-error-blacklisted": "Pilih judul lain yang deskriptif",
        "randomrootpage": "Halaman dasar sembarang",
        "log-action-filter-block": "Jenis pemblokiran:",
+       "log-action-filter-all": "Semua",
        "log-action-filter-block-block": "Blokir",
        "log-action-filter-suppress-block": "Perahasiaan pengguna menurut pemblokiran"
 }
index 44cee89..7cac47b 100644 (file)
                        "Adam-Yourist"
                ]
        },
-       "tog-underline": "Ð¥Ñ\8cожадеÑ\80га |ок|алÑ\82акадар:",
-       "tog-hideminor": "Ð¥Ñ\8cаÑ\82|аÑ\8fздаÑ\80а Ñ\87Ñ\83 ÐºÐµÑ\80даÑ\87а Ñ\85Ñ\83вÑ\86амаÑ\88a Ð·|амига Ð´Ð¾Ð»Ð° Ñ\85Ñ\83вÑ\86амаÑ\88 ÐºÑ\8aайладаккÑ\85а",
-       "tog-hidepatrolled": "Ð¥Ñ\8cаÑ\82|аÑ\8fздаÑ\80а Ñ\87Ñ\83 ÐºÐµÑ\80даÑ\87а Ñ\85Ñ\83вÑ\86амаÑ\88a Ð´|анийÑ\81адаÑ\8c Ð´Ð¾Ð»Ð° Ñ\85Ñ\83вÑ\86амаÑ\88 ÐºÑ\8aайладаккÑ\85а",
-       "tog-newpageshidepatrolled": "Ð¥Ñ\8cаÑ\82|аÑ\8fздаÑ\80а Ñ\87Ñ\83 ÐºÐµÑ\80даÑ\87а Ñ\85Ñ\83вÑ\86амаÑ\88a Ñ\85Ñ\8cанийÑ\81адаÑ\8c Ð´Ð¾Ð»Ð° Ð¾Ð°Ð³|онаÑ\88 ÐºÑ\8aайлаÑ\8fккÑ\85а",
-       "tog-hidecategorization": "Къайлаяккха оагӀонай категореш",
-       "tog-extendwatchlist": "ШеÑ\80адаÑ\8c Ñ\82еÑ\80кама Ñ\85Ñ\8cаÑ\82|аÑ\8fздаÑ\80, Ð¼Ð°Ñ\81Ñ\81адола Ñ\85Ñ\83вÑ\86амаÑ\88 Ñ\87Ñ\83лоаÑ\86аÑ\88 Ð´Ð¾Ð»Ð°, Ð°Ð»Ñ\85Ñ\85а Ñ\82|еÑ\85Ñ\8cаÑ\80а Ð´Ð°Ñ\8cÑ\80аÑ\88 Ð¼Ð°Ñ\80а Ð° Ð´Ð¾Ð°Ñ\86аÑ\88",
+       "tog-underline": "ТIаÑ\85Ñ\8cожаÑ\8fÑ\80га ÐºIала Ñ\82ака Ñ\85Ñ\8cакÑ\85ар:",
+       "tog-hideminor": "Ð\9aÑ\8aайладаккÑ\85а Ð·|амига Ð´Ð¾Ð»Ð° Ñ\85Ñ\83вÑ\86амаÑ\88 ÐºÐµÑ\80да Ñ\85Ñ\83вÑ\86амаÑ\88Ñ\82а Ñ\8eкÑ\8aеÑ\80а",
+       "tog-hidepatrolled": "Ð\9aÑ\8aайладаккÑ\85а Ñ\85а Ð´ÐµÑ\80а Ñ\87акÑ\85даÑ\8cнна Ð´Ð¾Ð»Ð° Ñ\85Ñ\83вÑ\86амаÑ\88 ÐºÐµÑ\80да Ñ\85Ñ\83вÑ\86амаÑ\88Ñ\82а Ñ\8eкÑ\8aеÑ\80а",
+       "tog-newpageshidepatrolled": "Ð\9aÑ\8aайлаÑ\8fÑ\8cккÑ\85а Ñ\85а Ð´ÐµÑ\80а Ñ\87акÑ\85Ñ\8aÑ\8fнна Ð¹Ð¾Ð»Ð° Ð¾Ð°Ð³IонаÑ\88 ÐºÐµÑ\80да Ð¾Ð°Ð³IонаÑ\88Ñ\82а Ñ\8eкÑ\8aеÑ\80а",
+       "tog-hidecategorization": "Къайлаяха оагӀонай категореш",
+       "tog-extendwatchlist": "Ð¥Ñ\8cаÑ\88еÑ\80Ñ\8aÑ\8fÑ\8c Ð¹Ð¾Ð»Ð° Ð·ÐµÐ¼ Ð±Ð°Ñ\80а Ñ\81пиÑ\81ок, Ð¼Ð°Ñ\81Ñ\81адола Ñ\85Ñ\83вÑ\86амаÑ\88 Ñ\88е Ñ\87Ñ\83лоаÑ\86аÑ\88, Ñ\82|еÑ\85Ñ\8cаÑ\80а Ð´Ð°Ñ\8c Ñ\85Ñ\83вÑ\86амаÑ\88 Ñ\85инна Ñ\86а IеÑ\88.",
        "tog-usenewrc": "Керда хувцамашка а хьат|аяздара зембаккхарга а эргадаккхараш тоабаде (JavaScript эша)",
-       "tog-numberheadings": "Ð\9aеÑ\80Ñ\82аÑ\88каÑ\88Ñ\82а Ð°Ð»Ð°Ð½Ð·Ð° Ñ\82аÑ\8cÑ\80аÑ\85Ñ\8cа Ñ\85оÑ\82Ñ\82а",
-       "tog-showtoolbar": "Г|алатнийcдара г|ирсагартакх хьахьокха (JavaScript)",
-       "tog-editondblclick": "Шозза Ð´|аÑ\82о|амÑ\86a oаг|Ñ\83в Ñ\85Ñ\83вÑ\86а (JavaScript)",
-       "tog-editsectiononrightclick": "РалÑ\81декÑ\8aаÑ\80аÑ\88 Ñ\85Ñ\83вÑ\86а Ð´Ð°Ñ\85каÑ\86а Ð°Ñ\8cÑ\82Ñ\82а Ð´|аÑ\82о|амÑ\86а  ÐºÐµÑ\80Ñ\82аÑ\88ка Ñ\82|а (JavaScript)",
-       "tog-watchcreations": "Tеркама хьат|аяздар т|а аз яь оаг|онаши чуяьккха паьлаши т|атоха",
-       "tog-watchdefault": "Tеркама хьат|аяздар т|а аз хийца оаг|онаши паьлаша кустяздараши т|атоха",
-       "tog-watchmoves": "Tеркама хьат|аяздар т|а аз ц|ихийца оагӀонаши паьлаши т|атоха",
-       "tog-watchdeletion": "Tеркама хьат|аяздар т|а аз д|аяьккха оагӀонаши паьлаши т|атоха",
+       "tog-numberheadings": "Ð\90вÑ\82омаÑ\82иÑ\87еÑ\81ки Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ñ\88Ñ\82а Ð½Ñ\83меÑ\80аÑ\86и Ñ\85Ñ\8cае",
+       "tog-showtoolbar": "ГIирсай панель хьахьокха хувцам беч хана",
+       "tog-editondblclick": "Ð\9dиÑ\81Ñ\8aе Ð¾Ð°Ð³Ó\80онаÑ\88 Ñ\88озза IоÑ\82Ó\80аÑ\82оÓ\80аеÑ\87а (JavaScript)",
+       "tog-editsectiononrightclick": "Ð\9dийÑ\81де Ð´Ð°ÐºÑ\8aа Ñ\88озза Ð´Ð°Ñ\85ка Ð°Ñ\8cÑ\82Ñ\82а Ñ\82оIаеÑ\80 Ñ\82Ó\80аÑ\82оÓ\80айиÑ\87а Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° Ñ\82Iа (JavaScript)",
+       "tog-watchcreations": "Зем бара списка т|атоха аз хьаяь оаг|онаши чуяьккха файлаши",
+       "tog-watchdefault": "Зем бара списка т|атоха аз хийца оаг|онаши файлай йоазонца сурташ оттадари",
+       "tog-watchmoves": "Зем бара списка т|атоха аз цIи хийца оаг|онаши файлаши",
+       "tog-watchdeletion": "Зем бара списка т|атоха аз дIаяьккха оаг|онаши файлаши",
        "tog-minordefault": "Теркамза хувцамашта лоархӀамза мо белгало хотта",
        "tog-previewontop": "ГӀалатнийсдара кора хьалхе бӀаргтассам оттае",
        "tog-previewonfirst": "ГӀалатнийсдаре дехьавоалаш/йоалаш бӀаргтассам гойта",
@@ -69,9 +69,9 @@
        "sun": "КIиранди",
        "mon": "Ор",
        "tue": "Шин",
-       "wed": "Кха",
+       "wed": "Кх",
        "thu": "Ер",
-       "fri": "П|аь",
+       "fri": "ПIаьр.",
        "sat": "Шоа",
        "january": "АгIой бутт",
        "february": "Саь-кур бутт",
@@ -87,7 +87,7 @@
        "december": "Чан-тар бутт",
        "january-gen": "АгIой бетт",
        "february-gen": "Саь-кур бетт",
-       "march-gen": "Муттхьол бетт",
+       "march-gen": "Мутт-хьал бетт",
        "april-gen": "Тушоли бетт",
        "may-gen": "Села бетт",
        "june-gen": "Этинга бетт",
@@ -96,7 +96,7 @@
        "september-gen": "Тов\\Михий бетт",
        "october-gen": "Ардарий\\АьрхIий бетт",
        "november-gen": "Лай чилла бетт",
-       "december-gen": "Чантар бетт",
+       "december-gen": "Чан-тар бетт",
        "jan": "АгIой",
        "feb": "Саь-кур",
        "mar": "Мутт-хьал",
        "period-pm": "ДТ",
        "pagecategories": "{{PLURAL:$1|1=Категори|Категореш}}",
        "category_header": "«$1» категори чура оагIонаш",
-       "subcategories": "ЧÑ\83Ñ\80акаÑ\82агаш",
+       "subcategories": "Ð\9aIалкаÑ\82егоÑ\80еш",
        "category-media-header": "\"$1\" Категори чура файлаш",
-       "category-empty": "''УкÑ\85 ÐºÐ°Ñ\82ага Ñ\87Ñ\83 Ñ\86Ñ\85Ñ\8cаккÑ\85а Ð¾Ð°Ð³|онаÑ\88 Ðµ Ð¿Ð°Ñ\8cлаÑ\88 Ñ\8fÑ\86.''",
+       "category-empty": "''Ð\95Ñ\80 ÐºÐ°Ñ\82егоÑ\80и Ñ\85Ó\80анза Ñ\8fÑ\8cÑ\81Ñ\81а Ñ\8f (Ñ\86Ñ\85Ñ\8cаккÑ\85а Ð¾Ð°Ð³IонаÑ\88 Ðµ Ñ\84айлаÑ\88 Ð¹Ð¾Ð°Ñ\86аÑ\88).''",
        "hidden-categories": "{{PLURAL:$1|1=Къайла категори|Къайла категореш}}",
        "hidden-category-category": "Къайла категореш",
-       "category-subcat-count": "{{PLURAL:$2|УкÑ\85 ÐºÐ°Ñ\82агa Ñ\82|еÑ\85Ñ\8cаÑ\80а Ð±Ñ\83Ñ\85каÑ\82аг Ñ\87Ñ\83лоаÑ\86.|{{PLURAL:$1|1=$1 Ð±Ñ\83Ñ\85каÑ\82аг Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ñ\8f|$1 Ð±Ñ\83Ñ\85каÑ\82агаÑ\88 Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ñ\8f}} $2 Ð¹Ð¾Ð»Ð°Ñ\87аÑ\80ex.}}",
-       "category-subcat-count-limited": "УкÑ\85 ÐºÐ°Ñ\82агa Ñ\87Ñ\83 {{PLURAL:$1|1=$1 Ðº|алкаÑ\82аг|$1 Ðº|алкаÑ\82агаÑ\88}}.",
-       "category-article-count": "{{PLURAL:$2|УкÑ\85 ÐºÐ°Ñ\82ага Ñ\86Ñ\85Ñ\8cа Ð¾Ð°Ð³|Ñ\83в Ð¼Ð°Ñ\80а Ñ\87Ñ\83лоаÑ\86аÑ\86.|{{PLURAL:$1|1=$1 Ð¾Ð°Ð³|Ñ\83в Ñ\85Ñ\8cаÑ\85екÑ\85а Ñ\8f|$1 Ð¾Ð°Ð³|oнаÑ\88 Ñ\85Ñ\8cаÑ\85екÑ\85а Ñ\8f}} Ñ\83кÑ\85 ÐºÐ°Ñ\82ага $2 Ð¹Ð¾Ð»Ð°Ñ\87аÑ\80\85.}}",
-       "category-article-count-limited": "УкÑ\85 ÐºÐ°Ñ\82ага Ñ\87Ñ\83 {{PLURAL:$1|1=$1 Ð¾Ð°Ð³|Ñ\83в|$1 Ð¾Ð°Ð³|oнаÑ\88}}.",
+       "category-subcat-count": "{{PLURAL:$2|УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 Ñ\8f Ñ\83кÑ\85ан ÐºIалÑ\85аÑ\80а ÐºÐ°Ñ\82егоÑ\80и.|УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 Ñ\8f $1 {{PLURAL:$1|кIалÑ\85аÑ\80а ÐºÐ°Ñ\82егоÑ\80и|кIалÑ\85аÑ\80а ÐºÐ°Ñ\82егоÑ\80еÑ\88}} $2 Ð¼Ð°Ñ\81Ñ\81айолÑ\87аÑ\80еÑ\85.}}",
+       "category-subcat-count-limited": "УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 {{PLURAL:$1|кIалÑ\85аÑ\80а ÐºÐ°Ñ\82егоÑ\80и|$1 ÐºIалÑ\85аÑ\80а ÐºÐ°Ñ\82егоÑ\80еÑ\88}} Ñ\8f.",
+       "category-article-count": "{{PLURAL:$2|УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 Ñ\86аI Ð¼Ð°Ñ\80а Ð¾Ð°Ð³IÑ\83в Ñ\8fÑ\86.|УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 Ñ\8f $2 Ð¾Ð°Ð³Ó\80Ñ\83в, Ñ\86аÑ\80еÑ\85 Ð¾Ð°Ð³Ó\80онгаÑ\85Ñ\8c {{PLURAL:$1|Ñ\85Ñ\8cагойÑ\82а $1 Ð¾Ð°Ð³Ó\80Ñ\83в}}}}",
+       "category-article-count-limited": "УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 {{PLURAL:$1|$1 Ð¾Ð°Ð³Ó\80Ñ\83в Ñ\8f|1=Ñ\86аI Ð¾Ð°Ð³Ó\80Ñ\83в Ð¼Ð°Ñ\80а Ñ\8fÑ\86}}.",
        "category-file-count": "{{PLURAL:$2|Укх катагори чу цаI мара файл яц.|{{PLURAL:$1|1=$1 файл хьахьокхаш я|$1 файл хьахьокхаш я}} укх категори $2 долачарeх.}}",
-       "category-file-count-limited": "УкÑ\85 ÐºÐ°Ñ\82ага Ñ\87Ñ\83 {{PLURAL:$1|1=$1 Ð»Ñ\83Ñ\80даÑ\80|$1 Ð»Ñ\83Ñ\80даÑ\80аÑ\88}}.",
+       "category-file-count-limited": "УкÑ\85 ÐºÐ°Ñ\82егоÑ\80и Ñ\87Ñ\83 {{PLURAL:$1|$1 Ñ\84айл|$1 Ñ\84айлаÑ\88|1=Ñ\86аI Ð¼Ð°Ñ\80а Ñ\84айл Ñ\8fÑ\86}}.",
        "listingcontinuesabbrev": "(дIахо)",
-       "index-category": "Ð\94|аÑ\85Ñ\8cожама Ð¾Ð°Ð³|онаш",
-       "noindex-category": "Ð\94|аÑ\85Ñ\8cожаманза Ð¾Ð°Ð³|онаш",
-       "broken-file-category": "Ð\9fаÑ\8cла Ñ\85Ñ\8cожадеÑ\80гаÑ\88Ñ\86а Ð±Ð¾Ð»Ñ\85беÑ\88 Ð¹Ð¾Ð°Ñ\86а Ð¾Ð°Ð³|онаш",
-       "about": "Ð\9bоаÑ\86ам",
+       "index-category": "Ð\98ндекÑ\81 Ð¾Ñ\82Ñ\82аеÑ\88 Ð¾Ð°Ð³Iонаш",
+       "noindex-category": "Ð\98ндекÑ\81 Ñ\86а Ð¾Ñ\82Ñ\82аеÑ\88 Ð¾Ð°Ð³Iонаш",
+       "broken-file-category": "Файла Ñ\82IаÑ\85Ñ\8cожаÑ\8fÑ\80гаÑ\88 Ð±Ð¾Ð»Ñ\85беÑ\88 Ð¹Ð¾Ð°Ñ\86а Ð¾Ð°Ð³Iонаш",
+       "about": "СÑ\83Ñ\80Ñ\82 Ð¾Ñ\82Ñ\82адаÑ\80",
        "article": "Йоазув",
-       "newwindow": "(кердача коре)",
+       "newwindow": "&nbsp;(керда кора чу)",
        "cancel": "Эшац",
-       "moredotdotdot": "Д|ахо",
-       "morenotlisted": "Ер |ояздар хьалдиззанз да.",
-       "mypage": "Oаг|ув",
-       "mytalk": "Дувцам",
-       "anontalk": "Дувцар",
+       "moredotdotdot": "ДIахо...",
+       "morenotlisted": "Ер список хьалйиза яц.",
+       "mypage": "ОагIув",
+       "mytalk": "Дувца оттадар",
+       "anontalk": "Дувца оттадар",
        "navigation": "Навигаци",
        "and": "&#32;а",
        "qbfind": "Лахар",
-       "qbbrowse": "Б|аргтасса",
-       "qbedit": "Ð¥Ñ\83вÑ\86а",
-       "qbpageoptions": "Оаг|он оттамаш",
+       "qbbrowse": "БIаргтохар",
+       "qbedit": "Ð\9dиÑ\81Ñ\8aе",
+       "qbpageoptions": "ОагIон оттамаш",
        "qbmyoptions": "Са оттамаш",
        "faq": "КТХ",
        "faqpage": "Project:КТХ",
-       "actions": "Ð¥|амдаÑ\80аш",
+       "actions": "Ð\90Ñ\80дамаш",
        "namespaces": "ЦIерий мотташ",
        "variants": "Варианташ",
        "navigation-heading": "Навигацен меню",
-       "errorpagetitle": "Г|алат",
-       "returnto": "цу $1 оаг|он т|а юхаг|о",
+       "errorpagetitle": "ГӀалат",
+       "returnto": "Укх $1 оагIона тIа юхагӀо.",
        "tagline": "Кечал укхазара: {{grammar:genitive|{{SITENAME}}}}",
-       "help": "Ð\93Ó\80о",
+       "help": "Ð\9dовкÑ\8a\81Ñ\82ал",
        "search": "Лахаp",
        "searchbutton": "Хьалáха",
-       "go": "Дехьа г|о",
+       "go": "Дехьавала",
        "searcharticle": "Дехьавала",
        "history": "Истори",
        "history_short": "Истори",
-       "updatedmarker": "Со Ñ\85анаÑ\87а Ð´ÐµÐ½Ñ\86а Ñ\85Ñ\83вÑ\86амаÑ\88 Ñ\85иннaд",
+       "updatedmarker": "Со Ñ\82IеÑ\85Ñ\85Ñ\8cаÑ\80а Ñ\83кÑ\85аз Ñ\85иннаÑ\87Ñ\83л Ñ\82IеÑ\85Ñ\8cагIа ÐºÐµÑ\80дадаÑ\8cккÑ\85ад",
        "printableversion": "Зарба тохара верси",
-       "permalink": "Даиман латташ йола хьожаярг",
-       "print": "Ð\9aепаÑ\82оÑ\85аÑ\80",
+       "permalink": "Ð\94аиман Ð»Ð°Ñ\82Ñ\82аÑ\88 Ð¹Ð¾Ð»Ð° Ñ\82IаÑ\85Ñ\8cожаÑ\8fÑ\80г",
+       "print": "Ð\97аÑ\80ба Ñ\82оÑ\85а",
        "view": "Хьажар",
-       "view-foreign": "УкÑ\85 $1 Ñ\8fÑ\85 Ñ\81айÑ\82а Ñ\87Ñ\83 Ñ\85Ñ\8cажа",
+       "view-foreign": "Укх $1 сайта чу хьажа",
        "edit": "Нийсде",
        "edit-local": "Хувца локальни йоазонца сурт оттадар",
-       "create": "Ð¥Ñ\8cаде",
+       "create": "Ð¥Ñ\8cакÑ\85олла",
        "create-local": "ТIатоха локальни йоазонца сурт оттадар",
-       "editthispage": "Ð\95Ñ\80 Ð¾Ð°Ð³|Ñ\83в Ñ\85Ñ\83вÑ\86а",
-       "create-this-page": "Ep oаг|ув хьае",
-       "delete": "Д|аяккха",
-       "deletethispage": "Ð\95Ñ\80 Ð¾Ð°Ð³|Ñ\83в Ð´|аÑ\8fÑ\8cккÑ\85а",
-       "undeletethispage": "Ð\95Ñ\80 Ð¾Ð°Ð³|Ñ\83в Ð´|аÑ\8fккÑ\85анз Ð¹Ð¸Ñ\82а",
-       "undelete_short": "Ð\9cеÑ\82Ñ\82аоÑ\82Ñ\82ае {{PLURAL:$1|1=Ñ\85Ñ\83вÑ\86ам|$1 Ñ\85Ñ\83вÑ\86амаÑ\88}}",
-       "viewdeleted_short": "Б|аргтасса {{PLURAL:$1|1=д|адаьккха хувцам|$1 д|адаьккха хувцамаш}}",
-       "protect": "Ð\9bоÑ\80аде",
+       "editthispage": "Ð\9dийÑ\81Ñ\8aе ÐµÑ\80 Ð¾Ð°Ð³IÑ\83в",
+       "create-this-page": "Хьакхолла ер оагӀув",
+       "delete": "ДӀаяккха",
+       "deletethispage": "Ð\94Ó\80аÑ\8fккÑ\85а ÐµÑ\80 Ð¾Ð°Ð³Ó\80Ñ\83в",
+       "undeletethispage": "ЮÑ\85амеÑ\82Ñ\82аоÑ\82Ñ\82ае ÐµÑ\80 Ð¾Ð°Ð³Ó\80Ñ\83в",
+       "undelete_short": "ЮÑ\85амеÑ\82Ñ\82аоÑ\82Ñ\82ае {{PLURAL:$1|$1 Ð½Ð¸Ð¹Ñ\81даÑ\80|$1 Ð½Ð¸Ð¹Ñ\81даÑ\80аÑ\88|1=нийÑ\81даÑ\80}}",
+       "viewdeleted_short": "{{PLURAL:$1|$1 дIадаьккха нийсдарга|дIадаьккха нийсдарга|$1 дIадаьккха нийсдарашга}} хьажар",
+       "protect": "Ð\93Iо Ð´Ð°Ñ\80",
        "protect_change": "хувца",
-       "protectthispage": "Ð\9bоÑ\80ае ÐµÑ\80 Ð¾Ð°Ð³|Ñ\83в",
-       "unprotect": "Ð\9bоÑ\80ам хувца",
-       "unprotectthispage": "Ð\9bоÑ\80ам хувца",
+       "protectthispage": "Ð\93Iо Ð´Ðµ Ñ\83кÑ\85 Ð¾Ð°Ð³Iон",
+       "unprotect": "Ð\93Iо хувца",
+       "unprotectthispage": "УкÑ\85 Ð¾Ð°Ð³Iон Ð³Iо хувца",
        "newpage": "Керда оагӀув",
-       "talkpage": "УкÑ\85 Ð¾Ð°Ð³|он Ñ\82|а Ð´Ñ\83вÑ\86ам Ð±Ðµ",
+       "talkpage": "Ð\95Ñ\80 Ð¾Ð°Ð³IÑ\83в Ñ\8eвÑ\86а",
        "talkpagelinktext": "дувца оттадар",
-       "specialpage": "Ð\93\83лакÑ\85адаÑ\80а Ð¾Ð°Ð³|ув",
+       "specialpage": "Ð\91алÑ\85а Ð¾Ð°Ð³Ó\80ув",
        "personaltools": "Доакъашхочун гӀирсаш",
        "articlepage": "Йоазон т|а б|аргтасса",
        "talk": "Дувца оттадар",
        "categorypage": "Катага оаг|oн т|а б|аргтасса",
        "viewtalkpage": "Дувцамага б|аргтасса",
        "otherlanguages": "Кхыча меттаех",
-       "redirectedfrom": "($1 Ñ\82IaÑ\80а Ñ\85Ñ\8cаÑ\85Ñ\8cожадаÑ\8c Ð´Ð°)",
+       "redirectedfrom": "($1 Ñ\82IaÑ\80а Ñ\83кÑ\85аз Ñ\85Ñ\8cаÑ\85Ñ\8cожаÑ\8fÑ\8c Ñ\8f)",
        "redirectpagesub": "Д|а-хьа дайта оаг|ув",
-       "redirectto": "ТIахьожадар укхаза:",
+       "redirectto": "Ð\94Iа-Ñ\81ахьожадар укхаза:",
        "lastmodifiedat": "Укх оагIoн тIеххьара хувцам: $2, $1.",
        "viewcount": "Укх оаг|oн т|а б|аргтассаб {{PLURAL:$1|цхьааца\n|$1 times}}. {{PLURAL:$1|1=цхьазза|$1за}}.",
        "protectedpage": "Лорама оаг|ув",
        "page-atom-feed": "«$1» — Atom-мугI",
        "red-link-title": "$1 (укх тайпара оагӀув яц)",
        "nstab-main": "Йоазув",
-       "nstab-user": "Ð\94акÑ\8aалаÑ\8cÑ\86аÑ\80хо",
+       "nstab-user": "Ð\94оакÑ\8aаÑ\88хо",
        "nstab-media": "Медифаг",
-       "nstab-special": "Ð\93Ó\80Ñ\83лакха оагӀув",
+       "nstab-special": "Ð\91алха оагӀув",
        "nstab-project": "Проектах лаьца",
        "nstab-image": "Файл",
        "nstab-mediawiki": "Хоам",
        "virus-unknownscanner": "довзашдоаца мазаундохьалург:",
        "welcomeuser": "Маьрша доаг|алд, $1!",
        "yourname": "Дакъалаьцархочунна цӀи:",
-       "userlogin-yourname": "Доакъашхочунна цӀи",
-       "userlogin-yourname-ph": "Чуйоалае доакъашхочун цӀи",
+       "userlogin-yourname": "Доакъашхочун цӀи",
+       "userlogin-yourname-ph": "Iочуязъе хьай учёта яздара (доакъашхочун) цӀи",
        "createacct-another-username-ph": "Чуйоалае доакъашхочун цӀи",
        "yourpassword": "КъайладIоагӀа:",
        "userlogin-yourpassword": "Пароль",
        "notloggedin": "Оаш шоай цӀи хьааьннадац",
        "nologin": "Леламе дIаяздар дац? '''$1'''.",
        "nologinlink": "Леламе дIаяздар кхолла",
-       "createaccount": "Учёта яздар кхолла",
+       "createaccount": "Учёта яздар хьакхолла",
        "gotaccount": "Укхаза дӀаязабенна дий шо? '''$1'''.",
        "gotaccountlink": "Чувала/яла",
        "userlogin-resetlink": "Чувала/яла цӀии дIоагӀаи дийцаденнадий?",
        "resetpass-submit-cancel": "Юхавал/ялa",
        "passwordreset-username": "Дакъалаьцархочунна цӀи:",
        "passwordreset-email": "Д-хоамни моттиг:",
-       "bold_sample": "Сома Ñ\8fздам",
-       "bold_tip": "Сома Ñ\8fздам",
-       "italic_sample": "Ð\9aÑ\83лга Ñ\8fздам",
-       "italic_tip": "Ð\9aÑ\83лга Ñ\8fздам",
-       "link_sample": "Ӏинка кортале",
-       "link_tip": "Чура хьожаярг",
-       "extlink_sample": "Ӏинка кортале http://www.example.com",
-       "extlink_tip": "Ð\90Ñ\80ен Ó\80инка (http:// Ñ\82амагÓ\80аÑ\85 Ð´Ð¸Ð¹Ñ\86а Ð¼Ð° Ð»Ðµ)",
-       "headline_sample": "Ð\9aоÑ\80Ñ\82ален Ñ\8fздам",
-       "headline_tip": "2-гӀа лагӀарлен кортале",
+       "bold_sample": "Ð\90Ñ\85\81ома Ñ\82екÑ\81Ñ\82",
+       "bold_tip": "Ð\90Ñ\85\81ома Ñ\82екÑ\81Ñ\82",
+       "italic_sample": "СиÑ\85а Ð¹Ð¾Ð°Ð·Ð¾Ð½ Ñ\82екÑ\81Ñ\82",
+       "italic_tip": "СиÑ\85а Ð¹Ð¾Ð°Ð·Ð¾Ð½ Ñ\82екÑ\81Ñ\82",
+       "link_sample": "ТIахьожаярга заголовок",
+       "link_tip": "ЧÑ\83Ñ\80а Ñ\82IаÑ\85Ñ\8cожаÑ\8fÑ\80г",
+       "extlink_sample": "http://www.example.com тIахьожаярга заголовок",
+       "extlink_tip": "Ð\90Ñ\80аÑ\85Ñ\8cаÑ\80а Ñ\82IаÑ\85Ñ\8cожаÑ\8fÑ\80г (йиÑ\86 Ð¼Ð° Ñ\8fлийÑ\82Ñ\82а Ð¿Ñ\80еÑ\84икÑ\81 http://)",
+       "headline_sample": "Ð\97аголовка Ñ\82екÑ\81Ñ\82",
+       "headline_tip": "2-гӀа лагӀа заголовок",
        "nowiki_sample": "Укхаза кийчаде дезаш доаца яздам оттаде",
        "nowiki_tip": "Масса-бустамлорг теркамза дита",
-       "image_tip": "ЧÑ\83Ñ\8fÑ\8cккÑ\85а Ð¿Ð°Ñ\8cла",
-       "media_tip": "Файлан тIахьожавар",
+       "image_tip": "Ð\94IаÑ\87Ñ\83оÑ\82Ñ\82аÑ\8fÑ\8c Ñ\84айл",
+       "media_tip": "Файла тIахьожавар",
        "sig_tip": "Шун кулгаяздар а, хӀанзара ха а",
-       "hr_tip": "Ð\9cÑ\83Ñ\85ала Ð¼Ñ\83гÓ\80 (могаÑ\88 Ñ\82айпаÑ\80а Ðº|еззига Ñ\85айÑ\80аде)",
+       "hr_tip": "Ð\9fÑ\85Ñ\8cоÑ\80агIен Ñ\82ака (Ñ\86оÑ\85 Ð¿Ð°Ð¹Ð´Ð° Ñ\8dÑ\86аÑ\80 Ñ\82IеÑ\85даÑ\8cнна ÐºÐ°Ñ\81Ñ\82Ñ\82а Ð¼Ð° де)",
        "summary": "Хувцамий белгалдер",
        "subject": "БӀагал/кортале:",
        "minoredit": "ЗӀамига хувцам",
        "watchthis": "Зем бе укх оагӀон",
        "savearticle": "ОагӀув дIаязъе",
        "preview": "Хьалхе бӀаргтассар",
-       "showpreview": "Хьалххе хьажар",
+       "showpreview": "Хьалххе бIаргтохар",
        "showdiff": "Даь хувцамаш",
        "anoneditwarning": "<strong>Теркам бе!</strong> Хьо автор хинна система чуваьннавац. Нагахьа санна Iа моллагIа хувцам бой, Хьа IP-адрес дийла массанен бIаргагуш хургда. Нагахьа санна Хьо <strong>[$1 хьачувоале]</strong> е <strong>[$2 учёта яздар хьакхолле]</strong>, нийсдараш (хувцамаш) бувзам болаш хургда Хьа доакъашхой цIерца, иштта кхыдола толажагIи гIойленагIи дола дикаьш хургда Хьона.",
        "summary-preview": "Лоацам ба:",
        "newarticle": "(Kерда)",
        "newarticletext": "Шо хьожаяргаца дехьадаьннад йоаца оагӀон тӀа.\nИз кхолларгьйолаш кӀалхагӀа доала корачу текст Iочуязаде (нагахьа кхетаде хала дале [$1 новкъосталан оагӀонга] хьажа).\nЦа ховш укхаза нийсденнадале, шоай браузера '''Юха''' (назад) кнопка тӀа пӀелга тоӀабе.",
        "noarticletext": "ХIанз укх оагӀув тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи хьоаяр кораде]] кхыйола йоазуваш чу, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарий яздаьраш], е\n'''[{{fullurl:{{FULLPAGENAME}}|action=edit}} изза мо цӀи йолаш оагӀув кхолла]'''</span>.",
-       "noarticletext-nopermission": "ХIанз укх оагӀув тӀа яздам дац.\nШун йиш я, кхыдола йоазувнашках [[Special:Search/{{PAGENAME}}|дола цӀерий хаттам корае]] е <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} нийсамий тептара йоазувнаш корае].</span>",
+       "noarticletext-nopermission": "ХIанз укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи белгалъяр хьалаха]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} цун тара дола тептарай яздаьраш].</span> Иштта йола (Ер) оагӀув хьакхолла Хьа бокъо яц.",
        "note": "'''ХӀамоалар:'''",
        "previewnote": "'''Хьалхе б|аргтассам мара бац.'''\nЯздам кхы яздаь дац!",
        "editing": "Хувцам: $1",
        "shown-title": "Хьóкха $1 {{PLURAL:$1|даь йоазо|даь йоазонаш}} укх оáгIувна тIа",
        "viewprevnext": "ДIахьажа ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Укх масса-хьахьоадайтамач ер оаг|ув \"[[:$1]]\" я'''",
-       "searchmenu-new": "<strong>Ð\9aÑ\85олла Ð¾Ð°Ð³IÑ\83в Â«[[:$1]]» Ñ\83кÑ\85 Ð²Ð¸ÐºÐ¸-пÑ\80оекÑ\82е!</strong>\n{{PLURAL:$2|0=|Ð\98Ñ\88Ñ\82Ñ\82а Ñ\85Ñ\8cажа Ñ\85Ñ\8cай Ð»Ð¸Ð¹Ñ\85а Ð¾Ð°Ð³IÑ\83внага.|Иштта хьажа хьай лахара хьахиннарашка.}}",
+       "searchmenu-new": "<strong>Ð¥Ñ\8cакÑ\85олла Ð¾Ð°Ð³IÑ\83в Â«[[:$1]]» Ñ\83кÑ\85 Ð²Ð¸ÐºÐ¸-пÑ\80оекÑ\82е!</strong>\n{{PLURAL:$2|0=|Ð\98Ñ\88Ñ\82Ñ\82а Ñ\85Ñ\8cажа IайÑ\85а Ð»Ð¸Ð¹Ñ\85а Ð¾Ð°Ð³Iонга.|Иштта хьажа хьай лахара хьахиннарашка.}}",
        "searchprofile-articles": "Кертера оагIонаш",
        "searchprofile-images": "Мультимедиа",
        "searchprofile-everything": "Массанахьа",
        "search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешаш}})",
        "search-result-category-size": "{{PLURAL:$1|1=$1 дакъа|$1 дакъаш}} ({{PLURAL:$2|1=$2 кIалцатег|$2 кIалцатегаш}}, {{PLURAL:$3|1=$3 паьла|$3 паьлий}})",
        "search-redirect": "(дIа-сахьожадар $1 тIара)",
-       "search-section": " (дакъа $1)",
+       "search-section": "(дакъа «$1»)",
        "search-suggest": "Хьона эшар ер хила мега: $1",
        "search-interwiki-caption": "Гаргалон хьахьоадайтамаш",
        "search-interwiki-default": "$1 хьахиннараш:",
        "powersearch-toggleall": "Деррига",
        "powersearch-togglenone": "Цхьаккха",
        "preferences": "Оттамаш",
-       "mypreferences": "Ð\9eÑ\82Ñ\82амаш",
+       "mypreferences": "Ð\93IиÑ\80Ñ\81аш",
        "prefs-skin": "БIагала куц",
        "skin-preview": "Хьажа",
        "prefs-personal": "Хьа хьай далам",
        "enhancedrc-history": "истори",
        "recentchanges": "Керда хувцамаш",
        "recentchanges-legend": "Керда хувцамай оттамаш",
-       "recentchanges-summary": "КIалхагIа лоарамий доаламе тIехьара оагIувний хувцамаш дIаязадаь да {{grammar:genitive|{{SITENAME}}}}.",
+       "recentchanges-summary": "КIалхагIа ханашца нийсдаь дIаяьздаь да {{grammar:genitive|{{SITENAME}}}}  оагIонай тIеххьара хувцамаш.",
        "recentchanges-feed-description": "Укх ларамца тIехьара массахувцамашт теркам бе.",
        "recentchanges-label-newpage": "Укх хувцамаца керда оагIув кхелла хиннай",
        "recentchanges-label-minor": "Ер зIамига хувцам ба",
        "rcnotefrom": "КIалхагIа хувцамаш хьахьекха я <strong>$2</strong> денза (<strong>$1</strong> кхачалца).",
        "rclistfrom": "$3 $2 денза даь хувцамаш хьахьокха",
        "rcshowhideminor": "$1 зIамига нийсдараш",
-       "rcshowhideminor-hide": "Ð\9aÑ\8aайлдаккха",
+       "rcshowhideminor-hide": "Ð\94IакÑ\8aайладаккха",
        "rcshowhidebots": "$1 боташ",
        "rcshowhidebots-show": "Хьахьокха",
        "rcshowhideliu": "$1 бовзийтарчара доакъашхой",
        "rcshowhideanons-hide": "Къайлабаха",
        "rcshowhidepatr": "$1 теркам даь хувцамаш",
        "rcshowhidemine": "$1 хьа нийсдараш",
-       "rcshowhidemine-hide": "Ð\9aÑ\8aайлдаккха",
+       "rcshowhidemine-hide": "Ð\94IакÑ\8aайладаккха",
        "rclinks": "Хьахьокха $2 дийнахь даь хинна тIеххьара $1 хувцамаш\n<br />$3",
        "diff": "башхало",
        "hist": "истори",
-       "hide": "Къайлдаккха",
+       "hide": "Ð\9aÑ\8aайладаккÑ\85а",
        "show": "Хьахьокха",
        "minoreditletter": "зI",
        "newpageletter": "К",
        "rc-change-size-new": "Хувцам баьнначул тӀехьагIа бола боарам: $1 {{PLURAL:$1|байт}}",
        "rc-enhanced-expand": "Ма дарра чулоацамаш хьахьокха (JavaScriptаца)",
        "rc-enhanced-hide": "Ма дарра чулоацамаш къайладаккха",
-       "recentchangeslinked": "Ð\93аÑ\80галон Ñ\85Ñ\83вÑ\86амаш",
+       "recentchangeslinked": "Ð\92IаÑ\88агIдÑ\83взаденна Ð½Ð¸Ð¹Ñ\81даÑ\80аш",
        "recentchangeslinked-feed": "Гаргалон хувцамаш",
        "recentchangeslinked-toolbox": "Укханца вIашагIдувзаденна хувцамаш",
        "recentchangeslinked-title": "$1ца вIашидувзаденна хувцамаш",
        "recentchangeslinked-summary": "Ер, Iинк яь йола оагIув (е укх цатегачу чуйоагIараш), дукха ха йоацаш хьийца оагIувнашкий дагарле я.\n[[Special:Watchlist|Шун теркама дагарленашках]] чуйоагIа оагIувнаш '''белгалаяь я'''.",
-       "recentchangeslinked-page": "ОагIува цIи",
-       "recentchangeslinked-to": "Ð\9eагIÑ\83внаÑ\88 Ñ\82Iа Ñ\85Ñ\83вÑ\86амаÑ\88 Ñ\85Ñ\8cаÑ\85Ñ\8cокÑ\85а, Ñ\85Ñ\8cаÑ\85Ñ\8cекÑ\85а Ð¹Ð¾Ð»Ð° Ð¾Ð°Ð³IÑ\83в Ñ\82Iа IинкаÑ\88 ÐµÑ\88 Ð¹Ð¾Ð»а.",
+       "recentchangeslinked-page": "ОагIон цIи",
+       "recentchangeslinked-to": "Ð\92еÑ\88Ñ\82а, Ð±ÐµÐ»Ð³Ð°Ð»Ñ\8fÑ\8cккÑ\85а Ð¾Ð°Ð³Iон Ñ\82IаÑ\85Ñ\8cожавеÑ\88 Ð´Ð¾Ð»Ð° Ð¾Ð°Ð³IонаÑ\88Ñ\82а Ð´Ð°Ñ\8c Ñ\85Ñ\83вÑ\86амаÑ\88 Ñ\85Ñ\8cаÑ\85Ñ\8cокÑ\85а.",
        "upload": "Файл чуяккха",
        "uploadbtn": "Паьл чуяьккха",
        "uploadlogpage": "Чуяьккхамий тептар",
-       "filedesc": "Ð\9bоаÑ\86а Ð»Ð¾Ð°Ñ\86ам",
+       "filedesc": "Ð\9bоаÑ\86а Ð¹Ð¾Ð°Ð·Ð¾Ð½Ñ\86а Ñ\81Ñ\83Ñ\80Ñ\82 Ð¾Ñ\82Ñ\82адаÑ\80",
        "fileuploadsummary": "Лоаца лоацам:",
        "license": "ЦIийяздар",
-       "license-header": "ЦIийÑ\8fздаÑ\80",
+       "license-header": "Ð\9bиÑ\86ензиÑ\80ование",
        "imgfile": "файл",
        "listfiles": "Паьлий дагарче",
        "listfiles_date": "Денха",
        "filehist-comment": "Белгалдаккхар",
        "imagelinks": "Файлах пайда эцар",
        "linkstoimage": "{{PLURAL:$1|1=ТIехьайоагIача $1 оагIуво тIахьожаву|ТIехьайоагIача $1 оагIувнаша тIахьожаву}} укх файла тIа:",
-       "nolinkstoimage": "Ð\99ола Ð¿Ð°Ñ\8cла Ñ\82Iа  Iинк Ñ\8e Ð¾Ð°Ð³IÑ\83внаÑ\88 Ð´Ð°Ñ\86",
+       "nolinkstoimage": "УкÑ\85 Ñ\84айла Ñ\82IаÑ\85Ñ\8cожавеÑ\88 Ð¹Ð¾Ð»Ð° Ð¾Ð°Ð³IонаÑ\88 Ñ\8fÑ\86.",
        "sharedupload": "Ер паьла $1чера я, кхыча хьахьоадайтамча хьахайраде йийшайолаш я.",
        "sharedupload-desc-here": "Ер файл $1 чура я, иштта кхыйола проекташ чу пайда эца аьттув болаш я.\nЦун [$2 йоазонца сурт оттадара оагIон] информаци кIалхахь хьайоалаяй.",
        "uploadnewversion-linktext": "Укх паьлий керда бIаса чуяьккха",
        "listgrouprights-members": "(тоабий дагарче)",
        "emailuser": "Дакъалаьцархочоа д-хоамни:",
        "watchlist": "Теркама дагарче",
-       "mywatchlist": "ТеÑ\80кама Ð´Ð°Ð³Ð°Ñ\80ле",
+       "mywatchlist": "Ð\97ем Ð±Ð°Ñ\80а Ñ\81пиÑ\81ок",
        "watchlistfor2": "$1 $2 царна",
        "addedwatchtext": "\"[[:$1]]\" оагIув, шун [[Special:Watchlist|теркама дагаршкахь]] чуяккха я. \nТехьара мел йола укх оагIувни хувцамаш цу дагаршкахь хоам беш хургья. Вешта [[Special:RecentChanges|керда хувцама дагаршкаехь]] сома къоалмаца хьакъоастлуш хургья.",
        "removedwatchtext": "\"[[:$1]]\" оагIув, шун [[Special:Watchlist|теркама дарагчера]] дIаяккха хиннай.",
        "sp-contributions-toponly": "ТIехьара доржамаш лоархаш дола хувцамаш мара ма хьокха",
        "sp-contributions-submit": "Хьалáха",
        "whatlinkshere": "Хьожаяргаш укхаза",
-       "whatlinkshere-title": "\"$1\" тIа Iинкаш еш йола оагIувнаш",
+       "whatlinkshere-title": "\"$1\" тIахьожавеш йола оагIонаш",
        "whatlinkshere-page": "ОагIув",
-       "linkshere": "ТIехьайоагIа оагIувнаш тIахьожаву «'''[[:$1]]'''»:",
+       "linkshere": "ТIехьайоагIа оагIонаш тIахьожаву «'''[[:$1]]'''»:",
        "nolinkshere": "'''[[:$1]]''' оагIув тIа, кхыдола оагIувашкара Iинкаш йоацаш я",
-       "isredirect": "ТIаÑ\85Ñ\8cожадаÑ\80ан Ð¾Ð°Ð³IÑ\83в",
+       "isredirect": "оагIÑ\83в-дIа-Ñ\81аÑ\85Ñ\8cожадаÑ\80",
        "istemplate": "юкъейоалаяр",
-       "isimage": "Файлан хьожаярг",
+       "isimage": "Файлови Ñ\82Iахьожаярг",
        "whatlinkshere-prev": "{{PLURAL:$1|1=хьалхайоагIа|хьалхайоагIараш}} $1",
        "whatlinkshere-next": "{{PLURAL:$1|1=тIехьайоагIар|тIехьайоагIараш}} $1",
-       "whatlinkshere-links": "← хьожаяргаш",
-       "whatlinkshere-hideredirs": "$1 дIа-хьа чуяьккхамаш",
-       "whatlinkshere-hidetrans": "$1 чуяьккхамаш",
-       "whatlinkshere-hidelinks": "$1 Iинкаш",
+       "whatlinkshere-links": "â\86\90 Ñ\82IаÑ\85Ñ\8cожаÑ\8fÑ\80гаÑ\88",
+       "whatlinkshere-hideredirs": "ДIакъайладаккха дIа-сахьожадар",
+       "whatlinkshere-hidetrans": "ДIакъайладаккха юкъедахараш",
+       "whatlinkshere-hidelinks": "ДIакъайлаяккха тIахьожаяргаш",
        "whatlinkshere-hideimages": "$1 суртIинкаш",
-       "whatlinkshere-filters": "ЦIенÑ\8aеÑ\80аÑ\88",
+       "whatlinkshere-filters": "ФилÑ\8cÑ\82Ñ\80Ñ\8b",
        "blockip": "Укх {{GENDER:$1|доакъошхочоа}} ч|ега бола",
        "ipboptions": "2 сахьат:2 hours,1 ди:1 day,3 ди:3 days,1 кIира:1 week,2 кIира:2 weeks,1 бутт:1 month,3 бутт:3 months,6 бутт:6 months,1 шу:1 year,сиха ца луш:infinite",
        "ipblocklist": "ЧIега бела дакъалаьцархой",
        "movelogpage": "ЦIи хувцара тептар",
        "movereason": "Бахьан",
        "revertmove": "юхаяьккха",
-       "export": "ОагIувий эхфортам",
+       "export": "ОагIонай экспорт",
        "allmessagesname": "ЦIи",
        "allmessagesdefault": "Сатийна улла яздам",
        "allmessages-filter-all": "Дерригаш",
        "thumbnail-more": "Доккха де",
        "thumbnail_error": "ЗIамигасуртанчий кхеллама гIалат: $1",
        "import-upload-filename": "ПаьлацIи:",
-       "tooltip-pt-userpage": "{{GENDER:|Хьа}} доакъашхочунна оагIув",
+       "tooltip-pt-userpage": "{{GENDER:|Хьа}} доакъашхочун оагIув",
        "tooltip-pt-mytalk": "{{GENDER:|Хьа}} дувца оттадара оагIув",
-       "tooltip-pt-preferences": "{{GENDER:|Ð¥Ñ\8cа Ð¾Ñ\82Ñ\82амаш}}",
-       "tooltip-pt-watchlist": "ОоагIувна дагарле, шо бIаргалокхаш йола",
+       "tooltip-pt-preferences": "{{GENDER:|Ð¥Ñ\8cа Ð³IиÑ\80Ñ\81аш}}",
+       "tooltip-pt-watchlist": "Iа зем бу оагIонаш",
        "tooltip-pt-mycontris": "{{GENDER:|хьа}} хувцамаш",
        "tooltip-pt-login": "Укхаза хьай цIи аьле чувала/яла йиша я, амма из параз дац",
        "tooltip-pt-logout": "Аравала/яла",
        "tooltip-t-recentchangeslinked": "Укх оагIуво тIахьожавеш йолча оагIонай тIеххьара хувцамаш",
        "tooltip-feed-rss": "Укх оагIувна RSSчу гойтар",
        "tooltip-feed-atom": "Укх оаг|увна Atomчу гойтар",
-       "tooltip-t-contributions": "{{GENDER:$1|Укх доакъашхочо хийца}} йола оагIувнаш",
+       "tooltip-t-contributions": "{{GENDER:$1|Укх доакъашхочо хийца}} йола оагIонаш",
        "tooltip-t-emailuser": "Укх дакъалаьцархочоа зIы яхьийта",
        "tooltip-t-upload": "Файлаш чуяккха",
        "tooltip-t-specialpages": "ГIулакха оагIувнаш",
        "tooltip-t-print": "Укх оагIон зарба тохара верси",
        "tooltip-t-permalink": "ОагIон укх версин тIахьожавеш йола даим латташ йола хьожаярг",
        "tooltip-ca-nstab-main": "ОагIон чурадар",
-       "tooltip-ca-nstab-user": "Ð\94акÑ\8aалаÑ\8cÑ\86аÑ\80Ñ\85оÑ\87Ñ\83нна Ñ\88ий оагIув",
+       "tooltip-ca-nstab-user": "Ð\94оакÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н Ñ\88е Ð´Ð¾Ð°Ð»Ð°Ñ\85Ñ\8c Ð¹Ð¾Ð»Ð° оагIув",
        "tooltip-ca-nstab-special": "Ер гIулакха оагIув я, из хувца бокъо яц",
        "tooltip-ca-nstab-project": "Проектан оагIув",
        "tooltip-ca-nstab-image": "Файлан оагӀув",
        "tooltip-ca-nstab-help": "ГӀон оагIув",
        "tooltip-ca-nstab-category": "Категорий оагӀув",
        "tooltip-minoredit": "Ер хувцар башха доаца санна белгалде",
-       "tooltip-save": "Ð¥Ñ\8cай Ñ\85Ñ\83вÑ\86амаÑ\88 Ð»Ð¾Ñ\80адеÑ\88 Ð´IаÑ\8fзаде",
-       "tooltip-preview": "Дехар да, оагӀув лораешь дIаязаелехь из мишта я тахка хьалххе хьажарах пайда эцаш!",
+       "tooltip-save": "Хьай хувцамаш лорадеш дIаязде",
+       "tooltip-preview": "Дехар да, оагӀув лораешь дIаязъелехь из мишта я тахка хьалххе хьажарах пайда эцаш!",
        "tooltip-diff": "ДIадолалу текстаца даь хувцамаш хьахьокха",
        "tooltip-compareselectedversions": "Укх оагIувни шин доржамаш тIа юкъера хувцамаш зе.",
        "tooltip-watch": "Ер оагIув теркам беча каьхата тIа яькха",
        "tooltip-rollback": "Цкъа пIелг тоIабе дIадаккха тIехьара редакторас даь хувцамаш",
        "tooltip-undo": "Даь хувцар дIадаьккха, хьалххе хьажар хьахьокха, дIадаккхара бахьан Iочуязаде аьттув болаш.",
-       "tooltip-summary": "Ð\9bоаÑ\86а Ð¹Ð¾Ð°Ð·Ð¾Ð½Ñ\86а Ñ\81Ñ\83Ñ\80Ñ\82 Ð¾Ñ\82Ñ\82адаÑ\80 IоÑ\87Ñ\83Ñ\8fзаде",
+       "tooltip-summary": "Лоаца йоазонца сурт оттадар Iочуязде",
        "pageinfo-hidden-categories": "{{PLURAL:$1|1=Къайла категори|Къайла категореш}} ($1)",
        "pageinfo-toolboxlink": "ОагIонах бола хоам",
        "previousdiff": "← Хьалхара нийсдар",
        "metadata-fields": "Укх списке дагaрадаь суртай метахоамай йистош, хьахьекха хургда суртан оагIон тIа, метахоамай таблица хьоарчая йолаш. Юхедиса йистош къайла хургда.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "Шерал",
        "exif-imagelength": "Лакхал",
-       "exif-orientation": "Суртан белгало",
+       "exif-orientation": "Сурта белгало",
        "exif-imagedescription": "Сурта цIи",
        "exif-model": "Камера модель",
        "exif-software": "Программни Iалашдар",
        "exif-artist": "Яздархо",
        "exif-exifversion": "Верси Exif",
-       "exif-colorspace": "Ð\91аÑ\81аÑ\80а Ð°Ñ\80е",
+       "exif-colorspace": "Ð\91еÑ\81ай Ð¼Ð¾Ñ\82Ñ\82",
        "exif-pixelxdimension": "Сурта шорал",
        "exif-pixelydimension": "Сурта лакхал",
        "exif-datetimedigitized": "Оцифровк яь таьрахь а, ха а",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|дувца оттадар]])",
        "duplicate-defaultsort": "Зем бе. Сатийна дIа-хьа хьоржама доагI \"$2\" хьалхара сатийна дIа-хьа хьоржама доагI \"$1\" хьахьоржа.",
        "version": "Доржам",
-       "version-specialpages": "Ð\93\83лакÑ\85ий Ð¾Ð°Ð³IÑ\83внаш",
+       "version-specialpages": "Ð\91алÑ\85а Ð¾Ð°Ð³Ó\80онаш",
        "version-version": "($1)",
        "version-software-version": "Доржам",
        "fileduplicatesearch-filename": "ПаьлацIи:",
index 087dea0..14da229 100644 (file)
        "changecontentmodel-success-text": "Il tipo di contenuto di [[:$1]] è stato modificato.",
        "changecontentmodel-cannot-convert": "Il contenuto di [[:$1]] non può essere convertito in tipo $2.",
        "changecontentmodel-nodirectediting": "Il modello di contenuto $1 non supporta la modifica diretta",
+       "changecontentmodel-emptymodels-title": "Nessun modello di contenuto disponibile",
+       "changecontentmodel-emptymodels-text": "Il contenuto di [[:$1]] non può essere convertito in alcun tipo.",
        "log-name-contentmodel": "Modifiche del modello contenuti",
        "log-description-contentmodel": "Eventi relativi al modello di contenuto di una pagina",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|ha creato}} la pagina $3 utilizzando un modello di contenuto non predefinito \"$5\"",
        "whatlinkshere-prev": "{{PLURAL:$1|precedente|precedenti $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|successivo|successivi $1}}",
        "whatlinkshere-links": "← collegamenti",
-       "whatlinkshere-hideredirs": "$1 redirect",
-       "whatlinkshere-hidetrans": "$1 inclusioni",
-       "whatlinkshere-hidelinks": "$1 collegamenti",
-       "whatlinkshere-hideimages": "$1 link da file",
+       "whatlinkshere-hideredirs": "Nascondi redirect",
+       "whatlinkshere-hidetrans": "Nascondi inclusioni",
+       "whatlinkshere-hidelinks": "Nascondi collegamenti",
+       "whatlinkshere-hideimages": "Nascondi collegamenti da file",
        "whatlinkshere-filters": "Filtri",
        "whatlinkshere-submit": "Vai",
        "autoblockid": "Autoblocco #$1",
        "lockdbsuccesstext": "Il database è stato bloccato.<br />\nRicordare di [[Special:UnlockDB|rimuovere il blocco]] dopo aver terminato le operazioni di manutenzione.",
        "unlockdbsuccesstext": "Il database è stato sbloccato.",
        "lockfilenotwritable": "Impossibile scrivere sul file di ''lock'' del database. L'accesso in scrittura a tale file da parte del server web è necessario per bloccare e sbloccare il database.",
+       "databaselocked": "Il database è già bloccato.",
        "databasenotlocked": "Il database non è bloccato.",
        "lockedbyandtime": "(da $1 il $2 alle $3)",
        "move-page": "Spostamento di $1",
        "autoredircomment": "Redirect alla pagina [[$1]]",
        "autosumm-new": "Creata pagina con \"$1\"",
        "autosumm-newblank": "Creata pagina vuota",
-       "size-bytes": "$1 byte",
+       "size-bytes": "$1 {{PLURAL:$1|byte}}",
        "lag-warn-normal": "Le modifiche apportate {{PLURAL:$1|nell'ultimo secondo|negli ultimi $1 secondi}} potrebbero non apparire in questa lista.",
        "lag-warn-high": "A causa di un eccessivo ritardo nell'aggiornamento del server di database, le modifiche apportate {{PLURAL:$1|nell'ultimo secondo|negli ultimi $1 secondi}} potrebbero non apparire in questa lista.",
        "watchlistedit-normal-title": "Modifica osservati speciali",
index cecead4..1b07ee9 100644 (file)
        "tog-ccmeonemails": "他の利用者に送信したメールの控えを自分にも送信",
        "tog-diffonly": "差分の下にページ内容を表示しない",
        "tog-showhiddencats": "隠しカテゴリを表示",
-       "tog-norollbackdiff": "巻き戻し後の差分を表示しない",
+       "tog-norollbackdiff": "ロールバック後の差分を表示しない",
        "tog-useeditwarning": "変更を保存せずに編集画面から離れようとしたら警告",
        "tog-prefershttps": "ログインする際、常に安全な接続を使用する",
        "underline-always": "常に付ける",
        "minoredit": "細部の編集",
        "watchthis": "このページをウォッチ",
        "savearticle": "ページを保存",
+       "publishpage": "ページを公開",
        "preview": "プレビュー",
        "showpreview": "プレビューを表示",
        "showdiff": "差分を表示",
        "content-model-css": "CSS",
        "content-json-empty-object": "空のオブジェクト",
        "content-json-empty-array": "空の配列",
-       "duplicate-args-warning": "<strong>警告:</strong> [[:$1]]は「$3」パラメータの値が複数存在する[[:$2]]を呼び出しています。提供されている最後の値のみが使用されます。",
+       "duplicate-args-warning": "<strong>警告:</strong> [[:$1]]は複数の「$3」パラメータを伴って[[:$2]]を呼び出しています。提供されている最後の値のみが使用されます。",
        "duplicate-args-category": "テンプレート呼び出しで引数が重複しているページ",
        "duplicate-args-category-desc": "引数が重複したテンプレート呼び出しを含むページ。例: <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code>、<code><nowiki>{{foo|bar|1=baz}}</nowiki></code>",
        "expensive-parserfunction-warning": "<strong>警告:</strong> このページでは、高負荷なパーサー関数の呼び出し回数が多過ぎます。\n\n{{PLURAL:$2|呼び出しを $2 回}}未満にしてください ({{PLURAL:$1|現在は $1 回}})。",
index 6a18f92..760019f 100644 (file)
                        "Matma Rex"
                ]
        },
-       "tog-underline": "Garisen ngisoré pranala:",
-       "tog-hideminor": "Dhelikaké besutan cilik ing owah-owahan pungkasan",
-       "tog-hidepatrolled": "Dhelikaké besutan awasan ing owah-owahan pungkasan",
-       "tog-newpageshidepatrolled": "Dhelikaké kaca kapanto saka daptar kaca anyar",
-       "tog-hidecategorization": "Dhelikaké kategorisasi kaca",
-       "tog-extendwatchlist": "Jembaraké daptar pangawasan kanggo nuduhaké kabèh owahan, ora mung sing paling anyar",
-       "tog-usenewrc": "Owah-owahané paguyuban miturut kaca nèng owah-owahan anyar lan daptar panto",
-       "tog-numberheadings": "Wènèhana nomer judul secara otomatis",
+       "tog-underline": "Nggaris ngisori pranala:",
+       "tog-hideminor": "Dhelikaké besutan cilik saka owah-owahan pungkasan",
+       "tog-hidepatrolled": "Dhelikaké besutan ingawasan saka owah-owahan pungkasan",
+       "tog-newpageshidepatrolled": "Dhelikaké kaca ingawasan saka pratélaning kaca anyar",
+       "tog-hidecategorization": "Dhelikaké gegebengan kaca",
+       "tog-extendwatchlist": "Ambakaké pawawangan nedya nuduhaké kabèh owahan, ora mung sing paling anyar",
+       "tog-usenewrc": "Golongaké owah-owahan miturut kaca ing owah-owahan anyar lan pawawangan",
+       "tog-numberheadings": "Wènèhi angkaning sesirah kanthi otomatis",
        "tog-showtoolbar": "Tuduhaké wilah piranti sarana besut",
        "tog-editondblclick": "Besut kaca sarana ngeklik pindho",
-       "tog-editsectiononrightclick": "Fungsèkna panyuntingan sub-bagian mawa klik-tengen ing judul bagian (mbutuhaké JavaScript)",
-       "tog-watchcreations": "Tambahaké kaca sing tak gawé lan berkas sing tak unggah nèng daptar pangawasan",
-       "tog-watchdefault": "Tambahaké kaca lan barkas sing tak sunting nyang pawawanganku",
-       "tog-watchmoves": "Tambahaké kaca lan berkas sing tak pindhahaké nèng daptar pangawasan",
-       "tog-watchdeletion": "Tambahaké kaca lan berkas sing tak busak nèng daptar pangawasan",
-       "tog-watchuploads": "Tambahaké barkas anyar sing tak unggah nyang pawawanganku",
-       "tog-watchrollback": "Tambahaké kaca sing tak wurungaké nyang pawawanganku",
-       "tog-minordefault": "Tandhanana kabèh suntingan dadi suntingan cilik secara baku",
+       "tog-editsectiononrightclick": "Idinaké mbesut pérangan sarana klik tengen ing sesirahing pérangan",
+       "tog-watchcreations": "Wuwuh kaca gawéanku lan barkas unggahanku nyang pawawanganku",
+       "tog-watchdefault": "Wuwuh kaca lan barkas besutanku nyang pawawanganku",
+       "tog-watchmoves": "Wuwuh kaca lan barkas lih-lihanku nyang pawawanganku",
+       "tog-watchdeletion": "Wuwuh kaca lan barkas busakanku nyang pawawanganku",
+       "tog-watchuploads": "Wuwuh barkas anyar unggahanku nyang pawawanganku",
+       "tog-watchrollback": "Wuwuh kaca sing tak wurungaké nyang pawawanganku",
+       "tog-minordefault": "Tengeri kabèh besutan minangka besutan cilik sacara baku",
        "tog-previewontop": "Deleng prawuryan sadurungé besut kothak",
-       "tog-previewonfirst": "Tuduhna pratayang ing suntingan kapisan",
-       "tog-enotifwatchlistpages": "Kirimi kula layang èlèktronik yèn ana kaca utawa berkas nèng daptar pangawasanku sing diowah",
-       "tog-enotifusertalkpages": "Kirimana aku layang e-mail yèn kaca dhiskusiku owah",
-       "tog-enotifminoredits": "Kirimi kula layang èlèktronik uga yèn ana suntingan cilik saka kaca lan berkas",
-       "tog-enotifrevealaddr": "Kirimana aku layang e-mail ing layang notifikasi",
-       "tog-shownumberswatching": "Tuduhna cacahé pangawas",
-       "tog-oldsig": "Tapak asma sing ana:",
-       "tog-fancysig": "Anggepen tapak asta minangka teks wiki (tanpa pranala otomatis)",
+       "tog-previewonfirst": "Tuduhaké prawuryan nalika mbesut pisanan",
+       "tog-enotifwatchlistpages": "Kirimi aku layangtronik yèn ana kaca utawa barkas ing pawawanganku sing diowah",
+       "tog-enotifusertalkpages": "Kirimi aku layangtronik yèn kaca gegunemanku diowah",
+       "tog-enotifminoredits": "Uga kirimi aku layangtronik yèn ana besutan cilik ing kaca lan barkas",
+       "tog-enotifrevealaddr": "Singkab alamat layangtronikku ing layang pawarta",
+       "tog-shownumberswatching": "Tuduhaké cacah wong sing ngawasi",
+       "tog-oldsig": "Tandha tangan sing ana:",
+       "tog-fancysig": "Anggep tandha tangan minangka tulisan wiki (tanpa pranala otomatis)",
        "tog-uselivepreview": "Trapaké prawuryan langsung",
        "tog-forceeditsummary": "Élingna aku menawa kothak ringkesan suntingan isih kosong",
-       "tog-watchlisthideown": "Delikna suntinganku ing daftar pangawasan",
+       "tog-watchlisthideown": "Dhelikaké besutanku saka pawawangan",
        "tog-watchlisthidebots": "Dhelikaké besutan bot saka pangawasan",
-       "tog-watchlisthideminor": "Delikna suntingan kecil di daftar pangawasan",
-       "tog-watchlisthideliu": "Ngumpetaké suntingan panganggo sing mlebu log seka daftar pangawasan",
+       "tog-watchlisthideminor": "Dhelikaké besutan cilik saka pawawangan",
+       "tog-watchlisthideliu": "Dhelikaké saka pawawangan besutaning wong sing mlebu",
        "tog-watchlistreloadautomatically": "Mot manèh pawawangan kanthi otomanis samangsa panyaring diowah (butuh JavaScript)",
-       "tog-watchlisthideanons": "Ngumpetaké suntingan panganggo anonim seka daftar pangawasan",
-       "tog-watchlisthidepatrolled": "Delikna suntingan sing wis dipatroli saka daftar pangawasan",
+       "tog-watchlisthideanons": "Dhelikaké saka pawawangan besutaning para anonim",
+       "tog-watchlisthidepatrolled": "Dhelikaké besutan ingawasan saka pawawangan",
        "tog-watchlisthidecategorization": "Dhelikaké kategorisasi kaca",
-       "tog-ccmeonemails": "Kirimana aku salinan layang e-mail sing tak-kirimaké menyang wong liya",
-       "tog-diffonly": "Aja dituduhaké isi kaca ing ngisor bédané suntingan",
-       "tog-showhiddencats": "Tuduhna kategori sing didelikaké",
-       "tog-norollbackdiff": "Lirwaaké prabédan sawusé nglakokaké sawijining pambalikan.",
-       "tog-useeditwarning": "Ã\88lingaké kula yèn kula ninggalaké suntingan sing durung kasimpen",
-       "tog-prefershttps": "Panggah sarana sambungan aman nalika mlebu",
-       "underline-always": "Mesthi",
+       "tog-ccmeonemails": "Kirimi aku salinan layangtronik sing tak kirim nyang wong liya",
+       "tog-diffonly": "Aja dituduhaké isining kaca ing ngisor bédané suntingan",
+       "tog-showhiddencats": "Tuduhaké kategori sing didhelikaké",
+       "tog-norollbackdiff": "Aja tuduhaké prabédan sawisé mbalèkaké.",
+       "tog-useeditwarning": "Ã\89lingaké kula yèn kula ninggalaké suntingan sing durung kasimpen",
+       "tog-prefershttps": "Tansah nganggo sambungan aman nalika mlebu",
+       "underline-always": "Tansah",
        "underline-never": "Ora tau",
-       "underline-default": "Kulit atau penjelajah bawaan",
-       "editfont-style": "Modhèl aksara (font) ing kotak suntingan:",
-       "editfont-default": "Standar panjelajah wèb",
+       "underline-default": "Baku kulit utawa pangluron",
+       "editfont-style": "Gagrag fon ing pambesutan:",
+       "editfont-default": "Baku pangluron",
        "editfont-monospace": "Fon monospasi",
        "editfont-sansserif": "Fon tansèrif",
        "editfont-serif": "Fon sèrif",
        "august-date": "Agustus $1",
        "september-date": "$1 Sèptèmber",
        "october-date": "Oktober $1",
-       "november-date": "$1 Novèmber",
-       "december-date": "$1 Dèsèmber",
+       "november-date": "$1 Nopèmber",
+       "december-date": "$1 Dsèmber",
        "period-am": "Isuk-Awan",
        "period-pm": "Soré-Wengi",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategori}}",
        "category_header": "Kaca sajeroning kategori \"$1\"",
-       "subcategories": "Subkategori",
+       "subcategories": "Anak kategori",
        "category-media-header": "Médhia sajeroning kategori \"$1\"",
-       "category-empty": "''Kategori iki saiki ora ngandhut artikel utawa média.''",
-       "hidden-categories": "{{PLURAL:$1|Kategori kadhelikaké|Kategori kadhelikaké}}",
+       "category-empty": "<em>Kategori iki lagi ora ngandhut artikel utawa médhia.</em>",
+       "hidden-categories": "{{PLURAL:$1|Kategori kadhelikan}}",
        "hidden-category-category": "Kategori kadhelikan",
-       "category-subcat-count": "{{PLURAL:$2|Kategori iki mung ngandhut subkategori ngisor iki.|Kategori iki ngandhut {{PLURAL:$1|subkategori|$1 subkategori}} ngisor iki saka gunggung $2 subkategori.}}",
-       "category-subcat-count-limited": "Kategori iki ora duwé {{PLURAL:$1|subkategori|$1 subkategori}} ''berikut''.",
+       "category-subcat-count": "{{PLURAL:$2|Kategori iki mung ngandhut saanak kategori ngisor iki.|Kategori iki ngandhut {{PLURAL:$1|anak kategori|$1 anak kategori}} ngisor iki saka gunggung $2 anak kategori.}}",
+       "category-subcat-count-limited": "Kategori iki duwé {{PLURAL:$1|anak kategori|$1 anak kategori}} kaya ngisor iki.",
        "category-article-count": "{{PLURAL:$2|Kategori iki mung ngandhut kaca ngisor iki.|{{PLURAL:$1|Kaca|$1 kaca}} ngisor iki ana ing kategori iki saka gunggung $2 kaca.}}",
        "category-article-count-limited": "Kategori iki ngandhut {{PLURAL:$1|kaca|$1 kaca-kaca}} sing kapacak ing ngisor iki.",
        "category-file-count": "{{PLURAL:$2|Kategori iki mung isi barkas iki.|{{PLURAL:$1|Barkas|$1 barkas}} iki ana sajeroning kategori iki saka $2 gunggungé.}}",
        "morenotlisted": "Pratélan iki ora jangkep.",
        "mypage": "Kaca",
        "mytalk": "Geguneman",
-       "anontalk": "Rembug",
+       "anontalk": "Geguneman",
        "navigation": "Napigasi",
        "and": "&#32;lan",
        "qbfind": "Golèk",
        "deletethispage": "Busak kaca iki",
        "undeletethispage": "Wurungaké pambusaking kaca iki",
        "undelete_short": "Batal busak {{PLURAL:$1|sabesutan|$1 besutan}}",
-       "viewdeleted_short": "Pirsani {{PLURAL:$1|suntingan|suntingan}} ingkang sampun kabusak",
+       "viewdeleted_short": "Deleng {{PLURAL:$1|sabesutan sing kabusak|$1 besutan sing kabusak}}",
        "protect": "Reksa",
        "protect_change": "owah",
        "protectthispage": "Reksa kaca iki",
        "redirectpagesub": "Alih kaca",
        "redirectto": "Ngalih menyang:",
        "lastmodifiedat": "Kaca iki pungkasan diowah kala $1, tabuh $2.",
-       "viewcount": "Kaca iki wis tau diaksès cacahé ping {{PLURAL:$1|siji|$1}}.",
+       "viewcount": "Kaca iki wis diaksès ping {{PLURAL:$1|siji|$1}}.",
        "protectedpage": "Kaca kareksa",
        "jumpto": "Jujug:",
        "jumptonavigation": "napigasi",
        "aboutpage": "Project:Bab",
        "copyright": "Kabèh isi kasedyakaké miturut $1.",
        "copyrightpage": "{{ns:project}}:Hak cipta",
-       "currentevents": "Kadadéan saiki",
-       "currentevents-url": "Project:Kadadéan saiki",
+       "currentevents": "Kadadian saiki",
+       "currentevents-url": "Project:Kadadian saiki",
        "disclaimers": "Sélakan",
        "disclaimerpage": "Project:Sélakan umum",
        "edithelp": "Pitulung besut",
        "laggedslavemode": "Pènget: Kaca iki mbokmenawa isiné dudu pangowahan pungkasan.",
        "readonly": "Umpak data kagembok",
        "enterlockreason": "Lebokna alesan panguncèn, kalebu uga prakiran kapan kunci bakal dibuka",
-       "readonlytext": "Database lagi dikunci marang panampan anyar. Pangurus sing ngunci mènèhi katrangan kaya mangkéné: <p>$1",
+       "readonlytext": "Juru administrasi sistem sing ngunci iku medhar mangkéné: $1",
        "missing-article": "Basis data ora bisa nemokaké tèks kaca sing kuduné ana, yaiku \"$1\" $2.\nBab iki bisasané disebabaké déning pranala daluwarsa menyang revisi sadurungé kaca sing wis dibusak.\nYèn dudu iki panyebabé, panjenengan manawa bisa nemokaké kasalahan (''bug'') jroning piranti alus (''software''). Mangga dilapuraké bab iki menyang [[Special:ListUsers/sysop|administrator]], kanthi nyebutaké alamat URL sing dituju",
        "missingarticle-rev": "(owahan#: $1)",
        "missingarticle-diff": "(Béda: $1, $2)",
        "actionthrottled": "Tindakan diwatesi",
        "actionthrottledtext": "Minangka sawijining pepesthèn anti-spam, panjenengan diwatesi nglakoni tindhakan iki sing cacahé kakèhan ing wektu cendhak.\nMangga dicoba manèh ing sawetara menit.",
        "protectedpagetext": "Kaca iki wis digembok supaya ora bisa disunting lan diapa-apakaké.",
-       "viewsourcetext": "Panjenengan bisa mirsani utawa nulad sumber kaca iki:",
-       "viewyourtext": "Sampéyan bisa ndelok lan nyalin sumber '''suntingan Sampéyan''' nèng kaca iki:",
+       "viewsourcetext": "Sampéyan bisa ndeleng lan nyalin sumbering kaca iki.",
+       "viewyourtext": "Sampéyan bisa ndeleng lan nyalin sumbering <strong>besutaning sampéyan</strong> ing kaca iki.",
        "protectedinterface": "Kaca iki isiné tèks antarmuka sing dienggo software lan wis dikunci kanggo menghindari kasalahan.",
        "editinginterface": "'''Pènget:''' Panjenengan nyunting kaca sing dianggo nyedyakaké tèks antarmuka kanggo piranti alus.\nPangowahan kaca iki bakal awèh pangaruh marang tampilan antarmuka panganggo kanggoné panganggo liya.\nKanggo terjemahan, mangga nganggo [//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], proyèk lokalisasi MediaWiki.",
        "cascadeprotected": "Kaca iki wis direksa saka panyuntingan amerga disertakaké ing {{PLURAL:$1|kaca|kaca-kaca}} ngisor iki sing wis direksa mawa opsi \"runtun\" diaktifaké:\n$2",
        "createaccount-title": "Gawé rékening kanggo {{SITENAME}}",
        "createaccount-text": "Ana wong sing nggawé sawijining akun utawa rékening kanggo alamat e-mail panjenengan ing {{SITENAME}} ($4) mawa jeneng \"$2\" lan tembung sandi \"$3\". Panjenengan disaranaké kanggo mlebu log lan ngganti tembung sandi panjenengan saiki.\n\nPanjenengan bisa nglirwakaké pesen iki yèn akun utawa rékening iki digawé déné sawijining kaluputan.",
        "login-throttled": "Panjenengan wis kakèhan njajal mlebu log.\nTulung nunggu dhisik $1 sadurungé njajal manèh.",
-       "login-abort-generic": "Sampéyan ora suksès mlebu log - Dibatalaké",
+       "login-abort-generic": "Sampéyan ora bisa mlebu - Kawurungan",
        "loginlanguagelabel": "Basa: $1",
        "suspicious-userlogout": "Panjaluk panjenengan supaya metu ditolak amarga katoné panjlajah internt utawa proksi panyinggah.",
        "createacct-another-realname-tip": "Jeneng asli ora kudu dilebokake.\n\nYen sampeyan milih nglebokake jeneng asli, jeneng kuwi bakal dinggo ngwenehi atribusi kanggo karya-karyane.",
        "user-mail-no-addy": "Njajal ngirim layang èlèktronik tanpa alamat layang èlèktronik.",
        "user-mail-no-body": "Nyoba ngirim layang e-mail, tapi isine kosong.",
        "changepassword": "Ganti tembung wadi",
-       "resetpass_announce": "Panjenengan wis mlebu log mawa kodhe sementara sing dikirim mawa e-mail. Menawa kersa nglanjutaké, panjenengan kudu milih tembung sandhi anyar ing kéné:",
+       "resetpass_announce": "Kanggo ngrampungaké lelakoning lumebu, sampéyan kudu masang tembung wadi anyar.",
        "resetpass_text": "<!-- Tambahaké teks ing kéné -->",
        "resetpass_header": "Ganti tembung wadining akun",
        "oldpassword": "Tembung wadi lawas:",
        "newpassword": "Tembung wadi anyar:",
        "retypenew": "Tik manèh tembung wadi anyaré:",
        "resetpass_submit": "Nata tembung sandhi lan mlebu log",
-       "changepassword-success": "Tembung sandhi panjenengan wis suksès diowahi!",
+       "changepassword-success": "Tembung wadining sampéyan kasil diowah!",
        "botpasswords": "Tembung wadi bot",
        "botpasswords-label-appid": "Jeneng bot:",
        "botpasswords-label-create": "Gawé",
        "passwordreset-emailtext-ip": "Ana uwong (mbok menawa Sampéyan, mawa angka IP $1) njaluk ganti tembung sandhiné Sampéyan ana ing {{SITENAME}} ($4). {{PLURAL:$3|Rèkèning|Rèkèning-rèkèning}} ngisor iki magepokan karo padunungané layang èlèktronik iki:\n\n$2\n\n{{PLURAL:$3|Tembung sandhi sawetara iki}} bakal kedaluwarsa ing {{PLURAL:$5|sak dina|$5 dina}}.\nSampéyan kudu mlebu log lan milih siji tembung sandhi anyar saiki. Yèn wong liya sing njaluk iki, utawa yèn Sampéyan jebul wis kèlingan tembung sandhiné sing lawas saéngga ora ana niyat kanggo ngganti, Sampéyan bisa ngejaraké wara-wara iki lan bacutaké nganggo tembung sandhiné lawas Sampéyan.",
        "passwordreset-emailtext-user": "Panganggo $1 seka {{SITENAME}} njaluk ganti tembung sandhiné Sampéyan ana ing {{SITENAME}} ($4). {{PLURAL:$3|Rèkèning|Rèkèning-rèkèning}} ngisor iki magepokan karo padunungané layang èlèktronik iki:\n\n$2\n\n{{PLURAL:$3|Tembung sandhi sawetara iki}} bakal kedaluwarsa ing {{PLURAL:$5|sak dina|$5 dina}}.\nSampéyan kudu mlebu log lan milih siji tembung sandhi anyar saiki. Yèn wong liya sing njaluk iki, utawa yèn Sampéyan jebul wis kèlingan tembung sandhiné sing lawas saéngga ora ana niyat kanggo ngganti, Sampéyan bisa ngejaraké wara-wara iki lan bacutaké nganggo tembung sandhiné lawas Sampéyan.",
        "passwordreset-emailelement": "Jeneng panganggo: \n$1\n\nTembung wadi sauntara: \n$2",
-       "passwordreset-emailsentemail": "Layang èlèktronik kanggo mbalèkaké tembung sandhi wis dikirim.",
+       "passwordreset-emailsentemail": "Yèn layang èlèktronik iki nggayut akuning sampéyan, layang kanggo salin tembung wadi bakal dikirim.",
        "passwordreset-emailsent-capture": "Layang èlèktronik kanggo mbalèkaké tembung sandhi wis dikirim, bisa didelok ngisor iki.",
        "passwordreset-emailerror-capture": "Layang èlèktronik pangèling tembung sandhi wis digawe, yaiku sing ditampilaké nèng ngisor iki, nanging ora kasil dikirim ing {{GENDER:$2|panganggo}}: $1",
-       "changeemail": "Ganti alamat layang èlèktronik",
+       "changeemail": "Owah utawa busak alamat layang èlèktronik",
        "changeemail-header": "Ganti alamat layang èlèktronik akun",
        "changeemail-no-info": "Sampéyan kudu mlebu log kanggo ngaksès kaca iki langsung.",
        "changeemail-oldemail": "Alamat layang èlèktronik saiki:",
        "sig_tip": "Tandha tangan sampéyan mawa tandha wayah",
        "hr_tip": "Garis horisontal",
        "summary": "Tingkesan:",
-       "subject": "Subyek/judhul:",
+       "subject": "Jejer:",
        "minoredit": "Iki besutan cilik",
        "watchthis": "Awasi kaca iki",
        "savearticle": "Simpen kaca",
        "missingcommenttext": "Tulung lebokna komentar ing ngisor iki.",
        "missingcommentheader": "'''Pangéling:''' Sampéyan durung nyadhiyakaké judhul/jejer kanggo tanggepan iki.\nYèn Sampéyan klik \"{{int:savearticle}}\" manèh, suntingan Sampéyan bakal kasimpen tanpa kuwi.",
        "summary-preview": "Prawuryan tingkesan:",
-       "subject-preview": "Pratayang subyèk/judhul:",
+       "subject-preview": "Prawuryaning jejer:",
        "blockedtitle": "Panganggo kapalangan",
        "blockedtext": "'''Asma panganggo utawa alamat IP panjenengan diblokir.'''\n\nBlokir iki sing nglakoni $1.\nAlesané ''$2''.\n\n* Diblokir wiwit: $8\n* Kadaluwarsa pemblokiran ing: $6\n* Sing arep diblokir: $7\n\nPanjenengan bisa ngubungi $1 utawa [[{{MediaWiki:Grouppage-sysop}}|pangurus liyané]] kanggo ngomongaké prakara iki.\n\nPanjenengan ora bisa nggunakaké fitur 'Kirim layang e-mail panganggo iki' kejaba panjenengan wis nglebokaké alamat e-mail sing sah ing [[Special:Preferences|préferènsi]] panjenengan.\n\nAlamat IP panjenengan iku $3, lan ID pamblokiran iku #$5.\nTulung kabèh informasi ing ndhuwur iki disertakaké ing saben pitakon panjenengan.",
        "autoblockedtext": "Alamat IP panjenangan wis diblokir minangka otomatis amerga dienggo déning panganggo liyané. Pamblokiran dilakoni déning $1 mawa alesan:\n\n:''$2''\n\n* Diblokir wiwit: $8\n* Blokir kadaluwarsa ing: $6\n* Sing dikarepaké diblokir: $7\n\nPanjenengan bisa ngubungi $1 utawa [[{{MediaWiki:Grouppage-sysop}}|pangurus liyané]] kanggo ngomongaké perkara iki.\n\nPanjenengan ora bisa nganggo fitur \"kirim e-mail panganggo iki\" kejaba panjenengan wis nglebokaké alamat e-mail sing sah ing [[Special:Preferences|préferènsi]] panjenengan lan panjenengan wis diblokir kanggo nggunakaké.\n\nID pamblokiran panjenengan iku #$5 lan alamat IP panjenengan iku $3. Tulung sertakna informasi ing dhuwur kabèh iki saben ngajokaké pitakonan panjenengan. Matur nuwun.",
        "logdelete-failure": "'''Aturan pandhelikan ora bisa disèt:'''\n$1",
        "revdel-restore": "Ngowahi visiblitas (pangatonan)",
        "pagehist": "Babading kaca",
-       "deletedhist": "Babad kabusakan",
+       "deletedhist": "Babad kabusak",
        "revdelete-hide-current": "Gagal ndhelikaké révisi tanggal $2, $1: iki arupa révisi paling anyar.\nRévisi iki ora bisa didhelikaké.",
        "revdelete-show-no-access": "Gagal nampilaké révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-modify-no-access": "Gagal ngowahi révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "search-external": "Panggolèkan èkstèrnal",
        "searchdisabled": "Sawetara wektu iki panjenengan ora bisa nggolèk mawa fungsi golèk {{SITENAME}}. Kanggo saiki mangga panjenengan bisa golèk nganggo Google. Nanging isi indèks Google kanggo {{SITENAME}} bisa waé lawas lan durung dianyari.",
        "search-error": "Ana kasalahan wektu nggoleki: $1",
-       "preferences": "Preferensi (pilihan)",
+       "preferences": "Pilihan",
        "mypreferences": "Pilihan",
        "prefs-edits": "Gunggung besutan:",
        "prefsnologintext2": "Tulung $1 kanggo ngganti preferensi sampeyan.",
        "prefs-skin": "Kulit",
-       "skin-preview": "Pratilik",
-       "datedefault": "Ora ana préferènsi",
+       "skin-preview": "Prawuryan",
+       "datedefault": "Ora ana pilihan",
        "prefs-labs": "Piranti lab",
-       "prefs-user-pages": "Kaca panganggo",
-       "prefs-personal": "Profil panganggo",
+       "prefs-user-pages": "Kacaning sing nganggo",
+       "prefs-personal": "Panjèrènging sing nganggo",
        "prefs-rc": "Owah-owahan pungkasan",
-       "prefs-watchlist": "Dhaftar pangawasan",
+       "prefs-watchlist": "Pawawangan",
+       "prefs-editwatchlist": "Besut pawawangan",
+       "prefs-editwatchlist-label": "Besut isining pawawanganing sampéyan",
+       "prefs-editwatchlist-edit": "Deleng lan busak sesirah ing pawawanganing sampéyan",
+       "prefs-editwatchlist-raw": "Besut pawawangan lakaran",
+       "prefs-editwatchlist-clear": "Resiki pawawanganing sampéyan",
        "prefs-watchlist-days": "Cacahé dina sing dituduhaké ing dhaftar pangawasan:",
        "prefs-watchlist-days-max": "Maksimum $1 {{PLURAL:$1|dina|dina}}",
        "prefs-watchlist-edits": "Cacahé suntingan maksimum sing dituduhaké ing dhaftar pangawasan sing luwih jangkep:",
        "prefs-watchlist-edits-max": "Gunggung maksimum: 1000",
-       "prefs-watchlist-token": "Token pantauan:",
+       "prefs-watchlist-token": "Tokening pawawangan:",
        "prefs-misc": "Liya-liya",
        "prefs-resetpass": "Ganti tembung sandi",
-       "prefs-changeemail": "Ganti alamat layang èlèktronik",
+       "prefs-changeemail": "Owah utawa busak alamat layangtronik",
        "prefs-setemail": "Setèl alamat layang èlèktronik",
        "prefs-email": "Opsi layang-e",
        "prefs-rendering": "Tampilan",
        "columns": "Kolom:",
        "searchresultshead": "Panggolèkan",
        "stub-threshold": "Ambang wates kanggo format <a href=\"#\" class=\"stub\">pranala rintisan</a>:",
+       "stub-threshold-sample-link": "pralampita",
        "stub-threshold-disabled": "Dipatèni",
        "recentchangesdays": "Cacahé dina sing dituduhaké ing owah-owahan pungkasan:",
        "recentchangesdays-max": "(maksimum $1 {{PLURAL:$1|dina|dina}})",
        "prefs-help-recentchangescount": "Iki klebu owah-owahan pungkasan, kaca sajarah, lan log.",
        "prefs-help-watchlist-token2": "Ini adalah kunci rahasia (token) ke web feed dari daftar pantauan Anda.\nSiapa saja yang tahu akan dapat melihat daftar pantauan Anda, jadi jangan dibagikan.\n[[Special:ResetTokens|Klik di sini jika Anda perlu menyetel ulang]].",
        "savedprefs": "Préferènsi Panjenengan wis disimpen",
+       "savedrights": "Haking panganggo {{GENDER:$1|$1}} wis kasimpen.",
        "timezonelegend": "Zona wektu:",
        "localtime": "Wektu saenggon:",
        "timezoneuseserverdefault": "Anggo gawan wiki ($1)",
        "prefs-help-signature": "Komentar ing kaca wicara kudu ditapak astani nganggo \"<nowiki>~~~~</nowiki>\" sing bakal dikonvèrsi dadi tapak asta panjenengan lan tanggal wektu.",
        "badsig": "Tapak astanipun klèntu; cèk rambu HTML.",
        "badsiglength": "Tapak asta panjenengan kedawan.\nAja luwih saka {{PLURAL:$1|karakter|karakter}}.",
-       "yourgender": "Jinis kelamin:",
-       "gender-unknown": "Ora dinyatakaké",
-       "gender-male": "Lanang",
-       "gender-female": "Wadon",
+       "yourgender": "Kepiyé sampéyan medhar priangganing sampéyan?",
+       "gender-unknown": "Nalika nyebut sampéyan, piranti alus iku bakal nganggo tembung sing nétral jèndher sabisané",
+       "gender-male": "Dhèwèké mbesut kaca wiki",
+       "gender-female": "Dhèwèké mbesut kaca wiki",
        "prefs-help-gender": "Opsional: Dipigunakaké kanggo panyebutan jinis kelamin sing bener déning piranti alus.\nInformasi iki bakal kabuka kanggo publik.",
-       "email": "Layang élèktronik (E-mail)",
-       "prefs-help-realname": "* <strong>Asma asli</strong> (ora wajib): menawa panjenengan maringi, asma asli panjenengan bakal digunakaké kanggo mènèhi akrédhitasi kanggo kasil karya tulis panjenengan.",
+       "email": "Layangtronik",
+       "prefs-help-realname": "Jeneng asli manasuka.\nMenawa diisi, iku bakal kanggo ngatribusi sampéyan awit karyaning sampéyan.",
        "prefs-help-email": "Alamat layang èlèktronik sipaté mung pilihan, nanging dibutuhaké kanggo nyetèl ulang tembung sandhi yèn Sampéyan lali.",
        "prefs-help-email-others": "Sampéyan uga bisa milih kanggo ngidinaké wong liya ngubungi Sampéyan liwat layang èlèktronik sing ana ing kaca panganggo utawa kaca guneman.\nAlamat layang èlèktronik Sampéyan ora dituduhaké nalika wong liya ngubungi Sampéyan.",
        "prefs-help-email-required": "Alamat layang-e dibutuhaké.",
-       "prefs-info": "Informasi dhasar",
+       "prefs-info": "Katerangan pokok",
        "prefs-i18n": "Internasionalisasi",
        "prefs-signature": "Tapak asma",
        "prefs-dateformat": "Format tanggal",
        "exif-sublocationdest": "Dhaèrahé kutha katampilaké",
        "exif-objectname": "Judhul cendhèk",
        "exif-specialinstructions": "Prèntah kusus",
-       "exif-headline": "Warta utama",
+       "exif-headline": "Tajuk",
        "exif-credit": "Krédit/Panyadhiya",
        "exif-source": "Sumber",
        "exif-editstatus": "Status kapanyuntingan gambar",
index 5bce2c6..e9a314c 100644 (file)
        "botpasswords-invalid-name": "지정된 사용자 이름은 봇 비밀번호 구분자(\"$1\")를 포함하고 있지 않습니다.",
        "botpasswords-not-exist": "\"$1\" 사용자가 이름이 \"$2\"인 봇의 비밀번호를 가지고 있지 않습니다.",
        "resetpass_forbidden": "비밀번호를 바꿀 수 없습니다",
-       "resetpass-no-info": "ì\9d´ í\8a¹ì\88\98 ë¬¸ì\84\9cì\97\90 ì§\81ì \91 ì \91ê·¼í\95\98려면 ë°\98ë\93\9cì\8b\9c 로그인해야 합니다.",
+       "resetpass-no-info": "ì\9d´ í\8e\98ì\9d´ì§\80ì\97\90 ì§\81ì \91 ì \91ê·¼í\95\98려면 로그인해야 합니다.",
        "resetpass-submit-loggedin": "비밀번호 바꾸기",
        "resetpass-submit-cancel": "취소",
        "resetpass-wrong-oldpass": "비밀번호가 잘못되었거나 현재의 비밀번호와 같습니다.\n이미 비밀번호를 바꾸었거나 새 임시 비밀번호를 요청했을 수 있습니다.",
        "changeemail": "이메일 주소를 바꾸거나 제거하기",
        "changeemail-header": "이메일 주소를 바꾸려면 이 양식을 채우세요. 계정에서 이메일 연동을 취소하고 싶다면 양식을 제출할 때 새 이메일 주소를 공란으로 두세요.",
        "changeemail-passwordrequired": "변경을 적용하려면 비밀번호를 입력해야 합니다.",
-       "changeemail-no-info": "ì\9d´ í\8a¹ì\88\98 ë¬¸ì\84\9cì\97\90 ì§\81ì \91 ì \91ê·¼í\95\98려면 ë°\98ë\93\9cì\8b\9c 로그인해야 합니다.",
+       "changeemail-no-info": "ì\9d´ í\8e\98ì\9d´ì§\80ì\97\90 ì§\81ì \91 ì \91ê·¼í\95\98려면 로그인해야 합니다.",
        "changeemail-oldemail": "현재 이메일 주소:",
        "changeemail-newemail": "새 이메일 주소:",
        "changeemail-newemail-help": "이메일 주소를 삭제하고자 한다면 이 칸을 빈칸으로 두세요. 비밀번호 재설정이 불가능해지며, 이메일 주소가 없다면 이메일을 받을 수 없습니다.",
index 00621a2..ca96110 100644 (file)
        "whatlinkshere-prev": "de vörijje {{PLURAL:$1||$1|noll}} zeije",
        "whatlinkshere-next": "de nächste {{PLURAL:$1||$1|noll}} zeije",
        "whatlinkshere-links": "← Links",
-       "whatlinkshere-hideredirs": "de Ömleijdonge $1",
+       "whatlinkshere-hideredirs": "De Ömleijdonge verschteijsche",
        "whatlinkshere-hidetrans": "de Oproofe $1",
-       "whatlinkshere-hidelinks": "de nommahle Lengks $1",
+       "whatlinkshere-hidelinks": "De nommahle Lengks verschteijsche",
        "whatlinkshere-hideimages": "$1 de Lengks op Datteihje",
        "whatlinkshere-filters": "Ußsööke",
        "whatlinkshere-submit": "Lohß jonn!",
        "searchsuggest-containing": "dren änthallde…",
        "api-error-badaccess-groups": "Do häs nit et Rääsch, Datteije en heh dat Wiki huhzelaade.",
        "api-error-badtoken": "Fähler: et Kännzeijsche (<i lang=\"en\">token</i>) es kappott.",
+       "api-error-blocked": "Do bes föh et Änndere jeschpächt.",
        "api-error-copyuploaddisabled": "Et Huhlaade vun enem <i lang=\"en\">URL</i> es op däm ẞööver heh nit zohjelohße.",
        "api-error-duplicate": "Mer han em Wiki ald {{PLURAL:$1|[en Dattei]|[$1 andere Datteije]|[kein Dattei]}} mem akeraat sellve Enhalldt.",
        "api-error-duplicate-archive": "Mer hatte {{PLURAL:$1|[en ander Dattei]|[ander Datteije]|[kein ander Dattei]}} heh em Wiki mem sellve Enhalt, ävver se {{PLURAL:$1|es|sen|es}} ald fottjeschmeße woode.",
index 8511b83..7caf232 100644 (file)
        "remembermypassword": "Prisiminti prisijungimo duomenis šiame kompiuteryje (daugiausiai $1 {{PLURAL:$1|dieną|dienas|dienų}})",
        "userlogin-remembermypassword": "Įsiminti mane",
        "userlogin-signwithsecure": "Naudoti saugią jungtį",
-       "cannotloginnow-title": "Negalima prisijungti dabar",
+       "cannotloginnow-title": "Dabar negalima prisijungti",
        "cannotloginnow-text": "Prisijungimas negalimas, kai naudojama $1.",
        "yourdomainname": "Jūsų domenas:",
        "password-change-forbidden": "Jus negalite keisti slaptažodžių šioje wiki.",
index 6c0b718..0235767 100644 (file)
        "site-atom-feed": "$1 Atom padeve",
        "page-rss-feed": "\"$1\" RSS barotne",
        "page-atom-feed": "\"$1\" Atom barotne",
-       "red-link-title": "$1 (lapa neeksistē)",
+       "red-link-title": "$1 (lapa nepastāv)",
        "sort-descending": "Kārtot dilstošā secībā",
        "sort-ascending": "Kārtot augošā secībā",
        "nstab-main": "Raksts",
        "special-characters-group-sinhala": "Singāļu",
        "special-characters-group-gujarati": "Gudžarati",
        "mw-widgets-dateinput-no-date": "Nav izvēlēts datums",
+       "mw-widgets-titleinput-description-new-page": "lapa vēl nepastāv",
        "api-error-blacklisted": "Lūdzu, izvēlieties citu, aprakstošu nosaukumu!"
 }
index 4093966..0f2260b 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|претходна|претходни $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|следна|следни $1}}",
        "whatlinkshere-links": "← врски",
-       "whatlinkshere-hideredirs": "$1 пренасочувања",
-       "whatlinkshere-hidetrans": "$1 превметнувања",
-       "whatlinkshere-hidelinks": "$1 врски",
+       "whatlinkshere-hideredirs": "Скриј пренасочувања",
+       "whatlinkshere-hidetrans": "Скриј превметнувања",
+       "whatlinkshere-hidelinks": "Скриј врски",
        "whatlinkshere-hideimages": "$1 врски кон податотека",
        "whatlinkshere-filters": "Филтри",
        "whatlinkshere-submit": "Дај",
index 97f08d2..bee6f50 100644 (file)
        "right-userrights": "Alle gebrukersrechten bewarken",
        "right-userrights-interwiki": "Gebrukersrechten van gebrukers in aandere wiki's wiezigen",
        "right-siteadmin": "De databanke blokkeren en weer vriegeven",
-       "right-override-export-depth": "Ziejen uutvoeren, oek de ziejen waor naor verwezen wörden, tot n diepte van 5",
+       "right-override-export-depth": "Ziejen exporteren, oek de ziejen waor naor verwezen wördt, tot n diepte van 5",
        "right-sendemail": "Bericht versturen naor aandere gebrukers",
        "right-passwordreset": "Bekiek netpostberichten veur t opniej instellen van joew wachtwoord",
        "newuserlogpage": "Logboek mit anwas",
        "semiprotectedpagemovewarning": "'''Waorschuwing:''' disse zied kan allinnig deur eregistreerden gebrukers herneumd wörden.\nDe leste logboekregel steet hieronder:",
        "move-over-sharedrepo": "== t Bestaand besteet al ==\n[[:$1]] besteet al in de edeelden mediadatabanke. A'j n bestaand naor disse titel herneumen, dan ku'j  t edeelden bestaand niet gebruken.",
        "file-exists-sharedrepo": "Disse bestaandsnaam besteet al in de edeelden mediadatabanke.\nKies n aandere bestaandsnaam.",
-       "export": "Ziejen uutvoeren",
-       "exporttext": "De tekste en geschiedenisse van n zied of n koppel ziejen kunnen in XML-formaot uutevoerd wörden. Dit bestaand ku'j daornao uutvoeren naor n aandere MediaWiki deur de [[Special:Import|invoerzied]] te gebruken.\n\nZet in t onderstaonde veld de namen van de ziejen die'j uutvoeren willen, één zied per regel, en gif an o'j alle versies mit de bewarkingssamenvatting uutvoeren willen of allinnig de leste versies mit de bewarkingssamenvatting.\n\nA'j dat leste doon willen dan ku'j oek n verwiezing gebruken, bieveurbeeld [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] veur de zied \"[[{{MediaWiki:Mainpage}}]]\".",
-       "exportall": "Alle ziejen uutvoeren",
+       "export": "Ziejen exporteren",
+       "exporttext": "De tekste en geschiedenisse van n zied of n koppel ziejen kunnen in XML-formaot uutevoerd wörden. Dit bestaand ku'j daornao exporteren naor n aandere MediaWiki deur de [[Special:Import|invoerzied]] te gebruken.\n\nZet in t onderstaonde veld de namen van de ziejen die'j exporteren willen, één zied per regel, en geef an o'j alle versies mit de bewarkingssamenvatting exporteren willen of allinnig de leste versies mit de bewarkingssamenvatting.\n\nA'j dat leste doon willen dan ku'j oek n verwiezing gebruken, bieveurbeeld [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] veur de zied \"[[{{MediaWiki:Mainpage}}]]\".",
+       "exportall": "Alle ziejen exporteren",
        "exportcuronly": "Allinnig de actuele versie, niet de veurgeschiedenisse",
-       "exportnohistory": "----\n'''NB:''' t uutvoeren van de hele geschiedenisse is uutezet vanwegen prestasieredens.",
+       "exportnohistory": "----\n'''NB:''' t exporteren van de hele geschiedenisse is uutezet vanwegen prestasieredens.",
        "exportlistauthors": "De hele auteurslieste opnemen veur elke zied",
-       "export-submit": "Uutvoeren",
+       "export-submit": "Exporteren",
        "export-addcattext": "Ziejen derbie doon uut de kategorie:",
        "export-addcat": "Derbie doon",
        "export-addnstext": "Ziejen uut de volgende naamruumte derbie doon:",
index c01b64a..2d415db 100644 (file)
                        "Gaute",
                        "Macofe",
                        "Chameleon222",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Mortensson"
                ]
        },
        "tog-underline": "Strek under lenkjer:",
        "tog-hideminor": "Gøym småplukk i lista over siste endringar",
        "tog-hidepatrolled": "Gøym patruljerte endringar i lista over siste endringar",
        "tog-newpageshidepatrolled": "Gøym patruljerte sider frå lista over nye sider",
+       "tog-hidecategorization": "Løyn sidekategoriseringa",
        "tog-extendwatchlist": "Utvid overvakingslista til å vise alle endringane, ikkje berre dei siste",
        "tog-usenewrc": "Grupper endringar etter side i siste endringane og på overvakingslista",
        "tog-numberheadings": "Vis nummererte overskrifter",
@@ -41,6 +43,8 @@
        "tog-watchdefault": "Legg til sidene og filene eg endrar på overvakingslista mi",
        "tog-watchmoves": "Legg til sidene og filene eg flytter på overvakingslista mi",
        "tog-watchdeletion": "Legg til sidene og filene eg slettar på overvakingslista mi",
+       "tog-watchuploads": "Overvak nye filer som eg lastar opp",
+       "tog-watchrollback": "Overvak sider som eg har attra",
        "tog-minordefault": "Merk endringar som «småplukk» som standard",
        "tog-previewontop": "Vis førehandsvisinga før endringsboksen",
        "tog-previewonfirst": "Førehandsvis første endring",
        "tog-watchlisthidebots": "Gøym endringar gjorde av robotar i overvakingslista",
        "tog-watchlisthideminor": "Gøym småplukk i overvakingslista",
        "tog-watchlisthideliu": "Gøym endringar av innlogga brukarar i overvakingslista.",
+       "tog-watchlistreloadautomatically": "Oppdater overvakingslista automatisk når eit filter vert endra (krev JavaScript)",
        "tog-watchlisthideanons": "Gøym endringar av anonyme brukarar i overvakingslista.",
        "tog-watchlisthidepatrolled": "Gøym patruljerte endringar i overvakingslista",
+       "tog-watchlisthidecategorization": "Løyn sidekategoriseringa",
        "tog-ccmeonemails": "Send meg kopi av e-postane eg sender til andre brukarar",
        "tog-diffonly": "Ikkje vis sideinnhaldet under skilnadene mellom versjonane",
        "tog-showhiddencats": "Vis gøymde kategoriar",
        "october-date": "$1. oktober",
        "november-date": "$1. november",
        "december-date": "$1. desember",
+       "period-am": "f.m.",
+       "period-pm": "e.m.",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategoriar}}",
        "category_header": "Artiklar i kategorien «$1»",
        "subcategories": "Underkategoriar",
        "morenotlisted": "Lista er ikkje heil.",
        "mypage": "Sida mi",
        "mytalk": "Diskusjon",
-       "anontalk": "Diskusjonside for denne IP-adressa",
+       "anontalk": "Diskusjon",
        "navigation": "Navigering",
        "and": "&#32;og",
        "qbfind": "Finn",
index 36f4051..97c6921 100644 (file)
@@ -4,7 +4,8 @@
                        "Denö",
                        "Hiloin Natoi",
                        "Ilja.mos",
-                       "Mashoi7"
+                       "Mashoi7",
+                       "Misosoof"
                ]
        },
        "tog-underline": "Linkien alleviivuamine:",
        "resetpass_submit": "Azeta peittosana da kirjuttai sistiemah:",
        "changepassword-success": "Sinun peittosana on vaihtettu!",
        "changepassword-throttled": "Olet oppinuh kirjuttuakseh liijan moni kerdua. Ole hyvä, vuota $1 enne ku opit uvvessah.",
+       "botpasswords-label-create": "Luaji",
+       "botpasswords-label-update": "Päivitä",
+       "botpasswords-label-cancel": "Hylgiä",
+       "botpasswords-label-delete": "Poista",
        "resetpass_forbidden": "Ei voi vaihtua peittosanua",
        "resetpass-no-info": "Et voi nähtä tädä sivuu kuni et ole kirjutannuhes.",
        "resetpass-submit-loggedin": "Vaihta peittosana",
index d711ecd..1449441 100644 (file)
        "preferences": "ପସନ୍ଦ",
        "mypreferences": "ପସନ୍ଦ",
        "prefs-edits": "ସମ୍ପାଦନା ସଂଖ୍ୟା:",
-       "prefsnologintext2": "ନିà¬\9cର à¬ªà¬¸à¬¨à­\8dଦ à¬¬à¬¦à¬²ାଇବା ପାଇଁ ଲଗ ଇନ କରନ୍ତୁ ।",
+       "prefsnologintext2": "ନିà¬\9cର à¬ªà¬¸à¬¨à­\8dଦ à¬¬à¬¦à¬³ାଇବା ପାଇଁ ଲଗ ଇନ କରନ୍ତୁ ।",
        "prefs-skin": "ବହିରାବରଣ",
        "skin-preview": "ସାଇତା ଆଗରୁ ଦେଖଣା",
        "datedefault": "କୌଣସି ପସନ୍ଦ ନାହିଁ",
index 480bcb2..43656ec 100644 (file)
        "tog-ccmeonemails": "Przesyłaj mi kopie wiadomości, które wysyłam do innych użytkowników",
        "tog-diffonly": "Nie pokazuj treści stron pod porównaniami zmian",
        "tog-showhiddencats": "Pokazuj ukryte kategorie",
-       "tog-norollbackdiff": "Pomiń pokazywanie zmian po użyciu funkcji „cofnij”",
+       "tog-norollbackdiff": "Nie pokazuj zmian po użyciu funkcji „cofnij”",
        "tog-useeditwarning": "Ostrzegaj mnie, gdy opuszczam stronę edycji bez zapisania zmian",
        "tog-prefershttps": "Zawsze używaj bezpiecznego połączenia po zalogowaniu",
        "underline-always": "Zawsze",
        "minoredit": "To jest drobna zmiana",
        "watchthis": "Obserwuj",
        "savearticle": "Zapisz",
+       "publishpage": "Opublikuj stronę",
        "preview": "Podgląd",
        "showpreview": "Pokaż podgląd",
        "showdiff": "Podgląd zmian",
        "whatlinkshere-prev": "{{PLURAL:$1|poprzednie|poprzednie $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|następne|następne $1}}",
        "whatlinkshere-links": "← linkujące",
-       "whatlinkshere-hideredirs": "$1 przekierowania",
-       "whatlinkshere-hidetrans": "$1 dołączenia",
-       "whatlinkshere-hidelinks": "$1 linki",
-       "whatlinkshere-hideimages": "$1 linki z plików",
+       "whatlinkshere-hideredirs": "Ukryj przekierowania",
+       "whatlinkshere-hidetrans": "Ukryj dołączenia",
+       "whatlinkshere-hidelinks": "Ukryj linki",
+       "whatlinkshere-hideimages": "Ukryj linki z plików",
        "whatlinkshere-filters": "Filtry",
        "whatlinkshere-submit": "Dalej",
        "autoblockid": "automatyczna blokada nr $1",
        "lockdbsuccesstext": "Baza danych została zablokowana.<br />\nPamiętaj by [[Special:UnlockDB|zdjąć blokadę]] po zakończeniu działań administracyjnych.",
        "unlockdbsuccesstext": "Baza danych została odblokowana.",
        "lockfilenotwritable": "Nie można zapisać pliku blokady bazy danych.\nBlokowanie i odblokowywanie bazy danych, wymaga by plik mógł być zapisywany przez web serwer.",
+       "databaselocked": "Baza danych jest już zablokowana.",
        "databasenotlocked": "Baza danych nie jest zablokowana.",
        "lockedbyandtime": "(przez $1 dnia $2 o $3)",
        "move-page": "Przenieś $1",
index f127930..ad33079 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|d'un andré|andré ëd $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|d'un anans|anans ëd $1}}",
        "whatlinkshere-links": "← anliure",
-       "whatlinkshere-hideredirs": "$1 le ridiression",
-       "whatlinkshere-hidetrans": "$1 anclusion",
-       "whatlinkshere-hidelinks": "$1 anliura",
+       "whatlinkshere-hideredirs": "Stërmé ridiression",
+       "whatlinkshere-hidetrans": "Stërmé transclusion",
+       "whatlinkshere-hidelinks": "Stërmé anliure",
        "whatlinkshere-hideimages": "$1 j'archivi lijà",
        "whatlinkshere-filters": "Filtr",
        "autoblockid": "Blocagi automàtich #$1",
        "javascripttest": "Preuva ëd JavaScript",
        "javascripttest-pagetext-unknownaction": "Assion nen conossùa «$1».",
        "javascripttest-qunit-intro": "Vëdde [$1 la documentassion dle preuve] dzora a mediawiki.org.",
-       "tooltip-pt-userpage": "Soa pàgina utent",
+       "tooltip-pt-userpage": "{{GENDER:|Soa}} pàgina utent",
        "tooltip-pt-anonuserpage": "La pàgina utent për l'IP con ël qual chiel a contribuiss",
        "tooltip-pt-mytalk": "Soa pàgina ëd discussion e ciaciarade",
        "tooltip-pt-anontalk": "La pàgina ëd ciaciarade an sle contribussion da costa adrëssa IP",
        "tooltip-pt-preferences": "Coma che i veuj mia {{SITENAME}}.",
        "tooltip-pt-watchlist": "Lista dle pàgine che chiel as ten sot euj.",
-       "tooltip-pt-mycontris": "Lista ëd soe contribussion",
+       "tooltip-pt-mycontris": "Lista ëd {{GENDER:|soe}} contribussion",
        "tooltip-pt-login": "Un a l'é nen obligà a rintré ant al sistema, ma se a lo fa a l'é mej",
        "tooltip-pt-logout": "Seurte da",
        "tooltip-pt-createaccount": "I-j consejoma ëd creé un cont e ëd rintré ant ël sistema; però a l'é nen obligatòri",
index 2438f95..76b66bf 100644 (file)
        "watchlistedit-raw-done": "ستاسې کتنلړ اوسمهاله شو.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 سرليک ورگډ شو|$1 سرليکونه ورگډ شوه}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 سرليک ليرې شو|$1 سرليکونه ليري شوه}}:",
-       "watchlistedit-clear-title": "کتنلړ سپين شو",
+       "watchlistedit-clear-title": "کتنلړ سپينول",
        "watchlistedit-clear-legend": "کتنلړ سپينول",
        "watchlistedit-clear-titles": "سرليکونه:",
        "watchlistedit-clear-submit": "کتنلړ سپينول (دا دايمي ده!)",
index 0a38088..1e20027 100644 (file)
        "image_tip": "This is the text that appears when you hover the mouse over the sixth (middle) button on the edit toolbar.\n\n{{Identical|Embedded file}}",
        "media_sample": "{{optional}}\n{{Identical|Example}}",
        "media_tip": "This is the text that appears when you hover the mouse over the fifth button from the right in the edit toolbar.\n{{Identical|File link}}",
+       "sig-text": "{{notranslate}} This is the text that appears when you click on the signature button (second button from the right) on the edit toolbar. $1 will be replaced with four tildes (which cannot be included directly in the message for technical reasons).",
        "sig_tip": "This is the text that appears when you hover the mouse over the second key from the right on the edit toolbar.\n{{Identical|Signature with timestamp}}",
        "hr_tip": "This is the text that appears when you hover the mouse over the first button on the right on the edit toolbar.",
        "summary": "The Summary text beside the edit summary field\n\nSee also:\n* {{msg-mw|Subject}}\nSee also:\n* {{msg-mw|Accesskey-summary}}\n* {{msg-mw|Tooltip-summary}}\n{{Identical|Summary}}",
        "right-managechangetags": "{{doc-right|managechangetags}}",
        "right-applychangetags": "{{doc-right|applychangetags}}",
        "right-changetags": "{{doc-right|changetags}}",
+       "right-deletechangetags": "{{doc-right|deletechangetags}}",
        "grant-generic": "Used if the grant name is not defined. Parameters:\n* $1 - grant name\n\nDefined grants (grant name refers: blockusers, createeditmovepage, ...):\n* {{msg-mw|grant-checkuser}}\n* {{msg-mw|grant-blockusers}}\n* {{msg-mw|grant-createaccount}}\n* {{msg-mw|grant-createeditmovepage}}\n* {{msg-mw|grant-delete}}\n* {{msg-mw|grant-editinterface}}\n* {{msg-mw|grant-editmycssjs}}\n* {{msg-mw|grant-editmyoptions}}\n* {{msg-mw|grant-editmywatchlist}}\n* {{msg-mw|grant-editpage}}\n* {{msg-mw|grant-editprotected}}\n* {{msg-mw|grant-highvolume}}\n* {{msg-mw|grant-oversight}}\n* {{msg-mw|grant-patrol}}\n* {{msg-mw|grant-protect}}\n* {{msg-mw|grant-rollback}}\n* {{msg-mw|grant-sendemail}}\n* {{msg-mw|grant-uploadeditmovefile}}\n* {{msg-mw|grant-uploadfile}}\n* {{msg-mw|grant-basic}}\n* {{msg-mw|grant-viewdeleted}}\n* {{msg-mw|grant-viewmywatchlist}}",
        "grant-group-page-interaction": "{{Related|grant-group}}",
        "grant-group-file-interaction": "{{Related|grant-group}}",
        "action-managechangetags": "{{doc-action|managechangetags}}",
        "action-applychangetags": "{{doc-action|applychangetags}}",
        "action-changetags": "{{doc-action|changetags}}",
+       "action-deletechangetags": "{{doc-action|deletechangetags}}",
        "nchanges": "Appears on enhanced watchlist and recent changes when page has more than one change on given date, linking to a diff of the changes.\n\nParameters:\n* $1 - the number of changes on that day (2 or more)\nThree messages are shown side-by-side: ({{msg-mw|Nchanges}} | {{msg-mw|Enhancedrc-since-last-visit}} | {{msg-mw|Enhancedrc-history}}).",
        "enhancedrc-since-last-visit": "Appears on enhanced watchlist and recent changes when page has more than one change on given date and at least one that the user hasn't seen yet, linking to a diff of the unviewed changes.\n\nParameters:\n* $1 - the number of unviewed changes (1 or more)\nThree messages are shown side-by-side: ({{msg-mw|nchanges}} | {{msg-mw|enhancedrc-since-last-visit}} | {{msg-mw|enhancedrc-history}}).",
        "enhancedrc-history": "Appears on enhanced watchlist and recent changes when page has more than one change on given date, linking to its history.\n\nThis is the same as {{msg-mw|hist}}, but not abbreviated.\n\nThree messages are shown side-by-side: ({{msg-mw|nchanges}} | {{msg-mw|enhancedrc-since-last-visit}} | {{msg-mw|enhancedrc-history}}).\n{{Identical|History}}",
        "whatlinkshere-prev": "This is part of the navigation message on the top and bottom of Whatlinkshere pages, where it is used as the first argument of {{msg-mw|Viewprevnext}}.\n\nParameters:\n* $1 - the number of items shown per page. It is not used when $1 is zero; not sure what happens when $1 is one.\nSpecial pages use {{msg-mw|Prevn}} instead (still as an argument to {{msg-mw|Viewprevnext}}).\n\nSee also:\n* {{msg-mw|Whatlinkshere-next}}\n{{Identical|Previous}}",
        "whatlinkshere-next": "This is part of the navigation message on the top and bottom of Whatlinkshere pages, where it is used as the second argument of {{msg-mw|Viewprevnext}}.\n\nParameters:\n* $1 - the number of items shown per page. It is not used when $1 is zero; not sure what happens when $1 is one.\nSpecial pages use {{msg-mw|Nextn}} instead (still as an argument to {{msg-mw|Viewprevnext}}).\n\nSee also:\n* {{msg-mw|Whatlinkshere-prev}}\n{{Identical|Next}}",
        "whatlinkshere-links": "Used on [[Special:WhatLinksHere]]. It is a link to the WhatLinksHere page of that page.\n\nExample line:\n* [[Main Page]] ([[Special:WhatLinksHere/Main Page|{{int:whatlinkshere-links}}]])\n{{Identical|Link}}",
-       "whatlinkshere-hideredirs": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}\n{{Identical|Redirect}}",
-       "whatlinkshere-hidetrans": "First filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}\n{{Identical|Transclusion}}",
-       "whatlinkshere-hidelinks": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}\n{{Identical|Link}}",
+       "whatlinkshere-hideredirs": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}",
+       "whatlinkshere-hidetrans": "First filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}",
+       "whatlinkshere-hidelinks": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}",
        "whatlinkshere-hideimages": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 - the {{msg-mw|Hide}} or {{msg-mw|Show}}\n\nSee also:\n*{{msg-mw|Isimage}}\n*{{msg-mw|Media tip}}",
        "whatlinkshere-filters": "{{Identical|Filter}}",
        "whatlinkshere-submit": "Label for submit button in [[Special:WhatLinksHere]]\n{{Identical|Go}}",
        "unlockdbsuccesssub": "Used as subtitle in [[Special:UnlockDB]].\n\nSee also:\n* {{msg-mw|Lockdbsuccesssub|subtitle}}\n* {{msg-mw|Lockdbsuccesstext|text}}\n* {{msg-mw|Unlockdbsuccesssub|subtitle}}\n* {{msg-mw|Unlockdbsuccesstext|text}}",
        "lockdbsuccesstext": "Used as message text in [[Special:LockDB]].\n\nSee also:\n* {{msg-mw|Lockdbsuccesssub|subtitle}}\n* {{msg-mw|Lockdbsuccesstext|text}}\n* {{msg-mw|Unlockdbsuccesssub|subtitle}}\n* {{msg-mw|Unlockdbsuccesstext|text}}",
        "unlockdbsuccesstext": "Used as message text in [[Special:UnlockDB]].\n\nSee also:\n* {{msg-mw|Lockdbsuccesssub|subtitle}}\n* {{msg-mw|Lockdbsuccesstext|text}}\n* {{msg-mw|Unlockdbsuccesssub|subtitle}}\n* {{msg-mw|Unlockdbsuccesstext|text}}",
-       "lockfilenotwritable": "'No longer needed' on wikipedia.",
+       "lockfilenotwritable": "Used as error message in [[Special:LockDB]].",
        "databaselocked": "Used as error message in [[Special:LockDB]].\nThe title of this error message is {{msg-mw|Lockdb}}.\n\nSee also:\n* {{msg-mw|Lockdb|title}}\n* {{msg-mw|Databaselocked|message}}",
        "databasenotlocked": "Used as error message in [[Special:UnlockDB]].\nThe title of this error message is {{msg-mw|Lockdb}}.\n\nSee also:\n* {{msg-mw|Lockdb|title}}\n* {{msg-mw|Databasenotlocked|message}}",
        "lockedbyandtime": "Used as part of the message when a database is locked through [[Special:LockDB]]. Parameters:\n* $1 is the user that locked the database.\n* $2 is the date on which the lock was made\n* $3 is the time at which the lock was made",
        "tags-delete-not-found": "Error message on [[Special:Tags]]",
        "tags-delete-too-many-uses": "Error message on [[Special:Tags]]",
        "tags-delete-warnings-after-delete": "Warning shown after deleting a tag.\n\nParameters:\n* $1 - the code name of the tag that was deleted\n* $2 - the number of warnings",
+       "tags-delete-no-permission": "Error message on [[Special:Tags]]",
        "tags-activate-title": "The title of a page used to activate a tag. For more information on tags see [[mw:Manual:Tags|MediaWiki]].",
        "tags-activate-question": "An explanation to tell users what they are about to do.\n\nParameters:\n* $1 - the code name of the tag that is about to be activated",
        "tags-activate-reason": "{{Identical|Reason}}",
index a2eecbe..bfd77ff 100644 (file)
        "userpage-userdoesnotexist": "Учётной записи «<nowiki>$1</nowiki>» не существует. Убедитесь, что вы действительно желаете создать или изменить эту страницу.",
        "userpage-userdoesnotexist-view": "Не зарегистрировано учётной записи «$1».",
        "blocked-notice-logextract": "Этот участник в данный момент заблокирован.\nНиже приведена последняя запись из журнала блокировок:",
-       "clearyourcache": "'''Замечание.''' Возможно, после сохранения вам придётся очистить кэш своего браузера, чтобы увидеть изменения.\n* '''Firefox / Safari:''' Удерживая клавишу ''Shift'', нажмите на панели инструментов ''Обновить'' либо нажмите ''Ctrl-F5'' или ''Ctrl-R'' (''⌘-R'' на Mac)\n* '''Google Chrome:''' Нажмите ''Ctrl-Shift-R'' (''⌘-Shift-R'' на Mac)\n* '''Internet Explorer:''' Удерживая ''Ctrl'', нажмите ''Обновить'' либо нажмите ''Ctrl-F5''\n* '''Opera:''' Выберите очистку кэша в меню ''Инструменты → Настройки''",
+       "clearyourcache": "<strong>Замечание.</strong> Возможно, после сохранения вам придётся очистить кэш своего браузера, чтобы увидеть изменения.\n* <strong>Firefox / Safari:</strong> Удерживая клавишу <em>Shift</em>, нажмите на панели инструментов <em>Обновить</em> либо нажмите <em>Ctrl-F5</em> или <em>Ctrl-R</em> (<em>⌘-R</em> на Mac)\n* <strong>Google Chrome:</strong> Нажмите <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Mac)\n* <strong>Internet Explorer:</strong> Удерживая <em>Ctrl</em>, нажмите <em>Обновить</em> либо нажмите <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Перейдите в <em>Menu → Настройки</em> (<em>Opera → Настройки</em> на Mac), а затем <em>Безопасность → Очистить историю посещений → Кэшированные изображения и файлы</em>",
        "usercssyoucanpreview": "'''Подсказка.''' Нажмите кнопку «{{int:showpreview}}», чтобы проверить свой новый CSS-файл перед сохранением.",
        "userjsyoucanpreview": "'''Подсказка.''' Нажмите кнопку «{{int:showpreview}}», чтобы проверить свой новый JS-файл перед сохранением.",
        "usercsspreview": "'''Помните, что это только предварительный просмотр вашего CSS-файла, он ещё не сохранён!'''",
        "changecontentmodel-success-text": "Модель содержимого [[:$1]] была изменена.",
        "changecontentmodel-cannot-convert": "Содержимое [[:$1]] не может быть преобразовано к типу $2.",
        "changecontentmodel-nodirectediting": "Модель содержимого $1 не поддерживает прямое редактирование",
+       "changecontentmodel-emptymodels-title": "Нет доступных моделей содержимого",
+       "changecontentmodel-emptymodels-text": "Содержимое на [[:$1]] не может быть преобразовано ни к одному типу.",
        "log-name-contentmodel": "Журнал изменения моделей содержимого",
        "log-description-contentmodel": "События, связанные с моделями содержимого страниц",
        "logentry-contentmodel-new": "$1 создал{{GENDER:$2||а}} страницу $3 с использованием нестандартной модели содержимого «$5»",
        "lockdbsuccesstext": "База данных проекта была заблокирована.<br />\nНе забудьте [[Special:UnlockDB|убрать блокировку]] после завершения процедуры обслуживания.",
        "unlockdbsuccesstext": "База данных проекта была разблокирована.",
        "lockfilenotwritable": "Нет права на запись в файл блокировки базы данных. Чтобы заблокировать или разблокировать базу данных, веб-сервер должен иметь разрешение на запись в этот файл.",
+       "databaselocked": "База данных уже заблокирована.",
        "databasenotlocked": "База данных не была заблокирована.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "$1 — переименование",
        "tooltip-ca-nstab-category": "Страница категории",
        "tooltip-minoredit": "Отметить это изменение как незначительное",
        "tooltip-save": "Сохранить ваши изменения",
+       "tooltip-publish": "Опубликовать ваши изменения",
        "tooltip-preview": "Предварительный просмотр страницы; пожалуйста, используйте его перед сохранением!",
        "tooltip-diff": "Показать изменения, сделанные по отношению к исходному тексту.",
        "tooltip-compareselectedversions": "Посмотреть разницу между двумя выбранными версиями этой страницы.",
        "feedback-useragent": "Браузер:",
        "searchsuggest-search": "Поиск",
        "searchsuggest-containing": "содержащие…",
+       "api-error-autoblocked": "Ваш IP-адрес был автоматически заблокирован, потому что он был использован заблокированным участником.",
        "api-error-badaccess-groups": "Вам не разрешено загружать файлы в эту вики.",
        "api-error-badtoken": "Внутренняя ошибка:  некорректный токен.",
+       "api-error-blocked": "Редактирование было для вас заблокировано.",
        "api-error-copyuploaddisabled": "Загрузка по URL-адресу отключена на этом сервере.",
        "api-error-duplicate": "Уже {{PLURAL:$1|существует другой файл|существуют другие файлы}} с таким же содержимым.",
        "api-error-duplicate-archive": "Раньше на сайте {{PLURAL:$1|1=уже был файл|были файлы}} с точно таким же содержанием, но {{PLURAL:$1|1=он был удалён|они были удалены}}.",
        "api-error-nomodule": "Внутренняя ошибка: не настроен модуль загрузки.",
        "api-error-ok-but-empty": "Внутренняя ошибка: нет ответа от сервера.",
        "api-error-overwrite": "Не допускается замена существующего файла.",
+       "api-error-ratelimited": "Вы пытаетесь загрузить несколько файлов за более короткий промежуток времени, чем это позволено.\nПожалуйста, попробуйте ещё раз через несколько минут.",
        "api-error-stashfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
        "api-error-publishfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
        "api-error-stasherror": "При загрузке файла в хранилище произошла ошибка.",
index 0edaa16..d284111 100644 (file)
@@ -31,7 +31,8 @@
                        "Macofe",
                        "Roonyh",
                        "Matma Rex",
-                       "SusithCM"
+                       "SusithCM",
+                       "Sandaru"
                ]
        },
        "tog-underline": "සබැඳි යටීර කිරීම:",
        "tog-newpageshidepatrolled": "විමසුමට ලක්කෙරුණු පිටු, අළුත් පිටු ලැයිස්තුව තුල නොපෙන්වන්න",
        "tog-hidecategorization": "පිටුවේ ප්‍රවර්ගීකරණය සගවන්න",
        "tog-extendwatchlist": "මෑත වෙනස්වීම් පමණක් නොව, අදාළ සියළු වෙනස්වීම් දක්වා පෙන්වන අයුරින් මුර-ලැයිස්තුව පුළුල් කරන්න",
-       "tog-usenewrc": "මෑත වෙනස්වීම් සහ මුර ලැයිස්තුව හී පිටුව අනුව සමූහ වෙනස්වීම් (ජාවාස්ක්‍රිප්ට් ඇවැසිය)",
+       "tog-usenewrc": "මෑත වෙනස්වීම් සහ මුර ලැයිස්තුව හී පිටුව අනුව සමූහ වෙනස්වීම්",
        "tog-numberheadings": "ශීර්ෂ-නාම ස්වයංක්‍රීයව අංකනය කරන්න",
        "tog-showtoolbar": "සංස්කරණ මෙවලම්තීරුව පෙන්වන්න",
        "tog-editondblclick": "ද්විත්ව-ක්ලික් කිරීම මගින් පිටු සංස්කරණය අරඹන්න",
-       "tog-editsectiononrightclick": "ඡේද ශීර්ෂ මත දකුණු-ක්ලික් කිරීමෙන් ඡේද සංස්කරණය සක්‍රීය කරන්න (ජාවාස්ක්‍රිප්ට්)",
+       "tog-editsectiononrightclick": "ඡේද ශීර්ෂ මත දකුණු-ක්ලික් කිරීමෙන් ඡේද සංස්කරණය සක්‍රීය කරන්න",
        "tog-watchcreations": "මම තනන පිටු හා මම උඩුගත කරන ගොනු මාගේ මුරලැයිස්තුවට එක් කරන්න",
        "tog-watchdefault": "මම සංස්කරණය කරන පිටු හා ගොනු මාගේ මුර ලැයිස්තුවට එක් කරන්න",
        "tog-watchmoves": "මම ගෙනයන පිටු හා ගොනු මාගේ මුර ලැයිස්තුවට එක් කරන්න",
index b0c11d0..bc7a6fc 100644 (file)
        "changecontentmodel-success-text": "Spremenili smo vrsto vsebine [[:$1]].",
        "changecontentmodel-cannot-convert": "Vsebine na [[:$1]] ni mogoče pretvoriti v vrsto $2.",
        "changecontentmodel-nodirectediting": "Model vsebine $1 ne podpira neposrednega urejanja",
+       "changecontentmodel-emptymodels-title": "Na voljo ni noben model vsebine",
+       "changecontentmodel-emptymodels-text": "Vsebine na [[:$1]] ni mogoče pretvoriti v katero koli vrsto.",
        "log-name-contentmodel": "Dnevnik sprememb modela vsebine",
        "log-description-contentmodel": "Dogodki, povezani z modeli vsebin strani",
        "logentry-contentmodel-new": "$1 je {{GENDER:$2|ustvaril|ustvarila|ustvaril(-a)}} stran $3 z neprivzetim modelom vsebine »$5«",
        "whatlinkshere-prev": "{{PLURAL:$1|prejšnji|prejšnja $1|prejšnji $1|prejšnjih $1|prejšnjih $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|naslednji|naslednja $1|naslednji $1|naslednjih $1|naslednjih $1}}",
        "whatlinkshere-links": "← povezave",
-       "whatlinkshere-hideredirs": "$1 preusmeritve",
-       "whatlinkshere-hidetrans": "$1 vključitve",
-       "whatlinkshere-hidelinks": "$1 povezave",
-       "whatlinkshere-hideimages": "$1 povezave datotek",
+       "whatlinkshere-hideredirs": "Skrij preusmeritve",
+       "whatlinkshere-hidetrans": "Skrij vključitve",
+       "whatlinkshere-hidelinks": "Skrij povezave",
+       "whatlinkshere-hideimages": "Skrij povezave datotek",
        "whatlinkshere-filters": "Filtri",
        "whatlinkshere-submit": "Pojdi",
        "autoblockid": "Samodejna blokada št. $1",
        "lockdbsuccesstext": "Podatkovna baza je bila zaklenjena.<br />\nNe pozabite je [[Special:UnlockDB|odkleniti]], ko boste končali z vzdrževanjem.",
        "unlockdbsuccesstext": "Zbirka podatkov {{GRAMMAR:rodilnik|{{SITENAME}}}} je spet odklenjena.",
        "lockfilenotwritable": "Datoteka zaklepanja zbirke podatkov ni zapisljiva.\nZa zaklepanje in odklepanje zbirke podatkov mora biti ta datoteka zapisljiva s strani spletnega strežnika.",
+       "databaselocked": "Zbirka podatkov je že zaklenjena.",
        "databasenotlocked": "Zbirka podatkov ni zaklenjena.",
        "lockedbyandtime": "($1 dne $2 ob $3)",
        "move-page": "Preimenuj $1",
index 979e838..1d64e07 100644 (file)
        "changecontentmodel-success-text": "Innehållstypen för [[:$1]] har ändrats.",
        "changecontentmodel-cannot-convert": "Innehållet på [[:$1]] kan inte konverteras till typen $2.",
        "changecontentmodel-nodirectediting": "Innehållsmodellen $1 stöder inte direkt redigering",
+       "changecontentmodel-emptymodels-title": "Inget innehållsmodeller finns tillgängliga",
+       "changecontentmodel-emptymodels-text": "Innehållet på [[:$1]] kan inte konverteras till någon typ.",
        "log-name-contentmodel": "Ändringslogg för innehållsmodellen",
        "log-description-contentmodel": "Händelser som är relaterade till en sidas innehållsmodeller",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|skapade}} sidan $3 med den icke-standardiserade innehållsmodellen \"$5\"",
        "whatlinkshere-prev": "{{PLURAL:$1|förra|förra $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|nästa|nästa $1}}",
        "whatlinkshere-links": "← länkar",
-       "whatlinkshere-hideredirs": "$1 omdirigeringar",
-       "whatlinkshere-hidetrans": "$1 inkluderingar",
-       "whatlinkshere-hidelinks": "$1 länkar",
-       "whatlinkshere-hideimages": "$1 fillänkar",
+       "whatlinkshere-hideredirs": "Dölj omdirigeringar",
+       "whatlinkshere-hidetrans": "Dölj inkluderingar",
+       "whatlinkshere-hidelinks": "Dölj länkar",
+       "whatlinkshere-hideimages": "Dölj fillänkar",
        "whatlinkshere-filters": "Filter",
        "whatlinkshere-submit": "Gå",
        "autoblockid": "Autoblockera #$1",
        "lockdbsuccesstext": "Databasen är nu låst.\n<br />Kom ihåg att [[Special:UnlockDB|ta bort låsningen]] när du är färdig med ditt underhåll.",
        "unlockdbsuccesstext": "Databasen är upplåst.",
        "lockfilenotwritable": "Det går inte att skriva till databasens låsfil. För att låsa eller låsa upp databasen, så måste webbservern kunna skriva till den filen.",
+       "databaselocked": "Databasen är redan låst.",
        "databasenotlocked": "Databasen är inte låst.",
        "lockedbyandtime": "(av $1 den $2 kl. $3)",
        "move-page": "Flytta $1",
        "log-action-filter-block-reblock": "Blockeringsändring",
        "log-action-filter-block-unblock": "Tog bort blockering",
        "log-action-filter-contentmodel-change": "Ändring av innehållsmodell",
+       "log-action-filter-contentmodel-new": "Skapande av sida med icke-standardiserad innehållsmodell",
        "log-action-filter-delete-delete": "Radering av sida",
        "log-action-filter-delete-restore": "Återställde sida",
        "log-action-filter-delete-event": "Radering av logg",
index 173d31f..842500f 100644 (file)
        "changecontentmodel-success-text": "Тип вмісту сторінки [[:$1]] було змінено.",
        "changecontentmodel-cannot-convert": "Вміст сторінки [[:$1]] не можна перетворити на вміст типу $2.",
        "changecontentmodel-nodirectediting": "Модель вмісту $1 не підтримує пряме редагування",
+       "changecontentmodel-emptymodels-title": "Немає доступних моделей коментарів",
+       "changecontentmodel-emptymodels-text": "Вміст сторінки [[:$1]] не може бути перетворений до будь якого типу.",
        "log-name-contentmodel": "Журнал змін моделі вмісту",
        "log-description-contentmodel": "Події, пов'язані з моделями вмісту сторінки",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|створив|створила}} сторінку $3, використовуючи нестандартну модель вмісту «$5»",
        "lockdbsuccesstext": "Базу даних проекту заблоковано.<br />\nНе забудьте її [[Special:UnlockDB|розблокувати]] після завершення обслуговування.",
        "unlockdbsuccesstext": "Базу даних проекту розблоковано.",
        "lockfilenotwritable": "Немає права на запис в файл блокування бази даних. Щоб заблокувати чи розблокувати БД, веб-сервер повинен мати дозвіл на запис в цей файл.",
+       "databaselocked": "База даних вже заблокована.",
        "databasenotlocked": "База даних не заблокована.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "Перейменування сторінки «$1»",
        "movesubpagetext": "Ця сторінка має $1 {{PLURAL:$1|підсторінку|підсторінки|підсторінок}}.",
        "movenosubpage": "Ця сторінка не має підсторінок.",
        "movereason": "Причина:",
-       "revertmove": "відкинути",
+       "revertmove": "скасувати перейменування",
        "delete_and_move_text": "Сторінка з назвою [[:$1|«$1»]] вже існує.\nБажаєте вилучити її для можливості перейменування?",
        "delete_and_move_confirm": "Так, вилучити для перейменування",
        "delete_and_move_reason": "Вилучена для можливості перейменування сторінки «[[$1]]»",
index 2589d77..66a40c6 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|kết quả trước|$1 kết quả trước}}",
        "whatlinkshere-next": "{{PLURAL:$1|kết quả sau|$1 kết quả sau}}",
        "whatlinkshere-links": "← liên kết",
-       "whatlinkshere-hideredirs": "$1 trang đổi hướng",
+       "whatlinkshere-hideredirs": "Ẩn trang đổi hướng",
        "whatlinkshere-hidetrans": "$1 trang nhúng",
-       "whatlinkshere-hidelinks": "$1 liên kết",
-       "whatlinkshere-hideimages": "$1 liên kết tập tin",
+       "whatlinkshere-hidelinks": "Ẩn liên kết",
+       "whatlinkshere-hideimages": "Ẩn liên kết tập tin",
        "whatlinkshere-filters": "Bộ lọc",
        "whatlinkshere-submit": "Xem",
        "autoblockid": "Cấm tự động #$1",
index e0e6ce8..d63feb5 100644 (file)
@@ -53,7 +53,7 @@
        "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",
-       "tog-norollbackdiff": "Iglat-ang an kaiban kahuman himoa an libot-pabalik",
+       "tog-norollbackdiff": "Ayaw igpakita an kaiban kahuman himoa an rollback",
        "tog-useeditwarning": "Pasabti ako kun nabaya ako hin ginliwat ng pakli nga waray katipig an mga pagbag-o",
        "tog-prefershttps": "Pirmihi paggamit hin segurado nga koneksyon kun nakalog-in",
        "underline-always": "Pirme",
        "createaccount-title": "Paghimo hin akawnt para han {{SITENAME}}",
        "createaccount-text": "Mayda tawo nga naghimo hin akawnt nga gingamit an imo email address ha {{SITENAME}} ($4) nga ginngaranan nga \"$2\", nga may-ada tigaman-panakob nga \"$3\".\n\nKinahanglan ka maglog-in ngan igbal-iw an imo tigaman-panakob yana dayon.\n\nPuydi nimo pabay-on ini nga mensahe, kun ini nga paghimo hin akwant in nagsayop la.",
        "login-throttled": "Damo na an imo login attempts ha pagkayana.\nAlayon paghulat hin $1 san-o ka umutro.",
-       "login-abort-generic": "An imo paglog-in in diri malinamposon - Naundang",
+       "login-abort-generic": "An imo paglog-in in pakyas - Gin-undang",
        "login-migrated-generic": "An imo account in nabalhin, ngan an imo agnay-gumaramit in waray na hinin nga wiki.",
        "loginlanguagelabel": "Pinulongan: $1",
        "suspicious-userlogout": "Waray ka tugoti pag-logout tungod nga baga ini ginpadangat hin usa nga broken browser o caching proxy.",
        "newpassword": "Bag-o nga tigaman-pagsulod:",
        "retypenew": "Utroha pagbutang an bag-o nga tigaman-pagsulod:",
        "resetpass_submit": "Igbutang an password ngan log in",
-       "changepassword-success": "Malinamposon an pagbal-iw hit imo tigaman-panakob!",
+       "changepassword-success": "Malinamposon an pagliwat hit imo password!",
        "changepassword-throttled": "Damo na nga mga paningkamot hin pagsakob an imo ginhimò.\nAlayon paghulat hin $1 san-o ka umutro.",
        "botpasswords": "Mga bot password",
        "botpasswords-disabled": "Ginparong an mga bot password.",
        "rev-delundel": "igpakita/igtago",
        "rev-showdeleted": "igpakita",
        "revisiondelete": "Pagpara/pagtanggal han pagpara nga mga rebisyon",
+       "revdelete-nooldid-title": "Diri ginkakarawat an target revision",
        "revdelete-show-file-confirm": "Sigurado ka nga gusto mo makita an ginpara nga pagliwat han file \"<nowiki>$1</nowiki>\" tikang $2 ha $3?",
        "revdelete-show-file-submit": "Oo",
        "revdelete-hide-text": "Rebisyon nga sinurat",
        "right-reupload": "Sapawa an mga aada nga mga paypay",
        "right-reupload-own": "Igsapaw an aada yana nga mga paypay nga ginkarga-pasaka nimo mismo",
        "right-upload_by_url": "Igkarga paigbaw an mga paypay tikang ha uska URL",
+       "right-purge": "Igpurge an site cache han pakli bisan waray kompirmasyon",
        "right-autoconfirmed": "Diri malalalbtan hin IP-nga-nahibasi nga mga rate hin paglimit",
        "right-bot": "Igtrato komo uska naglulugaring nga proseso",
        "right-writeapi": "Paggamit han write API",
        "listgrouprights-removegroup-self": "Igtanggal an {{PLURAL:$2|grupo|mga grupo}} tikang ha kalugaringon nga akawnt: $1",
        "listgrouprights-addgroup-self-all": "Igdugang an ngatanan nga mga grupo ha kalugaringon nga akawnt",
        "listgrouprights-removegroup-self-all": "Igtanggal an ngatanan nga mga grupo tikang ha kalugaringon nga akawnt",
+       "listgrants-rights": "Mga katungod",
+       "trackingcategories": "Ginsusubay an mga kategorya",
+       "trackingcategories-name": "Ngaran han mensahe",
        "mailnologin": "Waray kakadtoan nga address",
        "mailnologintext": "Kinahanglan nimo nga [[Special:UserLogin|nakalog-in]] ngan may-ada balido nga email address ha imo[[Special:Preferences|mga preperensya]] para makapadangat hin email ngadto ha iba nga mga gumaramit.",
        "emailuser": "Ig-e-mail ini nga gumaramit",
        "protectcomment": "Katadongan:",
        "protect-default": "Togota an ngatanan nga mga gumaramit",
        "protect-level-sysop": "Tuguti la an mga magdudumara",
+       "protect-expiring": "mawawaray gamit pag-$1 (UTC)",
+       "protect-expiring-local": "mawawaray gamit $1",
+       "protect-expiry-indefinite": "waray kataposan",
        "protect-othertime": "Lain nga oras:",
        "protect-othertime-op": "lain nga oras",
        "protect-otherreason": "Lain/dugang nga katadongan:",
index 36b1f8d..fe5623f 100644 (file)
@@ -17,7 +17,8 @@
                        "පසිඳු කාවින්ද",
                        "Matma Rex",
                        "Macofe",
-                       "Nemo bis"
+                       "Nemo bis",
+                       "Alefbeis"
                ]
        },
        "tog-underline": "שטרייכט אונטער לינקען",
@@ -35,6 +36,7 @@
        "tog-watchdefault": "צולייגן בלעטער וואס איך רעדאקטיר צו מיין אכטונג ליסטע",
        "tog-watchmoves": "צולייגן בלעטער וואס איך באוועג און טעקעס וואס איך לאד ארויף צו מיין אכטונג ליסטע",
        "tog-watchdeletion": "צולייגן בלעטער וואס איך מעק אויס צו מיין אויפפאסונג ליסטע",
+       "tog-watchuploads": "לייגט צו נייע פיילס וואס איך טוה ארויפלאדירן צו מיין וואטש ליסטע",
        "tog-watchrollback": "צולייגן צו מיין אויפפאסן ליסטע בלעטער אויף וואס איך האב אדורכגעפירט א צוריקשטעלן.",
        "tog-minordefault": "באגרענעצן אלע רעדאַקטירונגען גרונטלעך אלס מינערדיק",
        "tog-previewontop": "צײַג די \"פֿאָרויסיגע װײַזונג\" גלײַך בײַ דער ערשטער באַאַרבעטונג",
@@ -58,7 +60,7 @@
        "tog-ccmeonemails": "שיק מיר קאפיעס פון בליצבריוו וואס איך שיק צו אנדערע באַניצער",
        "tog-diffonly": "ווייז נישט אינהאלט אונטער די דיפערענץ",
        "tog-showhiddencats": "ווײַזן באהאלטענע קאטעגאריעס",
-       "tog-norollbackdiff": "×\94×\99פ×\98 ×\90×\99×\91ער ווײַזן אונטערשייד נאכן אויספֿירן א צוריקדריי",
+       "tog-norollbackdiff": "× ×\99ש×\98 ווײַזן אונטערשייד נאכן אויספֿירן א צוריקדריי",
        "tog-useeditwarning": "שטעלן א ווארענונג ווען איך לאז איבער א רעדאקטירונג בלאט מיט נישט אויפגעהיטענע ענדערונגען",
        "tog-prefershttps": "ניצט שטענדיק א זיכערע פארבינדונג ווען ארײַנגלאגירט",
        "underline-always": "אייביג",
        "february-gen": "פעברואר",
        "march-gen": "מערץ",
        "april-gen": "אַפּריל",
-       "may-gen": "×\9e×\99י",
+       "may-gen": "×\9e×\90Ö·י",
        "june-gen": "יוני",
        "july-gen": "יולי",
        "august-gen": "אויגוסט",
        "returnto": "צוריקקערן צו $1.",
        "tagline": "פֿון {{SITENAME}}",
        "help": "הילף",
-       "search": "×\96×\95×\9b×\9f",
-       "searchbutton": "×\96×\95×\9b×\9f",
+       "search": "×\96×\95×\9a",
+       "searchbutton": "×\96×\95×\9a",
        "go": "גיין",
        "searcharticle": "גיין",
        "history": "בלאט היסטאריע",
        "protectedpage": "באשיצטער בלאט",
        "jumpto": "שפּרינג צו:",
        "jumptonavigation": "נאַוויגאַציע",
-       "jumptosearch": "×\96×\95×\9b×\9f",
+       "jumptosearch": "×\96×\95×\9a",
        "view-pool-error": "אנטשולדיגט, די סערווערס זענען איבערגעפילט איצט.\nצופיל באניצער פרובירן צו ליינען דעם בלאט.\nביטע ווארטן א ביסל צייט בעפאר איר פרובירט ווידער אריינגיין אינעם בלאט.\n\n$1",
        "generic-pool-error": "אנטשולדיגט, די סערווערס זענען איבערגעפילט איצט.\nצופיל באניצער פרובירן צו באקוקן דעם רעסורס.\nביטע ווארטן א ביסל צייט בעפאר איר פרובירט ווידער אריינטרעטן אין דעם רעסורס.",
        "pool-timeout": "אַריבער דער צײַט וואַרטן פֿאר דער שליסונג",
        "newmessageslinkplural": "{{PLURAL:$1|א נייע מעלדונג|999=נייע מעלדונגען}}",
        "newmessagesdifflinkplural": "לעצטע {{PLURAL:$1|ענדערונג|999=ענדערונגען}}",
        "youhavenewmessagesmulti": "איר האט נייע מעלדונגען אין $1",
-       "editsection": "×\91×\90Ö·×\90ַר×\91×¢×\98ן",
+       "editsection": "רע×\93×\90ַק×\98×\99רן",
        "editold": "רעדאַקטירן",
        "viewsourceold": "ווײַזן מקור",
        "editlink": "רעדאַקטירן",
        "red-link-title": "$1 (בלאט טוט נאָך נישט עקזיסטירן)",
        "sort-descending": "סארטירן אַראָפ",
        "sort-ascending": "סארטירן אַרויף",
-       "nstab-main": "×\90ַר×\98×\99ק×\9c",
+       "nstab-main": "×\91×\9c×\90Ö·×\98",
        "nstab-user": "באַניצער בלאט",
        "nstab-media": "מעדיע בלאט",
        "nstab-special": "ספעציעלער בלאט",
        "virus-unknownscanner": "אומבאוואוסטער אנטי־ווירוס:",
        "logouttext": "'''איר האָט זיך ארויסלאָגירט.'''\n\nבאמערקט אז געוויסע בלעטער קענען זיך ווייטער ארויסשטעלן אזוי ווי ווען איר זענט אריינלאגירט, ביז איר וועט אויסליידיגן דעם בלעטערער זאפאס.",
        "cannotlogoutnow-title": "קען נישט ארויסלאגירן אצינד",
+       "cannotlogoutnow-text": "ארויסלאגירן נישט מעגלעך ווען מען ניצט $1.",
        "welcomeuser": "ברוך הבא, $1!",
        "welcomecreation-msg": "מ'האט געשאפן אייער קאנטע.\nפארגעסט נישט צו ענדערן אייערע [[Special:Preferences|{{SITENAME}} פרעפערענצן]].",
        "yourname": "באַניצער נאָמען:",
        "userlogin-remembermypassword": "לאז מיך בלײַבן ארײַנלאגירט",
        "userlogin-signwithsecure": "ניצן זיכערן סארווער",
        "cannotloginnow-title": "קען נישט אריינלאגירן אצינד",
+       "cannotloginnow-text": "אריינלאגירן נישט מעגלעך ווען מען ניצט $1.",
        "yourdomainname": "אײַער געביט:",
        "password-change-forbidden": "איר קען נישט ענדערן פאסווערטער אויף דער וויקי.",
        "externaldberror": "עס איז אדער פארגעקומען אן אויטענטיקאציע דאטנבאזע פֿעלער אדער איר זענט נישט ערמעגליכט צו דערהיינטיגן אייער דרויסנדיגע קאנטע.",
        "nocookieslogin": "{{SITENAME}} נוצט קיכלעך כדי אַרײַנלאגירן באַניצער.\nבײַ אײַך זענען קיכלעך אומדערמעגלעכט.\nביטע אַקטיווירט זיי און פרובירט נאכאַמאָל.",
        "nocookiesfornew": "מען האט נישט געשאַפֿן די באַניצער קאנטע, ווײַל מען האט נישט געקענט באַשטעטיקן איר מקור.\nטוט פֿעסטשטעלן אָז קיכלעך זענען אַקטיווירט, לאָדט אָן נאכאַמאל דעם בלאַט און פרואווט ווידער.",
        "noname": "איר האט נישט אַרײַנגעשטעלט קײַן גילטיקן באַניצער נאָמען.",
-       "loginsuccesstitle": "אריינלאגירט מיט הצלחה",
+       "loginsuccesstitle": "אריינלאגירט",
        "loginsuccess": "'''איר זענט אַצינד אַרײַנלאָגירט אין {{SITENAME}} ווי \"$1\".'''",
        "nosuchuser": "נישטא קיין באניצער מיטן נאמען  \"$1\".\n\nקוקט איבער אײַער אויסלייג, אדער [[Special:UserLogin/signup|שאַפֿט א נײַע קאנטע]].",
        "nosuchusershort": "נישטאָ קיין באַניצער מיטן נאָמען \"$1\".\nביטע באַשטעטיקט דעם אויסלייג.",
        "createaccount-title": "קאנטע באשאפֿן אין {{SITENAME}}",
        "createaccount-text": "עמעצער האט באשאפֿן א קאנטע פֿאר אייער ע-פאסט אדרעס אין {{SITENAME}} ($4) מיטן נאמען \"$2\" און  פאסווארט \"$3\". איר דארפט אצינד איינלאגירן און ענדערן דאס פאסווארט.\n\nאיר קענט איגנארירן די מעלדונג, ווען די קאנטע איז באשאפֿן בטעות.",
        "login-throttled": "איר האט געפרוווט צופֿיל מאל אריינלאגירן.\nזייט אזוי גוט און וואַרט $1 איידער איר פרוווט נאכאמאל.",
-       "login-abort-generic": "×\90ײַער ×\90רײַנ×\9c×\90×\92×\99ר×\95× ×\92 ×\90×\99×\96 × ×\99ש×\98 ×\92×¢×\95×\95×¢×\9f ×\93ערפֿ×\90×\9c×\92ר×\99×\99×\9a—אָפגעשטעלט",
+       "login-abort-generic": "×\90ײַער ×\90רײַנ×\9c×\90×\92×\99ר×\95× ×\92 ×\90×\99×\96 ×\93×\95ר×\9b×\92עפ×\90×\9c×\9f—אָפגעשטעלט",
        "login-migrated-generic": "אײַער קאנטע איז געווארן מיגרירט, און אײַער באניצער־נאמען איז מער נישט פאראן אויף דער וויקי.",
        "loginlanguagelabel": "שפראך: $1",
        "suspicious-userlogout": " אײַער בקשה אַרויסלאָגירן זיך איז אפגעלייגט געווארן ווייַל אייגנטלעך איז זי געשיקט דורך אַ צעבראכענעם בלעטערער אָדער א פראקסי מיט א זאפאס.",
        "createacct-another-realname-tip": "עכטער נאמען איז אפציאנאל.\nאויב איר וויילט אויס צוצושטעלן אים, וועט דאס גענוצט ווערן צו געבן אטריבוציע פאר זייער ארבעט.",
-       "pt-login": "אַרײַנלאגירן",
+       "pt-login": "אריינלאגירן",
        "pt-login-button": "אַרײַנלאָגירן",
        "pt-createaccount": "שאַפֿן אַ קאנטע",
        "pt-userlogout": "אַרויסלאָגירן",
        "newpassword": "ניי פּאסוואָרט:",
        "retypenew": "ווידער שרײַבן פאַסווארט:",
        "resetpass_submit": "שטעלן פאסווארט און אריינלאגירן",
-       "changepassword-success": "אייער פאַסווארט איז געטוישט געווארן מיט דערפֿאלג!",
+       "changepassword-success": "אייער פאַסווארט איז געטוישט געווארן!",
        "changepassword-throttled": "איר האט געפרוווט צופֿיל מאל אריינלאגירן.\nזייט אזוי גוט און וואַרט $1 איידער איר פרוווט נאכאמאל.",
+       "botpasswords": "באט פאסווערטער",
+       "botpasswords-label-appid": "באט נאמען:",
        "botpasswords-label-create": "שאַפֿן",
        "botpasswords-label-update": "דערהײַנטיקן",
        "botpasswords-label-cancel": "אַנולירן",
        "botpasswords-label-delete": "אויסמעקן",
        "botpasswords-label-resetpassword": "ווידערשטעלן פאַסווארט",
+       "botpasswords-label-grants-column": "נאכגעגעבן",
+       "botpasswords-created-title": "באט פאסווארט געשאפן",
+       "botpasswords-created-body": "דאס באט פאסווארט פאר באט־נאמען \"$1\" פון באניצער \"$2\" איז געשאפן געווארן.",
+       "botpasswords-updated-title": "באט פאסווארט דערהיינטיקט",
+       "botpasswords-updated-body": "דאס באט פאסווארט פאר באט־נאמען \"$1\" פון באניצער \"$2\" איז דערהיינטיקט געווארן.",
+       "botpasswords-deleted-title": "באט פאסווארט אויסגעמעקט",
        "resetpass_forbidden": "פאסווערטער קענען נישט ווערן געטוישט",
        "resetpass-no-info": "איר דארפֿט זיין אריינלאגירט צוצוקומען גלייך צו דעם דאזיגן בלאט.",
        "resetpass-submit-loggedin": "טוישן פאסווארט",
        "resetpass-submit-cancel": "אַנולירן",
-       "resetpass-wrong-oldpass": "×\90×\95×\9e×\92×\99×\9c×\98×\99×\92 ×¦×²Ö·×\98×\95×\95ײַ×\9c×\99ק ×\90×\93ער ×\9c×\95×\99פֿ×\99ק ×¤×\90ַס×\95×\95×\90ר×\98.\n×\90×\99ר ×\94×\90×\98 ×\9e×¢×\92×\9c×¢×\9a ×©×\95×\99×\9f ×\92×¢×\98×\95×\99ש×\98 ×\90×\99×\99ער ×¤×\90ַס×\95×\95×\90ר×\98 ×\9e×\99×\98 ×\94צ×\9c×\97×\94 ×\90×\93ער ×\92×¢×\91×¢×\98×\9f ×\90 × ×²Ö·  ×¦×²Ö·×\98×\95×\95ײַ×\9c×\99ק ×¤×\90ַס×\95×\95×\90ר×\98.",
+       "resetpass-wrong-oldpass": "אומגילטיג צײַטווײַליק אדער לויפֿיק פאַסווארט.\nאיר האט מעגלעך שוין געטוישט אייער פאַסווארט אדער געבעטן א נײַ  צײַטווײַליק פאַסווארט.",
        "resetpass-recycled": "זײַט אזוי גוט שטעטל אירע פאסווארט צו עפעס אנדערש פונעם לויפיקן פאסווארט.",
        "resetpass-temp-emailed": "איר האט זיך ארי לאגירת מיט א פראוויזארישן קאד געשיקט דורכן ע־פאסט. כדי שליסן דאס ארײַנלאגירן, דארט איר שטעלן א נײַ פאסווארט דא.",
        "resetpass-temp-password": "צײַטווייליק פאַסווארט:",
        "passwordreset-emailtext-user": "באניצער $1 אויף  {{SITENAME}} האט געבעטן צוריקצושטעלן אייער פאסווארט פאר {{SITENAME}} ($4).\nדי פאלגנדע באניצער {{PLURAL:$3|קאנטע איז|קאנטעס זענען}} פארבונדן מיט דעם ע־פאסט אדרעס:\n\n$2\n\n{{PLURAL:$3|דאס פראוויזארישע פאסווארט|די פראוויזארישע פאסווערטער}} וועלן אויסגיין נאך {{PLURAL:$5|איין טאג|$5 טעג}}.\nאיר זאלט אריינלאגירן און קלויבן א נייע פאסווארט אצינד. טאמער א צווייטער האט געשיקט די בקשה,\nאדער ווען איר געדענקט יא אייער פריעריקע פאסווארט, און וויל עס נישט ענדערן,\n קענט איר איגנארירן דעם אנזאג און ניצן ווייטער דאס אלטע פאסווארט.",
        "passwordreset-emailelement": "באַניצער נאָמען: \n$1\n\nפראוויזארישער פּאַראָל: \n$2",
        "passwordreset-emailsentemail": "טאמער איז דער ע־פאסט אדרעס פארקניפט מיט אייער קאנטע, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
+       "passwordreset-emailsentusername": "טאמער איז פאראן אן ע־פאסט אדרעס פארקניפט מיט דעם באניצער־נאמען, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-emailsent-capture": "מען האט געשיקט א פאסווארט צוריקשטעלן בליצבריוו, וואס ווערט געוויזן אונטן.",
        "passwordreset-emailerror-capture": "מען האט געשאפן א פאסווארט צוריקשטעלן בליצבריוו, וואס ווערט געוויזן אונטן, אבער שיקן צום {{GENDER:$2|באניצער}}איז דורכגעפאלן: $1",
        "changeemail": "ענדערן אדער אראפנעמען ע-פּאָסט אַדרעס",
        "minoredit": "דאס איז א מינערדיגע ענדערונג",
        "watchthis": "טוט אױפֿפּאַסן דעם בלאט",
        "savearticle": "אויפהיטן בלאַט",
+       "publishpage": "פובליצירן בלאט",
        "preview": "פֿאראויסקוק",
        "showpreview": "ווייזן פאָרױסקוק",
        "showdiff": "ווײַז די ענדערונגען",
        "userpage-userdoesnotexist": "באניצער קאנטע \"$1\" איז נישט אײַנגעשריבן.\nקוקט איבער צי איר ווילט שאפֿן/רעדאקטירן דעם בלאט.",
        "userpage-userdoesnotexist-view": "באניצער קאנטע \"$1\" איז נישט איינגעשריבן.",
        "blocked-notice-logextract": "דער באַניצער איז דערווייַל פֿאַרשפאַרט.\nדי לעצטע בלאָקירן לאג אַקציע איז צוגעשטעלט אונטן:",
-       "clearyourcache": "'''אַכטונג:''' נאכן אויפֿהיטן, ברויכט איר אפשר נאך אַריבערגיין דעם בלעטערערס זאַפאַס צו זען די ענדערונגען.\n\n* '''פֿייערפוקס/סאפֿארי:''' האלט אראפ ''שיפֿט'' בשעתן דרוקן ''Reload'', אדער דרוקט ''Ctrl-F5'' אדער ''Ctrl-R'' (אויף א מאקינטאש ''⌘-R'')\n\n* '''גוגל כראם:''' דרוקט ''Ctrl-Shift-R'' (אויף א מאקינטאש ''⌘-Shift-R'')\n\n* '''אינטערנעט עקספלארער:''' האלט אראפ ''Ctrl'' בשעתן קליקן ''Refresh'', אדער  דרוקט ''Ctrl-F5''\n\n* '''אפערע:''' ליידיגט אויס דעם זאַפאַס אין ''Tools → Preferences'' (''העדפות'' > ''כלים'')",
+       "clearyourcache": "'''אַכטונג:''' נאכן אויפֿהיטן, ברויכט איר אפשר נאך אַריבערגיין דעם בלעטערערס זאַפאַס צו זען די ענדערונגען.\n\n* '''פֿייערפוקס/סאפֿארי:''' האלט אראפ ''שיפֿט'' בשעתן דרוקן ''Reload'', אדער דרוקט ''Ctrl-F5'' אדער ''Ctrl-R'' (אויף א מאקינטאש ''⌘-R'')\n\n* '''גוגל כראם:''' דרוקט ''Ctrl-Shift-R'' (אויף א מאקינטאש ''⌘-Shift-R'')\n\n* '''אינטערנעט עקספלארער:''' האלט אראפ ''Ctrl'' בשעתן קליקן ''Refresh'', אדער  דרוקט ''Ctrl-F5''\n\n* <strong>אפערע:</strong> גייט צו <em>מעניו → שטעלונגען</em> (<em> אפערע → פרעפערנצן</em> אויף א מעק) און דערנאך צו <em>פריוואטקייט & און זיכערהייט → אראפראמען בלעטערן דאטן → בילדער און טעקעס אין זאפאס</em>",
        "usercssyoucanpreview": "'''טיפ:''' נוצט דאס {{int:showpreview}} קנעפל אויספרובירן אייער CSS בעפארן אויפהיטן.",
        "userjsyoucanpreview": "'''טיפ:''' נוצט דאס {{int:showpreview}} קנעפל אויספרובירן אייער  JavaScript בעפארן אויפהיטן.",
        "usercsspreview": "'''געדענקט אז איר טוט בלויז פאראויס זען אייער באניצער CSS.'''\n'''ער איז דערווייל נאכנישט אויפֿגעהיטן!'''",
        "revdelete-unsuppress": "טוה אפ באגרענעצונגן אין גענדערטע רעוויזיעס",
        "revdelete-log": "אורזאַך:",
        "revdelete-submit": "צושטעלן צו {{PLURAL:$1|סעלעקטירטער רעוויזיע| סעלעקטירטע רעוויזיעס}}",
-       "revdelete-success": "'''רע×\95×\95×\99×\96×\99×¢ ×\96×¢×\91×\90ַרק×\99×\99×\98 ×\93ערפֿ×\90×\9c×\92ר×\99×\99×\9a ×\93ער×\94ײַנ×\98×\99ק×\98.'''",
+       "revdelete-success": "'''רעוויזיע זעבאַרקייט דערהײַנטיקט.'''",
        "revdelete-failure": "'''נישט מעגלעך צו דערהײַנטיקן רעוויזיע זעבאַרקייט:'''\n$1",
-       "logdelete-success": "'''לאג באהאלטן איז סוקסעספול איינגעשטעלט.'''",
+       "logdelete-success": "'''לאגבוך זעבארקייט איינגעשטעלט.'''",
        "logdelete-failure": "'''נישט מעגלעך צו שטעלן לאג זעבאַרקייט:'''\n$1",
        "revdel-restore": "טויש די זעבארקייט",
        "pagehist": "בלאט היסטאריע",
        "userrights-changeable-col": "גרופעס איר קענט ענדערן",
        "userrights-unchangeable-col": "גרופעס איר קענט נישט ענדערן",
        "userrights-conflict": "קאנפֿליקט פון באניצער־רעכטן ענדערונגען! זייט אזוי גוט רעצענזירן און באשטעטיקן אײַערע ענדערונגען.",
-       "userrights-removed-self": "×\90×\99ר ×\94×\90×\98 ×\93ערפ×\90×\9c×\92ר×\99×\99×\9a ×\90ר×\90פ×\92×¢× ×\95×\9e×¢×\9f ×\90×\99×\99ערע ×\90×\99×\99×\92×¢× ×¢ ×¨×¢×\9b×\98×¢. אזוי קענט איר מער נישט דערגרייכן דעם בלאט.",
+       "userrights-removed-self": "×\90×\99ר ×\94×\90×\98 ×\90ר×\90פ×\92×¢× ×\95×\9e×¢×\9f ×\90×\99×\99ערע ×\90×\99×\99×\92×¢× ×¢ ×¨×¢×\9b×\98×\9f. אזוי קענט איר מער נישט דערגרייכן דעם בלאט.",
        "group": "גרופע:",
        "group-user": "באניצערס",
        "group-autoconfirmed": "באַשטעטיקטע באַניצער",
        "grant-group-file-interaction": "אינטעראגירן מיט מעדיע",
        "grant-group-email": "שיקן ע־פאסט",
        "grant-createaccount": "שאַפֿן קאנטעס",
+       "grant-uploadfile": "אַרויפֿלאָדן נייע טעקעס",
+       "grant-basic": "בעיסיק רעכטן",
+       "grant-viewmywatchlist": "קוקט אייער אויפפאסונג ליסטע",
        "newuserlogpage": "נייע באַניצערס לאָג-בוך",
        "newuserlogpagetext": "דאס איז א לאג פון באַניצערס אײַנשרײַבונגען.",
        "rightslog": "באַניצער רעכטן לאג",
        "imagelinks": "טעקע באַניץ",
        "linkstoimage": "{{PLURAL:$1|דער פאלגנדער בלאט ניצט|די פאלגנדע בלעטער ניצן}} דאס דאזיגע בילד:",
        "linkstoimage-more": "מער ווי $1 {{PLURAL:$1|בלאַט פֿאַרבינדט|בלעטער פֿאַרבינדן}} צו דער דאזיגער טעקע.\nדי פֿאלגנדע ליסטע ווײַזט  {{PLURAL:$1|דעם ערשטן בלאַט לינק|די ערשטע $1 בלאַט לינקען}} צו דער טעקע.\nס'איז פֿאַראַן[[Special:WhatLinksHere/$2|פֿולע רשימה]].",
-       "nolinkstoimage": "× ×\99ש×\98×\90 ×§×\99×\99×\9f ×\91×\9c×¢×\98ער ×\95×\95×\90ס × ×\99צ×\9f ×\93×\90ס ×\93×\90×\96×\99×\92×¢ ×\91×\99×\9c×\93.",
+       "nolinkstoimage": "× ×\99ש×\98×\90 ×§×\99×\99×\9f ×\91×\9c×¢×\98ער ×\95×\95×\90ס ×¤×\90ר×\91×\99× ×\93×\9f ×¦×\95 ×\93×\99 ×\98עקע.",
        "morelinkstoimage": "באַקוקן  [[Special:WhatLinksHere/$1|מער לינקען]] צו דער טעקע.",
        "linkstoimage-redirect": "$1 (טעקע ווײַטערפֿירונג) $2",
        "duplicatesoffile": "די פֿאלגנדע {{PLURAL:$1|טעקע דופליקירט|$1 טעקעס דופליקירן}} די דאזיגע טעקע ([[Special:FileDuplicateSearch/$2|נאך פרטים]]):",
        "unusedtemplates": "נישט באניצטע מוסטערן",
        "unusedtemplatestext": "דער בלאט ווײַזט אלע בלעטער אינעם {{ns:template}} נאמענטייל וואס זענען נישט אײַנגעשלאסן אין אן אנדער בלאט. געדענקט צו באקוקן אנדערע בלעטער פאר לינקען צו די מוסטערן איידער איר מעקט זיי אויס.",
        "unusedtemplateswlh": "אנדערע פֿאַרבינדונגען",
-       "randompage": "צ×\95פֿע×\9c×\99×\92ער ×\90ַר×\98×\99ק×\9c",
+       "randompage": "צ×\95פֿע×\9c×\99×\92ער ×\91×\9c×\90×\98",
        "randompage-nopages": "נישטא קיין בלעטער אין {{PLURAL:$2|דעם פאלגנדן נאמענטייל |די פאלגנדע נאמענטיילן}} \"$1\".",
        "randomincategory": "צופעליקער בלאט אין קאטעגאריע",
        "randomincategory-invalidcategory": "\"$1\" איז נישט קיין גילטיקער קאטעגאריע נאמען.",
        "querypage-disabled": "דער באַזונדער־בלאַט איז אומאַקטיווירט צוליב אויספֿירונג סיבות.",
        "apihelp": "API־הילף",
        "apihelp-no-such-module": "מאָדול \"$1\" נישט געפונען.",
+       "apisandbox-unfullscreen": "ווייזן בלאט",
        "apisandbox-reset": "רייניקן",
+       "apisandbox-retry": "פרובירן נאכאמאל",
+       "apisandbox-helpurls": "הילף לינקען",
+       "apisandbox-examples": "ביישפילן",
        "apisandbox-results": "רעזולטאטן",
        "booksources": "דרויסנדיגע ליטעראַטור ISBN",
        "booksources-search-legend": "זוכן פאר דרויסנדע ביכער מקורות",
        "logempty": "נישטא קיין פאַסנדיקע זאכן אין לאג.",
        "log-title-wildcard": "זוכן טיטלען וואס הייבן אָן מיט דעם טעקסט",
        "showhideselectedlogentries": "ווײַזן/באַהאַלטן געקליבענע לאגבוך אקציעס",
+       "checkbox-all": "אַלע",
+       "checkbox-none": "קיינע",
+       "checkbox-invert": "אומקערן",
        "allpages": "אַלע בלעטער",
        "nextpage": "קומענדיקער בלאַט ($1)",
        "prevpage": "פֿריִערדיקער בלאַט ($1)",
        "listgrouprights-namespaceprotection-header": "נאמענטייל באשרענקונגען",
        "listgrouprights-namespaceprotection-namespace": "נאָמענטייל",
        "listgrouprights-namespaceprotection-restrictedto": "רעכט(ן) וואס דערלויבט באניצער צו רעדאקטירן",
+       "listgrants-grant": "צולאזן",
+       "listgrants-rights": "רעכטן",
        "trackingcategories": "אויפפאסן־קאטעגאריעס",
        "trackingcategories-msg": "אויפפאסן־קאטעגאריע",
        "trackingcategories-name": "מעלדונג נאמען",
        "wlnote": "אונטן {{PLURAL:$1|איז די לעצטע ענדערונג|זענען די לעצטע <strong>$1</strong> ענדערונגען}} אין {{PLURAL:$2|דער לעצטער שעה|די לעצטע <strong>$2</strong> שעה'ן}} ביז $3, $4.",
        "wlshowlast": "ווײַזן די לעצטע $1 שעה'ן  $2 טעג",
        "watchlist-hide": "באַהאַלטן",
+       "watchlist-submit": "ווײַזן",
        "wlshowtime": "צייט־פעריאד צו ווייזן:",
        "wlshowhideminor": "מינערדיקער רעדאקטירונגען",
        "wlshowhidebots": "באטן",
        "wlshowhideanons": "אַנאָנימע באַניצער",
        "wlshowhidepatr": "פאטראלירטע רעדאקטירונגען",
        "wlshowhidemine": "מיינע רעקאקטירונגען",
+       "wlshowhidecategorization": "בלאט קאטעגאריזירונג",
        "watchlist-options": "אויפֿפאַסן ליסטע ברירות",
        "watching": "אויפפאסענדונג…",
        "unwatching": "נעמט אראפ פון אויפפאסונג ליסטע…",
        "delete-toobig": "דער בלאַט האט א גרויסע רעדאקטירונג היסטאריע, מער ווי $1 {{PLURAL:$1|רעוויזיע|רעוויזיעס}}. אויסמעקן אזעלכע בלעטער איז באַגרענעצט געווארן בכדי צו פֿאַרמײַדן א צופֿעליגע פֿאַרשטערונג פֿון  {{SITENAME}}.",
        "delete-warning-toobig": "דער בלאַט האט א גרויסע רעדאקטירונג היסטאריע, מער ווי $1 {{PLURAL:$1|רעוויזיע|רעוויזיעס}}. אויסמעקן אים קען פֿאַרשטערן דאַטנבאַזע אפעראַציעס פֿון {{SITENAME}}; זײַט פֿארזיכטיג איידער איר מעקט אויס.",
        "deleteprotected": "איר קענט נישט אויסמעקן דעם בלאט ווײַל ער איז געשיצט.",
-       "deleting-backlinks-warning": "'''ווארענוג:'''\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|אנדערע בלעטער]]  פארבינדן צום בלאט אדער אריבערשליסן דעם בלאט איר האלט ביי אויסמעקן.",
+       "deleting-backlinks-warning": "</strong>ווארענוג:<strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|אנדערע בלעטער]]  פארבינדן צום בלאט אדער אריבערשליסן דעם בלאט איר האלט ביי אויסמעקן.",
        "rollback": "צוריקדרייען רעדאַקטירונגען",
        "rollbacklink": "צוריקדרייען",
        "rollbacklinkcount": "צוריקדרייען $1 {{PLURAL:$1|רעדאקטירונג|רעדאקטירונגען}}",
        "rollbackfailed": "צוריקדרייען דורכגעפֿאַלן",
        "cantrollback": "מען קען נישט צוריקדרייען די ענדערונג – דער לעצטער בײַשטייערער איז דער איינציגסטער שרײַבער אין דעם בלאַט.",
        "alreadyrolled": "מען קען נישט צוריקדרייען די לעצטע ענדערונג פון בלאט [[:$1]] פֿון\n[[User:$2|$2]] ([[User talk:$2|רעדן]]{{int:pipe-separator}} [[Special:Contributions/$2|{{int:contribslink}}]]);\nאן אנדערער האט שוין געענדערט אדער צוריקגעדרייט דעם בלאט.\n\nדי לעצטע ענדערונג צום בלאַט איז געווען פון [[User:$3|$3]] ([[User talk:$3|רעדן]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
-       "editcomment": "קורץ ווארט איז געווען: \"'''$1'''\".",
+       "editcomment": "קורץ ווארט איז געווען: <em>$1</em>.",
        "revertpage": "רעדאַקטירונגען פֿון  [[Special:Contributions/$2|$2]] צוריקגענומען ([[User talk:$2|רעדן]])  צו דער לעצטער ווערסיע פֿון [[User:$1|$1]]",
        "revertpage-nouser": "צוריקגעשטעלט רעדאַקטירונגען פֿון א באהאלטענעם באַניצער צו לעצטער רעוויזיע פֿון {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "צוריקגעדרייט רעדאַקטירונגען פֿון $1 צו דער לעצטע ווערסיע פֿון $2",
        "changecontentmodel-title-label": "בלאט־טיטל",
        "changecontentmodel-model-label": "נייער אינהאלט מאדעל",
        "changecontentmodel-reason-label": "אורזאַך:",
+       "changecontentmodel-submit": "טוישן",
        "logentry-contentmodel-change-revertlink": "צוריקשטעלן",
        "logentry-contentmodel-change-revert": "צוריקשטעלן",
        "protectlogpage": "באשיצונג לאָג-בוך",
        "ipb-unblock": "אויפֿבלאקירן א באַניצער נאמען אדער IP אדרעס",
        "ipb-blocklist": "זעט עקזיסטירנדע בלאקירונגען",
        "ipb-blocklist-contribs": "בײַשטײַערונגען פֿון {{GENDER:$1|$1}}",
+       "ipb-blocklist-duration-left": "נאך $1",
        "unblockip": "אויפֿבלאקירן באניצער",
        "unblockiptext": "מיט דעם פארמולאר קענט איר צוריקשטעלן שרייבן ערלויבניש צו אן IP אדרעס אדער באניצער נאמען וואס איז געווען בלאקירט.",
        "ipusubmit": "אוועקנעמען דעם בלאק",
        "blocklink": "ארויסטרייבן",
        "unblocklink": "באַפֿרײַען",
        "change-blocklink": "ענדערן בלאק",
-       "contribslink": "×\91×\90Ö·× ×\99צערס ×\91ײַש×\98ײַער×\95× ×\92×¢×\9f",
+       "contribslink": "בײַשטײַערונגען",
        "emaillink": "שיקן ע־פאסט",
        "autoblocker": "אויטאמאטיש בלאקירט ווייל אײַער IP אדרעס איז לעצטנס געניצט געווארן דורך [[User:$1|$1]]. \nדער סיבה וואס איז אנגעבען געווארן איז: \"$2\".",
        "blocklogpage": "בלאקירן לאג",
        "import-nonewrevisions": "קיין רעוויזיעס נישט אימפארטירט (אדער אלע שוין דא, אדער איבערגעהיפט צוליב גרײַזן).",
        "xml-error-string": "$1 בײַ שורה $2, זייל $3 (בייט $4): $5",
        "import-upload": "אַרויפֿלאָדן XML דאַטן",
-       "import-token-mismatch": "×\90ָנ×\95×\95ער ×¤×\95×\9f ×¡×¢×¡×\99×¢ ×\93×\90Ö·×\98×\9f.\n ×\91×\99×\98×¢ ×¤×¨×\95×\91×\99ר×\98 × ×\90×\9b×\90×\9e×\90×\9c.",
+       "import-token-mismatch": "פ×\90ר×\9c×\95ס×\98 ×¤×\95×\9f ×¡×¢×¡×\99×¢ ×\93×\90×\98×\9f. \n\nקע×\9f ×\96×\99×\99×\9f ×\90×\96 ×\90×\99ר ×\96×¢× ×¢×\9f ×\92×¢×\95×\95×\90ר×\9f ×\90ר×\95×\99ס×\9c×\90×\92×\99ר×\98\n<strong>×\91×\99×\98×¢ ×¤×¨×\95×\91×\99ר×\98 × ×\90×\9b×\90×\9e×\90×\9c</strong>. \n\n×\90×\95×\99×\91 ×¡'×\90ר×\91×¢×\98 × ×\90×\9a ×\90×\9cס × ×\99×\98, ×¤×¨×\95×\91×\99ר×\98 [[Special:UserLogout|×\90ר×\95×\99ס×\9c×\90×\92×\99ר×\9f]] ×\90×\95×\9f ×\96×\99×\9a ×¦×\95ר×\99ק ×\90ר×\99×\99× ×\9c×\90×\92×\99ר×\9f.",
        "import-invalid-interwiki": "נישט מעגלעך צו אימפארטירן פון ספעציפֿירטער וויקי.",
        "import-error-edit": "דעם בלאט \"$1\" קען מען נישט אימפארטירן ווייל איר האט נישט די רעכט אים צו רעדאקטירן.",
        "import-error-create": "דעם  בלאט \"$1\" האט מען נישט אימפארטירט ווייל איר האט נישט די רעכט צו שאפן אים.",
        "tooltip-pt-mycontris": "ליסטע פון {{GENDER:|אייערע}} ביישטייערונגען",
        "tooltip-pt-login": "עס איז רעקאָמענדירט זיך אײַנשרײַבן; ס'איז אבער נישט קיין פֿליכט",
        "tooltip-pt-logout": "ארויסלאגירן",
-       "tooltip-pt-createaccount": "איר ווערט דערמוטיגט צו שאפן א קאנטע און אריינלאגירן; ס'איז אביר נישט אבליגאטאריש",
+       "tooltip-pt-createaccount": "איר ווערט דערמוטיגט צו שאַפֿן א קאנטע און אריינלאגירן; ס׳איז אבער נישט פארפליכטעט",
        "tooltip-ca-talk": "שמועס וועגן דעם אינהאַלט בלאַט",
-       "tooltip-ca-edit": "רעדאקטירן דעם בלאַט",
+       "tooltip-ca-edit": "רעדאַקטירן דעם בלאַט",
        "tooltip-ca-addsection": "אָנהייבן א נײַע אָפטיילונג",
        "tooltip-ca-viewsource": "דאס איז א פֿארשלאסענער בלאט, איר קענט נאר באַקוקן זיין מקור",
        "tooltip-ca-history": "פריערדיגע ווערסיעס פון דעם בלאט.",
        "tooltip-ca-move": "באַוועגן דעם בלאַט",
        "tooltip-ca-watch": "לייגט צו דעם בלאט אויפצופאסן",
        "tooltip-ca-unwatch": "נעמט אַראָפּ דעם בלאַט פון נאָכפאָלג־ליסטע",
-       "tooltip-search": "×\96×\95×\9b×\98 ×\90×\99× ×¢×\9d ×¡×\99×\99×\98",
+       "tooltip-search": "×\96×\95×\9a {{SITENAME}}",
        "tooltip-search-go": "גייט צו א בלאט מיט אט דעם נאמען, אויב ער עקסיסטירט",
        "tooltip-search-fulltext": "זוכט דעם טעקסט אין די בלעטער",
-       "tooltip-p-logo": "×\94×\95×\99פ×\98 ×\96×\99×\99ט",
+       "tooltip-p-logo": "×\91×\90Ö·×\96×\95×\9b×\9f ×\93×¢×\9d ×\94×\95×\99פ×\98 ×\91×\9c×\90Ö·ט",
        "tooltip-n-mainpage": "באַזוכט דעם הויפּט־זײַט",
        "tooltip-n-mainpage-description": "באַזוכן דעם הויפט בלאַט",
        "tooltip-n-portal": "גייט אריין אין די געמיינדע צו שמועסן",
        "tooltip-n-currentevents": "מער אינפארמאציע איבער אקטועלע געשענישען",
-       "tooltip-n-recentchanges": "ליסטע פון לעצטע ענדערונגען",
+       "tooltip-n-recentchanges": "ליסטע פון לעצטע ענדערונגען אין דעם וויקי",
        "tooltip-n-randompage": "וועלט אויס א צופעליגער בלאט",
-       "tooltip-n-help": "×\94×\99×\9c×£",
-       "tooltip-t-whatlinkshere": "×\90×\9c×¢ ×\91×\9c×¢×\98ער ×\95×\95×\90ס ×¤×\90ר×\91×\99× ×\93×¢×\9f ×¦×\95 ×\93×¢×\9d ×\91×\9c×\90×\98",
+       "tooltip-n-help": "×\93ער ×¤×\9c×\90×¥ ×\90×\95×\99סצ×\95×\92עפ×\99× ×¢×\9f",
+       "tooltip-t-whatlinkshere": "×\90×\9c×¢ ×\9c×\99ס×\98×¢ ×¤×\95×\9f ×\90×\9c×¢ ×\91×\9c×¢×\98ער ×\95×\95×\90ס ×¤×\90ר×\91×\99× ×\93×\98 ×\90×\94ער",
        "tooltip-t-recentchangeslinked": "אלע ענדערונגען פון בלעטער וואס זענען אהער פארבינדען",
        "tooltip-feed-rss": "דערהײַנטיגט אויטאמאטיש פון אר.עס.עס. RSS",
        "tooltip-feed-atom": "לייג צו אן אטאמאטישער אפדעיט דורך אטאם Atom",
        "lastmodifiedatby": "די לעצטע ענדערונג פֿון דעם בלאַט איז געווען $2, $1 דורך $3.",
        "othercontribs": "באזירט אויף ארבעט פון $1.",
        "others": "אנדערע",
-       "siteusers": "{{PLURAL:$2|באַניצער| באַניצערס}} {{SITENAME}} $1",
+       "siteusers": "{{SITENAME}} {{PLURAL:$2|{{GENDER:$1| באַניצער}}| באַניצערס}} $1",
        "anonusers": "{{SITENAME}} {{PLURAL:$2| אַנאנימער באַניצער|אַנאנימע באַניצער}} $1",
        "creditspage": "בלאט קרעדיטס",
        "nocredits": "נישט פאראן קיין אינפארמאציע פאר דעם בלאט.",
        "scarytranscludedisabled": "[אינטערוויקי אריבערשליסן איז אַנולירט]",
        "scarytranscludetoolong": "[URL צו לאנג]",
        "deletedwhileediting": "ווארענונג: דער בלאט איז געווארן אויסגעמעקט נאכדעם וואס איר האט אנגעהויבן רעדאקטירן!",
-       "confirmrecreate": "באַניצער [[User:$1|$1]] ([[User talk:$1|רעדן]]) האט אויסגעמעקט דעם בלאט נאכדעם וואס איר האט אנגעהויבן דאס צו ענדערן, אלס אָנגעבליכער סיבה:\n:'''$2'''\nביטע באשטעטיגט אז איר ווילט טאקע צוריקשטעלן דעם בלאט.",
+       "confirmrecreate": "באַניצער [[User:$1|$1]] ([[User talk:$1|רעדן]]) {{GENDER:$1|האט אויסגעמעקט}} דעם בלאט נאכדעם וואס איר האט אנגעהויבן דאס צו ענדערן, אלס אָנגעבליכער סיבה:\n: <em>$2</em>\nביטע באשטעטיגט אז איר ווילט טאקע צוריקשטעלן דעם בלאט.",
        "recreate": "שאַפֿן פֿונדאסניי",
        "confirm_purge_button": "אויספֿירן",
        "confirm-purge-top": "אויסקלארן די קאשעי פון דעם בלאט?",
        "fileduplicatesearch-submit": "זוכן",
        "fileduplicatesearch-info": "$1 × $2 פיקסעל<br />טעקע גרייס: $3<br /> טיפ MIME: $4",
        "fileduplicatesearch-noresults": "קיין טעקע מיטן נאמען \"$1\" נישט געטראפֿן.",
-       "specialpages": "ספּעציעלע זײַטן",
+       "specialpages": "ספעציעלע בלעטער",
        "specialpages-note-top": "לעגענדע",
        "specialpages-note": "* נארמאַלע באַזונדערע בלעטער.\n* <span class=\"mw-specialpagerestricted\">באַגרענעצטע באַזונדערע בלעטער.</span>",
        "specialpages-group-maintenance": "אויפֿהאַלטונג באַריכטן",
        "logentry-newusers-create2": "באניצער קאנטע $1 איז {{GENDER:$2|געשאפן געווארן}} דורך $3",
        "logentry-newusers-byemail": "באניצער קאנטע $3 איז {{GENDER:$2|געשאפן געווארן}} דורך $1 און דאס פאסווארט איז געשיקט געווארט דורך ע־פאסט",
        "logentry-newusers-autocreate": "באַניצער קאנטע $1 {{GENDER:$2|געשאפן}} אויטאמאטיש",
-       "logentry-rights-rights": "$1 האָט {{GENDER:$2|געביטן}} גרופּע מיטגלידערשאַפט פאַר $3 פון $4 אויף $5",
+       "logentry-rights-rights": "$1 האָט {{GENDER:$2|געביטן}} גרופּע מיטגלידערשאַפט פאַר {{GENDER:$6|$3}} פון $4 אויף $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|האט געביטן}} גרופע מיטגלידערשאפט פאר $3",
        "logentry-rights-autopromote": "$1 אויטאמאטיש  {{GENDER:$2|פראמאווירט}} פון $4 צו $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|האט ארויפגעלאדן}} $3",
        "feedback-subject": "טעמע:",
        "feedback-submit": "אײַנגעבן",
        "feedback-thanks": "ייש\"כ! אײַער פֿידבעק איז געווארן ארויפגעלעגט צום בלאט \"[$2 $1]\".",
-       "searchsuggest-search": "×\96×\95×\9b×\9f",
+       "searchsuggest-search": "×\96×\95×\9a",
        "searchsuggest-containing": "כולל…",
        "api-error-badaccess-groups": "איר האט נישט קיין רעכטן אַרויפֿלאָדן טעקעס אויף דער וויקי.",
        "api-error-badtoken": "אינערלעכער גרײַז: סימן טויג נישט.",
index cde58b0..f261a47 100644 (file)
        "changecontentmodel-success-text": "[[:$1]]的内容类型被更改。",
        "changecontentmodel-cannot-convert": "[[:$1]]上的内容不能转换为$2的一个类型。",
        "changecontentmodel-nodirectediting": "$1内容模型不支持直接编辑",
+       "changecontentmodel-emptymodels-title": "没有内容模型可用",
+       "changecontentmodel-emptymodels-text": "[[:$1]]上的内容不能转换为任何类型。",
        "log-name-contentmodel": "内容模型更改日志",
        "log-description-contentmodel": "与一个页面的内容模型相关的活动",
        "logentry-contentmodel-new": "$1已使用非默认的内容模型“$5”{{GENDER:$2|创建}}页面$3",
        "whatlinkshere-prev": "{{PLURAL:$1|前|前$1个}}",
        "whatlinkshere-next": "{{PLURAL:$1|后|后$1个}}",
        "whatlinkshere-links": "←链接",
-       "whatlinkshere-hideredirs": "$1重定向",
-       "whatlinkshere-hidetrans": "$1嵌入",
-       "whatlinkshere-hidelinks": "$1链接",
-       "whatlinkshere-hideimages": "$1文件链接",
+       "whatlinkshere-hideredirs": "隐藏重定向",
+       "whatlinkshere-hidetrans": "隐藏嵌入",
+       "whatlinkshere-hidelinks": "隐藏链接",
+       "whatlinkshere-hideimages": "隐藏文件链接",
        "whatlinkshere-filters": "过滤器",
        "whatlinkshere-submit": "提交",
        "autoblockid": "自动封禁#$1",
        "lockdbsuccesstext": "数据库已锁定。<br />请记住在维护工作完成后[[Special:UnlockDB|解锁数据库]]。",
        "unlockdbsuccesstext": "数据库已解锁。",
        "lockfilenotwritable": "数据库锁定文件不可写。要锁定和解锁数据库,该文件必须对网络服务器可写。",
+       "databaselocked": "数据库已被锁定。",
        "databasenotlocked": "数据库未被锁定。",
        "lockedbyandtime": "(由 {{GENDER:$1|$1}} 于$2 $3执行)",
        "move-page": "移动$1",
index 55b554f..2ec731c 100644 (file)
@@ -70,7 +70,8 @@
                        "Bowleerin",
                        "飞舞回堂前",
                        "Bbslam",
-                       "Zerng07"
+                       "Zerng07",
+                       "Reke"
                ]
        },
        "tog-underline": "底線標示連結:",
        "minoredit": "這是一個小修訂",
        "watchthis": "監視此頁面",
        "savearticle": "儲存頁面",
+       "publishpage": "發布頁面",
        "preview": "預覽",
        "showpreview": "顯示預覽",
        "showdiff": "顯示變更",
        "changecontentmodel-success-text": "已變更 [[:$1]] 的內容類型。",
        "changecontentmodel-cannot-convert": "[[:$1]] 的內容無法轉換為 $2 類型。",
        "changecontentmodel-nodirectediting": "$1 的內容模型不支援直接編輯",
+       "changecontentmodel-emptymodels-title": "沒有內容模型可用",
+       "changecontentmodel-emptymodels-text": "[[:$1]]上的內容不能轉換為任何類型。",
        "log-name-contentmodel": "內容模型變更日誌",
        "log-description-contentmodel": "與頁面內容模型相關的事件",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|已使用}}非預設的內容模型 \"$5\" 建立頁面 $3",
        "lockdbsuccesstext": "已鎖定資料庫。<br />\n請記得在維護完成後 [[Special:UnlockDB|解除鎖定]] 資料庫。",
        "unlockdbsuccesstext": "已解除鎖定資料庫。",
        "lockfilenotwritable": "沒有權限寫入資料庫鎖定檔案。\n網頁伺服器需要該檔案的寫入權限以鎖定和解除鎖定資料庫。",
+       "databaselocked": "資料庫已被鎖定。",
        "databasenotlocked": "資料庫尚未鎖定。",
        "lockedbyandtime": "(由 {{GENDER:$1|$1}} 於 $2 的 $3)",
        "move-page": "移動 $1",
        "tooltip-ca-nstab-category": "檢視分類頁面",
        "tooltip-minoredit": "標記為小修訂",
        "tooltip-save": "儲存您的變更",
+       "tooltip-publish": "發佈您的更改",
        "tooltip-preview": "請在儲存前預覽您的變更!",
        "tooltip-diff": "顯示您對內容所做的變更",
        "tooltip-compareselectedversions": "查閱此頁面兩個已選擇的修訂間的差異",
        "invalidateemail": "取消電子郵件確認",
        "notificationemail_subject_changed": "{{SITENAME}} 註冊的電子郵件位址已變更",
        "notificationemail_subject_removed": "{{SITENAME}} 註冊的電子郵件位址已移除",
+       "notificationemail_body_changed": "來自IP位址$1的某個人(可能是您),在{{SITENAME}}上將帳號\"$2\"的電子郵件位址改成\"$3\"。\n\n如果非您本人所為,請立即跟網站管理員聯繫。",
+       "notificationemail_body_removed": "來自IP位址$1的某人(可能是您),在{{SITENAME}}上移除了帳號$2的電子郵件位址。\n\n如果非您本人所為,請立即跟網站管理員聯繫。",
        "scarytranscludedisabled": "[Interwiki 轉換代碼不可用]",
        "scarytranscludefailed": "[模板 $1 讀取失敗]",
        "scarytranscludefailed-httpstatus": "[模板 $1 讀取失敗:HTTP $2]",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|已保護}} $3 $4 [連鎖]",
        "logentry-protect-modify": "$1 {{GENDER:$2|已更改}} $3 的保護層級 $4",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|已更改}} $3 的保護層級 $4 [連鎖]",
-       "logentry-rights-rights": "$1 {{GENDER:$2|已更改}} $3 的群組成員資格由 $4 成為 $5",
+       "logentry-rights-rights": "$1 {{GENDER:$2|已更改}} {{GENDER:$6|$3}} 的群組成員資格由 $4 成為 $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|已更改}} $3 的群組成員資格",
        "logentry-rights-autopromote": "$1 已自動{{GENDER:$2|提升}}從 $4 成為 $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|已上傳}} $3",
        "feedback-useragent": "使用者代理:",
        "searchsuggest-search": "搜尋",
        "searchsuggest-containing": "包含...",
+       "api-error-autoblocked": "您的IP位址已經被自動封禁,因為它曾經被一名已封禁的使用者使用過。",
        "api-error-badaccess-groups": "您沒有權限在此 Wiki 上傳檔案。",
        "api-error-badtoken": "內部錯誤:密鑰錯誤。",
+       "api-error-blocked": "您已被封禁,無法編輯。",
        "api-error-copyuploaddisabled": "此伺服器已停用使用 URL 上傳檔案的功能。",
        "api-error-duplicate": "在網站上已有相同內容的{{PLURAL:$1|其他檔案|其他檔案}}。",
        "api-error-duplicate-archive": "在網站上曾有相同內容的{{PLURAL:$1|其他檔案|其他檔案}},但已被刪除。",
        "api-error-nomodule": "內部錯誤:缺少上傳模組集。",
        "api-error-ok-but-empty": "內部錯誤:伺服器沒有回應。",
        "api-error-overwrite": "不允許覆蓋已存在的檔案。",
+       "api-error-ratelimited": "您正在嘗試在比本wiki所允許時間更短的時間內,上傳更多的檔案。請稍待幾分鐘之後再試一次。",
        "api-error-stashfailed": "內部錯誤:伺服器儲存暫存檔案失敗。",
        "api-error-publishfailed": "內部錯誤:伺服器發佈暫存檔案失敗。",
        "api-error-stasherror": "上傳檔案至儲藏庫時發生錯誤。",
        "api-error-unknownerror": "不明錯誤:\"$1\"。",
        "api-error-uploaddisabled": "此 Wiki 的上傳功能已停用。",
        "api-error-verification-error": "此檔案可能已損壞,或副檔名錯誤。",
+       "api-error-was-deleted": "與此名稱相同的檔案曾被上傳過,隨後遭到刪除。",
        "duration-seconds": "$1 秒",
        "duration-minutes": "$1 分鐘",
        "duration-hours": "$1 小時",
        "special-characters-group-ipa": "國際音標",
        "special-characters-group-symbols": "符號",
        "special-characters-group-greek": "希臘文",
+       "special-characters-group-greekextended": "希臘字母擴展",
        "special-characters-group-cyrillic": "斯拉夫文",
        "special-characters-group-arabic": "阿拉伯文",
        "special-characters-group-arabicextended": "阿拉伯文擴充",
index e54c4a7..2c577ed 100644 (file)
@@ -48,13 +48,19 @@ $namespaceGenderAliases = [
 $specialPageAliases = [
        'Activeusers'               => [ 'Aktivní_uživatelé', 'Aktivni_uzivatele' ],
        'Allmessages'               => [ 'Všechna_hlášení', 'Všechny_zprávy', 'Vsechna_hlaseni', 'Vsechny_zpravy' ],
+       'AllMyUploads'              => [ 'Všechny_moje_soubory', 'Všechny_mé_soubory' ],
        'Allpages'                  => [ 'Všechny_stránky', 'Vsechny_stranky' ],
        'Ancientpages'              => [ 'Nejstarší_stránky', 'Staré_stránky', 'Stare_stranky' ],
+       'ApiHelp'                   => [ 'Nápověda_k_API' ],
+       'ApiSandbox'                => [ 'API_pískoviště' ],
+       'Badtitle'                  => [ 'Neplatný_název' ],
        'Blankpage'                 => [ 'Prázdná_stránka' ],
        'Block'                     => [ 'Blokování', 'Blokovani', 'Blokovat_uživatele', 'Blokovat_IP', 'Blokovat_uzivatele' ],
        'Booksources'               => [ 'Zdroje_knih' ],
+       'BotPasswords'              => [ 'Hesla_pro_boty' ],
        'BrokenRedirects'           => [ 'Přerušená_přesměrování', 'Prerusena_presmerovani' ],
        'Categories'                => [ 'Kategorie' ],
+       'ChangeContentModel'        => [ 'Změnit_model_obsahu_stránky' ],
        'ChangeEmail'               => [ 'Změna_emailu', 'Zmena_emailu' ],
        'ChangePassword'            => [ 'Změna_hesla', 'Zmena_hesla' ],
        'ComparePages'              => [ 'Porovnání_stránek', 'PorovnáníStránek', 'Porovnani_stranek', 'PorovnaniStranek' ],
@@ -63,7 +69,10 @@ $specialPageAliases = [
        'CreateAccount'             => [ 'Vytvořit_účet', 'Vytvorit_ucet' ],
        'Deadendpages'              => [ 'Slepé_stránky', 'Slepe_stranky' ],
        'DeletedContributions'      => [ 'Smazané_příspěvky', 'Smazane_prispevky' ],
+       'Diff'                      => [ 'Rozdíl' ],
        'DoubleRedirects'           => [ 'Dvojitá_přesměrování', 'Dvojita_presmerovani' ],
+       'EditTags'                  => [ 'Upravit_značky' ],
+       'EditWatchlist'             => [ 'Upravit_seznam_sledovaných_stránek' ],
        'Emailuser'                 => [ 'E-mail' ],
        'ExpandTemplates'           => [ 'Testy_šablon' ],
        'Export'                    => [ 'Exportovat_stránky' ],
@@ -76,7 +85,9 @@ $specialPageAliases = [
        'LinkSearch'                => [ 'Hledání_odkazů', 'Hledani_odkazu' ],
        'Listadmins'                => [ 'Seznam_správců', 'Seznam_spravcu' ],
        'Listbots'                  => [ 'Seznam_botů', 'Seznam_botu' ],
+       'ListDuplicatedFiles'       => [ 'Seznam_duplicitních_souborů' ],
        'Listfiles'                 => [ 'Seznam_souborů', 'Seznam_souboru' ],
+       'Listgrants'                => [ 'Seznam_grantů' ],
        'Listgrouprights'           => [ 'Práva_uživatelských_skupin', 'Seznam_uživatelských_práv', 'Seznam_uzivatelskych_prav' ],
        'Listredirects'             => [ 'Seznam_přesměrování', 'Seznam_presmerovani' ],
        'Listusers'                 => [ 'Uživatelé', 'Uzivatele', 'Seznam_uživatelů', 'Seznam_uzivatelu' ],
@@ -84,6 +95,7 @@ $specialPageAliases = [
        'Log'                       => [ 'Protokolovací_záznamy', 'Protokoly', 'Protokol', 'Protokolovaci_zaznamy' ],
        'Lonelypages'               => [ 'Sirotčí_stránky', 'Sirotci_stranky' ],
        'Longpages'                 => [ 'Nejdelší_stránky', 'Nejdelsi_stranky' ],
+       'MediaStatistics'           => [ 'Statistika_souborů', 'Statistiky_souborů' ],
        'MergeHistory'              => [ 'Sloučení_historie', 'Slouceni_historie', 'Sloučit_historii' ],
        'MIMEsearch'                => [ 'Hledání_podle_MIME', 'Hledani_podle_MIME', 'Hledat_podle_MIME_typu' ],
        'Mostcategories'            => [ 'Stránky_s_nejvíce_kategoriemi', 'Stranky_s_nejvice_kategoriemi', 'Stránky_s_nejvyšším_počtem_kategorií' ],
@@ -94,18 +106,27 @@ $specialPageAliases = [
        'Mostrevisions'             => [ 'Stránky_s_nejvíce_editacemi', 'Stranky_s_nejvice_editacemi', 'Stránky_s_nejvyšším_počtem_editací' ],
        'Movepage'                  => [ 'Přesunout_stránku', 'Přejmenovat_stránku' ],
        'Mycontributions'           => [ 'Mé_příspěvky', 'Me_prispevky' ],
+       'MyLanguage'                => [ 'V_mém_jazyce', 'Můj_jazyk' ],
        'Mypage'                    => [ 'Moje_stránka', 'Moje_stranka' ],
        'Mytalk'                    => [ 'Moje_diskuse' ],
+       'Myuploads'                 => [ 'Moje_soubory', 'Mé_soubory' ],
        'Newimages'                 => [ 'Nové_obrázky', 'Galerie_nových_obrázků', 'Nove_obrazky' ],
        'Newpages'                  => [ 'Nové_stránky', 'Nove_stranky', 'Nejnovější_stránky', 'Nejnovejsi_stranky' ],
        'PasswordReset'             => [ 'Reset_hesla', 'Resetovat_heslo' ],
+       'PagesWithProp'             => [ 'Stránky_s_vlastností', 'Stránky_s_vlastnostmi' ],
+       'PasswordReset'             => [ 'Reset_hesla', 'Resetovat_heslo', 'Obnova_hesla', 'Obnovit_heslo' ],
+       'PermanentLink'             => [ 'Trvalý_odkaz' ],
        'Preferences'               => [ 'Nastavení', 'Nastaveni' ],
+       'Prefixindex'               => [ 'Stránky_podle_začátku' ],
        'Protectedpages'            => [ 'Zamčené_stránky', 'Zamcene_stranky' ],
        'Protectedtitles'           => [ 'Zamčené_názvy', 'Zamcene_nazvy', 'Stránky_které_nelze_vytvořit' ],
        'Randompage'                => [ 'Náhodná_stránka', 'Nahodna_stranka' ],
        'Randomredirect'            => [ 'Náhodné_přesměrování', 'Nahodne_presmerovani' ],
+       'RandomInCategory'          => [ 'Náhodná_stránka_v_kategorii' ],
+       'Randomrootpage'            => [ 'Náhodná_kořenová_stránka' ],
        'Recentchanges'             => [ 'Poslední_změny', 'Posledni_zmeny' ],
        'Recentchangeslinked'       => [ 'Související_změny', 'Souvisejici_zmeny' ],
+       'Redirect'                  => [ 'Přesměrování', 'Přesměrovat' ],
        'Revisiondelete'            => [ 'Smazat_revizi' ],
        'Search'                    => [ 'Hledání', 'Hledani' ],
        'Shortpages'                => [ 'Nejkratší_stránky', 'Nejkratsi_stranky' ],
index 6d9a616..922cc87 100644 (file)
@@ -34,7 +34,7 @@ require_once __DIR__ . '/Maintenance.php';
  */
 class UpdateCollation extends Maintenance {
        const BATCH_SIZE = 100; // Number of rows to process in one batch
-       const SYNC_INTERVAL = 20; // Wait for slaves after this many batches
+       const SYNC_INTERVAL = 5; // Wait for slaves after this many batches
 
        public $sizeHistogram = [];
 
@@ -70,6 +70,7 @@ TEXT
                global $wgCategoryCollation;
 
                $dbw = $this->getDB( DB_MASTER );
+               $dbr = $this->getDB( DB_SLAVE );
                $force = $this->getOption( 'force' );
                $dryRun = $this->getOption( 'dry-run' );
                $verboseStats = $this->getOption( 'verbose-stats' );
@@ -97,6 +98,7 @@ TEXT
                $options = [
                        'LIMIT' => self::BATCH_SIZE,
                        'ORDER BY' => $orderBy,
+                       'STRAIGHT_JOIN' // per T58041
                ];
 
                if ( $force || $dryRun ) {
@@ -110,7 +112,7 @@ TEXT
                                ];
                        }
 
-                       $count = $dbw->estimateRowCount(
+                       $count = $dbr->estimateRowCount(
                                'categorylinks',
                                '*',
                                $collationConds,
@@ -118,7 +120,7 @@ TEXT
                        );
                        // Improve estimate if feasible
                        if ( $count < 1000000 ) {
-                               $count = $dbw->selectField(
+                               $count = $dbr->selectField(
                                        'categorylinks',
                                        'COUNT(*)',
                                        $collationConds,
@@ -131,6 +133,7 @@ TEXT
                                return;
                        }
                        $this->output( "Fixing collation for $count rows.\n" );
+                       wfWaitForSlaves();
                }
                $count = 0;
                $batchCount = 0;
index 625c02e..9a8b058 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:02Z
+ * Date: 2016-05-10T22:58:27Z
  */
 ( function ( OO ) {
 
index eeb4b28..9ec7278 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-element-hidden {
        display: none !important;
        display: none;
 }
 .oo-ui-textInputWidget [type="search"]::-webkit-search-decoration,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-cancel-button,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-results-button,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-results-decoration {
+.oo-ui-textInputWidget [type="search"]::-webkit-search-cancel-button {
        display: none;
 }
 .oo-ui-textInputWidget > .oo-ui-iconElement-icon,
index 268a680..ba293e4 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-element-hidden {
        display: none !important;
@@ -62,9 +62,6 @@
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
        margin-left: 0.46875em;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #347bff, 0 0 0 1px #347bff;
-}
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator {
        margin-right: 0;
 }
@@ -77,6 +74,9 @@
        padding-right: 0.25em;
        color: #333333;
 }
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #347bff, 0 0 0 1px #347bff;
+}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > input.oo-ui-buttonElement-button,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #555555;
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
        color: #cccccc;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button:focus {
-       box-shadow: none;
-}
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
        display: none;
 }
 .oo-ui-textInputWidget [type="search"]::-webkit-search-decoration,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-cancel-button,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-results-button,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-results-decoration {
+.oo-ui-textInputWidget [type="search"]::-webkit-search-cancel-button {
        display: none;
 }
 .oo-ui-textInputWidget > .oo-ui-iconElement-icon,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
        outline: 0;
        border-color: #347bff;
-       box-shadow: inset 0 0 0 0.1em #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
index ddfee91..cbc02eb 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:02Z
+ * Date: 2016-05-10T22:58:27Z
  */
 ( function ( OO ) {
 
@@ -2001,10 +2001,13 @@ OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
 /**
  * Set the button's active state.
  *
- * The active state occurs when a {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} or
- * a {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} is pressed. This method does nothing
- * for other button types.
+ * The active state can be set on:
  *
+ *  - {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} when it is selected
+ *  - {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} when it is toggle on
+ *  - {@link OO.ui.ButtonWidget ButtonWidget} when clicking the button would only refresh the page
+ *
+ * @protected
  * @param {boolean} value Make button active
  * @chainable
  */
@@ -2017,6 +2020,7 @@ OO.ui.mixin.ButtonElement.prototype.setActive = function ( value ) {
 /**
  * Check if the button is active
  *
+ * @protected
  * @return {boolean} The button is active
  */
 OO.ui.mixin.ButtonElement.prototype.isActive = function () {
@@ -3332,6 +3336,7 @@ OO.ui.mixin.AccessKeyedElement.prototype.getAccessKey = function () {
  *
  * @constructor
  * @param {Object} [config] Configuration options
+ * @cfg {boolean} [active=false] Whether button should be shown as active
  * @cfg {string} [href] Hyperlink to visit when the button is clicked.
  * @cfg {string} [target] The frame or window in which to open the hyperlink.
  * @cfg {boolean} [noFollow] Search engine traversal hint (default: true)
@@ -3366,6 +3371,7 @@ OO.ui.ButtonWidget = function OoUiButtonWidget( config ) {
        this.$element
                .addClass( 'oo-ui-buttonWidget' )
                .append( this.$button );
+       this.setActive( config.active );
        this.setHref( config.href );
        this.setTarget( config.target );
        this.setNoFollow( config.noFollow );
@@ -3522,6 +3528,14 @@ OO.ui.ButtonWidget.prototype.setNoFollow = function ( noFollow ) {
        return this;
 };
 
+// Override method visibility hints from ButtonElement
+/**
+ * @method setActive
+ */
+/**
+ * @method isActive
+ */
+
 /**
  * A ButtonGroupWidget groups related buttons and is used together with OO.ui.ButtonWidget and
  * its subclasses. Each button in a group is addressed by a unique reference. Buttons can be added,
index c1150d0..3a99fba 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:02Z
+ * Date: 2016-05-10T22:58:27Z
  */
 ( function ( OO ) {
 
index 957dcfc..d757813 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
index 1faf7e5..82335a4 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
index 49b9674..d976448 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:02Z
+ * Date: 2016-05-10T22:58:27Z
  */
 ( function ( OO ) {
 
index 4de8cea..7a45a25 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
index 5c48ea5..a530235 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
        border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
        outline: 0;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on {
 .oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
        width: 2.5em;
 }
-.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
        border-top-right-radius: 0;
        border-bottom-right-radius: 0;
        border-right-width: 0;
 }
-.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
        border-top-left-radius: 0;
        border-bottom-left-radius: 0;
        border-left-width: 0;
index 366aa38..e3c2bd5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:02Z
+ * Date: 2016-05-10T22:58:27Z
  */
 ( function ( OO ) {
 
index 764f40c..6dfe142 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-actionWidget.oo-ui-pendingElement-pending {
        background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
index deda1d0..9a544d6 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:06Z
+ * Date: 2016-05-10T22:58:31Z
  */
 .oo-ui-window {
        background: transparent;
index 93a870c..37b7d90 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.1
+ * OOjs UI v0.17.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-05-03T22:58:02Z
+ * Date: 2016-05-10T22:58:27Z
  */
 ( function ( OO ) {
 
index b1a63b0..4c75e33 100644 (file)
@@ -14,7 +14,7 @@
                        $table = $( '#mw_metadata' ),
                        $tbody = $table.find( 'tbody' );
 
-               if ( !$tbody.length || !$tbody.find( '.collapsable' ).length ) {
+               if ( !$tbody.find( '.collapsable' ).length ) {
                        return;
                }
 
index 58115c3..2eb84e6 100644 (file)
         * @constructor
         * @inheritdoc
         */
-       function ForeignTitle() {
-               ForeignTitle.parent.apply( this, arguments );
+       function ForeignTitle( title, namespace ) {
+               // We only need to handle categories here... but we don't know the target language.
+               // So assume that any namespace-like prefix is the 'Category' namespace...
+               title = title.replace( /^(.+?)_*:_*(.*)$/, 'Category:$2' ); // HACK
+               ForeignTitle.parent.call( this, title, namespace );
        }
        OO.inheritClass( ForeignTitle, mw.Title );
        ForeignTitle.prototype.getNamespacePrefix = function () {
old mode 100644 (file)
new mode 100755 (executable)
index 8c2b53a..df03679
         * @cfg {boolean} [performSearchOnClick=true] If true, the script will start a search when-
         *  ever a user hits a suggestion. If false, the text of the suggestion is inserted into the
         *  text field only.
+        *  @cfg {string} [dataLocation='header'] Where the search input field will be
+        *  used (header or content).
         */
        mw.widgets.SearchInputWidget = function MwWidgetsSearchInputWidget( config ) {
                config = $.extend( {
                        type: 'search',
                        icon: 'search',
                        maxLength: undefined,
-                       performSearchOnClick: true
+                       performSearchOnClick: true,
+                       dataLocation: 'header'
                }, config );
 
                // Parent constructor
index 9cef1c4..3e010d0 100644 (file)
                 * @since 1.22
                 */
                postWithToken: function ( tokenType, params, ajaxOptions ) {
-                       var api = this;
+                       var api = this,
+                               abortable;
 
-                       return api.getToken( tokenType, params.assert ).then( function ( token ) {
+                       return ( abortable = api.getToken( tokenType, params.assert ) ).then( function ( token ) {
                                params.token = token;
-                               return api.post( params, ajaxOptions ).then(
+                               return ( abortable = api.post( params, ajaxOptions ) ).then(
                                        // If no error, return to caller as-is
                                        null,
                                        // Error handler
                                                        api.badToken( tokenType );
                                                        // Try again, once
                                                        params.token = undefined;
-                                                       return api.getToken( tokenType, params.assert ).then( function ( token ) {
+                                                       return ( abortable = api.getToken( tokenType, params.assert ) ).then( function ( token ) {
                                                                params.token = token;
-                                                               return api.post( params, ajaxOptions );
+                                                               return ( abortable = api.post( params, ajaxOptions ) ).promise();
                                                        } );
                                                }
 
                                                return this;
                                        }
                                );
-                       } );
+                       } ).promise( { abort: function () {
+                               abortable.abort();
+                       } } );
                },
 
                /**
index e905f69..b02fa36 100644 (file)
                                return result === null ? null : result.join( '' );
                        }
 
-                       asciiAlphabetLiteral = makeRegexParser( /[A-Za-z]+/ );
+                       asciiAlphabetLiteral = makeRegexParser( /^[A-Za-z]+/ );
                        htmlDoubleQuoteAttributeValue = makeRegexParser( /^[^"]*/ );
                        htmlSingleQuoteAttributeValue = makeRegexParser( /^[^']*/ );
 
index 78e5f6f..405f6a5 100644 (file)
@@ -27,6 +27,7 @@
  * @file
  * @ingroup Testing
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * @ingroup Testing
@@ -210,6 +211,7 @@ class ParserTest {
                # add a namespace shadowing a interwiki link, to test
                # proper precedence when resolving links. (bug 51680)
                $wgExtraNamespaces[100] = 'MemoryAlpha';
+               $wgExtraNamespaces[101] = 'MemoryAlpha talk';
 
                // XXX: tests won't run without this (for CACHE_DB)
                if ( $wgMainCacheType === CACHE_DB ) {
@@ -331,6 +333,18 @@ class ParserTest {
                Hooks::clear( 'InterwikiLoadPrefix' );
        }
 
+       /**
+        * Reset the Title-related services that need resetting
+        * for each test
+        */
+       public static function resetTitleServices() {
+               $services = MediaWikiServices::getInstance();
+               $services->resetServiceForTesting( 'TitleFormatter' );
+               $services->resetServiceForTesting( 'TitleParser' );
+               $services->resetServiceForTesting( '_MediaWikiTitleCodec' );
+
+       }
+
        public function setupRecorder( $options ) {
                if ( isset( $options['record'] ) ) {
                        $this->recorder = new DbTestRecorder( $this );
@@ -958,6 +972,8 @@ class ParserTest {
                MWTidy::destroySingleton();
                RepoGroup::destroySingleton();
 
+               self::resetTitleServices();
+
                return $context;
        }
 
index e50b4f1..d701a81 100644 (file)
@@ -309,4 +309,118 @@ class LinkerTest extends MediaWikiLangTestCase {
                ];
                // @codingStandardsIgnoreEnd
        }
+
+       public static function provideLinkBeginHook() {
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               return [
+                       // Modify $html
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $html = 'foobar';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage">foobar</a>'
+                       ],
+                       // Modify $attribs
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $attribs['bar'] = 'baz';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage" bar="baz">Special:BlankPage</a>'
+                       ],
+                       // Modify $query
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $query['bar'] = 'baz';
+                               },
+                               '<a href="/w/index.php?title=Special:BlankPage&amp;bar=baz" title="Special:BlankPage">Special:BlankPage</a>'
+                       ],
+                       // Force HTTP $options
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $options = [ 'http' ];
+                               },
+                               '<a href="http://example.org/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>'
+                       ],
+                       // Force 'forcearticlepath' in $options
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $options = [ 'forcearticlepath' ];
+                                       $query['foo'] = 'bar';
+                               },
+                               '<a href="/wiki/Special:BlankPage?foo=bar" title="Special:BlankPage">Special:BlankPage</a>'
+                       ],
+                       // Abort early
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $ret = 'foobar';
+                                       return false;
+                               },
+                               'foobar'
+                       ],
+               ];
+               // @codingStandardsIgnoreEnd
+       }
+
+       /**
+        * @dataProvider provideLinkBeginHook
+        */
+       public function testLinkBeginHook( $callback, $expected ) {
+               $this->setMwGlobals( [
+                       'wgArticlePath' => '/wiki/$1',
+                       'wgWellFormedXml' => true,
+                       'wgServer' => '//example.org',
+                       'wgCanonicalServer' => 'http://example.org',
+                       'wgScriptPath' => '/w',
+                       'wgScript' => '/w/index.php',
+               ] );
+
+               $this->setMwGlobals( 'wgHooks', [ 'LinkBegin' => [ $callback ] ] );
+               $title = SpecialPage::getTitleFor( 'Blankpage' );
+               $out = Linker::link( $title );
+               $this->assertEquals( $expected, $out );
+       }
+
+       public static function provideLinkEndHook() {
+               return [
+                       // Override $html
+                       [
+                               function( $dummy, $title, $options, &$html, &$attribs, &$ret ) {
+                                       $html = 'foobar';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage">foobar</a>'
+                       ],
+                       // Modify $attribs
+                       [
+                               function( $dummy, $title, $options, &$html, &$attribs, &$ret ) {
+                                       $attribs['bar'] = 'baz';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage" bar="baz">Special:BlankPage</a>'
+                       ],
+                       // Fully override return value and abort hook
+                       [
+                               function( $dummy, $title, $options, &$html, &$attribs, &$ret ) {
+                                       $ret = 'blahblahblah';
+                                       return false;
+                               },
+                               'blahblahblah'
+                       ],
+
+               ];
+       }
+
+       /**
+        * @dataProvider provideLinkEndHook
+        */
+       public function testLinkEndHook( $callback, $expected ) {
+               $this->setMwGlobals( [
+                       'wgArticlePath' => '/wiki/$1',
+                       'wgWellFormedXml' => true,
+               ] );
+
+               $this->setMwGlobals( 'wgHooks', [ 'LinkEnd' => [ $callback ] ] );
+
+               $title = SpecialPage::getTitleFor( 'Blankpage' );
+               $out = Linker::link( $title );
+               $this->assertEquals( $expected, $out );
+       }
 }
index 6c38d50..51ef9d7 100644 (file)
@@ -200,6 +200,10 @@ class MediaWikiServicesTest extends PHPUnit_Framework_TestCase {
 
                // All getters should be named just like the service, with "get" added.
                foreach ( $getServiceCases as $name => $case ) {
+                       if ( $name[0] === '_' ) {
+                               // Internal service, no getter
+                               continue;
+                       }
                        list( $service, $class ) = $case;
                        $getterCases[$name] = [
                                'get' . $service,
@@ -238,6 +242,11 @@ class MediaWikiServicesTest extends PHPUnit_Framework_TestCase {
                        'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', 'LBFactory' ],
                        'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
                        'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
+                       'GenderCache' => [ 'GenderCache', GenderCache::class ],
+                       'LinkCache' => [ 'LinkCache', LinkCache::class ],
+                       '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
+                       'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
+                       'TitleParser' => [ 'TitleParser', TitleParser::class ],
                ];
        }
 
index 7d025d2..7850f24 100644 (file)
@@ -144,6 +144,13 @@ class TitleTest extends MediaWikiTestCase {
                                ]
                        ]
                ] );
+
+               // Reset TitleParser since we modified $wgLocalInterwikis
+               $this->setService( 'TitleParser', new MediaWikiTitleCodec(
+                               Language::factory( 'en' ),
+                               new GenderCache(),
+                               [ 'localtestiw' ]
+               ) );
        }
 
        /**
@@ -702,4 +709,42 @@ class TitleTest extends MediaWikiTestCase {
                $this->assertEquals( $title->getInterwiki(), $fragmentTitle->getInterwiki() );
                $this->assertEquals( $fragment, $fragmentTitle->getFragment() );
        }
+
+       public function provideGetPrefixedText() {
+               return [
+                       // ns = 0
+                       [
+                               Title::makeTitle( NS_MAIN, 'Foobar' ),
+                               'Foobar'
+                       ],
+                       // ns = 2
+                       [
+                               Title::makeTitle( NS_USER, 'Foobar' ),
+                               'User:Foobar'
+                       ],
+                       // fragment not included
+                       [
+                               Title::makeTitle( NS_MAIN, 'Foobar', 'fragment' ),
+                               'Foobar'
+                       ],
+                       // ns = -2
+                       [
+                               Title::makeTitle( NS_MEDIA, 'Foobar' ),
+                               'Media:Foobar'
+                       ],
+                       // non-existent namespace
+                       [
+                               Title::makeTitle( 100000, 'Foobar' ),
+                               ':Foobar'
+                       ],
+               ];
+       }
+
+       /**
+        * @covers Title::getPrefixedText
+        * @dataProvider provideGetPrefixedText
+        */
+       public function testGetPrefixedText( Title $title, $expected ) {
+               $this->assertEquals( $expected, $title->getPrefixedText() );
+       }
 }
index e536205..be22260 100644 (file)
@@ -13,6 +13,14 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
                parent::setUp();
                self::$users['WatchedItemIntegrationTestUser']
                        = new TestUser( 'WatchedItemIntegrationTestUser' );
+
+               $this->hideDeprecated( 'WatchedItem::fromUserTitle' );
+               $this->hideDeprecated( 'WatchedItem::addWatch' );
+               $this->hideDeprecated( 'WatchedItem::removeWatch' );
+               $this->hideDeprecated( 'WatchedItem::isWatched' );
+               $this->hideDeprecated( 'WatchedItem::resetNotificationTimestamp' );
+               $this->hideDeprecated( 'WatchedItem::duplicateEntries' );
+               $this->hideDeprecated( 'WatchedItem::batchAddWatch' );
        }
 
        private function getUser() {
@@ -20,6 +28,7 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
        }
 
        public function testWatchAndUnWatchItem() {
+
                $user = $this->getUser();
                $title = Title::newFromText( 'WatchedItemIntegrationTestPage' );
                // Cleanup after previous tests
diff --git a/tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.php b/tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.php
new file mode 100644 (file)
index 0000000..f1f9295
--- /dev/null
@@ -0,0 +1,1592 @@
+<?php
+
+use MediaWiki\Linker\LinkTarget;
+use MediaWiki\MediaWikiServices;
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ *
+ * @covers ApiQueryWatchlist
+ */
+class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
+
+       public function __construct( $name = null, array $data = [], $dataName = '' ) {
+               parent::__construct( $name, $data, $dataName );
+               $this->tablesUsed = array_unique(
+                       array_merge( $this->tablesUsed, [ 'watchlist', 'recentchanges', 'page' ] )
+               );
+       }
+
+       protected function setUp() {
+               parent::setUp();
+               self::$users['ApiQueryWatchlistIntegrationTestUser']
+                       = new TestUser( 'ApiQueryWatchlistIntegrationTestUser' );
+               self::$users['ApiQueryWatchlistIntegrationTestUser2']
+                       = new TestUser( 'ApiQueryWatchlistIntegrationTestUser2' );
+               $this->doLogin( 'ApiQueryWatchlistIntegrationTestUser' );
+       }
+
+       private function getTestUser() {
+               return self::$users['ApiQueryWatchlistIntegrationTestUser']->getUser();
+       }
+
+       private function getNonLoggedInTestUser() {
+               return self::$users['ApiQueryWatchlistIntegrationTestUser2']->getUser();
+       }
+
+       private function getSysopTestUser() {
+               return self::$users['sysop']->getUser();
+       }
+
+       private function doPageEdit( User $user, LinkTarget $target, $content, $summary ) {
+               $title = Title::newFromLinkTarget( $target );
+               $page = WikiPage::factory( $title );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $content, $title ),
+                       $summary,
+                       0,
+                       false,
+                       $user
+               );
+       }
+
+       private function doMinorPageEdit( User $user, LinkTarget $target, $content, $summary ) {
+               $title = Title::newFromLinkTarget( $target );
+               $page = WikiPage::factory( $title );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $content, $title ),
+                       $summary,
+                       EDIT_MINOR,
+                       false,
+                       $user
+               );
+       }
+
+       private function doBotPageEdit( User $user, LinkTarget $target, $content, $summary ) {
+               $title = Title::newFromLinkTarget( $target );
+               $page = WikiPage::factory( $title );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $content, $title ),
+                       $summary,
+                       EDIT_FORCE_BOT,
+                       false,
+                       $user
+               );
+       }
+
+       private function doAnonPageEdit( LinkTarget $target, $content, $summary ) {
+               $title = Title::newFromLinkTarget( $target );
+               $page = WikiPage::factory( $title );
+               $page->doEditContent(
+                       ContentHandler::makeContent( $content, $title ),
+                       $summary,
+                       0,
+                       false,
+                       User::newFromId( 0 )
+               );
+       }
+
+       private function doPatrolledPageEdit(
+               User $user,
+               LinkTarget $target,
+               $content,
+               $summary,
+               User $patrollingUser
+       ) {
+               $title = Title::newFromLinkTarget( $target );
+               $page = WikiPage::factory( $title );
+               $status = $page->doEditContent(
+                       ContentHandler::makeContent( $content, $title ),
+                       $summary,
+                       0,
+                       false,
+                       $user
+               );
+               /** @var Revision $rev */
+               $rev = $status->value['revision'];
+               $rc = $rev->getRecentChange();
+               $rc->doMarkPatrolled( $patrollingUser, false, [] );
+       }
+
+       private function deletePage( LinkTarget $target, $reason ) {
+               $title = Title::newFromLinkTarget( $target );
+               $page = WikiPage::factory( $title );
+               $page->doDeleteArticleReal( $reason );
+       }
+
+       /**
+        * Performs a batch of page edits as a specified user
+        * @param User $user
+        * @param array $editData associative array, keys:
+        *                        - target    => LinkTarget page to edit
+        *                        - content   => string new content
+        *                        - summary   => string edit summary
+        *                        - minorEdit => bool mark as minor edit if true (defaults to false)
+        *                        - botEdit   => bool mark as bot edit if true (defaults to false)
+        */
+       private function doPageEdits( User $user, array $editData ) {
+               foreach ( $editData as $singleEditData ) {
+                       if ( array_key_exists( 'minorEdit', $singleEditData ) && $singleEditData['minorEdit'] ) {
+                               $this->doMinorPageEdit(
+                                       $user,
+                                       $singleEditData['target'],
+                                       $singleEditData['content'],
+                                       $singleEditData['summary']
+                               );
+                               continue;
+                       }
+                       if ( array_key_exists( 'botEdit', $singleEditData ) && $singleEditData['botEdit'] ) {
+                               $this->doBotPageEdit(
+                                       $user,
+                                       $singleEditData['target'],
+                                       $singleEditData['content'],
+                                       $singleEditData['summary']
+                               );
+                               continue;
+                       }
+                       $this->doPageEdit(
+                               $user,
+                               $singleEditData['target'],
+                               $singleEditData['content'],
+                               $singleEditData['summary']
+                       );
+               }
+       }
+
+       private function getWatchedItemStore() {
+               return MediaWikiServices::getInstance()->getWatchedItemStore();
+       }
+
+       /**
+        * @param User $user
+        * @param LinkTarget[] $targets
+        */
+       private function watchPages( User $user, array $targets ) {
+               $store = $this->getWatchedItemStore();
+               $store->addWatchBatchForUser( $user, $targets );
+       }
+
+       private function doListWatchlistRequest( array $params = [], $user = null ) {
+               return $this->doApiRequest(
+                       array_merge(
+                               [ 'action' => 'query', 'list' => 'watchlist' ],
+                               $params
+                       ), null, false, $user
+               );
+       }
+
+       private function doGeneratorWatchlistRequest( array $params = [] ) {
+               return $this->doApiRequest(
+                       array_merge(
+                               [ 'action' => 'query', 'generator' => 'watchlist' ],
+                               $params
+                       )
+               );
+       }
+
+       private function getItemsFromApiResponse( array $response ) {
+               return $response[0]['query']['watchlist'];
+       }
+
+       /**
+        * Convenience method to assert that actual items array fetched from API is equal to the expected
+        * array, Unlike assertEquals this only checks if values of specified keys are equal in both
+        * arrays. This could be used e.g. not to compare IDs that could change between test run
+        * but only stable keys.
+        * Optionally this also checks that specified keys are present in the actual item without
+        * performing any checks on the related values.
+        *
+        * @param array $actualItems               array of actual items (associative arrays)
+        * @param array $expectedItems             array of expected items (associative arrays),
+        *                                         those items have less keys than actual items
+        * @param array $keysUsedInValueComparison list of keys of the actual item that will be used
+        *                                         in the comparison of values
+        * @param array $requiredKeys              optional, list of keys that must be present in the
+        *                                         actual items. Values of those keys are not checked.
+        */
+       private function assertArraySubsetsEqual(
+               array $actualItems,
+               array $expectedItems,
+               array $keysUsedInValueComparison,
+               array $requiredKeys = []
+       ) {
+               $this->assertCount( count( $expectedItems ), $actualItems );
+
+               // not checking values of all keys of the actual item, so removing unwanted keys from comparison
+               $actualItemsOnlyComparedValues = array_map(
+                       function( array $item ) use ( $keysUsedInValueComparison ) {
+                               return array_intersect_key( $item, array_flip( $keysUsedInValueComparison ) );
+                       },
+                       $actualItems
+               );
+
+               $this->assertEquals(
+                       $expectedItems,
+                       $actualItemsOnlyComparedValues
+               );
+
+               // Check that each item in $actualItems contains all of keys specified in $requiredKeys
+               $actualItemsKeysOnly = array_map( 'array_keys', $actualItems );
+               foreach ( $actualItemsKeysOnly as $keysOfTheItem ) {
+                       $this->assertEmpty( array_diff( $requiredKeys, $keysOfTheItem ) );
+               }
+       }
+
+       private function getTitleFormatter() {
+               return new MediaWikiTitleCodec( Language::factory( 'en' ), GenderCache::singleton() );
+       }
+
+       private function getPrefixedText( LinkTarget $target ) {
+               $formatter = $this->getTitleFormatter();
+               return $formatter->getPrefixedText( $target );
+       }
+
+       private function cleanTestUsersWatchlist() {
+               $user = $this->getTestUser();
+               $store = $this->getWatchedItemStore();
+               $items = $store->getWatchedItemsForUser( $user );
+               foreach ( $items as $item ) {
+                       $store->removeWatch( $user, $item->getLinkTarget() );
+               }
+       }
+
+       public function testListWatchlist_returnsWatchedItemsWithRCInfo() {
+               // Clean up after previous tests that might have added something to the watchlist of
+               // the user with the same user ID as user used here as the test user
+               $this->cleanTestUsersWatchlist();
+
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest();
+
+               $this->assertArrayHasKey( 'query', $result[0] );
+               $this->assertArrayHasKey( 'watchlist', $result[0]['query'] );
+
+               $this->assertArraySubsetsEqual(
+                       $this->getItemsFromApiResponse( $result ),
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target ),
+                                       'bot' => false,
+                                       'new' => true,
+                                       'minor' => false,
+                               ]
+                       ],
+                       [ 'type', 'ns', 'title', 'bot', 'new', 'minor' ],
+                       [ 'pageid', 'revid', 'old_revid' ]
+               );
+       }
+
+       public function testIdsPropParameter() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'ids', ] );
+               $items = $this->getItemsFromApiResponse( $result );
+
+               $this->assertCount( 1, $items );
+               $this->assertArrayHasKey( 'pageid', $items[0] );
+               $this->assertArrayHasKey( 'revid', $items[0] );
+               $this->assertArrayHasKey( 'old_revid', $items[0] );
+               $this->assertEquals( 'new', $items[0]['type'] );
+       }
+
+       public function testTitlePropParameter() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $subjectTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $talkTarget,
+                                       'content' => 'Some Talk Page Content',
+                                       'summary' => 'Create Talk page',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'title', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $talkTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $talkTarget ),
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testFlagsPropParameter() {
+               $user = $this->getTestUser();
+               $normalEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $minorEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageM' );
+               $botEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageB' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $normalEditTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $minorEditTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $minorEditTarget,
+                                       'content' => 'Slightly Better Content',
+                                       'summary' => 'Change content',
+                                       'minorEdit' => true,
+                               ],
+                               [
+                                       'target' => $botEditTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page with a bot',
+                                       'botEdit' => true,
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $normalEditTarget, $minorEditTarget, $botEditTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'flags', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'new' => true,
+                                       'minor' => false,
+                                       'bot' => true,
+                               ],
+                               [
+                                       'type' => 'edit',
+                                       'new' => false,
+                                       'minor' => true,
+                                       'bot' => false,
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'new' => true,
+                                       'minor' => false,
+                                       'bot' => false,
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testUserPropParameter() {
+               $user = $this->getTestUser();
+               $userEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $anonEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageA' );
+               $this->doPageEdit(
+                       $user,
+                       $userEditTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->doAnonPageEdit(
+                       $anonEditTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $userEditTarget, $anonEditTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'user', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'anon' => true,
+                                       'user' => User::newFromId( 0 )->getName(),
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'user' => $user->getName(),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testUserIdPropParameter() {
+               $user = $this->getTestUser();
+               $userEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $anonEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageA' );
+               $this->doPageEdit(
+                       $user,
+                       $userEditTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->doAnonPageEdit(
+                       $anonEditTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $userEditTarget, $anonEditTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'userid', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'anon' => true,
+                                       'user' => 0,
+                                       'userid' => 0,
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'user' => $user->getId(),
+                                       'userid' => $user->getId(),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testCommentPropParameter() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the <b>page</b>'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'comment', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'comment' => 'Create the <b>page</b>',
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testParsedCommentPropParameter() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the <b>page</b>'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'parsedcomment', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'parsedcomment' => 'Create the &lt;b&gt;page&lt;/b&gt;',
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testTimestampPropParameter() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'timestamp', ] );
+               $items = $this->getItemsFromApiResponse( $result );
+
+               $this->assertCount( 1, $items );
+               $this->assertArrayHasKey( 'timestamp', $items[0] );
+               $this->assertInternalType( 'string', $items[0]['timestamp'] );
+       }
+
+       public function testSizesPropParameter() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'sizes', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'oldlen' => 0,
+                                       'newlen' => 12,
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testNotificationTimestampPropParameter() {
+               $otherUser = $this->getNonLoggedInTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $otherUser,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $store = $this->getWatchedItemStore();
+               $store->addWatch( $this->getTestUser(), $target );
+               $store->updateNotificationTimestamp(
+                       $otherUser,
+                       $target,
+                       '20151212010101'
+               );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'notificationtimestamp', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'notificationtimestamp' => '2015-12-12T01:01:01Z',
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       private function setupPatrolledSpecificFixtures( User $user ) {
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+
+               $this->doPatrolledPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page (this gets patrolled)',
+                       $user
+               );
+
+               $this->watchPages( $user, [ $target ] );
+       }
+
+       public function testPatrolPropParameter() {
+               $user = $this->getSysopTestUser();
+               $this->setupPatrolledSpecificFixtures( $user );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'patrol', ], $user );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'patrolled' => true,
+                                       'unpatrolled' => false,
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       private function createPageAndDeleteIt( LinkTarget $target ) {
+               $this->doPageEdit(
+                       $this->getTestUser(),
+                       $target,
+                       'Some Content',
+                       'Create the page that will be deleted'
+               );
+               $this->deletePage( $target, 'Important Reason' );
+       }
+
+       public function testLoginfoPropParameter() {
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->createPageAndDeleteIt( $target );
+
+               $this->watchPages( $this->getTestUser(), [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'loginfo', ] );
+
+               $this->assertArraySubsetsEqual(
+                       $this->getItemsFromApiResponse( $result ),
+                       [
+                               [
+                                       'type' => 'log',
+                                       'logtype' => 'delete',
+                                       'logaction' => 'delete',
+                                       'logparams' => [],
+                               ],
+                       ],
+                       [ 'type', 'logtype', 'logaction', 'logparams' ],
+                       [ 'logid' ]
+               );
+       }
+
+       public function testEmptyPropParameter() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => '', ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testNamespaceParam() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $subjectTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $talkTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the talk page',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlnamespace' => '0', ] );
+
+               $this->assertArraySubsetsEqual(
+                       $this->getItemsFromApiResponse( $result ),
+                       [
+                               [
+                                       'ns' => 0,
+                                       'title' => $this->getPrefixedText( $subjectTarget ),
+                               ],
+                       ],
+                       [ 'ns', 'title' ]
+               );
+       }
+
+       public function testUserParam() {
+               $user = $this->getTestUser();
+               $otherUser = $this->getNonLoggedInTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $subjectTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->doPageEdit(
+                       $otherUser,
+                       $talkTarget,
+                       'What is this page about?',
+                       'Create the talk page'
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $result = $this->doListWatchlistRequest( [
+                       'wlprop' => 'user|title',
+                       'wluser' => $otherUser->getName(),
+               ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $talkTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $talkTarget ),
+                                       'user' => $otherUser->getName(),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testExcludeUserParam() {
+               $user = $this->getTestUser();
+               $otherUser = $this->getNonLoggedInTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $subjectTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->doPageEdit(
+                       $otherUser,
+                       $talkTarget,
+                       'What is this page about?',
+                       'Create the talk page'
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $result = $this->doListWatchlistRequest( [
+                       'wlprop' => 'user|title',
+                       'wlexcludeuser' => $otherUser->getName(),
+               ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget ),
+                                       'user' => $user->getName(),
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testShowMinorParams() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $target,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $target,
+                                       'content' => 'Slightly Better Content',
+                                       'summary' => 'Change content',
+                                       'minorEdit' => true,
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $resultMinor = $this->doListWatchlistRequest( [ 'wlshow' => 'minor', 'wlprop' => 'flags' ] );
+               $resultNotMinor = $this->doListWatchlistRequest( [ 'wlshow' => '!minor', 'wlprop' => 'flags' ] );
+
+               $this->assertArraySubsetsEqual(
+                       $this->getItemsFromApiResponse( $resultMinor ),
+                       [
+                               [ 'minor' => true, ]
+                       ],
+                       [ 'minor' ]
+               );
+               $this->assertEmpty( $this->getItemsFromApiResponse( $resultNotMinor ) );
+       }
+
+       public function testShowBotParams() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doBotPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $resultBot = $this->doListWatchlistRequest( [ 'wlshow' => 'bot' ] );
+               $resultNotBot = $this->doListWatchlistRequest( [ 'wlshow' => '!bot' ] );
+
+               $this->assertArraySubsetsEqual(
+                       $this->getItemsFromApiResponse( $resultBot ),
+                       [
+                               [ 'bot' => true ],
+                       ],
+                       [ 'bot' ]
+               );
+               $this->assertEmpty( $this->getItemsFromApiResponse( $resultNotBot ) );
+       }
+
+       public function testShowAnonParams() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doAnonPageEdit(
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $resultAnon = $this->doListWatchlistRequest( [
+                       'wlprop' => 'user',
+                       'wlshow' => 'anon'
+               ] );
+               $resultNotAnon = $this->doListWatchlistRequest( [
+                       'wlprop' => 'user',
+                       'wlshow' => '!anon'
+               ] );
+
+               $this->assertArraySubsetsEqual(
+                       $this->getItemsFromApiResponse( $resultAnon ),
+                       [
+                               [ 'anon' => true ],
+                       ],
+                       [ 'anon' ]
+               );
+               $this->assertEmpty( $this->getItemsFromApiResponse( $resultNotAnon ) );
+       }
+
+       public function testShowUnreadParams() {
+               $user = $this->getTestUser();
+               $otherUser = $this->getNonLoggedInTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $subjectTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->doPageEdit(
+                       $otherUser,
+                       $talkTarget,
+                       'Some Content',
+                       'Create the talk page'
+               );
+               $store = $this->getWatchedItemStore();
+               $store->addWatchBatchForUser( $user, [ $subjectTarget, $talkTarget ] );
+               $store->updateNotificationTimestamp(
+                       $otherUser,
+                       $talkTarget,
+                       '20151212010101'
+               );
+
+               $resultUnread = $this->doListWatchlistRequest( [
+                       'wlprop' => 'notificationtimestamp|title',
+                       'wlshow' => 'unread'
+               ] );
+               $resultNotUnread = $this->doListWatchlistRequest( [
+                       'wlprop' => 'notificationtimestamp|title',
+                       'wlshow' => '!unread'
+               ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'notificationtimestamp' => '2015-12-12T01:01:01Z',
+                                       'ns' => $talkTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $talkTarget )
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $resultUnread )
+               );
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'notificationtimestamp' => '',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget )
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $resultNotUnread )
+               );
+       }
+
+       public function testShowPatrolledParams() {
+               $user = $this->getSysopTestUser();
+               $this->setupPatrolledSpecificFixtures( $user );
+
+               $resultPatrolled = $this->doListWatchlistRequest( [
+                       'wlprop' => 'patrol',
+                       'wlshow' => 'patrolled'
+               ], $user );
+               $resultNotPatrolled = $this->doListWatchlistRequest( [
+                       'wlprop' => 'patrol',
+                       'wlshow' => '!patrolled'
+               ], $user );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'patrolled' => true,
+                                       'unpatrolled' => false,
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $resultPatrolled )
+               );
+               $this->assertEmpty( $this->getItemsFromApiResponse( $resultNotPatrolled ) );
+       }
+
+       public function testNewAndEditTypeParameters() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $subjectTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $subjectTarget,
+                                       'content' => 'Some Other Content',
+                                       'summary' => 'Change the content',
+                               ],
+                               [
+                                       'target' => $talkTarget,
+                                       'content' => 'Some Talk Page Content',
+                                       'summary' => 'Create Talk page',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $resultNew = $this->doListWatchlistRequest( [ 'wlprop' => 'title', 'wltype' => 'new' ] );
+               $resultEdit = $this->doListWatchlistRequest( [ 'wlprop' => 'title', 'wltype' => 'edit' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $talkTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $talkTarget ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultNew )
+               );
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'edit',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultEdit )
+               );
+       }
+
+       public function testLogTypeParameters() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->createPageAndDeleteIt( $subjectTarget );
+               $this->doPageEdit(
+                       $user,
+                       $talkTarget,
+                       'Some Talk Page Content',
+                       'Create Talk page'
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'title', 'wltype' => 'log' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'log',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       private function getExternalRC( LinkTarget $target ) {
+               $title = Title::newFromLinkTarget( $target );
+
+               $rc = new RecentChange;
+               $rc->mTitle = $title;
+               $rc->mAttribs = [
+                       'rc_timestamp' => wfTimestamp( TS_MW ),
+                       'rc_namespace' => $title->getNamespace(),
+                       'rc_title' => $title->getDBkey(),
+                       'rc_type' => RC_EXTERNAL,
+                       'rc_source' => 'foo',
+                       'rc_minor' => 0,
+                       'rc_cur_id' => $title->getArticleID(),
+                       'rc_user' => 0,
+                       'rc_user_text' => 'External User',
+                       'rc_comment' => '',
+                       'rc_this_oldid' => $title->getLatestRevID(),
+                       'rc_last_oldid' => $title->getLatestRevID(),
+                       'rc_bot' => 0,
+                       'rc_ip' => '',
+                       'rc_patrolled' => 0,
+                       'rc_new' => 0,
+                       'rc_old_len' => $title->getLength(),
+                       'rc_new_len' => $title->getLength(),
+                       'rc_deleted' => 0,
+                       'rc_logid' => 0,
+                       'rc_log_type' => null,
+                       'rc_log_action' => '',
+                       'rc_params' => '',
+               ];
+               $rc->mExtra = [
+                       'prefixedDBkey' => $title->getPrefixedDBkey(),
+                       'lastTimestamp' => 0,
+                       'oldSize' => $title->getLength(),
+                       'newSize' => $title->getLength(),
+                       'pageStatus' => 'changed'
+               ];
+
+               return $rc;
+       }
+
+       public function testExternalTypeParameters() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $subjectTarget,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->doPageEdit(
+                       $user,
+                       $talkTarget,
+                       'Some Talk Page Content',
+                       'Create Talk page'
+               );
+
+               $rc = $this->getExternalRC( $subjectTarget );
+               $rc->save();
+
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'title', 'wltype' => 'external' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'external',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testCategorizeTypeParameter() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $categoryTarget = new TitleValue( NS_CATEGORY, 'ApiQueryWatchlistIntegrationTestCategory' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $categoryTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the category',
+                               ],
+                               [
+                                       'target' => $subjectTarget,
+                                       'content' => 'Some Content [[Category:ApiQueryWatchlistIntegrationTestCategory]]t',
+                                       'summary' => 'Create the page and add it to the category',
+                               ],
+                       ]
+               );
+               $title = Title::newFromLinkTarget( $subjectTarget );
+               $revision = Revision::newFromTitle( $title );
+
+               $rc = RecentChange::newForCategorization(
+                       $revision->getTimestamp(),
+                       Title::newFromLinkTarget( $categoryTarget ),
+                       $user,
+                       $revision->getComment(),
+                       $title,
+                       0,
+                       $revision->getId(),
+                       null,
+                       false
+               );
+               $rc->save();
+
+               $this->watchPages( $user, [ $subjectTarget, $categoryTarget ] );
+
+               $result = $this->doListWatchlistRequest( [ 'wlprop' => 'title', 'wltype' => 'categorize' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'categorize',
+                                       'ns' => $categoryTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $categoryTarget ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testLimitParam() {
+               $user = $this->getTestUser();
+               $target1 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $target2 = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $target3 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage2' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $target1,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $target2,
+                                       'content' => 'Some Talk Page Content',
+                                       'summary' => 'Create Talk page',
+                               ],
+                               [
+                                       'target' => $target3,
+                                       'content' => 'Some Other Content',
+                                       'summary' => 'Create the page',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $target1, $target2, $target3 ] );
+
+               $resultWithoutLimit = $this->doListWatchlistRequest( [ 'wlprop' => 'title' ] );
+               $resultWithLimit = $this->doListWatchlistRequest( [ 'wllimit' => 2, 'wlprop' => 'title' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target3->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target3 )
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target2->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target2 )
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target1->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target1 )
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultWithoutLimit )
+               );
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target3->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target3 )
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target2->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target2 )
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultWithLimit )
+               );
+               $this->assertArrayHasKey( 'continue', $resultWithLimit[0] );
+               $this->assertArrayHasKey( 'wlcontinue', $resultWithLimit[0]['continue'] );
+       }
+
+       public function testAllRevParam() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $target,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $target,
+                                       'content' => 'Some Other Content',
+                                       'summary' => 'Change the content',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $resultAllRev = $this->doListWatchlistRequest( [ 'wlprop' => 'title', 'wlallrev' => '', ] );
+               $resultNoAllRev = $this->doListWatchlistRequest( [ 'wlprop' => 'title' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'edit',
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultNoAllRev )
+               );
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'edit',
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target ),
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultAllRev )
+               );
+       }
+
+       public function testDirParams() {
+               $user = $this->getTestUser();
+               $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $subjectTarget,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $talkTarget,
+                                       'content' => 'Some Talk Page Content',
+                                       'summary' => 'Create Talk page',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $subjectTarget, $talkTarget ] );
+
+               $resultDirOlder = $this->doListWatchlistRequest( [ 'wldir' => 'older', 'wlprop' => 'title' ] );
+               $resultDirNewer = $this->doListWatchlistRequest( [ 'wldir' => 'newer', 'wlprop' => 'title' ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $talkTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $talkTarget )
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget )
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultDirOlder )
+               );
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $subjectTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $subjectTarget )
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $talkTarget->getNamespace(),
+                                       'title' => $this->getPrefixedText( $talkTarget )
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $resultDirNewer )
+               );
+       }
+
+       public function testStartEndParams() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $resultStart = $this->doListWatchlistRequest( [
+                       'wlstart' => '20010115000000',
+                       'wldir' => 'newer',
+                       'wlprop' => 'title',
+               ] );
+               $resultEnd = $this->doListWatchlistRequest( [
+                       'wlend' => '20010115000000',
+                       'wldir' => 'newer',
+                       'wlprop' => 'title',
+               ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target ),
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $resultStart )
+               );
+               $this->assertEmpty( $this->getItemsFromApiResponse( $resultEnd ) );
+       }
+
+       public function testContinueParam() {
+               $user = $this->getTestUser();
+               $target1 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $target2 = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
+               $target3 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage2' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $target1,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $target2,
+                                       'content' => 'Some Talk Page Content',
+                                       'summary' => 'Create Talk page',
+                               ],
+                               [
+                                       'target' => $target3,
+                                       'content' => 'Some Other Content',
+                                       'summary' => 'Create the page',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $target1, $target2, $target3 ] );
+
+               $firstResult = $this->doListWatchlistRequest( [ 'wllimit' => 2, 'wlprop' => 'title' ] );
+               $this->assertArrayHasKey( 'continue', $firstResult[0] );
+               $this->assertArrayHasKey( 'wlcontinue', $firstResult[0]['continue'] );
+
+               $continuationParam = $firstResult[0]['continue']['wlcontinue'];
+
+               $continuedResult = $this->doListWatchlistRequest(
+                       [ 'wlcontinue' => $continuationParam, 'wlprop' => 'title' ]
+               );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target3->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target3 ),
+                               ],
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target2->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target2 ),
+                               ],
+                       ],
+                       $this->getItemsFromApiResponse( $firstResult )
+               );
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target1->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target1 )
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $continuedResult )
+               );
+       }
+
+       public function testOwnerAndTokenParams() {
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $this->getTestUser(),
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+
+               $otherUser = $this->getNonLoggedInTestUser();
+               $otherUser->setOption( 'watchlisttoken', '1234567890' );
+               $otherUser->saveSettings();
+
+               $this->watchPages( $otherUser, [ $target ] );
+
+               $result = $this->doListWatchlistRequest( [
+                       'wlowner' => $otherUser->getName(),
+                       'wltoken' => '1234567890',
+                       'wlprop' => 'title',
+               ] );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       'type' => 'new',
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target )
+                               ]
+                       ],
+                       $this->getItemsFromApiResponse( $result )
+               );
+       }
+
+       public function testOwnerAndTokenParams_wrongToken() {
+               $otherUser = $this->getNonLoggedInTestUser();
+               $otherUser->setOption( 'watchlisttoken', '1234567890' );
+               $otherUser->saveSettings();
+
+               $this->setExpectedException( UsageException::class, 'Incorrect watchlist token provided' );
+
+               $this->doListWatchlistRequest( [
+                       'wlowner' => $otherUser->getName(),
+                       'wltoken' => 'wrong-token',
+               ] );
+       }
+
+       public function testOwnerAndTokenParams_noWatchlistTokenSet() {
+               $this->setExpectedException( UsageException::class, 'Incorrect watchlist token provided' );
+
+               $this->doListWatchlistRequest( [
+                       'wlowner' => $this->getNonLoggedInTestUser()->getName(),
+                       'wltoken' => 'some-token',
+               ] );
+       }
+
+       public function testGeneratorWatchlistPropInfo_returnsWatchedPages() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdit(
+                       $user,
+                       $target,
+                       'Some Content',
+                       'Create the page'
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doGeneratorWatchlistRequest( [ 'prop' => 'info' ] );
+
+               $this->assertArrayHasKey( 'query', $result[0] );
+               $this->assertArrayHasKey( 'pages', $result[0]['query'] );
+
+               // $result[0]['query']['pages'] uses page ids as keys. Page ids don't matter here, so drop them
+               $pages = array_values( $result[0]['query']['pages'] );
+
+               $this->assertArraySubsetsEqual(
+                       $pages,
+                       [
+                               [
+                                       'ns' => $target->getNamespace(),
+                                       'title' => $this->getPrefixedText( $target ),
+                                       'new' => true,
+                               ]
+                       ],
+                       [ 'ns', 'title', 'new' ]
+               );
+       }
+
+       public function testGeneratorWatchlistPropRevisions_returnsWatchedItemsRevisions() {
+               $user = $this->getTestUser();
+               $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
+               $this->doPageEdits(
+                       $user,
+                       [
+                               [
+                                       'target' => $target,
+                                       'content' => 'Some Content',
+                                       'summary' => 'Create the page',
+                               ],
+                               [
+                                       'target' => $target,
+                                       'content' => 'Some Other Content',
+                                       'summary' => 'Change the content',
+                               ],
+                       ]
+               );
+               $this->watchPages( $user, [ $target ] );
+
+               $result = $this->doGeneratorWatchlistRequest( [ 'prop' => 'revisions', 'gwlallrev' => '' ] );
+
+               $this->assertArrayHasKey( 'query', $result[0] );
+               $this->assertArrayHasKey( 'pages', $result[0]['query'] );
+
+               // $result[0]['query']['pages'] uses page ids as keys. Page ids don't matter here, so drop them
+               $pages = array_values( $result[0]['query']['pages'] );
+
+               $this->assertCount( 1, $pages );
+               $this->assertEquals( 0, $pages[0]['ns'] );
+               $this->assertEquals( $this->getPrefixedText( $target ), $pages[0]['title'] );
+               $this->assertArraySubsetsEqual(
+                       $pages[0]['revisions'],
+                       [
+                               [
+                                       'comment' => 'Create the page',
+                                       'user' => $user->getName(),
+                                       'minor' => false,
+                               ],
+                               [
+                                       'comment' => 'Change the content',
+                                       'user' => $user->getName(),
+                                       'minor' => false,
+                               ],
+                       ],
+                       [ 'comment', 'user', 'minor' ]
+               );
+       }
+
+}
index 91f27fb..545b964 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+use MediaWiki\MediaWikiServices;
 
 /**
  * @group ContentHandler
@@ -36,7 +37,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
                MWNamespace::getCanonicalNamespaces( true );
                $wgContLang->resetNamespaces();
                // And LinkCache
-               LinkCache::destroySingleton();
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'LinkCache' );
        }
 
        protected function tearDown() {
@@ -46,7 +47,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
                MWNamespace::getCanonicalNamespaces( true );
                $wgContLang->resetNamespaces();
                // And LinkCache
-               LinkCache::destroySingleton();
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'LinkCache' );
 
                parent::tearDown();
        }
diff --git a/tests/phpunit/includes/libs/XhprofDataTest.php b/tests/phpunit/includes/libs/XhprofDataTest.php
new file mode 100644 (file)
index 0000000..a0fb563
--- /dev/null
@@ -0,0 +1,277 @@
+<?php
+/**
+ * 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
+ */
+
+/**
+ * @uses XhprofData
+ * @uses AutoLoader
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
+ * @since 1.25
+ */
+class XhprofDataTest extends PHPUnit_Framework_TestCase {
+
+       /**
+        * @covers XhprofData::splitKey
+        * @dataProvider provideSplitKey
+        */
+       public function testSplitKey( $key, $expect ) {
+               $this->assertSame( $expect, XhprofData::splitKey( $key ) );
+       }
+
+       public function provideSplitKey() {
+               return [
+                       [ 'main()', [ null, 'main()' ] ],
+                       [ 'foo==>bar', [ 'foo', 'bar' ] ],
+                       [ 'bar@1==>bar@2', [ 'bar@1', 'bar@2' ] ],
+                       [ 'foo==>bar==>baz', [ 'foo', 'bar==>baz' ] ],
+                       [ '==>bar', [ '', 'bar' ] ],
+                       [ '', [ null, '' ] ],
+               ];
+       }
+
+       /**
+        * @covers XhprofData::pruneData
+        */
+       public function testInclude() {
+               $xhprofData = $this->getXhprofDataFixture( [
+                       'include' => [ 'main()' ],
+               ] );
+               $raw = $xhprofData->getRawData();
+               $this->assertArrayHasKey( 'main()', $raw );
+               $this->assertArrayHasKey( 'main()==>foo', $raw );
+               $this->assertArrayHasKey( 'main()==>xhprof_disable', $raw );
+               $this->assertSame( 3, count( $raw ) );
+       }
+
+       /**
+        * Validate the structure of data returned by
+        * Xhprof::getInclusiveMetrics(). This acts as a guard against unexpected
+        * structural changes to the returned data in lieu of using a more heavy
+        * weight typed response object.
+        *
+        * @covers XhprofData::getInclusiveMetrics
+        */
+       public function testInclusiveMetricsStructure() {
+               $metricStruct = [
+                       'ct' => 'int',
+                       'wt' => 'array',
+                       'cpu' => 'array',
+                       'mu' => 'array',
+                       'pmu' => 'array',
+               ];
+               $statStruct = [
+                       'total' => 'numeric',
+                       'min' => 'numeric',
+                       'mean' => 'numeric',
+                       'max' => 'numeric',
+                       'variance' => 'numeric',
+                       'percent' => 'numeric',
+               ];
+
+               $xhprofData = $this->getXhprofDataFixture();
+               $metrics = $xhprofData->getInclusiveMetrics();
+
+               foreach ( $metrics as $name => $metric ) {
+                       $this->assertArrayStructure( $metricStruct, $metric );
+
+                       foreach ( $metricStruct as $key => $type ) {
+                               if ( $type === 'array' ) {
+                                       $this->assertArrayStructure( $statStruct, $metric[$key] );
+                                       if ( $name === 'main()' ) {
+                                               $this->assertEquals( 100, $metric[$key]['percent'] );
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Validate the structure of data returned by
+        * Xhprof::getCompleteMetrics(). This acts as a guard against unexpected
+        * structural changes to the returned data in lieu of using a more heavy
+        * weight typed response object.
+        *
+        * @covers XhprofData::getCompleteMetrics
+        */
+       public function testCompleteMetricsStructure() {
+               $metricStruct = [
+                       'ct' => 'int',
+                       'wt' => 'array',
+                       'cpu' => 'array',
+                       'mu' => 'array',
+                       'pmu' => 'array',
+                       'calls' => 'array',
+                       'subcalls' => 'array',
+               ];
+               $statsMetrics = [ 'wt', 'cpu', 'mu', 'pmu' ];
+               $statStruct = [
+                       'total' => 'numeric',
+                       'min' => 'numeric',
+                       'mean' => 'numeric',
+                       'max' => 'numeric',
+                       'variance' => 'numeric',
+                       'percent' => 'numeric',
+                       'exclusive' => 'numeric',
+               ];
+
+               $xhprofData = $this->getXhprofDataFixture();
+               $metrics = $xhprofData->getCompleteMetrics();
+
+               foreach ( $metrics as $name => $metric ) {
+                       $this->assertArrayStructure( $metricStruct, $metric, $name );
+
+                       foreach ( $metricStruct as $key => $type ) {
+                               if ( in_array( $key, $statsMetrics ) ) {
+                                       $this->assertArrayStructure(
+                                               $statStruct, $metric[$key], $key
+                                       );
+                                       $this->assertLessThanOrEqual(
+                                               $metric[$key]['total'], $metric[$key]['exclusive']
+                                       );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @covers XhprofData::getCallers
+        * @covers XhprofData::getCallees
+        * @uses XhprofData
+        */
+       public function testEdges() {
+               $xhprofData = $this->getXhprofDataFixture();
+               $this->assertSame( [], $xhprofData->getCallers( 'main()' ) );
+               $this->assertSame( [ 'foo', 'xhprof_disable' ],
+                       $xhprofData->getCallees( 'main()' )
+               );
+               $this->assertSame( [ 'main()' ],
+                       $xhprofData->getCallers( 'foo' )
+               );
+               $this->assertSame( [], $xhprofData->getCallees( 'strlen' ) );
+       }
+
+       /**
+        * @covers XhprofData::getCriticalPath
+        * @uses XhprofData
+        */
+       public function testCriticalPath() {
+               $xhprofData = $this->getXhprofDataFixture();
+               $path = $xhprofData->getCriticalPath();
+
+               $last = null;
+               foreach ( $path as $key => $value ) {
+                       list( $func, $call ) = XhprofData::splitKey( $key );
+                       $this->assertSame( $last, $func );
+                       $last = $call;
+               }
+               $this->assertSame( $last, 'bar@1' );
+       }
+
+       /**
+        * Get an Xhprof instance that has been primed with a set of known testing
+        * data. Tests for the Xhprof class should laregly be concerned with
+        * evaluating the manipulations of the data collected by xhprof rather
+        * than the data collection process itself.
+        *
+        * The returned Xhprof instance primed will be with a data set created by
+        * running this trivial program using the PECL xhprof implementation:
+        * @code
+        * function bar( $x ) {
+        *   if ( $x > 0 ) {
+        *     bar($x - 1);
+        *   }
+        * }
+        * function foo() {
+        *   for ( $idx = 0; $idx < 2; $idx++ ) {
+        *     bar( $idx );
+        *     $x = strlen( 'abc' );
+        *   }
+        * }
+        * xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY );
+        * foo();
+        * $x = xhprof_disable();
+        * var_export( $x );
+        * @endcode
+        *
+        * @return Xhprof
+        */
+       protected function getXhprofDataFixture( array $opts = [] ) {
+               return new XhprofData( [
+                       'foo==>bar' => [
+                               'ct' => 2,
+                               'wt' => 57,
+                               'cpu' => 92,
+                               'mu' => 1896,
+                               'pmu' => 0,
+                       ],
+                       'foo==>strlen' => [
+                               'ct' => 2,
+                               'wt' => 21,
+                               'cpu' => 141,
+                               'mu' => 752,
+                               'pmu' => 0,
+                       ],
+                       'bar==>bar@1' => [
+                               'ct' => 1,
+                               'wt' => 18,
+                               'cpu' => 19,
+                               'mu' => 752,
+                               'pmu' => 0,
+                       ],
+                       'main()==>foo' => [
+                               'ct' => 1,
+                               'wt' => 304,
+                               'cpu' => 307,
+                               'mu' => 4008,
+                               'pmu' => 0,
+                       ],
+                       'main()==>xhprof_disable' => [
+                               'ct' => 1,
+                               'wt' => 8,
+                               'cpu' => 10,
+                               'mu' => 768,
+                               'pmu' => 392,
+                       ],
+                       'main()' => [
+                               'ct' => 1,
+                               'wt' => 353,
+                               'cpu' => 351,
+                               'mu' => 6112,
+                               'pmu' => 1424,
+                       ],
+               ], $opts );
+       }
+
+       /**
+        * Assert that the given array has the described structure.
+        *
+        * @param array $struct Array of key => type mappings
+        * @param array $actual Array to check
+        * @param string $label
+        */
+       protected function assertArrayStructure( $struct, $actual, $label = null ) {
+               $this->assertInternalType( 'array', $actual, $label );
+               $this->assertCount( count( $struct ), $actual, $label );
+               foreach ( $struct as $key => $type ) {
+                       $this->assertArrayHasKey( $key, $actual );
+                       $this->assertInternalType( $type, $actual[$key] );
+               }
+       }
+}
index 22925bf..6748115 100644 (file)
  * @file
  */
 
-/**
- * @uses Xhprof
- * @uses AutoLoader
- * @author Bryan Davis <bd808@wikimedia.org>
- * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
- * @since 1.25
- */
 class XhprofTest extends PHPUnit_Framework_TestCase {
-
-       public function setUp() {
-               if ( !function_exists( 'xhprof_enable' ) ) {
-                       $this->markTestSkipped( 'No xhprof support detected.' );
-               }
-       }
-
-       /**
-        * @covers Xhprof::splitKey
-        * @dataProvider provideSplitKey
-        */
-       public function testSplitKey( $key, $expect ) {
-               $this->assertSame( $expect, Xhprof::splitKey( $key ) );
-       }
-
-       public function provideSplitKey() {
-               return [
-                       [ 'main()', [ null, 'main()' ] ],
-                       [ 'foo==>bar', [ 'foo', 'bar' ] ],
-                       [ 'bar@1==>bar@2', [ 'bar@1', 'bar@2' ] ],
-                       [ 'foo==>bar==>baz', [ 'foo', 'bar==>baz' ] ],
-                       [ '==>bar', [ '', 'bar' ] ],
-                       [ '', [ null, '' ] ],
-               ];
-       }
-
-       /**
-        * @covers Xhprof::__construct
-        * @covers Xhprof::stop
-        * @covers Xhprof::getRawData
-        * @dataProvider provideRawData
-        */
-       public function testRawData( $flags, $keys ) {
-               $xhprof = new Xhprof( [ 'flags' => $flags ] );
-               $raw = $xhprof->getRawData();
-               $this->assertArrayHasKey( 'main()', $raw );
-               foreach ( $keys as $key ) {
-                       $this->assertArrayHasKey( $key, $raw['main()'] );
-               }
-       }
-
-       public function provideRawData() {
-               $tests = [
-                       [ 0, [ 'ct', 'wt' ] ],
-               ];
-
-               if ( defined( 'XHPROF_FLAGS_CPU' ) && defined( 'XHPROF_FLAGS_CPU' ) ) {
-                       $tests[] = [ XHPROF_FLAGS_MEMORY, [
-                               'ct', 'wt', 'mu', 'pmu',
-                       ] ];
-                       $tests[] = [ XHPROF_FLAGS_CPU, [
-                               'ct', 'wt', 'cpu',
-                       ] ];
-                       $tests[] = [ XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU, [
-                                       'ct', 'wt', 'mu', 'pmu', 'cpu',
-                               ] ];
-               }
-
-               return $tests;
-       }
-
-       /**
-        * @covers Xhprof::pruneData
-        */
-       public function testInclude() {
-               $xhprof = $this->getXhprofFixture( [
-                       'include' => [ 'main()' ],
-               ] );
-               $raw = $xhprof->getRawData();
-               $this->assertArrayHasKey( 'main()', $raw );
-               $this->assertArrayHasKey( 'main()==>foo', $raw );
-               $this->assertArrayHasKey( 'main()==>xhprof_disable', $raw );
-               $this->assertSame( 3, count( $raw ) );
-       }
-
        /**
-        * Validate the structure of data returned by
-        * Xhprof::getInclusiveMetrics(). This acts as a guard against unexpected
-        * structural changes to the returned data in lieu of using a more heavy
-        * weight typed response object.
+        * Trying to enable Xhprof when it is already enabled causes an exception
+        * to be thrown.
         *
-        * @covers Xhprof::getInclusiveMetrics
-        */
-       public function testInclusiveMetricsStructure() {
-               $metricStruct = [
-                       'ct' => 'int',
-                       'wt' => 'array',
-                       'cpu' => 'array',
-                       'mu' => 'array',
-                       'pmu' => 'array',
-               ];
-               $statStruct = [
-                       'total' => 'numeric',
-                       'min' => 'numeric',
-                       'mean' => 'numeric',
-                       'max' => 'numeric',
-                       'variance' => 'numeric',
-                       'percent' => 'numeric',
-               ];
-
-               $xhprof = $this->getXhprofFixture();
-               $metrics = $xhprof->getInclusiveMetrics();
-
-               foreach ( $metrics as $name => $metric ) {
-                       $this->assertArrayStructure( $metricStruct, $metric );
-
-                       foreach ( $metricStruct as $key => $type ) {
-                               if ( $type === 'array' ) {
-                                       $this->assertArrayStructure( $statStruct, $metric[$key] );
-                                       if ( $name === 'main()' ) {
-                                               $this->assertEquals( 100, $metric[$key]['percent'] );
-                                       }
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Validate the structure of data returned by
-        * Xhprof::getCompleteMetrics(). This acts as a guard against unexpected
-        * structural changes to the returned data in lieu of using a more heavy
-        * weight typed response object.
-        *
-        * @covers Xhprof::getCompleteMetrics
-        */
-       public function testCompleteMetricsStructure() {
-               $metricStruct = [
-                       'ct' => 'int',
-                       'wt' => 'array',
-                       'cpu' => 'array',
-                       'mu' => 'array',
-                       'pmu' => 'array',
-                       'calls' => 'array',
-                       'subcalls' => 'array',
-               ];
-               $statsMetrics = [ 'wt', 'cpu', 'mu', 'pmu' ];
-               $statStruct = [
-                       'total' => 'numeric',
-                       'min' => 'numeric',
-                       'mean' => 'numeric',
-                       'max' => 'numeric',
-                       'variance' => 'numeric',
-                       'percent' => 'numeric',
-                       'exclusive' => 'numeric',
-               ];
-
-               $xhprof = $this->getXhprofFixture();
-               $metrics = $xhprof->getCompleteMetrics();
-
-               foreach ( $metrics as $name => $metric ) {
-                       $this->assertArrayStructure( $metricStruct, $metric, $name );
-
-                       foreach ( $metricStruct as $key => $type ) {
-                               if ( in_array( $key, $statsMetrics ) ) {
-                                       $this->assertArrayStructure(
-                                               $statStruct, $metric[$key], $key
-                                       );
-                                       $this->assertLessThanOrEqual(
-                                               $metric[$key]['total'], $metric[$key]['exclusive']
-                                       );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * @covers Xhprof::getCallers
-        * @covers Xhprof::getCallees
-        * @uses Xhprof
-        */
-       public function testEdges() {
-               $xhprof = $this->getXhprofFixture();
-               $this->assertSame( [], $xhprof->getCallers( 'main()' ) );
-               $this->assertSame( [ 'foo', 'xhprof_disable' ],
-                       $xhprof->getCallees( 'main()' )
-               );
-               $this->assertSame( [ 'main()' ],
-                       $xhprof->getCallers( 'foo' )
-               );
-               $this->assertSame( [], $xhprof->getCallees( 'strlen' ) );
-       }
-
-       /**
-        * @covers Xhprof::getCriticalPath
-        * @uses Xhprof
-        */
-       public function testCriticalPath() {
-               $xhprof = $this->getXhprofFixture();
-               $path = $xhprof->getCriticalPath();
-
-               $last = null;
-               foreach ( $path as $key => $value ) {
-                       list( $func, $call ) = Xhprof::splitKey( $key );
-                       $this->assertSame( $last, $func );
-                       $last = $call;
-               }
-               $this->assertSame( $last, 'bar@1' );
-       }
-
-       /**
-        * Get an Xhprof instance that has been primed with a set of known testing
-        * data. Tests for the Xhprof class should laregly be concerned with
-        * evaluating the manipulations of the data collected by xhprof rather
-        * than the data collection process itself.
-        *
-        * The returned Xhprof instance primed will be with a data set created by
-        * running this trivial program using the PECL xhprof implementation:
-        * @code
-        * function bar( $x ) {
-        *   if ( $x > 0 ) {
-        *     bar($x - 1);
-        *   }
-        * }
-        * function foo() {
-        *   for ( $idx = 0; $idx < 2; $idx++ ) {
-        *     bar( $idx );
-        *     $x = strlen( 'abc' );
-        *   }
-        * }
-        * xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY );
-        * foo();
-        * $x = xhprof_disable();
-        * var_export( $x );
-        * @endcode
-        *
-        * @return Xhprof
-        */
-       protected function getXhprofFixture( array $opts = [] ) {
-               $xhprof = new Xhprof( $opts );
-               $xhprof->loadRawData( [
-                       'foo==>bar' => [
-                               'ct' => 2,
-                               'wt' => 57,
-                               'cpu' => 92,
-                               'mu' => 1896,
-                               'pmu' => 0,
-                       ],
-                       'foo==>strlen' => [
-                               'ct' => 2,
-                               'wt' => 21,
-                               'cpu' => 141,
-                               'mu' => 752,
-                               'pmu' => 0,
-                       ],
-                       'bar==>bar@1' => [
-                               'ct' => 1,
-                               'wt' => 18,
-                               'cpu' => 19,
-                               'mu' => 752,
-                               'pmu' => 0,
-                       ],
-                       'main()==>foo' => [
-                               'ct' => 1,
-                               'wt' => 304,
-                               'cpu' => 307,
-                               'mu' => 4008,
-                               'pmu' => 0,
-                       ],
-                       'main()==>xhprof_disable' => [
-                               'ct' => 1,
-                               'wt' => 8,
-                               'cpu' => 10,
-                               'mu' => 768,
-                               'pmu' => 392,
-                       ],
-                       'main()' => [
-                               'ct' => 1,
-                               'wt' => 353,
-                               'cpu' => 351,
-                               'mu' => 6112,
-                               'pmu' => 1424,
-                       ],
-               ] );
-               return $xhprof;
-       }
-
-       /**
-        * Assert that the given array has the described structure.
-        *
-        * @param array $struct Array of key => type mappings
-        * @param array $actual Array to check
-        * @param string $label
-        */
-       protected function assertArrayStructure( $struct, $actual, $label = null ) {
-               $this->assertInternalType( 'array', $actual, $label );
-               $this->assertCount( count( $struct ), $actual, $label );
-               foreach ( $struct as $key => $type ) {
-                       $this->assertArrayHasKey( $key, $actual );
-                       $this->assertInternalType( $type, $actual[$key] );
-               }
+        * @expectedException        Exception
+        * @expectedExceptionMessage already enabled
+        * @covers Xhprof::enable
+        */
+       public function testEnable() {
+               $xhprof = new ReflectionClass( 'Xhprof' );
+               $enabled = $xhprof->getProperty( 'enabled' );
+               $enabled->setAccessible( true );
+               $enabled->setValue( true );
+               $xhprof->getMethod( 'enable' )->invoke( null );
        }
 }
diff --git a/tests/phpunit/includes/objectcache/RedisBagOStuffTest.php b/tests/phpunit/includes/objectcache/RedisBagOStuffTest.php
new file mode 100644 (file)
index 0000000..cf87a98
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/**
+ * @group BagOStuff
+ */
+class RedisBagOStuffTest extends MediaWikiTestCase {
+       /** @var RedisBagOStuff */
+       private $cache;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->cache = TestingAccessWrapper::newFromObject( new RedisBagOStuff( [ 'servers' => [] ] ) );
+       }
+
+       /**
+        * @covers RedisBagOStuff::unserialize
+        * @dataProvider unserializeProvider
+        */
+       public function testUnserialize( $expected, $input, $message ) {
+               $actual = $this->cache->unserialize( $input );
+               $this->assertSame( $expected, $actual, $message );
+       }
+
+       public function unserializeProvider() {
+               return [
+                       [
+                               -1,
+                               '-1',
+                               'String representation of \'-1\'',
+                       ],
+                       [
+                               0,
+                               '0',
+                               'String representation of \'0\'',
+                       ],
+                       [
+                               1,
+                               '1',
+                               'String representation of \'1\'',
+                       ],
+                       [
+                               -1.0,
+                               'd:-1;',
+                               'Serialized negative double',
+                       ],
+                       [
+                               'foo',
+                               's:3:"foo";',
+                               'Serialized string',
+                       ]
+               ];
+       }
+
+       /**
+        * @covers RedisBagOStuff::serialize
+        * @dataProvider serializeProvider
+        */
+       public function testSerialize( $expected, $input, $message ) {
+               $actual = $this->cache->serialize( $input );
+               $this->assertSame( $expected, $actual, $message );
+       }
+
+       public function serializeProvider() {
+               return [
+                       [
+                               -1,
+                               -1,
+                               '-1 as integer',
+                       ],
+                       [
+                               0,
+                               0,
+                               '0 as integer',
+                       ],
+                       [
+                               1,
+                               1,
+                               '1 as integer',
+                       ],
+                       [
+                               'd:-1;',
+                               -1.0,
+                               'Negative double',
+                       ],
+                       [
+                               's:3:"2.1";',
+                               '2.1',
+                               'Decimal string',
+                       ],
+                       [
+                               's:1:"1";',
+                               '1',
+                               'String representation of 1',
+                       ],
+                       [
+                               's:3:"foo";',
+                               'foo',
+                               'String',
+                       ],
+               ];
+       }
+}
index 22bb237..695f99a 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * Although marked as a stub, can work independently.
  *
@@ -151,7 +150,10 @@ class NewParserTest extends MediaWikiTestCase {
                $tmpGlobals['wgHooks'] = $tmpHooks;
                # add a namespace shadowing a interwiki link, to test
                # proper precedence when resolving links. (bug 51680)
-               $tmpGlobals['wgExtraNamespaces'] = [ 100 => 'MemoryAlpha' ];
+               $tmpGlobals['wgExtraNamespaces'] = [
+                       100 => 'MemoryAlpha',
+                       101 => 'MemoryAlpha_talk'
+               ];
 
                $tmpGlobals['wgLocalInterwikis'] = [ 'local', 'mi' ];
                # "extra language links"
@@ -179,6 +181,7 @@ class NewParserTest extends MediaWikiTestCase {
 
                MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
                $wgContLang->resetNamespaces(); # reset namespace cache
+               ParserTest::resetTitleServices();
        }
 
        protected function tearDown() {
index 15b47b4..d57ad04 100644 (file)
@@ -56,17 +56,26 @@ class PoolCounterTest extends MediaWikiTestCase {
 
                $keysWithTwoSlots = $keysWithFiveSlots = [];
                foreach ( range( 1, 100 ) as $i ) {
-                       $keysWithTwoSlots[] = $hashKeyIntoSlots->invoke( $poolCounter, 'key ' . $i, 2 );
-                       $keysWithFiveSlots[] = $hashKeyIntoSlots->invoke( $poolCounter, 'key ' . $i, 5 );
+                       $keysWithTwoSlots[] = $hashKeyIntoSlots->invoke( $poolCounter, 'test', 'key ' . $i, 2 );
+                       $keysWithFiveSlots[] = $hashKeyIntoSlots->invoke( $poolCounter, 'test', 'key ' . $i, 5 );
                }
 
-               $this->assertArrayEquals( range( 0, 1 ), array_unique( $keysWithTwoSlots ) );
-               $this->assertArrayEquals( range( 0, 4 ), array_unique( $keysWithFiveSlots ) );
+               $twoSlotKeys = [];
+               for ( $i = 0; $i <= 1; $i++ ) {
+                       $twoSlotKeys[] = "test:$i";
+               }
+               $fiveSlotKeys = [];
+               for ( $i = 0; $i <= 4; $i++ ) {
+                       $fiveSlotKeys[] = "test:$i";
+               }
+
+               $this->assertArrayEquals( $twoSlotKeys, array_unique( $keysWithTwoSlots ) );
+               $this->assertArrayEquals( $fiveSlotKeys, array_unique( $keysWithFiveSlots ) );
 
                // make sure it is deterministic
                $this->assertEquals(
-                       $hashKeyIntoSlots->invoke( $poolCounter, 'asdfgh', 1000 ),
-                       $hashKeyIntoSlots->invoke( $poolCounter, 'asdfgh', 1000 )
+                       $hashKeyIntoSlots->invoke( $poolCounter, 'test', 'asdfgh', 1000 ),
+                       $hashKeyIntoSlots->invoke( $poolCounter, 'test', 'asdfgh', 1000 )
                );
        }
 }
index ff22bfa..8f7b2a6 100644 (file)
@@ -103,6 +103,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertSame( $anonInfo, $info->getUserInfo() );
                $this->assertTrue( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertFalse( $info->wasPersisted() );
                $this->assertFalse( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
@@ -118,6 +119,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
                $this->assertTrue( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertFalse( $info->wasPersisted() );
                $this->assertFalse( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
@@ -132,6 +134,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertSame( $userInfo, $info->getUserInfo() );
                $this->assertTrue( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertFalse( $info->wasPersisted() );
                $this->assertTrue( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
@@ -150,6 +153,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertSame( $anonInfo, $info->getUserInfo() );
                $this->assertFalse( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertTrue( $info->wasPersisted() );
                $this->assertFalse( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
@@ -165,6 +169,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertSame( $userInfo, $info->getUserInfo() );
                $this->assertFalse( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertFalse( $info->wasPersisted() );
                $this->assertTrue( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
@@ -180,6 +185,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertSame( $userInfo, $info->getUserInfo() );
                $this->assertFalse( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertTrue( $info->wasPersisted() );
                $this->assertFalse( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
@@ -231,6 +237,25 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
                $this->assertTrue( $info->isIdSafe() );
 
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, [
+                       'id' => $id,
+                       'forceUse' => true,
+               ] );
+               $this->assertFalse( $info->forceUse(), 'no provider' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, [
+                       'provider' => $provider,
+                       'forceUse' => true,
+               ] );
+               $this->assertFalse( $info->forceUse(), 'no id' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, [
+                       'provider' => $provider,
+                       'id' => $id,
+                       'forceUse' => true,
+               ] );
+               $this->assertTrue( $info->forceUse(), 'correct use' );
+
                $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
                        'id' => $id,
                        'forceHTTPS' => 1,
@@ -242,6 +267,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                        'provider' => $provider,
                        'userInfo' => $userInfo,
                        'idIsSafe' => true,
+                       'forceUse' => true,
                        'persisted' => true,
                        'remembered' => true,
                        'forceHTTPS' => true,
@@ -255,6 +281,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( $provider, $info->getProvider() );
                $this->assertSame( $userInfo, $info->getUserInfo() );
                $this->assertTrue( $info->isIdSafe() );
+               $this->assertTrue( $info->forceUse() );
                $this->assertTrue( $info->wasPersisted() );
                $this->assertTrue( $info->wasRemembered() );
                $this->assertTrue( $info->forceHTTPS() );
@@ -265,6 +292,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                        'provider' => $provider2,
                        'userInfo' => $unverifiedUserInfo,
                        'idIsSafe' => false,
+                       'forceUse' => false,
                        'persisted' => false,
                        'remembered' => false,
                        'forceHTTPS' => false,
@@ -276,6 +304,7 @@ class SessionInfoTest extends MediaWikiTestCase {
                $this->assertSame( $provider2, $info->getProvider() );
                $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
                $this->assertFalse( $info->isIdSafe() );
+               $this->assertFalse( $info->forceUse() );
                $this->assertFalse( $info->wasPersisted() );
                $this->assertFalse( $info->wasRemembered() );
                $this->assertFalse( $info->forceHTTPS() );
index d04d7ec..5f387ea 100644 (file)
@@ -642,6 +642,35 @@ class SessionManagerTest extends MediaWikiTestCase {
                }
        }
 
+       public function testInvalidateSessionsForUser() {
+               $user = User::newFromName( 'UTSysop' );
+               $manager = $this->getManager();
+
+               $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( [ 'invalidateSessionsForUser', '__toString' ] );
+
+               $provider1 = $providerBuilder->getMock();
+               $provider1->expects( $this->once() )->method( 'invalidateSessionsForUser' )
+                       ->with( $this->identicalTo( $user ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider1' ) );
+
+               $provider2 = $providerBuilder->getMock();
+               $provider2->expects( $this->once() )->method( 'invalidateSessionsForUser' )
+                       ->with( $this->identicalTo( $user ) );
+               $provider2->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider2' ) );
+
+               $this->config->set( 'SessionProviders', [
+                       $this->objectCacheDef( $provider1 ),
+                       $this->objectCacheDef( $provider2 ),
+               ] );
+
+               $oldToken = $user->getToken( true );
+               $manager->invalidateSessionsForUser( $user );
+               $this->assertNotEquals( $oldToken, $user->getToken() );
+       }
+
        public function testGetVaryHeaders() {
                $manager = $this->getManager();
 
@@ -1756,5 +1785,21 @@ class SessionManagerTest extends MediaWikiTestCase {
                        [ LogLevel::WARNING, 'Session "{session}": Hook aborted' ],
                ], $logger->getBuffer() );
                $logger->clearBuffer();
+               $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionCheckInfo' => [] ] );
+
+               // forceUse deletes bad backend data
+               $this->store->setSessionMeta( $id, [ 'userToken' => 'Bad' ] + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo,
+                       'forceUse' => true,
+               ] );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertFalse( $this->store->getSession( $id ) );
+               $this->assertSame( [
+                       [ LogLevel::WARNING, 'Session "{session}": User token mismatch' ],
+               ], $logger->getBuffer() );
+               $logger->clearBuffer();
        }
 }
index 18b1efd..f80baf2 100644 (file)
@@ -27,6 +27,8 @@ class SessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( $manager, $priv->manager );
                $this->assertSame( $manager, $provider->getManager() );
 
+               $provider->invalidateSessionsForUser( new \User );
+
                $this->assertSame( [], $provider->getVaryHeaders() );
                $this->assertSame( [], $provider->getVaryCookies() );
                $this->assertSame( null, $provider->suggestLoginUsername( new \FauxRequest ) );
index e321bdb..e55a3a4 100644 (file)
@@ -179,6 +179,39 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $actual );
        }
 
+       public static function provideGetPrefixedDBkey() {
+               return [
+                       [ NS_MAIN, 'Foo_Bar', '', '', 'en', 'Foo_Bar' ],
+                       [ NS_USER, 'Hansi_Maier', 'stuff_and_so_on', '', 'en', 'User:Hansi_Maier' ],
+
+                       // No capitalization or normalization is applied while formatting!
+                       [ NS_USER_TALK, 'hansi__maier', '', '', 'en', 'User_talk:hansi__maier' ],
+
+                       // getGenderCache() provides a mock that considers first
+                       // names ending in "a" to be female.
+                       [ NS_USER, 'Lisa_Müller', '', '', 'de', 'Benutzerin:Lisa_Müller' ],
+
+                       [ NS_MAIN, 'Remote_page', '', 'remotetestiw', 'en', 'remotetestiw:Remote_page' ],
+
+                       // non-existent namespace
+                       [ 10000000, 'Foobar', '', '', 'en', ':Foobar' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetPrefixedDBkey
+        */
+       public function testGetPrefixedDBkey( $namespace, $dbkey, $fragment,
+               $interwiki, $lang, $expected
+       ) {
+               $codec = $this->makeCodec( $lang );
+               $title = new TitleValue( $namespace, $dbkey, $fragment, $interwiki );
+
+               $actual = $codec->getPrefixedDBkey( $title );
+
+               $this->assertEquals( $expected, $actual );
+       }
+
        public static function provideGetFullText() {
                return [
                        [ NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ],
index 7922553..4dbda74 100644 (file)
@@ -42,6 +42,7 @@ class TitleValueTest extends MediaWikiTestCase {
                $title = new TitleValue( $ns, $text, $fragment, $interwiki );
 
                $this->assertEquals( $ns, $title->getNamespace() );
+               $this->assertTrue( $title->inNamespace( $ns ) );
                $this->assertEquals( $text, $title->getText() );
                $this->assertEquals( $fragment, $title->getFragment() );
                $this->assertEquals( $hasFragment, $title->hasFragment() );
index ee948bb..6ee0ff4 100644 (file)
        } );
 
        // HTML in wikitext
-       QUnit.test( 'HTML', 32, function ( assert ) {
+       QUnit.test( 'HTML', 33, function ( assert ) {
                mw.messages.set( 'jquerymsg-italics-msg', '<i>Very</i> important' );
 
                assertBothModes( assert, [ 'jquerymsg-italics-msg' ], mw.messages.get( 'jquerymsg-italics-msg' ), 'Simple italics unchanged' );
                        'Self-closing tags don\'t cause a parse error'
                );
 
+               mw.messages.set( 'jquerymsg-asciialphabetliteral-regression', '<b >>>="dir">asd</b>' );
+               assert.htmlEqual(
+                       formatParse( 'jquerymsg-asciialphabetliteral-regression' ),
+                       '<b>&gt;&gt;="dir"&gt;asd</b>',
+                       'Regression test for bad "asciiAlphabetLiteral" definition'
+               );
+
                mw.messages.set( 'jquerymsg-entities1', 'A&B' );
                mw.messages.set( 'jquerymsg-entities2', 'A&gt;B' );
                mw.messages.set( 'jquerymsg-entities3', 'A&rarr;B' );