Merge "api: Update QueryFilearchive to provide information to everyone"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 9 Sep 2019 19:29:48 +0000 (19:29 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 9 Sep 2019 19:29:48 +0000 (19:29 +0000)
199 files changed:
RELEASE-NOTES-1.34
api.php
autoload.php
composer.json
docs/extension.schema.v1.json
docs/extension.schema.v2.json
docs/hooks.txt
img_auth.php
includes/ActorMigration.php
includes/AjaxResponse.php
includes/DefaultSettings.php
includes/EditPage.php
includes/FileDeleteForm.php
includes/OutputPage.php
includes/PHPVersionCheck.php
includes/Revision.php
includes/ServiceWiring.php
includes/Setup.php
includes/actions/InfoAction.php
includes/api/ApiExpandTemplates.php
includes/api/ApiMain.php
includes/api/ApiModuleManager.php
includes/api/ApiQuery.php
includes/api/ApiQueryAllRevisions.php
includes/api/ApiQueryAllUsers.php
includes/api/ApiQueryContributors.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQueryUserContribs.php
includes/api/ApiQueryUserInfo.php
includes/api/i18n/fr.json
includes/api/i18n/ru.json
includes/auth/AuthenticationRequest.php
includes/auth/ResetPasswordSecondaryAuthenticationProvider.php
includes/block/DatabaseBlock.php
includes/cache/UserCache.php
includes/changes/RecentChange.php
includes/composer/ComposerPhpunitXmlCoverageEdit.php [new file with mode: 0644]
includes/config/ConfigFactory.php
includes/config/ConfigRepository.php
includes/context/RequestContext.php
includes/dao/DBAccessBase.php
includes/debug/logger/ConsoleLogger.php
includes/deferred/CdnCacheUpdate.php
includes/deferred/JobQueueEnqueueUpdate.php
includes/deferred/MessageCacheUpdate.php
includes/deferred/SiteStatsUpdate.php
includes/deferred/UserEditCountUpdate.php
includes/diff/DifferenceEngine.php
includes/filerepo/file/ArchivedFile.php
includes/filerepo/file/LocalFile.php
includes/filerepo/file/LocalFileDeleteBatch.php
includes/filerepo/file/OldLocalFile.php
includes/import/ImportStreamSource.php
includes/import/ImportStringSource.php
includes/installer/DatabaseUpdater.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/i18n/ckb.json
includes/installer/i18n/fr.json
includes/installer/i18n/it.json
includes/installer/i18n/nl.json
includes/libs/StatusValue.php
includes/libs/filebackend/FSFileBackend.php
includes/libs/filebackend/SwiftFileBackend.php
includes/logging/LogPage.php
includes/logging/ManualLogEntry.php
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/parser/PPDPart.php
includes/parser/PPDStack.php
includes/parser/PPDStackElement.php
includes/parser/PPFrame.php
includes/parser/PPFrame_DOM.php
includes/parser/PPTemplateFrame_Hash.php
includes/parser/Preprocessor.php
includes/parser/Preprocessor_Hash.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/revisiondelete/RevDelList.php
includes/revisiondelete/RevisionDeleteUser.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialContributions.php
includes/specials/SpecialLog.php
includes/specials/SpecialPageData.php
includes/specials/SpecialRecentChangesLinked.php
includes/specials/SpecialStatistics.php
includes/specials/pagers/ActiveUsersPager.php
includes/specials/pagers/NewFilesPager.php
includes/upload/UploadFromStash.php
includes/user/User.php
includes/watcheditem/NoWriteWatchedItemStore.php
index.php
languages/i18n/ar.json
languages/i18n/ast.json
languages/i18n/ban.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/ckb.json
languages/i18n/dtp.json
languages/i18n/en.json
languages/i18n/exif/id.json
languages/i18n/exif/sd.json
languages/i18n/fr.json
languages/i18n/gom-deva.json
languages/i18n/gom-latn.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/io.json
languages/i18n/khw.json
languages/i18n/ko.json
languages/i18n/luz.json
languages/i18n/min.json
languages/i18n/mni.json
languages/i18n/my.json
languages/i18n/nap.json
languages/i18n/nds-nl.json
languages/i18n/nl.json
languages/i18n/nqo.json
languages/i18n/pl.json
languages/i18n/pt-br.json
languages/i18n/qqq.json
languages/i18n/sd.json
languages/i18n/sk.json
languages/i18n/szl.json
languages/i18n/te.json
languages/i18n/tr.json
languages/messages/MessagesHyw.php
load.php
maintenance/Maintenance.php
maintenance/archives/patch-drop-user-fields.sql [new file with mode: 0644]
maintenance/cleanupImages.php
maintenance/compareParsers.php
maintenance/dumpIterator.php
maintenance/dumpUploads.php
maintenance/getReplicaServer.php
maintenance/importDump.php
maintenance/includes/BackupDumper.php
maintenance/includes/MigrateActors.php
maintenance/jsduck/categories.json
maintenance/mergeMessageFileList.php
maintenance/mwdocgen.php
maintenance/postgres/tables.sql
maintenance/preprocessDump.php
maintenance/preprocessorFuzzTest.php
maintenance/reassignEdits.php
maintenance/rebuildImages.php
maintenance/recountCategories.php
maintenance/removeUnusedAccounts.php
maintenance/renderDump.php
maintenance/resetUserTokens.php
maintenance/sqlite/archives/patch-archive-drop-ar_user.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-filearchive-drop-fa_user.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-image-drop-img_user.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-ipblocks-drop-ipb_by.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-logging-drop-log_user.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-oldimage-drop-oi_user.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-recentchanges-drop-rc_user.sql [new file with mode: 0644]
maintenance/storage/recompressTracked.php
maintenance/tables.sql
opensearch_desc.php
profileinfo.php
resources/Resources.php
resources/src/jquery.tablesorter/jquery.tablesorter.js
resources/src/jquery/jquery.highlightText.js
resources/src/mediawiki.RegExp.js [deleted file]
resources/src/mediawiki.Title/Title.js
resources/src/mediawiki.htmlform/cloner.js
resources/src/mediawiki.inspect.js
resources/src/mediawiki.page.watch.ajax.js
resources/src/mediawiki.util/.eslintrc.json [new file with mode: 0644]
resources/src/mediawiki.util/jquery.accessKeyLabel.js
resources/src/mediawiki.util/util.js
resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
resources/src/moment/moment-locale-overrides.js
rest.php
tests/parser/ParserTestRunner.php
tests/phpunit/includes/ActorMigrationTest.php
tests/phpunit/includes/ActorMigrationTest.sql [new file with mode: 0644]
tests/phpunit/includes/Revision/RevisionQueryInfoTest.php
tests/phpunit/includes/Revision/RevisionStoreDbTestBase.php
tests/phpunit/includes/RevisionDbTestBase.php
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/api/ApiModuleManagerTest.php
tests/phpunit/includes/api/ApiQueryLanguageinfoTest.php
tests/phpunit/includes/api/format/ApiFormatTestBase.php
tests/phpunit/includes/api/query/ApiQueryUserContribsTest.php
tests/phpunit/includes/filebackend/FileBackendTest.php
tests/phpunit/includes/logging/DatabaseLogEntryTest.php
tests/phpunit/includes/page/PageArchiveTestBase.php
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/maintenance/deleteAutoPatrolLogsTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js [deleted file]
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
thumb.php
thumb_handler.php

index 3ba9537..e0f4752 100644 (file)
@@ -80,6 +80,7 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
 * $wgWikiDiff2MovedParagraphDetectionCutoff — If you still want a custom change
   size threshold, please specify in php.ini, using the configuration variable
   wikidiff2.moved_paragraph_detection_cutoff.
+* $wgUseESI - This experimental setting, deprecated in 1.33, is now removed.
 * $wgDebugPrintHttpHeaders - The default of including HTTP headers in the
   debug log channel is no longer configurable. The debug log itself remains
   configurable via $wgDebugLogFile.
@@ -90,6 +91,8 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
 * $wgDBOracleDRCP - If you must use persistent connections, set DBO_PERSISTENT
   in the 'flags' field for servers in $wgDBServers (or $wgLBFactoryConf).
 * $wgMemCachedDebug - Set the cache "debug" field in $wgObjectCaches instead.
+* $wgActorTableSchemaMigrationStage has been removed. Extension code for
+  MediaWiki 1.31+ finding it unset should treat it as being SCHEMA_COMPAT_NEW.
 
 === New user-facing features in 1.34 ===
 * Special:Mute has been added as a quick way for users to block unwanted emails
@@ -117,6 +120,12 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
   GetBlockedStatus.
 * ObjectFactory is available as a service. When used as a service, the object
   specs can now specify needed DI services.
+* (T222388) Special pages can now be specified as an ObjectFactory spec,
+  allowing the construction of special pages that require services to be
+  injected in their constructor.
+* (T222388) API modules can now be specified as an ObjectFactory spec,
+  allowing the construction of modules that require services to be injected
+  in their constructor.
 
 === External library changes in 1.34 ===
 
@@ -157,6 +166,11 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
   user right.
 
 === Action API internal changes in 1.34 ===
+* The exception thrown in ApiModuleManager::getModule has been changed
+  from an MWException to an UnexpectedValueException, thrown by ObjectFactory.
+  ApiModuleManager::getModule now also throws InvalidArgumentExceptions when
+  ObjectFactory is presented with an invalid spec or incorrectly constructed
+  objects.
 * …
 
 === Languages updated in 1.34 ===
@@ -373,7 +387,19 @@ because of Phabricator reports.
   should only be used to unblock a blocked user.
 * Parameters for index.php from PATH_INFO, such as the title, are no longer
   written to $_GET.
-* …
+* The selectFields() methods on classes LocalFile, ArchivedFile, OldLocalFile,
+  DatabaseBlock, and RecentChange, deprecated in 1.31, have been removed. Use
+  the corresponding getQueryInfo() methods instead.
+* The following methods on Revision, deprecated since 1.31, have been removed.
+  Use RevisionStore::getQueryInfo() or RevisionStore::getArchiveQueryInfo()
+  instead.
+  * Revision::userJoinCond()
+  * Revision::pageJoinCond()
+  * Revision::selectFields()
+  * Revision::selectArchiveFields()
+  * Revision::selectTextFields()
+  * Revision::selectPageFields()
+  * Revision::selectUserFields()
 
 === Deprecations in 1.34 ===
 * The MWNamespace class is deprecated. Use NamespaceInfo.
@@ -493,6 +519,17 @@ because of Phabricator reports.
   class. If you extend this class please be sure to override all its methods
   or extend RevisionSearchResult.
 * Skin::getSkinNameMessages() is deprecated and no longer used.
+* The mediawiki.RegExp module is deprecated; use mw.util.escapeRegExp() instead.
+* Specifying a SpecialPage object for the list of special pages (either through
+  the SpecialPage_initList hook or by adding to $wgSpecialPages) is now
+  deprecated.
+* Use of ActorMigration with 'ar_user', 'img_user', 'oi_user', 'fa_user',
+  'rc_user', 'log_user', and 'ipb_by' is deprecated. Queries should be adjusted
+  to use the corresponding actor fields directly. Note that use with
+  'rev_user' is *not* deprecated at this time.
+* Specifying both the class and factory parameters for
+  ApiModuleManager::addModule is now deprecated. The ObjectFactory spec should
+  be used instead.
 
 === Other changes in 1.34 ===
 * …
diff --git a/api.php b/api.php
index fe13263..6f4bac3 100644 (file)
--- a/api.php
+++ b/api.php
@@ -31,6 +31,7 @@ use MediaWiki\Logger\LegacyLogger;
 
 // So extensions (and other code) can check whether they're running in API mode
 define( 'MW_API', true );
+define( 'MW_ENTRY_POINT', 'api' );
 
 require __DIR__ . '/includes/WebStart.php';
 
index 0255a23..87b2bb9 100644 (file)
@@ -298,6 +298,7 @@ $wgAutoloadLocalClasses = [
        'ComposerJson' => __DIR__ . '/includes/libs/composer/ComposerJson.php',
        'ComposerLock' => __DIR__ . '/includes/libs/composer/ComposerLock.php',
        'ComposerPackageModifier' => __DIR__ . '/includes/composer/ComposerPackageModifier.php',
+       'ComposerPhpunitXmlCoverageEdit' => __DIR__ . '/includes/composer/ComposerPhpunitXmlCoverageEdit.php',
        'ComposerVendorHtaccessCreator' => __DIR__ . '/includes/composer/ComposerVendorHtaccessCreator.php',
        'ComposerVersionNormalizer' => __DIR__ . '/includes/composer/ComposerVersionNormalizer.php',
        'CompressOld' => __DIR__ . '/maintenance/storage/compressOld.php',
index ac89d71..c1f9037 100644 (file)
@@ -97,7 +97,8 @@
        "autoload": {
                "psr-0": {
                        "ComposerHookHandler": "includes/composer",
-                       "ComposerVendorHtaccessCreator": "includes/composer"
+                       "ComposerVendorHtaccessCreator": "includes/composer",
+                       "ComposerPhpunitXmlCoverageEdit":"includes/composer"
                }
        },
        "autoload-dev": {
                "phpunit": "phpunit",
                "phpunit:unit": "phpunit --colors=always --testsuite=core:unit,extensions:unit,skins:unit",
                "phpunit:integration": "phpunit --colors=always --testsuite=core:integration,extensions:integration,skins:integration",
-               "phpunit:coverage": "phpunit --testsuite=core:unit --exclude-group Dump,Broken"
+               "phpunit:coverage": "phpunit --testsuite=core:unit --exclude-group Dump,Broken",
+               "phpunit:coverage-edit": "ComposerPhpunitXmlCoverageEdit::onEvent"
        },
        "config": {
                "optimize-autoloader": true,
index 9ce016f..06701cd 100644 (file)
                },
                "SpecialPages": {
                        "type": "object",
-                       "description": "SpecialPages implemented in this extension (mapping of page name to class name)"
+                       "description": "SpecialPages implemented in this extension (mapping of page name to class name or to ObjectFactory spec)"
                },
                "AutoloadNamespaces": {
                        "type": "object",
index 9d874f4..56d274b 100644 (file)
                },
                "SpecialPages": {
                        "type": "object",
-                       "description": "SpecialPages implemented in this extension (mapping of page name to class name)"
+                       "description": "SpecialPages implemented in this extension (mapping of page name to class name or to ObjectFactory spec)"
                },
                "AutoloadNamespaces": {
                        "type": "object",
index b7ea02c..a248c29 100644 (file)
@@ -2827,6 +2827,7 @@ or request state must be added through MakeGlobalVariablesScript instead.
 Skin is made available for skin specific config.
 &$vars: [ variable name => value ]
 $skin: Skin
+$config: Config object (since 1.34)
 
 'ResourceLoaderJqueryMsgModuleMagicWords': Called in
 ResourceLoaderJqueryMsgModule to allow adding magic words for jQueryMsg.
index 6e45e4e..f23de4f 100644 (file)
@@ -39,6 +39,7 @@
  */
 
 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
+define( 'MW_ENTRY_POINT', 'img_auth' );
 require __DIR__ . '/includes/WebStart.php';
 
 # Set action base paths so that WebRequest::getPathInfo()
index 5dde8a0..c79074d 100644 (file)
@@ -28,15 +28,18 @@ use Wikimedia\Rdbms\IDatabase;
  * This class handles the logic for the actor table migration.
  *
  * This is not intended to be a long-term part of MediaWiki; it will be
- * deprecated and removed along with $wgActorTableSchemaMigrationStage.
+ * deprecated and removed once actor migration is complete.
  *
  * @since 1.31
+ * @since 1.34 Use with 'ar_user', 'img_user', 'oi_user', 'fa_user',
+ *  'rc_user', 'log_user', and 'ipb_by' is deprecated. Callers should
+ *  reference the corresponding actor fields directly.
  */
 class ActorMigration {
 
        /**
         * Constant for extensions to feature-test whether $wgActorTableSchemaMigrationStage
-        * expects MIGRATION_* or SCHEMA_COMPAT_*
+        * (in MW <1.34) expects MIGRATION_* or SCHEMA_COMPAT_*
         */
        const MIGRATION_STAGE_SCHEMA_COMPAT = 1;
 
@@ -68,6 +71,28 @@ class ActorMigration {
         */
        private static $formerTempTables = [];
 
+       /**
+        * Define fields that are deprecated for use with this class.
+        * @var (string|null)[] Keys are '$key', value is null for soft deprecation
+        *  or a string naming the deprecated version for hard deprecation.
+        */
+       private static $deprecated = [
+               'ar_user' => null, // 1.34
+               'img_user' => null, // 1.34
+               'oi_user' => null, // 1.34
+               'fa_user' => null, // 1.34
+               'rc_user' => null, // 1.34
+               'log_user' => null, // 1.34
+               'ipb_by' => null, // 1.34
+       ];
+
+       /**
+        * Define fields that are removed for use with this class.
+        * @var string[] Keys are '$key', value is the MediaWiki version in which
+        *  use was removed.
+        */
+       private static $removed = [];
+
        /**
         * Define fields that use non-standard mapping
         * @var array Keys are the user id column name, values are arrays with two
@@ -112,6 +137,21 @@ class ActorMigration {
                return MediaWikiServices::getInstance()->getActorMigration();
        }
 
+       /**
+        * Issue deprecation warning/error as appropriate.
+        * @param string $key
+        */
+       private static function checkDeprecation( $key ) {
+               if ( isset( self::$removed[$key] ) ) {
+                       throw new InvalidArgumentException(
+                               "Use of " . static::class . " for '$key' was removed in MediaWiki " . self::$removed[$key]
+                       );
+               }
+               if ( !empty( self::$deprecated[$key] ) ) {
+                       wfDeprecated( static::class . " for '$key'", self::$deprecated[$key], false, 3 );
+               }
+       }
+
        /**
         * Return an SQL condition to test if a user field is anonymous
         * @param string $field Field name or SQL fragment
@@ -152,6 +192,8 @@ class ActorMigration {
         * @phan-return array{tables:string[],fields:string[],joins:array}
         */
        public function getJoin( $key ) {
+               self::checkDeprecation( $key );
+
                if ( !isset( $this->joinCache[$key] ) ) {
                        $tables = [];
                        $fields = [];
@@ -203,6 +245,8 @@ class ActorMigration {
         * @return array to merge into `$values` to `IDatabase->update()` or `$a` to `IDatabase->insert()`
         */
        public function getInsertValues( IDatabase $dbw, $key, UserIdentity $user ) {
+               self::checkDeprecation( $key );
+
                if ( isset( self::$tempTables[$key] ) ) {
                        throw new InvalidArgumentException( "Must use getInsertValuesWithTempTable() for $key" );
                }
@@ -236,6 +280,8 @@ class ActorMigration {
         *    and extra fields needed for the temp table.
         */
        public function getInsertValuesWithTempTable( IDatabase $dbw, $key, UserIdentity $user ) {
+               self::checkDeprecation( $key );
+
                if ( isset( self::$formerTempTables[$key] ) ) {
                        wfDeprecated( __METHOD__ . " for $key", self::$formerTempTables[$key] );
                } elseif ( !isset( self::$tempTables[$key] ) ) {
@@ -319,6 +365,8 @@ class ActorMigration {
         *  All tables and joins are aliased, so `+` is safe to use.
         */
        public function getWhere( IDatabase $db, $key, $users, $useId = true ) {
+               self::checkDeprecation( $key );
+
                $tables = [];
                $conds = [];
                $joins = [];
index 323c5d3..0664652 100644 (file)
@@ -182,16 +182,7 @@ class AjaxResponse {
                        if ( $this->mConfig->get( 'UseCdn' ) ) {
                                # Expect explicit purge of the proxy cache, but require end user agents
                                # to revalidate against the proxy on each visit.
-                               # Surrogate-Control controls our CDN, Cache-Control downstream caches
-
-                               if ( $this->mConfig->get( 'UseESI' ) ) {
-                                       wfDeprecated( '$wgUseESI = true', '1.33' );
-                                       header( 'Surrogate-Control: max-age=' . $this->mCacheDuration . ', content="ESI/1.0"' );
-                                       header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
-                               } else {
-                                       header( 'Cache-Control: s-maxage=' . $this->mCacheDuration . ', must-revalidate, max-age=0' );
-                               }
-
+                               header( 'Cache-Control: s-maxage=' . $this->mCacheDuration . ', must-revalidate, max-age=0' );
                        } else {
                                # Let the client do the caching. Cache is not purged.
                                header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT" );
index 5d3fba7..f6ac342 100644 (file)
@@ -2748,12 +2748,6 @@ $wgExtensionInfoMTime = false;
  */
 $wgUseCdn = false;
 
-/**
- * If you run Squid3 with ESI support, enable this (default:false):
- * @deprecated in 1.33. This was a now-defunct experimental feature.
- */
-$wgUseESI = false;
-
 /**
  * Add X-Forwarded-Proto to the Vary and Key headers for API requests and
  * RSS/Atom feeds. Use this if you have an SSL termination setup
@@ -8985,24 +8979,6 @@ $wgMultiContentRevisionSchemaMigrationStage = SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_
  */
 $wgXmlDumpSchemaVersion = XML_DUMP_SCHEMA_VERSION_10;
 
-/**
- * Actor table schema migration stage.
- *
- * Use the SCHEMA_COMPAT_XXX flags. Supported values:
- * - SCHEMA_COMPAT_OLD
- * - SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- * - SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
- * - SCHEMA_COMPAT_NEW
- *
- * Note that reading the old and new schema at the same time is not supported
- * in 1.32, but was (with significant query performance issues) in 1.31.
- *
- * @since 1.31
- * @since 1.32 changed allowed flags
- * @var int An appropriate combination of SCHEMA_COMPAT_XXX flags.
- */
-$wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_NEW;
-
 /**
  * Flag to enable Partial Blocks. This allows an admin to prevent a user from editing specific pages
  * or namespaces.
index 6ae4371..c346b75 100644 (file)
@@ -1193,6 +1193,8 @@ class EditPage {
         * @since 1.21
         */
        protected function getContentObject( $def_content = null ) {
+               global $wgDisableAnonTalk;
+
                $content = false;
 
                $user = $this->context->getUser();
@@ -1292,8 +1294,11 @@ class EditPage {
                                                                                $undo
                                                                        )->inContentLanguage()->text();
                                                                } else {
+                                                                       $undoMessage = ( $undorev->getUser() === 0 && $wgDisableAnonTalk ) ?
+                                                                               'undo-summary-anon' :
+                                                                               'undo-summary';
                                                                        $undoSummary = $this->context->msg(
-                                                                               'undo-summary',
+                                                                               $undoMessage,
                                                                                $undo,
                                                                                $userText
                                                                        )->inContentLanguage()->text();
index 1241e1c..e31f9d2 100644 (file)
@@ -36,18 +36,18 @@ class FileDeleteForm {
        private $title = null;
 
        /**
-        * @var File
+        * @var LocalFile
         */
        private $file = null;
 
        /**
-        * @var File
+        * @var LocalFile
         */
        private $oldfile = null;
        private $oldimage = '';
 
        /**
-        * @param File $file File object we're deleting
+        * @param LocalFile $file File object we're deleting
         */
        public function __construct( $file ) {
                $this->title = $file->getTitle();
@@ -451,9 +451,9 @@ class FileDeleteForm {
         * value was provided, does it correspond to an
         * existing, local, old version of this file?
         *
-        * @param File &$file
-        * @param File &$oldfile
-        * @param File $oldimage
+        * @param LocalFile &$file
+        * @param LocalFile &$oldfile
+        * @param LocalFile $oldimage
         * @return bool
         */
        public static function haveDeletableFile( &$file, &$oldfile, $oldimage ) {
index 15a759b..7f005fb 100644 (file)
@@ -2414,32 +2414,16 @@ class OutputPage extends ContextSource {
                                $this->mCdnMaxage != 0 &&
                                !$this->haveCacheVaryCookies()
                        ) {
-                               if ( $config->get( 'UseESI' ) ) {
-                                       wfDeprecated( '$wgUseESI = true', '1.33' );
-                                       # We'll purge the proxy cache explicitly, but require end user agents
-                                       # to revalidate against the proxy on each visit.
-                                       # Surrogate-Control controls our CDN, Cache-Control downstream caches
-                                       wfDebug( __METHOD__ .
-                                               ": proxy caching with ESI; {$this->mLastModified} **", 'private' );
-                                       # start with a shorter timeout for initial testing
-                                       # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
-                                       $response->header(
-                                               "Surrogate-Control: max-age={$config->get( 'CdnMaxAge' )}" .
-                                               "+{$this->mCdnMaxage}, content=\"ESI/1.0\""
-                                       );
-                                       $response->header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
-                               } else {
-                                       # We'll purge the proxy cache for anons explicitly, but require end user agents
-                                       # to revalidate against the proxy on each visit.
-                                       # IMPORTANT! The CDN needs to replace the Cache-Control header with
-                                       # Cache-Control: s-maxage=0, must-revalidate, max-age=0
-                                       wfDebug( __METHOD__ .
-                                               ": local proxy caching; {$this->mLastModified} **", 'private' );
-                                       # start with a shorter timeout for initial testing
-                                       # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
-                                       $response->header( "Cache-Control: " .
-                                               "s-maxage={$this->mCdnMaxage}, must-revalidate, max-age=0" );
-                               }
+                               # We'll purge the proxy cache for anons explicitly, but require end user agents
+                               # to revalidate against the proxy on each visit.
+                               # IMPORTANT! The CDN needs to replace the Cache-Control header with
+                               # Cache-Control: s-maxage=0, must-revalidate, max-age=0
+                               wfDebug( __METHOD__ .
+                                       ": local proxy caching; {$this->mLastModified} **", 'private' );
+                               # start with a shorter timeout for initial testing
+                               # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
+                               $response->header( "Cache-Control: " .
+                                       "s-maxage={$this->mCdnMaxage}, must-revalidate, max-age=0" );
                        } else {
                                # We do want clients to cache if they can, but they *must* check for updates
                                # on revisiting the page.
index b63a84d..bf138c4 100644 (file)
@@ -31,8 +31,6 @@
  *
  * @note This class uses setter methods instead of a constructor so that
  * it can be compatible with PHP 4, PHP 5 and PHP 7 (without warnings).
- *
- * @class
  */
 class PHPVersionCheck {
        /* @var string The number of the MediaWiki version used. */
index c6e727e..292d6ba 100644 (file)
@@ -298,203 +298,6 @@ class Revision implements IDBAccessObject {
                return $rec ? new Revision( $rec ) : null;
        }
 
-       /**
-        * Return the value of a select() JOIN conds array for the user table.
-        * This will get user table rows for logged-in users.
-        * @since 1.19
-        * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'user' ] ) instead.
-        * @return array
-        */
-       public static function userJoinCond() {
-               global $wgActorTableSchemaMigrationStage;
-
-               wfDeprecated( __METHOD__, '1.31' );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's
-                       // no way the join it's trying to do can work once the old fields
-                       // aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               return [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
-       }
-
-       /**
-        * Return the value of a select() page conds array for the page table.
-        * This will assure that the revision(s) are not orphaned from live pages.
-        * @since 1.19
-        * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'page' ] ) instead.
-        * @return array
-        */
-       public static function pageJoinCond() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return [ 'JOIN', [ 'page_id = rev_page' ] ];
-       }
-
-       /**
-        * Return the list of revision fields that should be selected to create
-        * a new revision.
-        * @deprecated since 1.31, use RevisionStore::getQueryInfo() instead.
-        * @return array
-        */
-       public static function selectFields() {
-               global $wgContentHandlerUseDB, $wgActorTableSchemaMigrationStage;
-               global $wgMultiContentRevisionSchemaMigrationStage;
-
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->rev_user or $row->rev_user_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               if ( !( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->rev_text_id or $row->rev_content_model and we can't give it
-                       // useful values here once those aren't being written anymore,
-                       // and may not exist at all.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__ . ' when $wgMultiContentRevisionSchemaMigrationStage '
-                               . 'does not have SCHEMA_COMPAT_WRITE_OLD set.'
-                       );
-               }
-
-               wfDeprecated( __METHOD__, '1.31' );
-
-               $fields = [
-                       'rev_id',
-                       'rev_page',
-                       'rev_text_id',
-                       'rev_timestamp',
-                       'rev_user_text',
-                       'rev_user',
-                       'rev_actor' => 'NULL',
-                       'rev_minor_edit',
-                       'rev_deleted',
-                       'rev_len',
-                       'rev_parent_id',
-                       'rev_sha1',
-               ];
-
-               $fields += CommentStore::getStore()->getFields( 'rev_comment' );
-
-               if ( $wgContentHandlerUseDB ) {
-                       $fields[] = 'rev_content_format';
-                       $fields[] = 'rev_content_model';
-               }
-
-               return $fields;
-       }
-
-       /**
-        * Return the list of revision fields that should be selected to create
-        * a new revision from an archive row.
-        * @deprecated since 1.31, use RevisionStore::getArchiveQueryInfo() instead.
-        * @return array
-        */
-       public static function selectArchiveFields() {
-               global $wgContentHandlerUseDB, $wgActorTableSchemaMigrationStage;
-               global $wgMultiContentRevisionSchemaMigrationStage;
-
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->ar_user or $row->ar_user_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               if ( !( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->ar_text_id or $row->ar_content_model and we can't give it
-                       // useful values here once those aren't being written anymore,
-                       // and may not exist at all.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__ . ' when $wgMultiContentRevisionSchemaMigrationStage '
-                               . 'does not have SCHEMA_COMPAT_WRITE_OLD set.'
-                       );
-               }
-
-               wfDeprecated( __METHOD__, '1.31' );
-
-               $fields = [
-                       'ar_id',
-                       'ar_page_id',
-                       'ar_rev_id',
-                       'ar_text_id',
-                       'ar_timestamp',
-                       'ar_user_text',
-                       'ar_user',
-                       'ar_actor' => 'NULL',
-                       'ar_minor_edit',
-                       'ar_deleted',
-                       'ar_len',
-                       'ar_parent_id',
-                       'ar_sha1',
-               ];
-
-               $fields += CommentStore::getStore()->getFields( 'ar_comment' );
-
-               if ( $wgContentHandlerUseDB ) {
-                       $fields[] = 'ar_content_format';
-                       $fields[] = 'ar_content_model';
-               }
-               return $fields;
-       }
-
-       /**
-        * Return the list of text fields that should be selected to read the
-        * revision text
-        * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'text' ] ) instead.
-        * @return array
-        */
-       public static function selectTextFields() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return [
-                       'old_text',
-                       'old_flags'
-               ];
-       }
-
-       /**
-        * Return the list of page fields that should be selected from page table
-        * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'page' ] ) instead.
-        * @return array
-        */
-       public static function selectPageFields() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return [
-                       'page_namespace',
-                       'page_title',
-                       'page_id',
-                       'page_latest',
-                       'page_is_redirect',
-                       'page_len',
-               ];
-       }
-
-       /**
-        * Return the list of user fields that should be selected from user table
-        * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'user' ] ) instead.
-        * @return array
-        */
-       public static function selectUserFields() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return [ 'user_name' ];
-       }
-
        /**
         * Return the tables, fields, and join conditions to be selected to create
         * a new revision object.
index 740377c..0b0aaf5 100644 (file)
@@ -77,9 +77,7 @@ use Wikimedia\ObjectFactory;
 
 return [
        'ActorMigration' => function ( MediaWikiServices $services ) : ActorMigration {
-               return new ActorMigration(
-                       $services->getMainConfig()->get( 'ActorTableSchemaMigrationStage' )
-               );
+               return new ActorMigration( SCHEMA_COMPAT_NEW );
        },
 
        'BadFileLookup' => function ( MediaWikiServices $services ) : BadFileLookup {
@@ -734,7 +732,8 @@ return [
                return new SpecialPageFactory(
                        new ServiceOptions(
                                SpecialPageFactory::$constructorOptions, $services->getMainConfig() ),
-                       $services->getContentLanguage()
+                       $services->getContentLanguage(),
+                       $services->getObjectFactory()
                );
        },
 
index cfb2ac1..39f0c81 100644 (file)
@@ -52,6 +52,17 @@ if ( ini_get( 'mbstring.func_overload' ) ) {
        die( 'MediaWiki does not support installations where mbstring.func_overload is non-zero.' );
 }
 
+// Define MW_ENTRY_POINT if it's not already, so that config code can check the
+// value without using defined()
+if ( !defined( 'MW_ENTRY_POINT' ) ) {
+       /**
+        * The entry point, which may be either the script filename without the
+        * file extension, or "cli" for maintenance scripts, or "unknown" for any
+        * entry point that does not set the constant.
+        */
+       define( 'MW_ENTRY_POINT', 'unknown' );
+}
+
 // Start the autoloader, so that extensions can derive classes from core files
 require_once "$IP/includes/AutoLoader.php";
 
index 7bcfc88..207721e 100644 (file)
@@ -743,8 +743,6 @@ class InfoAction extends FormlessAction {
                        self::getCacheKey( $cache, $page->getTitle(), $page->getLatest() ),
                        WANObjectCache::TTL_WEEK,
                        function ( $oldValue, &$ttl, &$setOpts ) use ( $page, $config, $fname, $services ) {
-                               global $wgActorTableSchemaMigrationStage;
-
                                $title = $page->getTitle();
                                $id = $title->getArticleID();
 
@@ -752,19 +750,11 @@ class InfoAction extends FormlessAction {
                                $dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' );
                                $setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist );
 
-                               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                                       $tables = [ 'revision_actor_temp' ];
-                                       $field = 'revactor_actor';
-                                       $pageField = 'revactor_page';
-                                       $tsField = 'revactor_timestamp';
-                                       $joins = [];
-                               } else {
-                                       $tables = [ 'revision' ];
-                                       $field = 'rev_user_text';
-                                       $pageField = 'rev_page';
-                                       $tsField = 'rev_timestamp';
-                                       $joins = [];
-                               }
+                               $tables = [ 'revision_actor_temp' ];
+                               $field = 'revactor_actor';
+                               $pageField = 'revactor_page';
+                               $tsField = 'revactor_timestamp';
+                               $joins = [];
 
                                $watchedItemStore = $services->getWatchedItemStore();
 
index a5e7437..4b74a3d 100644 (file)
@@ -76,10 +76,6 @@ class ApiExpandTemplates extends ApiBase {
                                        $this->addWarning( [ 'apierror-revwrongpage', $rev->getId(),
                                                wfEscapeWikiText( $pTitleObj->getPrefixedText() ) ] );
                                }
-                       } else {
-                               // Consider the title derived from the revid as having
-                               // been provided.
-                               $titleProvided = true;
                        }
                }
 
index a9fe258..7bbce97 100644 (file)
@@ -281,7 +281,10 @@ class ApiMain extends ApiBase {
                }
                $this->mResult->setErrorFormatter( $this->getErrorFormatter() );
 
-               $this->mModuleMgr = new ApiModuleManager( $this );
+               $this->mModuleMgr = new ApiModuleManager(
+                       $this,
+                       MediaWikiServices::getInstance()->getObjectFactory()
+               );
                $this->mModuleMgr->addModules( self::$Modules, 'action' );
                $this->mModuleMgr->addModules( $config->get( 'APIModules' ), 'action' );
                $this->mModuleMgr->addModules( self::$Formats, 'format' );
@@ -293,7 +296,6 @@ class ApiMain extends ApiBase {
                $this->mEnableWrite = $enableWrite;
 
                $this->mCdnMaxAge = -1; // flag for executeActionWithErrorHandling()
-               $this->mCommit = false;
        }
 
        /**
index d2df013..3d4ccaf 100644 (file)
@@ -21,6 +21,9 @@
  * @since 1.21
  */
 
+use MediaWiki\MediaWikiServices;
+use Wikimedia\ObjectFactory;
+
 /**
  * This class holds a list of modules and handles instantiation
  *
@@ -45,64 +48,35 @@ class ApiModuleManager extends ContextSource {
         * @var array[]
         */
        private $mModules = [];
+       /**
+        * @var ObjectFactory
+        */
+       private $objectFactory;
 
        /**
         * Construct new module manager
+        *
         * @param ApiBase $parentModule Parent module instance will be used during instantiation
+        * @param ObjectFactory|null $objectFactory Object factory to use when instantiating modules
         */
-       public function __construct( ApiBase $parentModule ) {
+       public function __construct( ApiBase $parentModule, ObjectFactory $objectFactory = null ) {
                $this->mParent = $parentModule;
+               $this->objectFactory = $objectFactory ?? MediaWikiServices::getInstance()->getObjectFactory();
        }
 
        /**
         * Add a list of modules to the manager. Each module is described
-        * by a module spec.
-        *
-        * Each module spec is an associative array containing at least
-        * the 'class' key for the module's class, and optionally a
-        * 'factory' key for the factory function to use for the module.
+        * by an ObjectFactory spec.
         *
-        * That factory function will be called with two parameters,
-        * the parent module (an instance of ApiBase, usually ApiMain)
-        * and the name the module was registered under. The return
-        * value must be an instance of the class given in the 'class'
-        * field.
+        * This simply calls `addModule()` for each module in `$modules`.
         *
-        * For backward compatibility, the module spec may also be a
-        * simple string containing the module's class name. In that
-        * case, the class' constructor will be called with the parent
-        * module and module name as parameters, as described above.
-        *
-        * Examples for defining module specs:
-        *
-        * @code
-        *  $modules['foo'] = 'ApiFoo';
-        *  $modules['bar'] = [
-        *      'class' => ApiBar::class,
-        *      'factory' => function( $main, $name ) { ... }
-        *  ];
-        *  $modules['xyzzy'] = [
-        *      'class' => ApiXyzzy::class,
-        *      'factory' => [ XyzzyFactory::class, 'newApiModule' ]
-        *  ];
-        * @endcode
-        *
-        * @param array $modules A map of ModuleName => ModuleSpec; The ModuleSpec
-        *        is either a string containing the module's class name, or an associative
-        *        array (see above for details).
+        * @see ApiModuleManager::addModule()
+        * @param array $modules A map of ModuleName => ModuleSpec
         * @param string $group Which group modules belong to (action,format,...)
         */
        public function addModules( array $modules, $group ) {
                foreach ( $modules as $name => $moduleSpec ) {
-                       if ( is_array( $moduleSpec ) ) {
-                               $class = $moduleSpec['class'];
-                               $factory = ( $moduleSpec['factory'] ?? null );
-                       } else {
-                               $class = $moduleSpec;
-                               $factory = null;
-                       }
-
-                       $this->addModule( $name, $group, $class, $factory );
+                       $this->addModule( $name, $group, $moduleSpec );
                }
        }
 
@@ -111,14 +85,21 @@ class ApiModuleManager extends ContextSource {
         * classes who wish to add their own modules to their lexicon or override the
         * behavior of inherent ones.
         *
+        * ObjectFactory is used to instantiate the module when needed. The parent module
+        * (`$parentModule` from `__construct()`) and the `$name` are passed as extraArgs.
+        *
+        * @since 1.34, accepts an ObjectFactory spec as the third parameter. The old calling convention,
+        *  passing a class name as parameter #3 and an optional factory callable as parameter #4, is
+        *  deprecated.
         * @param string $name The identifier for this module.
         * @param string $group Name of the module group
-        * @param string $class The class where this module is implemented.
-        * @param callable|null $factory Callback for instantiating the module.
+        * @param string|array $spec The ObjectFactory spec for instantiating the module,
+        *  or a class name to instantiate.
+        * @param callable|null $factory Callback for instantiating the module (deprecated).
         *
         * @throws InvalidArgumentException
         */
-       public function addModule( $name, $group, $class, $factory = null ) {
+       public function addModule( $name, $group, $spec, $factory = null ) {
                if ( !is_string( $name ) ) {
                        throw new InvalidArgumentException( '$name must be a string' );
                }
@@ -127,16 +108,24 @@ class ApiModuleManager extends ContextSource {
                        throw new InvalidArgumentException( '$group must be a string' );
                }
 
-               if ( !is_string( $class ) ) {
-                       throw new InvalidArgumentException( '$class must be a string' );
-               }
+               if ( is_string( $spec ) ) {
+                       $spec = [
+                               'class' => $spec
+                       ];
 
-               if ( $factory !== null && !is_callable( $factory ) ) {
-                       throw new InvalidArgumentException( '$factory must be a callable (or null)' );
+                       if ( is_callable( $factory ) ) {
+                               // Uncomment this when callers are cleaned up:
+                               // wfDeprecated( __METHOD__ . ' with $class and $factory', '1.34' );
+                               $spec['factory'] = $factory;
+                       }
+               } elseif ( !is_array( $spec ) ) {
+                       throw new InvalidArgumentException( '$spec must be a string or an array' );
+               } elseif ( !isset( $spec['class'] ) ) {
+                       throw new InvalidArgumentException( '$spec must define a class name' );
                }
 
                $this->mGroups[$group] = null;
-               $this->mModules[$name] = [ $group, $class, $factory ];
+               $this->mModules[$name] = [ $group, $spec ];
        }
 
        /**
@@ -153,7 +142,7 @@ class ApiModuleManager extends ContextSource {
                        return null;
                }
 
-               list( $moduleGroup, $moduleClass, $moduleFactory ) = $this->mModules[$moduleName];
+               list( $moduleGroup, $spec ) = $this->mModules[$moduleName];
 
                if ( $group !== null && $moduleGroup !== $group ) {
                        return null;
@@ -164,7 +153,7 @@ class ApiModuleManager extends ContextSource {
                        return $this->mInstances[$moduleName];
                } else {
                        // new instance
-                       $instance = $this->instantiateModule( $moduleName, $moduleClass, $moduleFactory );
+                       $instance = $this->instantiateModule( $moduleName, $spec );
 
                        if ( !$ignoreCache ) {
                                // cache this instance in case it is needed later
@@ -179,28 +168,22 @@ class ApiModuleManager extends ContextSource {
         * Instantiate the module using the given class or factory function.
         *
         * @param string $name The identifier for this module.
-        * @param string $class The class where this module is implemented.
-        * @param callable|null $factory Callback for instantiating the module.
+        * @param array $spec The ObjectFactory spec for instantiating the module.
         *
-        * @throws MWException
+        * @throws UnexpectedValueException
         * @return ApiBase
         */
-       private function instantiateModule( $name, $class, $factory = null ) {
-               if ( $factory !== null ) {
-                       // create instance from factory
-                       $instance = call_user_func( $factory, $this->mParent, $name );
-
-                       if ( !$instance instanceof $class ) {
-                               throw new MWException(
-                                       "The factory function for module $name did not return an instance of $class!"
-                               );
-                       }
-               } else {
-                       // create instance from class name
-                       $instance = new $class( $this->mParent, $name );
-               }
-
-               return $instance;
+       private function instantiateModule( $name, $spec ) {
+               return $this->objectFactory->createObject(
+                       $spec,
+                       [
+                               'extraArgs' => [
+                                       $this->mParent,
+                                       $name
+                               ],
+                               'assertClass' => $spec['class']
+                       ]
+               );
        }
 
        /**
@@ -213,8 +196,8 @@ class ApiModuleManager extends ContextSource {
                        return array_keys( $this->mModules );
                }
                $result = [];
-               foreach ( $this->mModules as $name => $grpCls ) {
-                       if ( $grpCls[0] === $group ) {
+               foreach ( $this->mModules as $name => $groupAndSpec ) {
+                       if ( $groupAndSpec[0] === $group ) {
                                $result[] = $name;
                        }
                }
@@ -229,9 +212,9 @@ class ApiModuleManager extends ContextSource {
         */
        public function getNamesWithClasses( $group = null ) {
                $result = [];
-               foreach ( $this->mModules as $name => $grpCls ) {
-                       if ( $group === null || $grpCls[0] === $group ) {
-                               $result[$name] = $grpCls[1];
+               foreach ( $this->mModules as $name => $groupAndSpec ) {
+                       if ( $group === null || $groupAndSpec[0] === $group ) {
+                               $result[$name] = $groupAndSpec[1]['class'];
                        }
                }
 
@@ -247,7 +230,7 @@ class ApiModuleManager extends ContextSource {
         */
        public function getClassName( $module ) {
                if ( isset( $this->mModules[$module] ) ) {
-                       return $this->mModules[$module][1];
+                       return $this->mModules[$module][1]['class'];
                }
 
                return false;
index c78e445..a7ff729 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
 use Wikimedia\Rdbms\IDatabase;
 
 /**
@@ -134,7 +135,10 @@ class ApiQuery extends ApiBase {
        public function __construct( ApiMain $main, $action ) {
                parent::__construct( $main, $action );
 
-               $this->mModuleMgr = new ApiModuleManager( $this );
+               $this->mModuleMgr = new ApiModuleManager(
+                       $this,
+                       MediaWikiServices::getInstance()->getObjectFactory()
+               );
 
                // Allow custom modules to be added in LocalSettings.php
                $config = $this->getConfig();
index 3751102..3d4c49b 100644 (file)
@@ -40,8 +40,6 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
         * @return void
         */
        protected function run( ApiPageSet $resultPageSet = null ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $db = $this->getDB();
                $params = $this->extractRequestParams( false );
                $services = MediaWikiServices::getInstance();
@@ -54,9 +52,7 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
                $tsField = 'rev_timestamp';
                $idField = 'rev_id';
                $pageField = 'rev_page';
-               if ( $params['user'] !== null &&
-                       ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
-               ) {
+               if ( $params['user'] !== null ) {
                        // The query is probably best done using the actor_timestamp index on
                        // revision_actor_temp. Use the denormalized fields from that table.
                        $tsField = 'revactor_timestamp';
index e0513e2..a7d4fb9 100644 (file)
@@ -41,8 +41,6 @@ class ApiQueryAllUsers extends ApiQueryBase {
        }
 
        public function execute() {
-               global $wgActorTableSchemaMigrationStage;
-
                $params = $this->extractRequestParams();
                $activeUserDays = $this->getConfig()->get( 'ActiveUserDays' );
 
@@ -181,22 +179,17 @@ class ApiQueryAllUsers extends ApiQueryBase {
                        ] ] );
 
                        // Actually count the actions using a subquery (T66505 and T66507)
-                       $tables = [ 'recentchanges' ];
-                       $joins = [];
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_OLD ) {
-                               $userCond = 'rc_user_text = user_name';
-                       } else {
-                               $tables[] = 'actor';
-                               $joins['actor'] = [ 'JOIN', 'rc_actor = actor_id' ];
-                               $userCond = 'actor_user = user_id';
-                       }
+                       $tables = [ 'recentchanges', 'actor' ];
+                       $joins = [
+                               'actor' => [ 'JOIN', 'rc_actor = actor_id' ],
+                       ];
                        $timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
                        $this->addFields( [
                                'recentactions' => '(' . $db->selectSQLText(
                                        $tables,
                                        'COUNT(*)',
                                        [
-                                               $userCond,
+                                               'actor_user = user_id',
                                                'rc_type != ' . $db->addQuotes( RC_EXTERNAL ), // no wikidata
                                                'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ),
                                                'rc_timestamp >= ' . $db->addQuotes( $timestamp ),
index a1945c4..f2e306f 100644 (file)
@@ -46,8 +46,6 @@ class ApiQueryContributors extends ApiQueryBase {
        }
 
        public function execute() {
-               global $wgActorTableSchemaMigrationStage;
-
                $db = $this->getDB();
                $params = $this->extractRequestParams();
                $this->requireMaxOneParameter( $params, 'group', 'excludegroup', 'rights', 'excluderights' );
@@ -80,14 +78,10 @@ class ApiQueryContributors extends ApiQueryBase {
                $result = $this->getResult();
                $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo();
 
-               // For SCHEMA_COMPAT_READ_NEW, target indexes on the
-               // revision_actor_temp table, otherwise on the revision table.
-               $pageField = ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
-                       ? 'revactor_page' : 'rev_page';
-               $idField = ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
-                       ? 'revactor_actor' : $revQuery['fields']['rev_user'];
-               $countField = ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
-                       ? 'revactor_actor' : $revQuery['fields']['rev_user_text'];
+               // Target indexes on the revision_actor_temp table.
+               $pageField = 'revactor_page';
+               $idField = 'revactor_actor';
+               $countField = 'revactor_actor';
 
                // First, count anons
                $this->addTables( $revQuery['tables'] );
index d616ad4..5b2b1b7 100644 (file)
@@ -85,8 +85,6 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
        }
 
        protected function run( ApiPageSet $resultPageSet = null ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $params = $this->extractRequestParams( false );
                $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
 
@@ -137,9 +135,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                $idField = 'rev_id';
                $tsField = 'rev_timestamp';
                $pageField = 'rev_page';
-               if ( $params['user'] !== null &&
-                       ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
-               ) {
+               if ( $params['user'] !== null ) {
                        // We're going to want to use the page_actor_timestamp index (on revision_actor_temp)
                        // so use that table's denormalized fields.
                        $idField = 'revactor_rev';
index 919c763..189f957 100644 (file)
@@ -42,8 +42,6 @@ class ApiQueryUserContribs extends ApiQueryBase {
                $fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
 
        public function execute() {
-               global $wgActorTableSchemaMigrationStage;
-
                // Parse some parameters
                $this->params = $this->extractRequestParams();
 
@@ -82,8 +80,6 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        // a wiki with users "Test00000001" to "Test99999999"), use a
                        // generator with batched lookup and continuation.
                        $userIter = call_user_func( function () use ( $dbSecondary, $sort, $op, $fname ) {
-                               global $wgActorTableSchemaMigrationStage;
-
                                $fromName = false;
                                if ( !is_null( $this->params['continue'] ) ) {
                                        $continue = explode( '|', $this->params['continue'] );
@@ -97,26 +93,13 @@ class ApiQueryUserContribs extends ApiQueryBase {
 
                                do {
                                        $from = $fromName ? "$op= " . $dbSecondary->addQuotes( $fromName ) : false;
-
-                                       // For the new schema, pull from the actor table. For the
-                                       // old, pull from rev_user.
-                                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                                               $res = $dbSecondary->select(
-                                                       'actor',
-                                                       [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
-                                                       array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
-                                                       $fname,
-                                                       [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ]
-                                               );
-                                       } else {
-                                               $res = $dbSecondary->select(
-                                                       'revision',
-                                                       [ 'actor_id' => 'NULL', 'user_id' => 'rev_user', 'user_name' => 'rev_user_text' ],
-                                                       array_merge( [ "rev_user_text$like" ], $from ? [ "rev_user_text $from" ] : [] ),
-                                                       $fname,
-                                                       [ 'DISTINCT', 'ORDER BY' => [ "rev_user_text $sort" ], 'LIMIT' => $limit ]
-                                               );
-                                       }
+                                       $res = $dbSecondary->select(
+                                               'actor',
+                                               [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
+                                               array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
+                                               $fname,
+                                               [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ]
+                                       );
 
                                        $count = 0;
                                        $fromName = false;
@@ -159,25 +142,13 @@ class ApiQueryUserContribs extends ApiQueryBase {
                                $from = "$op= $fromId";
                        }
 
-                       // For the new schema, just select from the actor table. For the
-                       // old, select from user.
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                               $res = $dbSecondary->select(
-                                       'actor',
-                                       [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
-                                       array_merge( [ 'actor_user' => $ids ], $from ? [ "actor_id $from" ] : [] ),
-                                       __METHOD__,
-                                       [ 'ORDER BY' => "user_id $sort" ]
-                               );
-                       } else {
-                               $res = $dbSecondary->select(
-                                       'user',
-                                       [ 'actor_id' => 'NULL', 'user_id' => 'user_id', 'user_name' => 'user_name' ],
-                                       array_merge( [ 'user_id' => $ids ], $from ? [ "user_id $from" ] : [] ),
-                                       __METHOD__,
-                                       [ 'ORDER BY' => "user_id $sort" ]
-                               );
-                       }
+                       $res = $dbSecondary->select(
+                               'actor',
+                               [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
+                               array_merge( [ 'actor_user' => $ids ], $from ? [ "actor_id $from" ] : [] ),
+                               __METHOD__,
+                               [ 'ORDER BY' => "user_id $sort" ]
+                       );
                        $userIter = UserArray::newFromResult( $res );
                        $batchSize = count( $ids );
                } else {
@@ -222,57 +193,22 @@ class ApiQueryUserContribs extends ApiQueryBase {
                                $from = "$op= " . $dbSecondary->addQuotes( $fromName );
                        }
 
-                       // For the new schema, just select from the actor table. For the
-                       // old, select from user then merge in any unknown users (IPs and imports).
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                               $res = $dbSecondary->select(
-                                       'actor',
-                                       [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
-                                       array_merge( [ 'actor_name' => array_keys( $names ) ], $from ? [ "actor_id $from" ] : [] ),
-                                       __METHOD__,
-                                       [ 'ORDER BY' => "actor_name $sort" ]
-                               );
-                               $userIter = UserArray::newFromResult( $res );
-                       } else {
-                               $res = $dbSecondary->select(
-                                       'user',
-                                       [ 'actor_id' => 'NULL', 'user_id', 'user_name' ],
-                                       array_merge( [ 'user_name' => array_keys( $names ) ], $from ? [ "user_name $from" ] : [] ),
-                                       __METHOD__
-                               );
-                               foreach ( $res as $row ) {
-                                       $names[$row->user_name] = $row;
-                               }
-                               if ( $this->params['dir'] == 'newer' ) {
-                                       ksort( $names, SORT_STRING );
-                               } else {
-                                       krsort( $names, SORT_STRING );
-                               }
-                               $neg = $op === '>' ? -1 : 1;
-                               $userIter = call_user_func( function () use ( $names, $fromName, $neg ) {
-                                       foreach ( $names as $name => $row ) {
-                                               if ( $fromName === false || $neg * strcmp( $name, $fromName ) <= 0 ) {
-                                                       $user = $row ? User::newFromRow( $row ) : User::newFromName( $name, false );
-                                                       yield $user;
-                                               }
-                                       }
-                               } );
-                       }
+                       $res = $dbSecondary->select(
+                               'actor',
+                               [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
+                               array_merge( [ 'actor_name' => array_keys( $names ) ], $from ? [ "actor_id $from" ] : [] ),
+                               __METHOD__,
+                               [ 'ORDER BY' => "actor_name $sort" ]
+                       );
+                       $userIter = UserArray::newFromResult( $res );
                        $batchSize = count( $names );
                }
 
-               // With the new schema, the DB query will order by actor so update $this->orderBy to match.
-               if ( $batchSize > 1 && ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ) {
+               // The DB query will order by actor so update $this->orderBy to match.
+               if ( $batchSize > 1 ) {
                        $this->orderBy = 'actor';
                }
 
-               // Use the 'contributions' replica, but only if we're querying by user ID (T216656).
-               if ( $this->orderBy === 'id' &&
-                       !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
-               ) {
-                       $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
-               }
-
                $count = 0;
                $limit = $this->params['limit'];
                $userIter->rewind();
@@ -325,47 +261,33 @@ class ApiQueryUserContribs extends ApiQueryBase {
         * @param int $limit
         */
        private function prepareQuery( array $users, $limit ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $this->resetQueryParams();
                $db = $this->getDB();
 
                $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo( [ 'page' ] );
 
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
-                       $orderUserField = 'rev_actor';
-                       $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
-                       $tsField = 'revactor_timestamp';
-                       $idField = 'revactor_rev';
-
-                       // T221511: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor`
-                       // before `revision_actor_temp` and filesorting is somehow better than querying $limit+1 rows
-                       // from `revision_actor_temp`. Tell it not to reorder the query (and also reorder it ourselves
-                       // because as generated by RevisionStore it'll have `revision` first rather than
-                       // `revision_actor_temp`). But not when uctag is used, as it seems as likely to be harmed as
-                       // helped in that case, and not when there's only one User because in that case it fetches
-                       // the one `actor` row as a constant and doesn't filesort.
-                       if ( count( $users ) > 1 && !isset( $this->params['tag'] ) ) {
-                               $revQuery['joins']['revision'] = $revQuery['joins']['temp_rev_user'];
-                               unset( $revQuery['joins']['temp_rev_user'] );
-                               $this->addOption( 'STRAIGHT_JOIN' );
-                               // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
-                               // when join conditions are given for all joins, but Gergő is wary of relying on that so pull
-                               // `revision_actor_temp` to the start.
-                               $revQuery['tables'] =
-                                       [ 'temp_rev_user' => $revQuery['tables']['temp_rev_user'] ] + $revQuery['tables'];
-                       }
-               } else {
-                       // If we're dealing with user names (rather than IDs) in read-old mode,
-                       // pass false for ActorMigration::getWhere()'s $useId parameter so
-                       // $revWhere['conds'] isn't an OR.
-                       $revWhere = ActorMigration::newMigration()
-                               ->getWhere( $db, 'rev_user', $users, $this->orderBy === 'id' );
-                       $orderUserField = $this->orderBy === 'id' ? 'rev_user' : 'rev_user_text';
-                       $userField = $revQuery['fields'][$orderUserField];
-                       $tsField = 'rev_timestamp';
-                       $idField = 'rev_id';
+               $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
+               $orderUserField = 'rev_actor';
+               $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
+               $tsField = 'revactor_timestamp';
+               $idField = 'revactor_rev';
+
+               // T221511: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor`
+               // before `revision_actor_temp` and filesorting is somehow better than querying $limit+1 rows
+               // from `revision_actor_temp`. Tell it not to reorder the query (and also reorder it ourselves
+               // because as generated by RevisionStore it'll have `revision` first rather than
+               // `revision_actor_temp`). But not when uctag is used, as it seems as likely to be harmed as
+               // helped in that case, and not when there's only one User because in that case it fetches
+               // the one `actor` row as a constant and doesn't filesort.
+               if ( count( $users ) > 1 && !isset( $this->params['tag'] ) ) {
+                       $revQuery['joins']['revision'] = $revQuery['joins']['temp_rev_user'];
+                       unset( $revQuery['joins']['temp_rev_user'] );
+                       $this->addOption( 'STRAIGHT_JOIN' );
+                       // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
+                       // when join conditions are given for all joins, but Gergő is wary of relying on that so pull
+                       // `revision_actor_temp` to the start.
+                       $revQuery['tables'] =
+                               [ 'temp_rev_user' => $revQuery['tables']['temp_rev_user'] ] + $revQuery['tables'];
                }
 
                $this->addTables( $revQuery['tables'] );
index 12d7435..28dea3b 100644 (file)
@@ -303,32 +303,17 @@ class ApiQueryUserInfo extends ApiQueryBase {
         * @return string|null ISO 8601 timestamp of current user's last contribution or null if none
         */
        protected function getLatestContributionTime() {
-               global $wgActorTableSchemaMigrationStage;
-
                $user = $this->getUser();
                $dbr = $this->getDB();
 
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       if ( $user->getActorId() === null ) {
-                               return null;
-                       }
-                       $res = $dbr->selectField( 'revision_actor_temp',
-                               'MAX(revactor_timestamp)',
-                               [ 'revactor_actor' => $user->getActorId() ],
-                               __METHOD__
-                       );
-               } else {
-                       if ( $user->isLoggedIn() ) {
-                               $conds = [ 'rev_user' => $user->getId() ];
-                       } else {
-                               $conds = [ 'rev_user_text' => $user->getName() ];
-                       }
-                       $res = $dbr->selectField( 'revision',
-                               'MAX(rev_timestamp)',
-                               $conds,
-                               __METHOD__
-                       );
+               if ( $user->getActorId() === null ) {
+                       return null;
                }
+               $res = $dbr->selectField( 'revision_actor_temp',
+                       'MAX(revactor_timestamp)',
+                       [ 'revactor_actor' => $user->getActorId() ],
+                       __METHOD__
+               );
 
                return $res ? wfTimestamp( TS_ISO_8601, $res ) : null;
        }
index b72d519..628edfa 100644 (file)
                        "Lucas Werkmeister (WMDE)"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> L’API MediaWiki est une interface stable et mature qui est supportée et améliorée de façon active. Bien que nous essayions de l’éviter, nous pouvons avoir parfois besoin de faire des modifications impactantes ; inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Test :</strong> Pour faciliter le test des requêtes à l’API, voyez [[Special:ApiSandbox]].</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> L’API MediaWiki est une interface stable et mature qui est supportée et améliorée de façon active. Bien que nous essayions de l’éviter, nous pouvons avoir parfois besoin de faire des modifications impactantes ; inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Test :</strong> Pour faciliter le test des requêtes à l’API, voyez [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Quelle action effectuer.",
        "apihelp-main-param-format": "Le format de sortie.",
-       "apihelp-main-param-maxlag": "La latence maximale peut être utilisée quand MédiaWiki est installé sur un cluster de base de données répliqué. Pour éviter des actions provoquant un supplément de latence de réplication de site, ce paramètre peut faire attendre le client jusqu’à ce que la latence de réplication soit inférieure à une valeur spécifiée. En cas de latence excessive, le code d’erreur <samp>maxlag</samp> est renvoyé avec un message tel que <samp>Attente de $host : $lag secondes de délai</samp>.<br />Voyez [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manuel: paramètre Maxlag]] pour plus d’information.",
+       "apihelp-main-param-maxlag": "La latence maximale peut être utilisée quand MediaWiki est installé sur un cluster de base de données répliqué. Pour éviter des actions provoquant un supplément de latence de réplication de site, ce paramètre peut faire attendre le client jusqu’à ce que la latence de réplication soit inférieure à une valeur spécifiée. En cas de latence excessive, le code d’erreur <samp>maxlag</samp> est renvoyé avec un message tel que <samp>Attente de $host : $lag secondes de délai</samp>.<br />Voyez [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manuel: paramètre Maxlag]] pour plus d’information.",
        "apihelp-main-param-smaxage": "Fixer l’entête HTTP de contrôle de cache <code>s-maxage</code> à ce nombre de secondes. Les erreurs ne sont jamais mises en cache.",
        "apihelp-main-param-maxage": "Fixer l’entête HTTP de contrôle de cache <code>max-age</code> à ce nombre de secondes. Les erreurs ne sont jamais mises en cache.",
        "apihelp-main-param-assert": "Vérifier si l’utilisateur est connecté si la valeur est <kbd>user</kbd>, ou s’il a le droit d’un utilisateur robot si la valeur est <kbd>bot</kbd>.",
@@ -51,7 +51,7 @@
        "apihelp-main-param-responselanginfo": "Inclure les langues utilisées pour <var>uselang</var> et <var>errorlang</var> dans le résultat.",
        "apihelp-main-param-origin": "En accédant à l’API en utilisant une requête AJAX inter-domaines (CORS), mettre le domaine d’origine dans ce paramètre. Il doit être inclus dans toute requête de pre-flight, et doit donc faire partie de l’URI de la requête (pas du corps du POST).\n\nPour les requêtes authentifiées, il doit correspondre exactement à une des origines dans l’entête <code>Origin</code> header, donc il doit être fixé avec quelque chose comme <kbd>https://en.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Si ce paramètre ne correspond pas à l’entête <code>Origin</code>, une réponse 403 sera renvoyée. Si ce paramètre correspond à l’entête <code>Origin</code> et que l’origine est en liste blanche, des entêtes <code>Access-Control-Allow-Origin</code> et <code>Access-Control-Allow-Credentials</code> seront positionnés.\n\nPour les requêtes non authentifiées, spécifiez la valeur <kbd>*</kbd>. Cela positionnera l’entête <code>Access-Control-Allow-Origin</code>, mais <code>Access-Control-Allow-Credentials</code> vaudra <code>false</code> et toutes les données spécifiques à l’utilisateur seront filtrées.",
        "apihelp-main-param-uselang": "Langue à utiliser pour les traductions de message. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> avec <kbd>siprop=languages</kbd> renvoie une liste de codes de langue, ou en spécifiant <kbd>user</kbd> pour utiliser la préférence de langue de l’utilisateur actuel, ou en spécifiant <kbd>content</kbd> pour utiliser le langage du contenu de ce wiki.",
-       "apihelp-main-param-errorformat": "Format à utiliser pour la sortie du texte d’avertissement et d’erreur.\n; plaintext: Wikitexte avec balises HTML supprimées et les entités remplacées.\n; wikitext: wikitexte non analysé.\n; html: HTML.\n; raw: Clé de message et paramètres.\n; none: Aucune sortie de texte, uniquement les codes erreur.\n; bc: Format utilisé avant MédiaWiki 1.29. <var>errorlang</var> et <var>errorsuselocal</var> sont ignorés.",
+       "apihelp-main-param-errorformat": "Format à utiliser pour la sortie du texte d’avertissement et d’erreur.\n; plaintext: Wikitexte avec balises HTML supprimées et les entités remplacées.\n; wikitext: wikitexte non analysé.\n; html: HTML.\n; raw: Clé de message et paramètres.\n; none: Aucune sortie de texte, uniquement les codes erreur.\n; bc: Format utilisé avant MediaWiki 1.29. <var>errorlang</var> et <var>errorsuselocal</var> sont ignorés.",
        "apihelp-main-param-errorlang": "Langue à utiliser pour les avertissements et les erreurs. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> avec <kbd>siprop=languages</kbd> renvoyant une liste de codes de langue, ou spécifier <kbd>content</kbd> pour utiliser la langue du contenu de ce wiki, ou spécifier <kbd>uselang</kbd> pour utiliser la même valeur que le paramètre <var>uselang</var>.",
        "apihelp-main-param-errorsuselocal": "S’il est fourni, les textes d’erreur utiliseront des messages adaptés à la langue dans l’espace de noms {{ns:MediaWiki}}.",
        "apihelp-block-summary": "Bloquer un utilisateur.",
@@ -86,7 +86,7 @@
        "apihelp-clientlogin-example-login": "Commencer le processus de connexion au wiki en tant qu’utilisateur <kbd>Exemple</kbd> avec le mot de passe <kbd>ExempleMotDePasse</kbd>.",
        "apihelp-clientlogin-example-login2": "Continuer la connexion après une réponse de l’<samp>IHM</samp> pour l’authentification à deux facteurs, en fournissant un <var>OATHToken</var> valant <kbd>987654</kbd>.",
        "apihelp-compare-summary": "Obtenir la différence entre deux pages.",
-       "apihelp-compare-extended-description": "Vous devez passer un numéro de révision, un titre de page, ou un ID de page, à la fois pour « from » et « to ».",
+       "apihelp-compare-extended-description": "Vous devez passer un numéro de révision, un titre de page, ou un ID de page, à la fois pour « from » et « to ».",
        "apihelp-compare-param-fromtitle": "Premier titre à comparer.",
        "apihelp-compare-param-fromid": "ID de la première page à comparer.",
        "apihelp-compare-param-fromrev": "Première révision à comparer.",
        "apihelp-edit-param-summary": "Modifier le résumé. Également le titre de la section quand $1section=new et $1sectiontitle n’est pas défini.",
        "apihelp-edit-param-tags": "Modifier les balises à appliquer à la version.",
        "apihelp-edit-param-minor": "Marquer cette modification comme étant mineure.",
-       "apihelp-edit-param-notminor": "Ne pas marquer cette modification comme mineure, même si la préférence utilisateur « {{int:tog-minordefault}} » est positionnée.",
+       "apihelp-edit-param-notminor": "Ne pas marquer cette modification comme mineure, même si la préférence utilisateur « {{int:tog-minordefault}} » est positionnée.",
        "apihelp-edit-param-bot": "Marquer cette modification comme effectuée par un robot.",
        "apihelp-edit-param-basetimestamp": "Horodatage de la révision de base, utilisé pour détecter les conflits de modification. Peut être obtenu via [[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]].",
        "apihelp-edit-param-starttimestamp": "L'horodatage, lorsque le processus d'édition est démarré, est utilisé pour détecter les conflits de modification. Une valeur appropriée peut être obtenue en utilisant <var>[[Special:ApiHelp/main|curtimestamp]]</var> lors du démarrage du processus d'édition (par ex. en chargeant le contenu de la page à modifier).",
        "apihelp-opensearch-param-limit": "Nombre maximal de résultats à renvoyer.",
        "apihelp-opensearch-param-namespace": "Espaces de nom à rechercher.  Ignoré if <var>$1search</var> commence avec le préfixe d'un espace de noms valide.",
        "apihelp-opensearch-param-suggest": "Ne rien faire si <var>[[mw:Special:MyLanguage/Manual:$wgEnableOpenSearchSuggest|$wgEnableOpenSearchSuggest]]</var> vaut faux.",
-       "apihelp-opensearch-param-redirects": "Comment gérer les redirections :\n;return:Renvoie la redirection elle-même.\n;resolve:Renvoie la page cible. Peut renvoyer moins de $1limit résultats.\nPour des raisons historiques, la valeur par défaut est « return » pour $1format=json et « resolve » pour les autres formats.",
+       "apihelp-opensearch-param-redirects": "Comment gérer les redirections :\n;return:Renvoie la redirection elle-même.\n;resolve:Renvoie la page cible. Peut renvoyer moins de $1limit résultats.\nPour des raisons historiques, la valeur par défaut est « return » pour $1format=json et « resolve » pour les autres formats.",
        "apihelp-opensearch-param-format": "Le format de sortie.",
        "apihelp-opensearch-param-warningsaserror": "Si des avertissements apparaissent avec <kbd>format=json</kbd>, renvoyer une erreur d’API au lieu de les ignorer.",
        "apihelp-opensearch-example-te": "Trouver les pages commençant par <kbd>Te</kbd>.",
        "apihelp-parse-param-effectivelanglinks": "Inclut les liens de langue fournis par les extensions (à utiliser avec <kbd>$1prop=langlinks</kbd>).",
        "apihelp-parse-param-section": "Traiter uniquement le contenu de la section ayant ce numéro.\n\nQuand la valeur est <kbd>new</kbd>, traite <var>$1text</var> et <var>$1sectiontitle</var> comme s’ils correspondaient à une nouvelle section de la page.\n\nLa valeur <kbd>new</kbd> n’est autorisée que si <var>text</var> est défini.",
        "apihelp-parse-param-sectiontitle": "Nouveau titre de section quand <var>section</var> vaut <kbd>nouveau</kbd>.\n\nÀ la différence de la modification de page, cela ne revient pas à <var>summary</var> quand il est omis ou vide.",
-       "apihelp-parse-param-disablelimitreport": "Omettre le rapport de limite (« rapport de limite du nouveau PP ») de la sortie de l’analyseur.",
+       "apihelp-parse-param-disablelimitreport": "Omettre le rapport de limite (« rapport de limite du nouveau PP ») de la sortie de l’analyseur.",
        "apihelp-parse-param-disablepp": "Utiliser <var>$1disablelimitreport</var> à la place.",
        "apihelp-parse-param-disableeditsection": "Omettre les liens de modification de section de la sortie de l’analyseur.",
        "apihelp-parse-param-disabletidy": "Ne pas exécuter de nettoyage du code HTML (par exemple,  réagencer) sur la sortie de l'analyseur.",
        "apihelp-query+embeddedin-param-limit": "Combien de pages renvoyer au total.",
        "apihelp-query+embeddedin-example-simple": "Afficher les pages incluant <kbd>Template:Stub</kbd>.",
        "apihelp-query+embeddedin-example-generator": "Obtenir des informations sur les pages incluant <kbd>Template:Stub</kbd>.",
-       "apihelp-query+extlinks-summary": "Renvoyer toutes les URLs externes (non interwikis) des pages données.",
+       "apihelp-query+extlinks-summary": "Renvoyer toutes les URL externes (non interwikis) des pages données.",
        "apihelp-query+extlinks-param-limit": "Combien de liens renvoyer.",
        "apihelp-query+extlinks-param-protocol": "Protocole de l’URL. Si vide et <var>$1query</var> est positionné, le protocole est <kbd>http</kbd>. Laisser à la fois ceci et <var>$1query</var> vides pour lister tous les liens externes.",
        "apihelp-query+extlinks-param-query": "Rechercher une chaîne sans protocole. Utile pour vérifier si une certaine page contient une certaine URL externe.",
-       "apihelp-query+extlinks-param-expandurl": "Étendre les URLs relatives au protocole avec le protocole canonique.",
+       "apihelp-query+extlinks-param-expandurl": "Étendre les URL relatives au protocole avec le protocole canonique.",
        "apihelp-query+extlinks-example-simple": "Obtenir une liste des liens externes de <kbd>Main Page</kbd>.",
        "apihelp-query+exturlusage-summary": "Énumérer les pages contenant une URL donnée.",
        "apihelp-query+exturlusage-param-prop": "Quelles informations inclure :",
        "apihelp-query+exturlusage-param-query": "Rechercher une chaîne sans protocole. Voyez [[Special:LinkSearch]]. Le laisser vide pour lister tous les liens externes.",
        "apihelp-query+exturlusage-param-namespace": "Les espaces de nom à énumérer.",
        "apihelp-query+exturlusage-param-limit": "Combien de pages renvoyer.",
-       "apihelp-query+exturlusage-param-expandurl": "Étendre les URLs relatives au protocole avec le protocole canonique.",
+       "apihelp-query+exturlusage-param-expandurl": "Étendre les URL relatives au protocole avec le protocole canonique.",
        "apihelp-query+exturlusage-example-simple": "Afficher les pages avec un lien vers <kbd>https://www.mediawiki.org</kbd>.",
        "apihelp-query+filearchive-summary": "Énumérer séquentiellement tous les fichiers supprimés.",
        "apihelp-query+filearchive-param-from": "Le titre de l’image auquel démarrer l’énumération.",
        "apihelp-query+filearchive-param-limit": "Combien d’images renvoyer au total.",
        "apihelp-query+filearchive-param-dir": "La direction dans laquelle lister.",
        "apihelp-query+filearchive-param-sha1": "Hachage SHA1 de l’image. Écrase $1sha1base36.",
-       "apihelp-query+filearchive-param-sha1base36": "Hachage SHA1 de l’image en base 36 (utilisé dans MédiaWiki).",
+       "apihelp-query+filearchive-param-sha1base36": "Hachage SHA1 de l’image en base 36 (utilisé dans MediaWiki).",
        "apihelp-query+filearchive-param-prop": "Quelle information obtenir sur l’image :",
        "apihelp-query+filearchive-paramvalue-prop-sha1": "Ajoute le hachage SHA-1 pour l’image.",
        "apihelp-query+filearchive-paramvalue-prop-timestamp": "Ajoute l’horodatage à la version téléversée.",
        "apihelp-query+filerepoinfo-paramvalue-prop-local": "Si ce dépôt est local ou non.",
        "apihelp-query+filerepoinfo-paramvalue-prop-name": "La clé du dépôt — utilisée dans les valeurs de retour, par ex. <var>[[mw:Special:MyLanguage/Manual:$wgForeignFileRepos|$wgForeignFileRepos]]</var> et [[Special:ApiHelp/query+imageinfo|imageinfo]] return values.",
        "apihelp-query+filerepoinfo-paramvalue-prop-rootUrl": "Chemin de l’URL racine pour les chemins d’image.",
-       "apihelp-query+filerepoinfo-paramvalue-prop-scriptDirUrl": "Chemin de l’URL racine pour l’installation de MédiaWiki du wiki du dépôt.",
+       "apihelp-query+filerepoinfo-paramvalue-prop-scriptDirUrl": "Chemin de l’URL racine pour l’installation de MediaWiki du wiki du dépôt.",
        "apihelp-query+filerepoinfo-paramvalue-prop-server": "<var>[[mw:Special:MyLanguage/Manual:$wgServer|$wgServer]]</var> du wiki du dépôt, ou équivalent.",
        "apihelp-query+filerepoinfo-paramvalue-prop-thumbUrl": "Chemin de l’URL racine pour les chemins des vignettes.",
        "apihelp-query+filerepoinfo-paramvalue-prop-url": "Chemin de l’URL de la zone publique.",
        "apihelp-query+imageinfo-paramvalue-prop-extmetadata": "Liste les métadonnées mises en forme combinées depuis diverses sources. Les résultats sont au format HTML.",
        "apihelp-query+imageinfo-paramvalue-prop-archivename": "Ajoute le nom de fichier de la version d’archive pour les versions autres que la dernière.",
        "apihelp-query+imageinfo-paramvalue-prop-bitdepth": "Ajoute la profondeur de bits de la version.",
-       "apihelp-query+imageinfo-paramvalue-prop-uploadwarning": "Utilisé par la page Special:Upload pour obtenir de l’information sur un fichier existant. Non prévu pour être utilisé en dehors du cœur de MédiaWiki.",
+       "apihelp-query+imageinfo-paramvalue-prop-uploadwarning": "Utilisé par la page Special:Upload pour obtenir de l’information sur un fichier existant. Non prévu pour être utilisé en dehors du cœur de MediaWiki.",
        "apihelp-query+imageinfo-paramvalue-prop-badfile": "Ajoute l'indication que le fichier est sur [[MediaWiki:Bad image list]]",
        "apihelp-query+imageinfo-param-limit": "Combien de révisions de fichier renvoyer par fichier.",
        "apihelp-query+imageinfo-param-start": "Horodatage auquel démarrer la liste.",
        "apihelp-query+info-example-simple": "Obtenir des informations sur la page <kbd>Main Page</kbd>.",
        "apihelp-query+info-example-protection": "Obtenir des informations générales et de protection sur la page <kbd>Main Page</kbd>.",
        "apihelp-query+iwbacklinks-summary": "Trouver toutes les pages qui ont un lien vers le lien interwiki indiqué.",
-       "apihelp-query+iwbacklinks-extended-description": "Peut être utilisé pour trouver tous les liens avec un préfixe, ou tous les liens vers un titre (avec un préfixe donné). Sans paramètre, équivaut à « tous les liens interwiki ».",
+       "apihelp-query+iwbacklinks-extended-description": "Peut être utilisé pour trouver tous les liens avec un préfixe, ou tous les liens vers un titre (avec un préfixe donné). Sans paramètre, équivaut à « tous les liens interwiki ».",
        "apihelp-query+iwbacklinks-param-prefix": "Préfixe pour l’interwiki.",
        "apihelp-query+iwbacklinks-param-title": "Lien interwiki à rechercher. Doit être utilisé avec <var>$1blprefix</var>.",
        "apihelp-query+iwbacklinks-param-limit": "Combien de pages renvoyer.",
        "apihelp-query+iwlinks-param-dir": "La direction dans laquelle lister.",
        "apihelp-query+iwlinks-example-simple": "Obtenir les liens interwiki de la page <kbd>Main Page</kbd>.",
        "apihelp-query+langbacklinks-summary": "Trouver toutes les pages qui ont un lien vers le lien de langue indiqué.",
-       "apihelp-query+langbacklinks-extended-description": "Peut être utilisé pour trouver tous les liens avec un code de langue, ou tous les liens vers un titre (avec une langue donnée). Sans paramètre équivaut à « tous les liens de langue ».\n\nNotez que cela peut ne pas prendre en compte les liens de langue ajoutés par les extensions.",
+       "apihelp-query+langbacklinks-extended-description": "Peut être utilisé pour trouver tous les liens avec un code de langue, ou tous les liens vers un titre (avec une langue donnée). Sans paramètre équivaut à « tous les liens de langue ».\n\nNotez que cela peut ne pas prendre en compte les liens de langue ajoutés par les extensions.",
        "apihelp-query+langbacklinks-param-lang": "Langue pour le lien de langue.",
        "apihelp-query+langbacklinks-param-title": "Lien interlangue à rechercher. Doit être utilisé avec $1lang.",
        "apihelp-query+langbacklinks-param-limit": "Combien de pages renvoyer au total.",
        "apihelp-query+languageinfo-summary": "Renvoyer des informations sur les langues disponibles.",
        "apihelp-query+languageinfo-extended-description": "[[mw:API:Query#Continuing queries|Un prolongement]] peut être appliqué si la récupération de l’information prend trop de temps pour une requête.",
        "apihelp-query+languageinfo-param-prop": "Quelle information obtenir pour chaque langue.",
-       "apihelp-query+languageinfo-paramvalue-prop-code": "Le code de langue (ce code est spécifique à MédiaWiki, bien qu’il y ait des recouvrements avec d’autres standards).",
+       "apihelp-query+languageinfo-paramvalue-prop-code": "Le code de langue (ce code est spécifique à MediaWiki, bien qu’il y ait des recouvrements avec d’autres standards).",
        "apihelp-query+languageinfo-paramvalue-prop-bcp47": "Le code de langue BCP-47.",
        "apihelp-query+languageinfo-paramvalue-prop-dir": "La direction d’écriture de la langue (<code>ltr</code> ou <code>rtl</code>).",
        "apihelp-query+languageinfo-paramvalue-prop-autonym": "L’autonyme d’une langue, c’est-à-dire son nom dans cette langue.",
        "apihelp-query+siteinfo-paramvalue-prop-fileextensions": "Renvoie la liste des extensions de fichiers (types de fichiers) autorisées au téléversement.",
        "apihelp-query+siteinfo-paramvalue-prop-rightsinfo": "Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.",
        "apihelp-query+siteinfo-paramvalue-prop-restrictions": "Renvoie l’information sur les types de restriction disponibles (protection).",
-       "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que MédiaWiki prend en charge (éventuellement localisée en utilisant <var>$1inlanguagecode</var>).",
+       "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que MediaWiki prend en charge (éventuellement localisée en utilisant <var>$1inlanguagecode</var>).",
        "apihelp-query+siteinfo-paramvalue-prop-languagevariants": "Renvoie une liste de codes de langue pour lesquels [[mw:Special:MyLanguage/LanguageConverter|LanguageConverter]] est activé, et les variantes prises en charge pour chacun.",
        "apihelp-query+siteinfo-paramvalue-prop-skins": "Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant <var>$1inlanguagecode</var>, sinon dans la langue du contenu).",
        "apihelp-query+siteinfo-paramvalue-prop-extensiontags": "Renvoie une liste des balises d’extension de l’analyseur.",
        "apihelp-query+users-paramvalue-prop-editcount": "Ajoute le compteur de modifications de l’utilisateur.",
        "apihelp-query+users-paramvalue-prop-registration": "Ajoute l’horodatage d’inscription de l’utilisateur.",
        "apihelp-query+users-paramvalue-prop-emailable": "Marque si l’utilisateur peut et veut recevoir des courriels via [[Special:Emailuser]].",
-       "apihelp-query+users-paramvalue-prop-gender": "Marque le sexe de l’utilisateur. Renvoie « male », « female », ou « unknown ».",
+       "apihelp-query+users-paramvalue-prop-gender": "Marque le sexe de l’utilisateur. Renvoie « male », « female », ou « unknown ».",
        "apihelp-query+users-paramvalue-prop-centralids": "Ajoute les IDs centraux et l’état d’attachement de l’utilisateur.",
        "apihelp-query+users-paramvalue-prop-cancreate": "Indique si un compte peut être créé pour les noms d’utilisateurs valides mais non enregistrés.",
        "apihelp-query+users-param-attachedwiki": "Avec <kbd>$1prop=centralids</kbd>, indiquer si l’utilisateur est attaché au wiki identifié par cet ID.",
        "apihelp-rsd-summary": "Exporter un schéma RSD (Découverte Très Simple).",
        "apihelp-rsd-example-simple": "Exporter le schéma RSD",
        "apihelp-setnotificationtimestamp-summary": "Mettre à jour l’horodatage de notification pour les pages suivies.",
-       "apihelp-setnotificationtimestamp-extended-description": "Cela affecte la mise en évidence des pages modifiées dans la liste de suivi et l’historique, et l’envoi de courriel quand la préférence « {{int:tog-enotifwatchlistpages}} » est activée.",
+       "apihelp-setnotificationtimestamp-extended-description": "Cela affecte la mise en évidence des pages modifiées dans la liste de suivi et l’historique, et l’envoi de courriel quand la préférence « {{int:tog-enotifwatchlistpages}} » est activée.",
        "apihelp-setnotificationtimestamp-param-entirewatchlist": "Travailler sur toutes les pages suivies.",
        "apihelp-setnotificationtimestamp-param-timestamp": "Horodatage auquel dater la notification.",
        "apihelp-setnotificationtimestamp-param-torevid": "Révision pour laquelle fixer l’horodatage de notification (une page uniquement).",
        "apihelp-unlinkaccount-summary": "Supprimer un compte tiers lié de l’utilisateur actuel.",
        "apihelp-unlinkaccount-example-simple": "Essayer de supprimer le lien de l’utilisateur actuel pour le fournisseur associé avec <kbd>FooAuthenticationRequest</kbd>.",
        "apihelp-upload-summary": "Téléverser un fichier, ou obtenir l’état des téléversements en cours.",
-       "apihelp-upload-extended-description": "Plusieurs méthodes sont disponibles :\n* Téléverser directement le contenu du fichier, en utilisant le paramètre <var>$1file</var>.\n* Téléverser le fichier par morceaux, en utilisant les paramètres <var>$1filesize</var>, <var>$1chunk</var>, and <var>$1offset</var>.\n* Pour que le serveur MédiaWiki cherche un fichier depuis une URL, utilisez le paramètre <var>$1url</var>.\n* Terminer un téléversement précédent qui a échoué à cause d’avertissements, en utilisant le paramètre <var>$1filekey</var>.\nNoter que le POST HTTP doit être fait comme un téléversement de fichier (par exemple en utilisant <code>multipart/form-data</code>) en envoyant le <var>$1file</var>.",
+       "apihelp-upload-extended-description": "Plusieurs méthodes sont disponibles :\n* Téléverser directement le contenu du fichier, en utilisant le paramètre <var>$1file</var>.\n* Téléverser le fichier par morceaux, en utilisant les paramètres <var>$1filesize</var>, <var>$1chunk</var>, and <var>$1offset</var>.\n* Pour que le serveur MediaWiki cherche un fichier depuis une URL, utilisez le paramètre <var>$1url</var>.\n* Terminer un téléversement précédent qui a échoué à cause d’avertissements, en utilisant le paramètre <var>$1filekey</var>.\nNoter que le POST HTTP doit être fait comme un téléversement de fichier (par exemple en utilisant <code>multipart/form-data</code>) en envoyant le <var>$1file</var>.",
        "apihelp-upload-param-filename": "Nom de fichier cible.",
        "apihelp-upload-param-comment": "Téléverser le commentaire. Utilisé aussi comme texte de la page initiale pour les nouveaux fichiers si <var>$1text</var> n’est pas spécifié.",
        "apihelp-upload-param-tags": "Modifier les balises à appliquer à l’entrée du journal de téléversement et à la révision de la page du fichier.",
        "api-pageset-param-titles": "Une liste des titres sur lesquels travailler.",
        "api-pageset-param-pageids": "Une liste des IDs de pages sur lesquelles travailler.",
        "api-pageset-param-revids": "Une liste des IDs de révisions sur lesquelles travailler.",
-       "api-pageset-param-generator": "Obtenir la liste des pages sur lesquelles travailler en exécutant le module de requête spécifié.\n\n<strong>NOTE :<strong> les noms de paramètre du générateur doivent être préfixés avec un « g », voir les exemples.",
+       "api-pageset-param-generator": "Obtenir la liste des pages sur lesquelles travailler en exécutant le module de requête spécifié.\n\n<strong>NOTE :<strong> les noms de paramètre du générateur doivent être préfixés avec un « g », voir les exemples.",
        "api-pageset-param-redirects-generator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>, et dans les pages renvoyées par <var>$1generator</var>.",
        "api-pageset-param-redirects-nogenerator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>.",
        "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki prend en charge la conversion en variantes. Les langues qui prennent en charge la conversion en variantes incluent $1.",
        "api-help-param-templated-var-first": "<var>&#x7B;$1&#x7D;</var> dans le nom du paramètre doit être remplacé par des valeurs de <var>$2</var>",
        "api-help-param-templated-var": "<var>&#x7B;$1&#x7D;</var> par les valeurs de <var>$2</var>",
        "api-help-datatypes-header": "Type de données",
-       "api-help-datatypes": "Les entrées dans MédiaWiki doivent être en UTF-8 à la norme NFC. MédiaWiki peut tenter de convertir d’autres types d’entrées, mais cela peut faire échouer certaines opérations (comme les [[Special:ApiHelp/edit|modifications]] avec contrôles MD5).\n\nCertains types de paramètres dans les requêtes de l’API nécessitent plus d’explication&nbsp;:\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML&nbsp;: si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes, voir [[mw:Special:MyLanguage/Timestamp|les formats d’entrées de la librairie Timestampdocumentés sur mediawiki.org]] pour plus de détails. La date et heure ISO 8601 est recommandée&nbsp;: <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd>. De plus, la chaîne de caractères <kbd>now</kbd> peut être utilisée pour spécifier le fuseau horaire actuel.\n;séparateur multi-valeurs alternatif\n:Les paramètres prenant plusieurs valeurs sont normalement validés lorsque celles-ci sont séparées par le caractère «&nbsp;pipe&nbsp;» (|), ex. <kbd>paramètre=valeur1|valeur2</kbd> ou <kbd>paramètre=valeur1%7Cvaleur2</kbd>. Si une valeur doit contenir le caractère «&nbsp;pipe&nbsp;», utiliser U+001F (séparateur de sous-articles) comme séparateur ''et'' la préfixer de U+001F, ex. <kbd>paramètre=%1Fvaleur1%1Fvaleur2</kbd>.",
+       "api-help-datatypes": "Les entrées dans MediaWiki doivent être en UTF-8 à la norme NFC. MediaWiki peut tenter de convertir d’autres types d’entrées, mais cela peut faire échouer certaines opérations (comme les [[Special:ApiHelp/edit|modifications]] avec contrôles MD5).\n\nCertains types de paramètres dans les requêtes de l’API nécessitent plus d’explication&nbsp;:\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML&nbsp;: si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes, voir [[mw:Special:MyLanguage/Timestamp|les formats d’entrées de la librairie Timestampdocumentés sur mediawiki.org]] pour plus de détails. La date et heure ISO 8601 est recommandée&nbsp;: <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd>. De plus, la chaîne de caractères <kbd>now</kbd> peut être utilisée pour spécifier le fuseau horaire actuel.\n;séparateur multi-valeurs alternatif\n:Les paramètres prenant plusieurs valeurs sont normalement validés lorsque celles-ci sont séparées par le caractère «&nbsp;pipe&nbsp;» (|), ex. <kbd>paramètre=valeur1|valeur2</kbd> ou <kbd>paramètre=valeur1%7Cvaleur2</kbd>. Si une valeur doit contenir le caractère «&nbsp;pipe&nbsp;», utiliser U+001F (séparateur de sous-articles) comme séparateur ''et'' la préfixer de U+001F, ex. <kbd>paramètre=%1Fvaleur1%1Fvaleur2</kbd>.",
        "api-help-templatedparams-header": "Paramètres de modèle",
        "api-help-templatedparams": "Les paramètres de modèle supportent les cas où un module d’API a besoin d’une valeur pour chaque valeur d’un autre paramètre quelconque. Par exemple, s’il y avait un module d’API pour demander un fruit, il pourrait avoir un paramètre <var>fruits</var> pour spécifier quels fruits sont demandés et un paramètre de modèle <var>{fruit}-quantité</var> pour spécifier la quantité demandée de chaque fruit. Un client de l’API qui voudrait une pomme, cinq bananes et vingt fraises pourrait alors faire une requête comme <kbd>fruits=pommes|bananes|fraises&pommes-quantité=1&bananes-quantité=5&fraises-quantité=20</kbd>.",
        "api-help-param-type-limit": "Type : entier ou <kbd>max</kbd>",
        "api-help-param-multi-all": "Pour spécifier toutes les valeurs, utiliser <kbd>$1</kbd>.",
        "api-help-param-default": "Par défaut : $1",
        "api-help-param-default-empty": "Par défaut : <span class=\"apihelp-empty\">(vide)</span>",
-       "api-help-param-token": "Un jeton « $1 » récupéré par [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
+       "api-help-param-token": "Un jeton « $1 » récupéré par [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
        "api-help-param-token-webui": "Pour rester compatible, le jeton utilisé dans l’IHM web est aussi accepté.",
        "api-help-param-disabled-in-miser-mode": "Désactivé à cause du [[mw:Special:MyLanguage/Manual:$wgMiserMode|mode minimal]].",
        "api-help-param-limited-in-miser-mode": "<strong>NOTE :</strong> Du fait du [[mw:Special:MyLanguage/Manual:$wgMiserMode|mode minimal]], utiliser cela peut aboutir à moins de résultats que <var>$1limit</var> renvoyés avant de continuer ; dans les cas extrêmes, zéro résultat peut être renvoyé.",
        "apierror-appendnotsupported": "Impossible d’ajouter aux pages utilisant le modèle de contenu $1.",
        "apierror-articleexists": "L’article que vous essayez de créer l’a déjà été.",
        "apierror-assertbotfailed": "La vérification que l’utilisateur a le droit <code>bot</code> a échoué.",
-       "apierror-assertnameduserfailed": "La vérification que l’utilisateur est « $1 » a échoué.",
+       "apierror-assertnameduserfailed": "La vérification que l’utilisateur est « $1 » a échoué.",
        "apierror-assertuserfailed": "La vérification que l’utilisateur est connecté a échoué.",
        "apierror-autoblocked": "Votre adresse IP a été bloquée automatiquement, parce qu’elle a été utilisée par un utilisateur bloqué.",
        "apierror-bad-badfilecontexttitle": "Titre invalide dans le paramètre <var>$1badfilecontexttitle</var> .",
        "apierror-badgenerator-unknown": "<kbd>generator=$1</kbd> inconnu.",
        "apierror-badip": "Paramètre IP non valide.",
        "apierror-badmd5": "Le hachage MD5 fourni n’était pas correct.",
-       "apierror-badmodule-badsubmodule": "Le module <kbd>$1</kbd> n’a pas de sous-module « $2 ».",
+       "apierror-badmodule-badsubmodule": "Le module <kbd>$1</kbd> n’a pas de sous-module « $2 ».",
        "apierror-badmodule-nosubmodules": "Le module <kbd>$1</kbd> n’a pas de sous-modules.",
        "apierror-badparameter": "Valeur non valide pour le paramètre <var>$1</var>.",
        "apierror-badquery": "Requête invalide.",
-       "apierror-badtimestamp": "Valeur non valide « $2 » pour le paramètre de référence horaire  <var>$1</var>.",
+       "apierror-badtimestamp": "Valeur non valide « $2 » pour le paramètre de référence horaire  <var>$1</var>.",
        "apierror-badtoken": "Jeton CSRF non valide.",
        "apierror-badupload": "Le paramètre de téléversement de fichier <var>$1</var> n’est pas un téléversement de fichier ; assurez-vous d’utiliser <code>multipart/form-data</code> pour votre POST et d’inclure un nom de fichier dans l’entête <code>Content-Disposition</code>.",
-       "apierror-badurl": "Valeur « $2 » non valide pour le paramètre d’URL <var>$1</var>.",
-       "apierror-baduser": "Valeur « $2 » non valide pour le paramètre utilisateur <var>$1</var>.",
+       "apierror-badurl": "Valeur « $2 » non valide pour le paramètre d’URL <var>$1</var>.",
+       "apierror-baduser": "Valeur « $2 » non valide pour le paramètre utilisateur <var>$1</var>.",
        "apierror-badvalue-notmultivalue": "La séparation multi-valeur U+001F ne peut être utilisée que pour des paramètres multi-valeurs.",
        "apierror-bad-watchlist-token": "Jeton de liste de suivi fourni non valide. Veuillez mettre un jeton valide dans [[Special:Preferences]].",
        "apierror-blockedfrommail": "Vous avez été bloqué pour l’envoi de courriel.",
        "apierror-invalidsection": "Le paramètre <var>section</var> doit être un ID de section valide ou <kbd>new</kbd>.",
        "apierror-invalidsha1base36hash": "Le hachage SHA1Base36 fourni n’est pas valide.",
        "apierror-invalidsha1hash": "Le hachage SHA1 fourni n’est pas valide.",
-       "apierror-invalidtitle": "Mauvais titre « $1 ».",
+       "apierror-invalidtitle": "Mauvais titre « $1 ».",
        "apierror-invalidurlparam": "Valeur non valide pour <var>$1urlparam</var> (<kbd>$2=$3</kbd>).",
        "apierror-invaliduser": "Nom d'utilisateur invalide \"$1\".",
        "apierror-invaliduserid": "L'ID d'utilisateur <var>$1</var> n'est pas valide.",
        "apierror-paramempty": "Le paramètre <var>$1</var> ne peut pas être vide.",
        "apierror-parsetree-notwikitext": "<kbd>prop=parsetree</kbd> n’est pris en charge que pour le contenu wikitexte.",
        "apierror-parsetree-notwikitext-title": "<kbd>prop=parsetree</kbd> n’est pris en charge que pour le contenu wikitexte. $1 utilise le modèle de contenu $2.",
-       "apierror-pastexpiry": "La date d’expiration « $1 » est dépassée.",
+       "apierror-pastexpiry": "La date d’expiration « $1 » est dépassée.",
        "apierror-permissiondenied": "Vous n’avez pas le droit de $1.",
        "apierror-permissiondenied-generic": "Autorisation refusée.",
        "apierror-permissiondenied-patrolflag": "Vous avez besoin du droit <code>patrol</code> ou <code>patrolmarks</code> pour demander le drapeau patrouillé.",
        "apierror-permissiondenied-unblock": "Vous n’avez pas le droit de débloquer les utilisateurs.",
        "apierror-prefixsearchdisabled": "La recherche de préfixe est désactivée en mode misérable.",
        "apierror-promised-nonwrite-api": "L’entête HTTP <code>Promise-Non-Write-API-Action</code> ne peut pas être envoyé aux modules de l’API en mode écriture.",
-       "apierror-protect-invalidaction": "Type de protection non valide « $1 ».",
-       "apierror-protect-invalidlevel": "Niveau de protection non valide « $1 ».",
+       "apierror-protect-invalidaction": "Type de protection non valide « $1 ».",
+       "apierror-protect-invalidlevel": "Niveau de protection non valide « $1 ».",
        "apierror-ratelimited": "Vous avez dépassé votre limite de débit. Veuillez attendre un peu et réessayer.",
        "apierror-readapidenied": "Vous avez besoin du droit de lecture pour utiliser ce module.",
        "apierror-readonly": "Ce wiki est actuellement en mode lecture seule.",
        "apiwarn-deprecation-login-token": "La récupération d’un jeton via <kbd>action=login</kbd> est désuète. Utilisez <kbd>action=query&meta=tokens&type=login</kbd> à la place.",
        "apiwarn-deprecation-missingparam": "Comme <var>$1</var> n’a pas été spécifié, un format ancien a été utilisé pour la sortie. Ce format est obsolète, et dans le futur, le nouveau format sera toujours utilisé.",
        "apiwarn-deprecation-parameter": "Le paramètre <var>$1</var> est désuet.",
-       "apiwarn-deprecation-parse-headitems": "<kbd>prop=headitems</kbd> est désuet depuis MédiaWiki 1.28. Utilisez <kbd>prop=headhtml</kbd> lors de la création de nouveaux documents HTML, ou <kbd>prop=modules|jsconfigvars</kbd> lors de la mise à jour d’un document côté client.",
+       "apiwarn-deprecation-parse-headitems": "<kbd>prop=headitems</kbd> est désuet depuis MediaWiki 1.28. Utilisez <kbd>prop=headhtml</kbd> lors de la création de nouveaux documents HTML, ou <kbd>prop=modules|jsconfigvars</kbd> lors de la mise à jour d’un document côté client.",
        "apiwarn-deprecation-post-without-content-type": "Une requête POST a été faite sans entête <code>Content-Type</code>. Cela ne fonctionne pas de façon fiable.",
        "apiwarn-deprecation-purge-get": "L’utilisation de <kbd>action=purge</kbd> via un GET est désuète. Utiliser POST à la place.",
        "apiwarn-deprecation-withreplacement": "<kbd>$1</kbd> est désuet. Veuillez utiliser <kbd>$2</kbd> à la place.",
        "apiwarn-difftohidden": "Impossible de faire un diff avec r$1 : le contenu est masqué.",
        "apiwarn-errorprinterfailed": "Erreur échec imprimante. Nouvel essai sans paramètres.",
        "apiwarn-ignoring-invalid-templated-value": "Ignorer la valeur <kbd>$2</kbd> dans <var>$1</var> en traitant les paramètres de modèle.",
-       "apiwarn-invalidcategory": "« $1 » n'est pas une catégorie.",
-       "apiwarn-invalidtitle": "« $1 » n’est pas un titre valide.",
+       "apiwarn-invalidcategory": "« $1 » n'est pas une catégorie.",
+       "apiwarn-invalidtitle": "« $1 » n’est pas un titre valide.",
        "apiwarn-invalidxmlstylesheetext": "Une feuille de style doit avoir une extension <code>.xsl</code>.",
        "apiwarn-invalidxmlstylesheet": "Feuille de style spécifiée non valide ou inexistante.",
        "apiwarn-invalidxmlstylesheetns": "La feuille de style devrait être dans l’espace de noms {{ns:MediaWiki}}.",
        "apiwarn-moduleswithoutvars": "La propriété <kbd>modules</kbd> a été définie mais pas <kbd>jsconfigvars</kbd> ni <kbd>encodedjsconfigvars</kbd>. Les variables de configuration sont nécessaires pour une utilisation correcte du module.",
-       "apiwarn-notfile": "« $1 » n'est pas un fichier.",
+       "apiwarn-notfile": "« $1 » n'est pas un fichier.",
        "apiwarn-nothumb-noimagehandler": "Impossible de créer la vignette car $1 n’a pas de gestionnaire d’image associé.",
        "apiwarn-parse-nocontentmodel": "Ni <var>title</var> ni <var>contentmodel</var> n’ont été fournis, $1 est supposé.",
        "apiwarn-parse-revidwithouttext": "<var>revid</var> utilisé sans <var>text</var>, et les propriétés de la page analysée ont été demandées. Vouliez-vous utiliser <var>oldid</var> au lieu de <var>revid</var> ?",
        "apiwarn-parse-titlewithouttext": "<var>title</var> utilisé sans <var>text</var>, et les propriétés de page analysées sont nécessaires. Voulez-vous dire que vous voulez utiliser <var>page</var> à la place de <var>title</var> ?",
        "apiwarn-redirectsandrevids": "La résolution de la redirection ne peut pas être utilisée avec le paramètre <var>revids</var>. Toutes les redirections vers lesquelles pointent <var>revids</var> n’ont pas été résolues.",
-       "apiwarn-tokennotallowed": "L'action « $1 » n'est pas autorisée pour l'utilisateur actuel.",
+       "apiwarn-tokennotallowed": "L'action « $1 » n'est pas autorisée pour l'utilisateur actuel.",
        "apiwarn-tokens-origin": "Les jetons ne peuvent pas être obtenus quand la politique de même origine n’est pas appliquée.",
        "apiwarn-truncatedresult": "Ce résultat a été tronqué parce que sinon, il dépasserait la limite de $1 octets.",
-       "apiwarn-unclearnowtimestamp": "Passer « $2 » comme paramètre d’horodatage <var>$1</var> est obsolète. Si, pour une raison quelconque, vous avez besoin de spécifier explicitement l’heure courante sans la recalculer du côté client, utilisez <kbd>now</kbd>.",
+       "apiwarn-unclearnowtimestamp": "Passer « $2 » comme paramètre d’horodatage <var>$1</var> est obsolète. Si, pour une raison quelconque, vous avez besoin de spécifier explicitement l’heure courante sans la recalculer du côté client, utilisez <kbd>now</kbd>.",
        "apiwarn-unrecognizedvalues": "{{PLURAL:$3|Valeur non reconnue|Valeurs non reconnues}} pour le paramètre <var>$1</var> : $2.",
        "apiwarn-unsupportedarray": "Le paramètre <var>$1</var> utilise une syntaxe PHP de tableau non prise en charge.",
        "apiwarn-urlparamwidth": "Valeur de la largeur définie dans <var>$1urlparam</var> ($2) ignorée en faveur de la largeur calculée à partir de <var>$1urlwidth</var>/<var>$1urlheight</var> ($3).",
index c91976f..4d02204 100644 (file)
@@ -37,7 +37,8 @@
                        "Edward Chernenko",
                        "Vlad5250",
                        "Diralik",
-                       "DmitTrix"
+                       "DmitTrix",
+                       "Марио"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Документация]]\n* [[mw:Special:MyLanguage/API:FAQ|ЧаВО]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Почтовая рассылка]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Новости API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Ошибки и запросы]\n</div>\n<strong>Статус:</strong> MediaWiki API — зрелый и стабильный интерфейс, активно поддерживаемый и улучшаемый. Мы стараемся избегать ломающих изменений, однако изредка они могут быть необходимы. Подпишитесь на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ почтовую рассылку mediawiki-api-announce], чтобы быть в курсе обновлений.\n\n<strong>Ошибочные запросы:</strong> Если API получает запрос с ошибкой, вернётся заголовок HTTP с ключом «MediaWiki-API-Error», после чего значение заголовка и код ошибки будут отправлены обратно и установлены в то же значение. Более подробную информацию см. [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Ошибки и предупреждения]].\n\n<p class=\"mw-apisandbox-link\"><strong>Тестирование:</strong> для удобства тестирования API-запросов, см. [[Special:ApiSandbox]].</p>",
@@ -94,6 +95,7 @@
        "apihelp-compare-param-fromid": "Идентификатор первой сравниваемой страницы.",
        "apihelp-compare-param-fromrev": "Первая сравниваемая версия.",
        "apihelp-compare-param-frompst": "Выполнить преобразование перед записью правки (PST) над <var>fromtext-&#x7B;slot}</var>.",
+       "apihelp-compare-param-fromslots": "Переопределение содержимого версии, заданной параметром <var>fromtitle</var>, <var>fromid</var> или <var>fromrev</var>.\n\nЭтот параметр определяет слоты, которые должны быть изменены. Используйте <var>fromtext-&#x7B;slot}</var>, <var>fromcontentmodel-&#x7B;slot}</var>, и <var>fromcontentformat-&#x7B;slot}</var> для определения содержимого для каждого слота.",
        "apihelp-compare-param-fromtext": "Укажите <kbd>fromslots=main</kbd> и используйте <var>fromtext-main</var>.",
        "apihelp-compare-param-fromcontentmodel": "Укажите <kbd>fromslots=main</kbd> и используйте <var>fromcontentmodel-main</var>.",
        "apihelp-compare-param-fromcontentformat": "Укажите <kbd>fromslots=main</kbd> и используйте <var>fromcontentformat-main</var>.",
        "apihelp-query+blocks-paramvalue-prop-reason": "Добавляет причину блокировки.",
        "apihelp-query+blocks-paramvalue-prop-range": "Добавляет диапазон IP-адресов, затронутых блокировкой.",
        "apihelp-query+blocks-paramvalue-prop-flags": "Добавляет бану метку (autoblock, anonoly, и так далее).",
+       "apihelp-query+blocks-paramvalue-prop-restrictions": "Добавляет ограничения частичных блокировок, если блокировка не действует во всём проекте.",
        "apihelp-query+blocks-param-show": "Показать только элементы, удовлетворяющие этим критериям.\nНапример, чтобы отобразить только бессрочные блокировки IP-адресов, установите <kbd>$1show=ip|!temp</kbd>.",
        "apihelp-query+blocks-example-simple": "Список блокировок.",
        "apihelp-query+blocks-example-users": "Список блокировок участников <kbd>Alice</kbd> и <kbd>Bob</kbd>.",
        "apierror-bad-watchlist-token": "Предоставлен некорректный токен списка наблюдения. Пожалуйста, установите корректный токен в [[Special:Preferences]].",
        "apierror-blockedfrommail": "Отправка электронной почты была для вас заблокирована.",
        "apierror-blocked": "Редактирование было для вас заблокировано.",
+       "apierror-blocked-partial": "Вы были заблокированы от редактирования этой страницы.",
        "apierror-botsnotsupported": "Этот интерфейс не поддерживается для ботов.",
        "apierror-cannot-async-upload-file": "Параметры <var>async</var> и <var>file</var> не могут применяться вместе. Если вы хотите ассинхронно обработать загруженный файл, сначала загрузите его во временное хранилище (используя параметр <var>stash</var>), а затем опубликуйте этот файл ассинхронно (используя параметры <var>filekey</var> и <var>async</var>).",
        "apierror-cannotreauthenticate": "Это действие недоступно, так как ваша личность не может быть подтверждена.",
index e7527d1..bc70d5e 100644 (file)
@@ -247,12 +247,18 @@ abstract class AuthenticationRequest {
 
        /**
         * Select a request by class name.
+        *
+        * @codingStandardsIgnoreStart
+        * @phan-template T
+        * @codingStandardsIgnoreEnd
         * @param AuthenticationRequest[] $reqs
         * @param string $class Class name
+        * @phan-param class-string<T> $class
         * @param bool $allowSubclasses If true, also returns any request that's a subclass of the given
         *   class.
         * @return AuthenticationRequest|null Returns null if there is not exactly
         *  one matching request.
+        * @phan-return T|null
         */
        public static function getRequestByClass( array $reqs, $class, $allowSubclasses = false ) {
                $requests = array_filter( $reqs, function ( $req ) use ( $class, $allowSubclasses ) {
index c831fc8..25a1754 100644 (file)
@@ -96,7 +96,9 @@ class ResetPasswordSecondaryAuthenticationProvider extends AbstractSecondaryAuth
                        }
                }
 
+               /** @var PasswordAuthenticationRequest $needReq */
                $needReq = $data->req ?? new PasswordAuthenticationRequest();
+               '@phan-var PasswordAuthenticationRequest $needReq';
                if ( !$needReq->action ) {
                        $needReq->action = AuthManager::ACTION_CHANGE;
                }
index 6007abd..b1a8e21 100644 (file)
@@ -24,7 +24,6 @@ namespace MediaWiki\Block;
 
 use ActorMigration;
 use AutoCommitUpdate;
-use BadMethodCallException;
 use CommentStore;
 use DeferredUpdates;
 use Hooks;
@@ -161,47 +160,6 @@ class DatabaseBlock extends AbstractBlock {
                }
        }
 
-       /**
-        * Return the list of ipblocks fields that should be selected to create
-        * a new block.
-        * @deprecated since 1.31, use self::getQueryInfo() instead.
-        * @return array
-        */
-       public static function selectFields() {
-               global $wgActorTableSchemaMigrationStage;
-
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->ipb_by or $row->ipb_by_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               wfDeprecated( __METHOD__, '1.31' );
-               return [
-                       'ipb_id',
-                       'ipb_address',
-                       'ipb_by',
-                       'ipb_by_text',
-                       'ipb_by_actor' => 'NULL',
-                       'ipb_timestamp',
-                       'ipb_auto',
-                       'ipb_anon_only',
-                       'ipb_create_account',
-                       'ipb_enable_autoblock',
-                       'ipb_expiry',
-                       'ipb_deleted',
-                       'ipb_block_email',
-                       'ipb_allow_usertalk',
-                       'ipb_parent_block_id',
-                       'ipb_sitewide',
-               ] + CommentStore::getStore()->getFields( 'ipb_reason' );
-       }
-
        /**
         * Return the tables, fields, and join conditions to be selected to create
         * a new block object.
index 8f816d9..bc0bbfa 100644 (file)
@@ -78,8 +78,6 @@ class UserCache {
         * @param string $caller The calling method
         */
        public function doQuery( array $userIds, $options = [], $caller = '' ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $usersToCheck = [];
                $usersToQuery = [];
 
@@ -100,21 +98,12 @@ class UserCache {
                // Lookup basic info for users not yet loaded...
                if ( count( $usersToQuery ) ) {
                        $dbr = wfGetDB( DB_REPLICA );
-                       $tables = [ 'user' ];
+                       $tables = [ 'user', 'actor' ];
                        $conds = [ 'user_id' => $usersToQuery ];
-                       $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id' ];
-                       $joinConds = [];
-
-                       // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
-                       // but it does little harm and might be needed for write callers loading a User.
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
-                               $tables[] = 'actor';
-                               $fields[] = 'actor_id';
-                               $joinConds['actor'] = [
-                                       ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ? 'JOIN' : 'LEFT JOIN',
-                                       [ 'actor_user = user_id' ]
-                               ];
-                       }
+                       $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id', 'actor_id' ];
+                       $joinConds = [
+                               'actor' => [ 'JOIN', 'actor_user = user_id' ],
+                       ];
 
                        $comment = __METHOD__;
                        if ( strval( $caller ) !== '' ) {
@@ -127,9 +116,7 @@ class UserCache {
                                $this->cache[$userId]['name'] = $row->user_name;
                                $this->cache[$userId]['real_name'] = $row->user_real_name;
                                $this->cache[$userId]['registration'] = $row->user_registration;
-                               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
-                                       $this->cache[$userId]['actor'] = $row->actor_id;
-                               }
+                               $this->cache[$userId]['actor'] = $row->actor_id;
                                $usersToCheck[$userId] = $row->user_name;
                        }
                }
index 1d590d9..e184825 100644 (file)
@@ -221,55 +221,6 @@ class RecentChange implements Taggable {
                }
        }
 
-       /**
-        * Return the list of recentchanges fields that should be selected to create
-        * a new recentchanges object.
-        * @deprecated since 1.31, use self::getQueryInfo() instead.
-        * @return array
-        */
-       public static function selectFields() {
-               global $wgActorTableSchemaMigrationStage;
-
-               wfDeprecated( __METHOD__, '1.31' );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->rc_user or $row->rc_user_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               return [
-                       'rc_id',
-                       'rc_timestamp',
-                       'rc_user',
-                       'rc_user_text',
-                       'rc_actor' => 'NULL',
-                       'rc_namespace',
-                       'rc_title',
-                       'rc_minor',
-                       'rc_bot',
-                       'rc_new',
-                       'rc_cur_id',
-                       'rc_this_oldid',
-                       'rc_last_oldid',
-                       'rc_type',
-                       'rc_source',
-                       'rc_patrolled',
-                       'rc_ip',
-                       'rc_old_len',
-                       'rc_new_len',
-                       'rc_deleted',
-                       'rc_logid',
-                       'rc_log_type',
-                       'rc_log_action',
-                       'rc_params',
-               ] + CommentStore::getStore()->getFields( 'rc_comment' );
-       }
-
        /**
         * Return the tables, fields, and join conditions to be selected to create
         * a new recentchanges object.
diff --git a/includes/composer/ComposerPhpunitXmlCoverageEdit.php b/includes/composer/ComposerPhpunitXmlCoverageEdit.php
new file mode 100644 (file)
index 0000000..7db4b11
--- /dev/null
@@ -0,0 +1,60 @@
+<?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.
+ *
+ */
+
+/**
+ * Edit phpunit.xml to speed up code coverage generation.
+ *
+ * Usage: composer phpunit:coverage-edit -- extensions/ExtensionName
+ *
+ * This class runs *outside* of the normal MediaWiki
+ * environment and cannot depend upon any MediaWiki
+ * code.
+ */
+class ComposerPhpunitXmlCoverageEdit {
+
+       public static function onEvent( $event ) {
+               $IP = dirname( dirname( __DIR__ ) );
+               // TODO: Support passing arbitrary directories for core (or extensions/skins).
+               $args = $event->getArguments();
+               if ( count( $args ) !== 1 ) {
+                       throw new InvalidArgumentException( 'Pass extensions/$extensionName as an argument, ' .
+                               'e.g. "composer phpunit:coverage-edit -- extensions/BoilerPlate"' );
+               }
+               $project = current( $args );
+               $phpunitXml = \PHPUnit\Util\Xml::loadFile( $IP . '/phpunit.xml.dist' );
+               $whitelist = iterator_to_array( $phpunitXml->getElementsByTagName( 'whitelist' ) );
+               /** @var DOMNode $childNode */
+               foreach ( $whitelist as $childNode ) {
+                       $childNode->parentNode->removeChild( $childNode );
+               }
+               $whitelistElement = $phpunitXml->createElement( 'whitelist' );
+               $whitelistElement->setAttribute( 'addUncoveredFilesFromWhitelist', 'false' );
+               // TODO: Use AutoloadClasses from extension.json to load the relevant directories
+               foreach ( [ 'includes', 'src', 'maintenance' ] as $dir ) {
+                       $dirElement = $phpunitXml->createElement( 'directory', $project . '/' . $dir );
+                       $dirElement->setAttribute( 'suffix', '.php' );
+                       $whitelistElement->appendChild( $dirElement );
+
+               }
+               $phpunitXml->getElementsByTagName( 'filter' )->item( 0 )
+                       ->appendChild( $whitelistElement );
+               $phpunitXml->formatOutput = true;
+               $phpunitXml->save( $IP . '/phpunit.xml' );
+       }
+}
index 696bbf4..bd174b2 100644 (file)
@@ -66,7 +66,8 @@ class ConfigFactory implements SalvageableService {
        public function salvage( SalvageableService $other ) {
                Assert::parameterType( self::class, $other, '$other' );
 
-               /** @var ConfigFactory $other */
+               /** @var self $other */
+               '@phan-var self $other';
                foreach ( $other->factoryFunctions as $name => $otherFunc ) {
                        if ( !isset( $this->factoryFunctions[$name] ) ) {
                                continue;
index d48eb0e..ceb3944 100644 (file)
@@ -188,6 +188,8 @@ class ConfigRepository implements SalvageableService {
         */
        public function salvage( SalvageableService $other ) {
                Assert::parameterType( self::class, $other, '$other' );
+               /** @var self $other */
+               '@phan-var self $other';
 
                foreach ( $other->configItems['public'] as $name => $otherConfig ) {
                        if ( isset( $this->configItems['public'][$name] ) ) {
index e6a856c..cbcaba1 100644 (file)
@@ -81,6 +81,12 @@ class RequestContext implements IContextSource, MutableContext {
         */
        private static $instance = null;
 
+       /**
+        * Boolean flag to guard against recursion in getLanguage
+        * @var bool
+        */
+       private $languageRecursion = false;
+
        /**
         * @param Config $config
         */
@@ -318,7 +324,7 @@ class RequestContext implements IContextSource, MutableContext {
         * @since 1.19
         */
        public function getLanguage() {
-               if ( isset( $this->recursion ) ) {
+               if ( $this->languageRecursion === true ) {
                        trigger_error( "Recursion detected in " . __METHOD__, E_USER_WARNING );
                        $e = new Exception;
                        wfDebugLog( 'recursion-guard', "Recursion detected:\n" . $e->getTraceAsString() );
@@ -326,7 +332,7 @@ class RequestContext implements IContextSource, MutableContext {
                        $code = $this->getConfig()->get( 'LanguageCode' ) ?: 'en';
                        $this->lang = Language::factory( $code );
                } elseif ( $this->lang === null ) {
-                       $this->recursion = true;
+                       $this->languageRecursion = true;
 
                        try {
                                $request = $this->getRequest();
@@ -348,7 +354,7 @@ class RequestContext implements IContextSource, MutableContext {
                                        $this->lang = $obj;
                                }
                        } finally {
-                               unset( $this->recursion );
+                               $this->languageRecursion = false;
                        }
                }
 
index e099b38..09314ed 100644 (file)
@@ -32,24 +32,24 @@ use Wikimedia\Rdbms\ILoadBalancer;
  * @author Daniel Kinzler
  */
 abstract class DBAccessBase implements IDBAccessObject {
-       /**
-        * @var string|bool $wiki The target wiki's name. This must be an ID
-        * that LBFactory can understand.
-        */
-       protected $wiki = false;
+       /** @var ILoadBalancer */
+       private $lb;
+
+       /** @var string|bool The target wiki's DB domain */
+       protected $dbDomain = false;
 
        /**
-        * @param string|bool $wiki The target wiki's name. This must be an ID
-        * that LBFactory can understand.
+        * @param string|bool $dbDomain The target wiki's DB domain
         */
-       public function __construct( $wiki = false ) {
-               $this->wiki = $wiki;
+       public function __construct( $dbDomain = false ) {
+               $this->dbDomain = $dbDomain;
+               $this->lb = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()
+                       ->getMainLB( $dbDomain );
        }
 
        /**
         * Returns a database connection.
         *
-        * @see wfGetDB()
         * @see LoadBalancer::getConnection()
         *
         * @since 1.21
@@ -60,9 +60,7 @@ abstract class DBAccessBase implements IDBAccessObject {
         * @return IDatabase
         */
        protected function getConnection( $id, array $groups = [] ) {
-               $loadBalancer = $this->getLoadBalancer();
-
-               return $loadBalancer->getConnection( $id, $groups, $this->wiki );
+               return $this->getLoadBalancer()->getConnectionRef( $id, $groups, $this->dbDomain );
        }
 
        /**
@@ -73,12 +71,10 @@ abstract class DBAccessBase implements IDBAccessObject {
         * @since 1.21
         *
         * @param IDatabase $db The database connection to release.
+        * @deprecated Since 1.34
         */
        protected function releaseConnection( IDatabase $db ) {
-               if ( $this->wiki !== false ) {
-                       $loadBalancer = $this->getLoadBalancer();
-                       $loadBalancer->reuseConnection( $db );
-               }
+               // no-op
        }
 
        /**
@@ -90,8 +86,7 @@ abstract class DBAccessBase implements IDBAccessObject {
         *
         * @return ILoadBalancer The database load balancer object
         */
-       public function getLoadBalancer() {
-               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
-               return $lbFactory->getMainLB( $this->wiki );
+       protected function getLoadBalancer() {
+               return $this->lb;
        }
 }
index 5a5e507..a48faf1 100644 (file)
@@ -10,10 +10,16 @@ use Psr\Log\AbstractLogger;
  * goal.
  */
 class ConsoleLogger extends AbstractLogger {
+       /**
+        * @param string $channel
+        */
        public function __construct( $channel ) {
                $this->channel = $channel;
        }
 
+       /**
+        * @inheritDoc
+        */
        public function log( $level, $message, array $context = [] ) {
                fwrite( STDERR, "[$level] " .
                        LegacyLogger::format( $this->channel, $message, $context ) );
index 66ce9a3..ddffaa3 100644 (file)
@@ -39,8 +39,9 @@ class CdnCacheUpdate implements DeferrableUpdate, MergeableUpdate {
        }
 
        public function merge( MergeableUpdate $update ) {
-               /** @var CdnCacheUpdate $update */
+               /** @var self $update */
                Assert::parameterType( __CLASS__, $update, '$update' );
+               '@phan-var self $update';
 
                $this->urls = array_merge( $this->urls, $update->urls );
        }
index 1691da2..d1b592d 100644 (file)
@@ -41,8 +41,9 @@ class JobQueueEnqueueUpdate implements DeferrableUpdate, MergeableUpdate {
        }
 
        public function merge( MergeableUpdate $update ) {
-               /** @var JobQueueEnqueueUpdate $update */
+               /** @var self $update */
                Assert::parameterType( __CLASS__, $update, '$update' );
+               '@phan-var self $update';
 
                foreach ( $update->jobsByDomain as $domain => $jobs ) {
                        $this->jobsByDomain[$domain] = $this->jobsByDomain[$domain] ?? [];
index c499d08..7f56a36 100644 (file)
@@ -42,8 +42,9 @@ class MessageCacheUpdate implements DeferrableUpdate, MergeableUpdate {
        }
 
        public function merge( MergeableUpdate $update ) {
-               /** @var MessageCacheUpdate $update */
+               /** @var self $update */
                Assert::parameterType( __CLASS__, $update, '$update' );
+               '@phan-var self $update';
 
                foreach ( $update->replacements as $code => $messages ) {
                        $this->replacements[$code] = array_merge( $this->replacements[$code] ?? [], $messages );
index 11e9337..dbd7c50 100644 (file)
@@ -56,6 +56,7 @@ class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
        public function merge( MergeableUpdate $update ) {
                /** @var SiteStatsUpdate $update */
                Assert::parameterType( __CLASS__, $update, '$update' );
+               '@phan-var SiteStatsUpdate $update';
 
                foreach ( self::$counters as $field ) {
                        $this->$field += $update->$field;
index 687dfbe..4333c94 100644 (file)
@@ -46,6 +46,7 @@ class UserEditCountUpdate implements DeferrableUpdate, MergeableUpdate {
        public function merge( MergeableUpdate $update ) {
                /** @var UserEditCountUpdate $update */
                Assert::parameterType( __CLASS__, $update, '$update' );
+               '@phan-var UserEditCountUpdate $update';
 
                foreach ( $update->infoByUser as $userId => $info ) {
                        if ( !isset( $this->infoByUser[$userId] ) ) {
index 7fcda4c..8a5caa2 100644 (file)
@@ -544,7 +544,7 @@ class DifferenceEngine extends ContextSource {
                        if ( $samePage && $this->mNewPage && $permissionManager->quickUserCan(
                                'edit', $user, $this->mNewPage
                        ) ) {
-                               if ( $this->mNewRev->isCurrent() && $permissionManager->userCan(
+                               if ( $this->mNewRev->isCurrent() && $permissionManager->quickUserCan(
                                        'rollback', $user, $this->mNewPage
                                ) ) {
                                        $rollbackLink = Linker::generateRollback( $this->mNewRev, $this->getContext(),
index 6a3e819..17fa146 100644 (file)
@@ -213,50 +213,6 @@ class ArchivedFile {
                return $file;
        }
 
-       /**
-        * Fields in the filearchive table
-        * @deprecated since 1.31, use self::getQueryInfo() instead.
-        * @return string[]
-        */
-       static function selectFields() {
-               global $wgActorTableSchemaMigrationStage;
-
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->fa_user or $row->fa_user_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               wfDeprecated( __METHOD__, '1.31' );
-               return [
-                       'fa_id',
-                       'fa_name',
-                       'fa_archive_name',
-                       'fa_storage_key',
-                       'fa_storage_group',
-                       'fa_size',
-                       'fa_bits',
-                       'fa_width',
-                       'fa_height',
-                       'fa_metadata',
-                       'fa_media_type',
-                       'fa_major_mime',
-                       'fa_minor_mime',
-                       'fa_user',
-                       'fa_user_text',
-                       'fa_actor' => 'NULL',
-                       'fa_timestamp',
-                       'fa_deleted',
-                       'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
-                       'fa_sha1',
-               ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'fa_description' );
-       }
-
        /**
         * Return the tables, fields, and join conditions to be selected to create
         * a new archivedfile object.
index 3090632..ceb8dda 100644 (file)
@@ -202,44 +202,6 @@ class LocalFile extends File {
                }
        }
 
-       /**
-        * Fields in the image table
-        * @deprecated since 1.31, use self::getQueryInfo() instead.
-        * @return string[]
-        */
-       static function selectFields() {
-               global $wgActorTableSchemaMigrationStage;
-
-               wfDeprecated( __METHOD__, '1.31' );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->img_user or $row->img_user_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               return [
-                       'img_name',
-                       'img_size',
-                       'img_width',
-                       'img_height',
-                       'img_metadata',
-                       'img_bits',
-                       'img_media_type',
-                       'img_major_mime',
-                       'img_minor_mime',
-                       'img_user',
-                       'img_user_text',
-                       'img_actor' => 'NULL',
-                       'img_timestamp',
-                       'img_sha1',
-               ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'img_description' );
-       }
-
        /**
         * Return the tables, fields, and join conditions to be selected to create
         * a new localfile object.
@@ -1449,8 +1411,6 @@ class LocalFile extends File {
                $oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null, $tags = [],
                $createNullRevision = true, $revert = false
        ) {
-               global $wgActorTableSchemaMigrationStage;
-
                if ( is_null( $user ) ) {
                        global $wgUser;
                        $user = $wgUser;
@@ -1553,40 +1513,10 @@ class LocalFile extends File {
                                'oi_major_mime' => 'img_major_mime',
                                'oi_minor_mime' => 'img_minor_mime',
                                'oi_sha1' => 'img_sha1',
+                               'oi_actor' => 'img_actor',
                        ];
                        $joins = [];
 
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                               $fields['oi_user'] = 'img_user';
-                               $fields['oi_user_text'] = 'img_user_text';
-                       }
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                               $fields['oi_actor'] = 'img_actor';
-                       }
-
-                       if (
-                               ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) === SCHEMA_COMPAT_WRITE_BOTH
-                       ) {
-                               // Upgrade any rows that are still old-style. Otherwise an upgrade
-                               // might be missed if a deletion happens while the migration script
-                               // is running.
-                               $res = $dbw->select(
-                                       [ 'image' ],
-                                       [ 'img_name', 'img_user', 'img_user_text' ],
-                                       [ 'img_name' => $this->getName(), 'img_actor' => 0 ],
-                                       __METHOD__
-                               );
-                               foreach ( $res as $row ) {
-                                       $actorId = User::newFromAnyId( $row->img_user, $row->img_user_text, null )->getActorId( $dbw );
-                                       $dbw->update(
-                                               'image',
-                                               [ 'img_actor' => $actorId ],
-                                               [ 'img_name' => $row->img_name, 'img_actor' => 0 ],
-                                               __METHOD__
-                                       );
-                               }
-                       }
-
                        # (T36993) Note: $oldver can be empty here, if the previous
                        # version of the file was broken. Allow registration of the new
                        # version to continue anyway, because that's better than having
index 61faa09..85988f6 100644 (file)
@@ -178,8 +178,6 @@ class LocalFileDeleteBatch {
        }
 
        protected function doDBInserts() {
-               global $wgActorTableSchemaMigrationStage;
-
                $now = time();
                $dbw = $this->file->repo->getMasterDB();
 
@@ -225,7 +223,8 @@ class LocalFileDeleteBatch {
                                'fa_minor_mime' => 'img_minor_mime',
                                'fa_description_id' => 'img_description_id',
                                'fa_timestamp' => 'img_timestamp',
-                               'fa_sha1' => 'img_sha1'
+                               'fa_sha1' => 'img_sha1',
+                               'fa_actor' => 'img_actor',
                        ];
                        $joins = [];
 
@@ -234,37 +233,6 @@ class LocalFileDeleteBatch {
                                $commentStore->insert( $dbw, 'fa_deleted_reason', $this->reason )
                        );
 
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                               $fields['fa_user'] = 'img_user';
-                               $fields['fa_user_text'] = 'img_user_text';
-                       }
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                               $fields['fa_actor'] = 'img_actor';
-                       }
-
-                       if (
-                               ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) === SCHEMA_COMPAT_WRITE_BOTH
-                       ) {
-                               // Upgrade any rows that are still old-style. Otherwise an upgrade
-                               // might be missed if a deletion happens while the migration script
-                               // is running.
-                               $res = $dbw->select(
-                                       [ 'image' ],
-                                       [ 'img_name', 'img_user', 'img_user_text' ],
-                                       [ 'img_name' => $this->file->getName(), 'img_actor' => 0 ],
-                                       __METHOD__
-                               );
-                               foreach ( $res as $row ) {
-                                       $actorId = User::newFromAnyId( $row->img_user, $row->img_user_text, null )->getActorId( $dbw );
-                                       $dbw->update(
-                                               'image',
-                                               [ 'img_actor' => $actorId ],
-                                               [ 'img_name' => $row->img_name, 'img_actor' => 0 ],
-                                               __METHOD__
-                                       );
-                               }
-                       }
-
                        $dbw->insertSelect( 'filearchive', $tables, $fields,
                                [ 'img_name' => $this->file->getName() ], __METHOD__, [], [], $joins );
                }
index 584e001..f5b7d43 100644 (file)
@@ -106,46 +106,6 @@ class OldLocalFile extends LocalFile {
                }
        }
 
-       /**
-        * Fields in the oldimage table
-        * @deprecated since 1.31, use self::getQueryInfo() instead.
-        * @return string[]
-        */
-       static function selectFields() {
-               global $wgActorTableSchemaMigrationStage;
-
-               wfDeprecated( __METHOD__, '1.31' );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->oi_user or $row->oi_user_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               return [
-                       'oi_name',
-                       'oi_archive_name',
-                       'oi_size',
-                       'oi_width',
-                       'oi_height',
-                       'oi_metadata',
-                       'oi_bits',
-                       'oi_media_type',
-                       'oi_major_mime',
-                       'oi_minor_mime',
-                       'oi_user',
-                       'oi_user_text',
-                       'oi_actor' => 'NULL',
-                       'oi_timestamp',
-                       'oi_deleted',
-                       'oi_sha1',
-               ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'oi_description' );
-       }
-
        /**
         * Return the tables, fields, and join conditions to be selected to create
         * a new oldlocalfile object.
index e6936cb..2f8f5dd 100644 (file)
@@ -30,6 +30,9 @@ use MediaWiki\MediaWikiServices;
  * @ingroup SpecialPage
  */
 class ImportStreamSource implements ImportSource {
+       /**
+        * @param resource $handle
+        */
        function __construct( $handle ) {
                $this->mHandle = $handle;
        }
index 85983b1..fdd1f77 100644 (file)
@@ -32,6 +32,9 @@
  * @ingroup SpecialPage
  */
 class ImportStringSource implements ImportSource {
+       /**
+        * @param string $string
+        */
        function __construct( $string ) {
                $this->mString = $string;
                $this->mRead = false;
index e1df844..3412aef 100644 (file)
@@ -1308,10 +1308,7 @@ abstract class DatabaseUpdater {
         * @since 1.31
         */
        protected function migrateActors() {
-               global $wgActorTableSchemaMigrationStage;
-               if ( ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) &&
-                       !$this->updateRowExists( 'MigrateActors' )
-               ) {
+               if ( !$this->updateRowExists( 'MigrateActors' ) ) {
                        $this->output(
                                "Migrating actors to the 'actor' table, printing progress markers. For large\n" .
                                "databases, you may want to hit Ctrl-C and do this manually with\n" .
@@ -1401,4 +1398,43 @@ abstract class DatabaseUpdater {
                        }
                }
        }
+
+       /**
+        * Only run a function if the `actor` table does not exist
+        *
+        * The transition to the actor table is dropping several indexes (and a few
+        * fields) that old upgrades want to add. This function is used to prevent
+        * those from running to re-add things when the `actor` table exists, while
+        * still allowing them to run if this really is an upgrade from an old MW
+        * version.
+        *
+        * @since 1.34
+        * @param string|array|static $func Normally this is the string naming the method on $this to
+        *  call. It may also be an array callable. If passed $this, it's assumed to be a call from
+        *  runUpdates() with $passSelf = true: $params[0] is assumed to be the real $func and $this
+        *  is prepended to the rest of $params.
+        * @param mixed ...$params Parameters for `$func`
+        * @return mixed Whatever $func returns, or null when skipped.
+        */
+       protected function ifNoActorTable( $func, ...$params ) {
+               if ( $this->tableExists( 'actor' ) ) {
+                       return null;
+               }
+
+               // Handle $passSelf from runUpdates().
+               $passSelf = false;
+               if ( $func === $this ) {
+                       $passSelf = true;
+                       $func = array_shift( $params );
+               }
+
+               if ( !is_array( $func ) && method_exists( $this, $func ) ) {
+                       $func = [ $this, $func ];
+               } elseif ( $passSelf ) {
+                       array_unshift( $params, $this );
+               }
+
+               // @phan-suppress-next-line PhanUndeclaredInvokeInCallable Phan is confused
+               return $func( ...$params );
+       }
 }
index 0d516b4..64c017b 100644 (file)
@@ -101,8 +101,10 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addTable', 'querycache_info', 'patch-querycacheinfo.sql' ],
                        [ 'addTable', 'filearchive', 'patch-filearchive.sql' ],
                        [ 'addField', 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ],
-                       [ 'addIndex', 'recentchanges', 'rc_ns_usertext', 'patch-recentchanges-utindex.sql' ],
-                       [ 'addIndex', 'recentchanges', 'rc_user_text', 'patch-rc_user_text-index.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'recentchanges', 'rc_ns_usertext',
+                               'patch-recentchanges-utindex.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'recentchanges', 'rc_user_text',
+                               'patch-rc_user_text-index.sql' ],
 
                        // 1.9
                        [ 'addField', 'user', 'user_newpass_time', 'patch-user_newpass_time.sql' ],
@@ -130,9 +132,12 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addField', 'ipblocks', 'ipb_block_email', 'patch-ipb_emailban.sql' ],
                        [ 'doCategorylinksIndicesUpdate' ],
                        [ 'addField', 'oldimage', 'oi_metadata', 'patch-oi_metadata.sql' ],
-                       [ 'addIndex', 'archive', 'usertext_timestamp', 'patch-archive-user-index.sql' ],
-                       [ 'addIndex', 'image', 'img_usertext_timestamp', 'patch-image-user-index.sql' ],
-                       [ 'addIndex', 'oldimage', 'oi_usertext_timestamp', 'patch-oldimage-user-index.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'archive', 'usertext_timestamp',
+                               'patch-archive-user-index.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'image', 'img_usertext_timestamp',
+                               'patch-image-user-index.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'oldimage', 'oi_usertext_timestamp',
+                               'patch-oldimage-user-index.sql' ],
                        [ 'addField', 'archive', 'ar_page_id', 'patch-archive-page_id.sql' ],
                        [ 'addField', 'image', 'img_sha1', 'patch-img_sha1.sql' ],
 
@@ -140,7 +145,7 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addTable', 'protected_titles', 'patch-protected_titles.sql' ],
 
                        // 1.13
-                       [ 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ],
+                       [ 'ifNoActorTable', 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ],
                        [ 'addTable', 'page_props', 'patch-page_props.sql' ],
                        [ 'addTable', 'updatelog', 'patch-updatelog.sql' ],
                        [ 'addTable', 'category', 'patch-category.sql' ],
@@ -150,7 +155,7 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'doPopulateParentId' ],
                        [ 'checkBin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ],
                        [ 'doMaybeProfilingMemoryUpdate' ],
-                       [ 'doFilearchiveIndicesUpdate' ],
+                       [ 'ifNoActorTable', 'doFilearchiveIndicesUpdate' ],
 
                        // 1.14
                        [ 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ],
@@ -163,9 +168,9 @@ class MysqlUpdater extends DatabaseUpdater {
                        // 1.16
                        [ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
                        [ 'addTable', 'log_search', 'patch-log_search.sql' ],
-                       [ 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
+                       [ 'ifNoActorTable', 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
                        # listed separately from the previous update because 1.16 was released without this update
-                       [ 'doLogUsertextPopulation' ],
+                       [ 'ifNoActorTable', 'doLogUsertextPopulation' ],
                        [ 'doLogSearchPopulation' ],
                        [ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
                        [ 'dropIndex', 'change_tag', 'ct_rc_id', 'patch-change_tag-indexes.sql' ],
@@ -240,9 +245,10 @@ class MysqlUpdater extends DatabaseUpdater {
 
                        // 1.23
                        [ 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ],
-                       [ 'addIndex', 'logging', 'log_user_text_type_time',
+                       [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_type_time',
                                'patch-logging_user_text_type_time_index.sql' ],
-                       [ 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_time',
+                               'patch-logging_user_text_time_index.sql' ],
                        [ 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ],
                        [ 'addField', 'user', 'user_password_expires', 'patch-user_password_expire.sql' ],
 
@@ -288,13 +294,14 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'doNonUniquePlTlIl' ],
                        [ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
                        [ 'modifyField', 'recentchanges', 'rc_ip', 'patch-rc_ip_modify.sql' ],
-                       [ 'addIndex', 'archive', 'usertext_timestamp', 'patch-rename-ar_usertext_timestamp.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'archive', 'usertext_timestamp',
+                               'patch-rename-ar_usertext_timestamp.sql' ],
 
                        // 1.29
                        [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
                        [ 'dropIndex', 'user_groups', 'ug_user_group', 'patch-user_groups-primary-key.sql' ],
                        [ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
-                       [ 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
 
                        // 1.30
                        [ 'modifyField', 'image', 'img_media_type', 'patch-add-3d.sql' ],
@@ -377,6 +384,9 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'dropTable', 'tag_summary' ],
                        [ 'dropField', 'protected_titles', 'pt_reason', 'patch-drop-comment-fields.sql' ],
                        [ 'modifyTable', 'job', 'patch-job-params-mediumblob.sql' ],
+
+                       // 1.34
+                       [ 'dropField', 'logging', 'log_user', 'patch-drop-user-fields.sql' ],
                ];
        }
 
index 31827a1..b2c7d66 100644 (file)
@@ -110,7 +110,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'addPgField', 'image', 'img_sha1', "TEXT NOT NULL DEFAULT ''" ],
                        [ 'addPgField', 'ipblocks', 'ipb_allow_usertalk', 'SMALLINT NOT NULL DEFAULT 0' ],
                        [ 'addPgField', 'ipblocks', 'ipb_anon_only', 'SMALLINT NOT NULL DEFAULT 0' ],
-                       [ 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ],
+                       [ 'ifNoActorTable', 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ],
                        [ 'addPgField', 'ipblocks', 'ipb_block_email', 'SMALLINT NOT NULL DEFAULT 0' ],
                        [ 'addPgField', 'ipblocks', 'ipb_create_account', 'SMALLINT NOT NULL DEFAULT 1' ],
                        [ 'addPgField', 'ipblocks', 'ipb_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
@@ -152,7 +152,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'addPgField', 'revision', 'rev_content_format', 'TEXT' ],
                        [ 'addPgField', 'site_stats', 'ss_active_users', "INTEGER DEFAULT '-1'" ],
                        [ 'addPgField', 'user_newtalk', 'user_last_timestamp', 'TIMESTAMPTZ' ],
-                       [ 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ],
+                       [ 'ifNoActorTable', 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ],
                        [ 'addPgField', 'logging', 'log_page', 'INTEGER' ],
                        [ 'addPgField', 'interwiki', 'iw_api', "TEXT NOT NULL DEFAULT ''" ],
                        [ 'addPgField', 'interwiki', 'iw_wikiid', "TEXT NOT NULL DEFAULT ''" ],
@@ -240,7 +240,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'checkOiDeleted' ],
 
                        # New indexes
-                       [ 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ],
+                       [ 'ifNoActorTable', 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ],
                        [ 'addPgIndex', 'image', 'img_sha1', '(img_sha1)' ],
                        [ 'addPgIndex', 'ipblocks', 'ipb_parent_block_id', '(ipb_parent_block_id)' ],
                        [ 'addPgIndex', 'oldimage', 'oi_sha1', '(oi_sha1)' ],
@@ -253,7 +253,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'addPgIndex', 'watchlist', 'wl_user', '(wl_user)' ],
                        [ 'addPgIndex', 'watchlist', 'wl_user_notificationtimestamp',
                                '(wl_user, wl_notificationtimestamp)' ],
-                       [ 'addPgIndex', 'logging', 'logging_user_type_time',
+                       [ 'ifNoActorTable', 'addPgIndex', 'logging', 'logging_user_type_time',
                                '(log_user, log_type, log_timestamp)' ],
                        [ 'addPgIndex', 'logging', 'logging_page_id_time', '(log_page,log_timestamp)' ],
                        [ 'addPgIndex', 'iwlinks', 'iwl_prefix_from_title', '(iwl_prefix, iwl_from, iwl_title)' ],
@@ -263,9 +263,10 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'addPgIndex', 'job', 'job_cmd_token', '(job_cmd, job_token, job_random)' ],
                        [ 'addPgIndex', 'job', 'job_cmd_token_id', '(job_cmd, job_token, job_id)' ],
                        [ 'addPgIndex', 'filearchive', 'fa_sha1', '(fa_sha1)' ],
-                       [ 'addPgIndex', 'logging', 'logging_user_text_type_time',
+                       [ 'ifNoActorTable', 'addPgIndex', 'logging', 'logging_user_text_type_time',
                                '(log_user_text, log_type, log_timestamp)' ],
-                       [ 'addPgIndex', 'logging', 'logging_user_text_time', '(log_user_text, log_timestamp)' ],
+                       [ 'ifNoActorTable', 'addPgIndex', 'logging', 'logging_user_text_time',
+                               '(log_user_text, log_timestamp)' ],
 
                        [ 'checkIndex', 'pagelink_unique', [
                                [ 'pl_from', 'int4_ops', 'btree', 0 ],
@@ -363,30 +364,36 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'checkIwlPrefix' ],
 
                        # All FK columns should be deferred
-                       [ 'changeFkeyDeferrable', 'archive', 'ar_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'archive', 'ar_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'categorylinks', 'cl_from', 'page(page_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'externallinks', 'el_from', 'page(page_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'filearchive', 'fa_deleted_user',
                                'mwuser(user_id) ON DELETE SET NULL' ],
-                       [ 'changeFkeyDeferrable', 'filearchive', 'fa_user', 'mwuser(user_id) ON DELETE SET NULL' ],
-                       [ 'changeFkeyDeferrable', 'image', 'img_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'filearchive', 'fa_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ],
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'image', 'img_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'imagelinks', 'il_from', 'page(page_id) ON DELETE CASCADE' ],
-                       [ 'changeFkeyDeferrable', 'ipblocks', 'ipb_by', 'mwuser(user_id) ON DELETE CASCADE' ],
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'ipblocks', 'ipb_by',
+                               'mwuser(user_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'ipblocks', 'ipb_user', 'mwuser(user_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'ipblocks', 'ipb_parent_block_id',
                                'ipblocks(ipb_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'langlinks', 'll_from', 'page(page_id) ON DELETE CASCADE' ],
-                       [ 'changeFkeyDeferrable', 'logging', 'log_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'logging', 'log_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'oldimage', 'oi_name',
                                'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ],
-                       [ 'changeFkeyDeferrable', 'oldimage', 'oi_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'oldimage', 'oi_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'pagelinks', 'pl_from', 'page(page_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'page_props', 'pp_page', 'page (page_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'page_restrictions', 'pr_page',
                                'page(page_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'protected_titles', 'pt_user',
                                'mwuser(user_id) ON DELETE SET NULL' ],
-                       [ 'changeFkeyDeferrable', 'recentchanges', 'rc_user',
+                       [ 'ifNoActorTable', 'changeFkeyDeferrable', 'recentchanges', 'rc_user',
                                'mwuser(user_id) ON DELETE SET NULL' ],
                        [ 'changeFkeyDeferrable', 'redirect', 'rd_from', 'page(page_id) ON DELETE CASCADE' ],
                        [ 'changeFkeyDeferrable', 'revision', 'rev_page', 'page (page_id) ON DELETE CASCADE' ],
@@ -618,6 +625,34 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'dropDefault', 'logging', 'log_comment_id' ],
                        [ 'dropPgField', 'protected_titles', 'pt_reason' ],
                        [ 'dropDefault', 'protected_titles', 'pt_reason_id' ],
+
+                       // 1.34
+                       [ 'dropPgIndex', 'archive', 'archive_user_text' ],
+                       [ 'dropPgField', 'archive', 'ar_user' ],
+                       [ 'dropPgField', 'archive', 'ar_user_text' ],
+                       [ 'dropDefault', 'archive', 'ar_actor' ],
+                       [ 'dropPgField', 'ipblocks', 'ipb_by' ],
+                       [ 'dropPgField', 'ipblocks', 'ipb_by_text' ],
+                       [ 'dropDefault', 'ipblocks', 'ipb_by_actor' ],
+                       [ 'dropPgField', 'image', 'img_user' ],
+                       [ 'dropPgField', 'image', 'img_user_text' ],
+                       [ 'dropDefault', 'image', 'img_actor' ],
+                       [ 'dropPgField', 'oldimage', 'oi_user' ],
+                       [ 'dropPgField', 'oldimage', 'oi_user_text' ],
+                       [ 'dropDefault', 'oldimage', 'oi_actor' ],
+                       [ 'dropPgField', 'filearchive', 'fa_user' ],
+                       [ 'dropPgField', 'filearchive', 'fa_user_text' ],
+                       [ 'dropDefault', 'filearchive', 'fa_actor' ],
+                       [ 'dropPgField', 'recentchanges', 'rc_user' ],
+                       [ 'dropPgField', 'recentchanges', 'rc_user_text' ],
+                       [ 'dropDefault', 'recentchanges', 'rc_actor' ],
+                       [ 'dropPgIndex', 'logging', 'logging_user_time' ],
+                       [ 'dropPgIndex', 'logging', 'logging_user_type_time' ],
+                       [ 'dropPgIndex', 'logging', 'logging_user_text_type_time' ],
+                       [ 'dropPgIndex', 'logging', 'logging_user_text_time' ],
+                       [ 'dropPgField', 'logging', 'log_user' ],
+                       [ 'dropPgField', 'logging', 'log_user_text' ],
+                       [ 'dropDefault', 'logging', 'log_actor' ],
                ];
        }
 
index 17ced50..7c3878c 100644 (file)
@@ -47,9 +47,9 @@ class SqliteUpdater extends DatabaseUpdater {
                        // 1.16
                        [ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
                        [ 'addTable', 'log_search', 'patch-log_search.sql' ],
-                       [ 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
+                       [ 'ifNoActorTable', 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
                        # listed separately from the previous update because 1.16 was released without this update
-                       [ 'doLogUsertextPopulation' ],
+                       [ 'ifNoActorTable', 'doLogUsertextPopulation' ],
                        [ 'doLogSearchPopulation' ],
                        [ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
                        [ 'dropIndex', 'change_tag', 'ct_rc_id', 'patch-change_tag-indexes.sql' ],
@@ -119,9 +119,10 @@ class SqliteUpdater extends DatabaseUpdater {
 
                        // 1.23
                        [ 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ],
-                       [ 'addIndex', 'logging', 'log_user_text_type_time',
+                       [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_type_time',
                                'patch-logging_user_text_type_time_index.sql' ],
-                       [ 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_time',
+                               'patch-logging_user_text_time_index.sql' ],
                        [ 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ],
                        [ 'addField', 'user', 'user_password_expires', 'patch-user_password_expire.sql' ],
 
@@ -159,7 +160,7 @@ class SqliteUpdater extends DatabaseUpdater {
                        // 1.29
                        [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
                        [ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
-                       [ 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
+                       [ 'ifNoActorTable', 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
 
                        // 1.30
                        [ 'modifyField', 'image', 'img_media_type', 'patch-add-3d.sql' ],
@@ -249,6 +250,15 @@ class SqliteUpdater extends DatabaseUpdater {
                        [ 'dropField', 'recentchanges', 'rc_comment', 'patch-recentchanges-drop-rc_comment.sql' ],
                        [ 'dropField', 'logging', 'log_comment', 'patch-logging-drop-log_comment.sql' ],
                        [ 'dropField', 'protected_titles', 'pt_reason', 'patch-protected_titles-drop-pt_reason.sql' ],
+
+                       // 1.34
+                       [ 'dropField', 'archive', 'ar_user', 'patch-archive-drop-ar_user.sql' ],
+                       [ 'dropField', 'ipblocks', 'ipb_by', 'patch-ipblocks-drop-ipb_by.sql' ],
+                       [ 'dropField', 'image', 'img_user', 'patch-image-drop-img_user.sql' ],
+                       [ 'dropField', 'oldimage', 'oi_user', 'patch-oldimage-drop-oi_user.sql' ],
+                       [ 'dropField', 'filearchive', 'fa_user', 'patch-filearchive-drop-fa_user.sql' ],
+                       [ 'dropField', 'recentchanges', 'rc_user', 'patch-recentchanges-drop-rc_user.sql' ],
+                       [ 'dropField', 'logging', 'log_user', 'patch-logging-drop-log_user.sql' ],
                ];
        }
 
index 1b814a2..a7a9d53 100644 (file)
@@ -35,7 +35,7 @@
        "config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] دامەزراوە",
        "config-db-type": "جۆری داتابەیس:",
        "config-db-host": "خانەخوێی داتابەیس:",
-       "config-db-name": "ناوی بنکەدراوە:",
+       "config-db-name": "ناوی بنکەدراوە (بێ دابڕین (-)):",
        "config-db-install-account": "ھەژماری بەکارھێنەری بۆ دامەزراندن",
        "config-db-username": "ناوی بەکارھێنەری بنکەدراوە:",
        "config-db-password": "تێپەڕوشەی بنکەدراوە",
@@ -56,5 +56,5 @@
        "config-install-step-done": "کرا",
        "config-help": "یارمەتی",
        "mainpagetext": "<strong>میدیاویکی بە سەرکەوتوویی دامەزرا.</strong>",
-       "mainpagedocfooter": "لە [https://meta.wikimedia.org/wiki/Help:Contents ڕێنوێنیی بەکارھێنەران] بۆ زانیاری سەبارەت بە بەکارھێنانی نەرمامێری ویکی کەڵک وەربگرە.\n\n== دەستپێکردن ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings پێرستی ڕێکخستنەکانی شێوەپێدان]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ پرسیارە دووپاتکراوەکانی میدیاویکی (MediaWiki FAQ)]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce پێرستی ئیمەیلی وەشانەکانی میدیاویکی]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources خۆماڵیکردنی ویکیمیدیا بۆ زمانەکەت]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam فێربە چۆن ڕووبەڕووى ئیمەیڵە بێزارکەرەکانی ویکییەکەت دەبیتەوە]"
+       "mainpagedocfooter": "لە [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents ڕێنوێنیی بەکارھێنەران] بۆ زانیاری سەبارەت بە بەکارھێنانی نەرمامێری ویکی کەڵک وەربگرە.\n\n== دەستپێکردن ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings پێرستی ڕێکخستنەکانی شێوەپێدان]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ پرسیارە دووپاتکراوەکانی میدیاویکی (MediaWiki FAQ)]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce پێرستی ئیمەیلی وەشانەکانی میدیاویکی]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources خۆماڵیکردنی ویکیمیدیا بۆ زمانەکەت]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam فێربە چۆن ڕووبەڕووى ئیمەیڵە بێزارکەرەکانی ویکییەکەت دەبیتەوە]"
 }
index e7e653b..b57ed3d 100644 (file)
        "config-no-uri": "<strong>Erreur :</strong> impossible de déterminer l’URI du script actuel.\nInstallation interrompue.",
        "config-no-cli-uri": "<strong>Attention :</strong> aucun <code>--scriptpath</code> n’a été spécifié ; <code>$1</code> sera utilisé par défaut.",
        "config-using-server": "Utilisation du nom de serveur « <nowiki>$1</nowiki> ».",
-       "config-using-uri": "Utilisation de l’URL de serveur « <nowiki>$1$2</nowiki> ».",
+       "config-using-uri": "Utilisation de l’URL de serveur « <nowiki>$1$2</nowiki> ».",
        "config-uploads-not-safe": "<strong>Attention :</strong> votre répertoire par défaut pour les téléversements, <code>$1</code>, est vulnérable, car il peut exécuter n’importe quel script.\nBien que MediaWiki vérifie tous les fichiers téléversés, il est fortement recommandé de [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security fermer cette faille de sécurité] (texte en anglais) avant d’activer les téléversements.",
        "config-no-cli-uploads-check": "'''Attention :''' votre répertoire par défaut pour les imports (<code>$1</code>) n’est pas contrôlé concernant la vulnérabilité d’exécution de scripts arbitraires lors de l’installation CLI.",
        "config-brokenlibxml": "Votre système utilise une combinaison de versions de PHP et libxml2 qui est boguée et peut engendrer des corruptions cachées de données dans MediaWiki et d’autres applications web.\nVeuillez mettre à jour votre système vers libxml2 2.7.3 ou plus récent ([https://bugs.php.net/bug.php?id=45996 anomalie signalée auprès de PHP]).\nInstallation interrompue.",
-       "config-suhosin-max-value-length": "Suhosin est installé et limite la <code>longueur</code> de paramètre GET à $1 octets.\nLe composant ResourceLoader de MediaWiki va répondre en respectant cette limite, mais ses performances seront dégradées. Si possible, vous devriez définir <code>suhosin.get.max_value_length</code> à 1024 ou plus dans le fichier <code>php.ini</code>, et fixer <code>$wgResourceLoaderMaxQueryLength</code> à la même valeur dans <code>LocalSettings.php</code>.",
+       "config-suhosin-max-value-length": "Suhosin est installé et limite la <code>longueur</code> de paramètre GET à $1 octets.\nMediaWiki exige que <code>suhosin.get.max_value_length</code> vaille au moins $2. Désactiver ce paramètre, ou augmenter sa valeur à $3 dans <code>php.ini</code>.",
        "config-using-32bit": "<strong>Attention :</strong> votre système semble utiliser les entiers sur 32 bits. Ceci n’est [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit pas recommandé].",
        "config-db-type": "Type de base de données :",
        "config-db-host": "Nom d’hôte de la base de données :",
-       "config-db-host-help": "Si votre serveur de base de données est sur un serveur différent, saisissez ici son nom d’hôte ou son adresse IP.\n\nSi vous utilisez un hébergement mutualisé, votre hébergeur doit vous avoir fourni le nom d’hôte correct dans sa documentation.\n\nSi vous utilisez MySQL, « localhost » peut ne pas fonctionner comme nom de serveur. S’il ne fonctionne pas, essayez « 127.0.0.1 » comme adresse IP locale.\n\nSi vous utilisez PostgreSQL, laissez ce champ vide pour vous connecter via un socket Unix.",
+       "config-db-host-help": "Si votre serveur de base de données est sur un serveur différent, saisissez ici son nom d’hôte ou son adresse IP.\n\nSi vous utilisez un hébergement mutualisé, votre hébergeur doit vous avoir fourni le nom d’hôte correct dans sa documentation.\n\nSi vous utilisez MySQL, « localhost » peut ne pas fonctionner comme nom de serveur. S’il ne fonctionne pas, essayez « 127.0.0.1 » comme adresse IP locale.\n\nSi vous utilisez PostgreSQL, laissez ce champ vide pour vous connecter via un socket Unix.",
        "config-db-wiki-settings": "Identifier ce wiki",
        "config-db-name": "Nom de la base de données (sans tirets) :",
        "config-db-name-help": "Choisissez un nom qui identifie votre wiki.\nIl ne doit pas contenir d’espaces.\n\nSi vous utilisez un hébergement web partagé, votre hébergeur vous fournira un nom spécifique de base de données à utiliser, ou bien vous permet de créer des bases de données via un panneau de contrôle.",
        "config-missing-db-host": "Vous devez entrer une valeur pour « {{int:config-db-host}} ».",
        "config-invalid-db-name": "Nom de la base de données invalide (« $1 »).\nUtiliser seulement les lettres ASCII (a-z, A-Z), les chiffres (0-9), les caractères de soulignement (_) et les tirets (-).",
        "config-invalid-db-prefix": "Préfixe de la base de données non valide « $1 ».\nUtiliser seulement les lettres ASCII (a-z, A-Z), les chiffres (0-9), les caractères de soulignement (_) et les tirets (-).",
-       "config-connection-error": "$1.\n\nVérifier le nom d’hôte, le nom d’utilisateur et le mot de passe ci-dessous puis réessayer. Si vous utilisez « localhost » comme hôte de base de données, essayez d’utiliser « 127.0.0.1 » à la place (ou inversement).",
+       "config-connection-error": "$1.\n\nVérifier le nom d’hôte, le nom d’utilisateur et le mot de passe ci-dessous puis réessayer. Si vous utilisez « localhost » comme hôte de base de données, essayez d’utiliser « 127.0.0.1 » à la place (ou inversement).",
        "config-invalid-schema": "Schéma invalide pour MediaWiki « $1 ».\nUtiliser seulement les lettres ASCII (a-z, A-Z), les chiffres (0-9) et les caractères de soulignement (_).",
        "config-postgres-old": "PostgreSQL $1 ou version ultérieure est requis. Vous avez $2.",
        "config-sqlite-name-help": "Choisir un nom qui identifie votre wiki.\nNe pas utiliser d'espaces ni de traits d'union.\nIl sera utilisé pour le fichier de données SQLite.",
        "config-sqlite-cant-create-db": "Impossible de créer le fichier de base de données <code>$1</code>.",
        "config-sqlite-fts3-downgrade": "PHP n’a pas trouvé la prise en charge FTS3, les tables sont restreintes.",
        "config-can-upgrade": "Il y a des tables MediaWiki dans cette base de données.\nPour les mettre au niveau de MediaWiki $1, cliquez sur '''Continuer'''.",
-       "config-upgrade-error": "Une erreur est survenue durant la mise à jour des tables MédiaWiki de votre base de données.\n\nPour plus d'informations voir le journal ci-dessus, pour réessayer, cliquez sur <strong>Continuer</strong>.",
+       "config-upgrade-error": "Une erreur est survenue durant la mise à jour des tables MediaWiki de votre base de données.\n\nPour plus d'informations voir le journal ci-dessus, pour réessayer, cliquez sur <strong>Continuer</strong>.",
        "config-upgrade-done": "Mise à jour terminée.\n\nVous pouvez maintenant [$1 commencer à utiliser votre wiki].\n\nSi vous souhaitez régénérer votre fichier <code>LocalSettings.php</code>, cliquez sur le bouton ci-dessous.\nCeci '''n'est pas recommandé''' sauf si vous rencontrez des problèmes avec votre wiki.",
        "config-upgrade-done-no-regenerate": "Mise à jour terminée.\n\nVous pouvez maintenant [$1 commencer à utiliser votre wiki].",
        "config-regenerate": "Regénérer LocalSettings.php →",
        "config-ns-site-name": "Même nom que le wiki : $1",
        "config-ns-other": "Autre (préciser)",
        "config-ns-other-default": "MonWiki",
-       "config-project-namespace-help": "Suivant l’exemple de Wikipédia, plusieurs wikis gardent leurs pages de politique séparées de leurs pages de contenu, dans un '''espace de noms de niveau projet''' propre.\nTous les titres de page de cet espace de noms commence par un préfixe défini, que vous pouvez spécifier ici.\nTraditionnellement, ce préfixe est dérivé du nom du wiki, et ne peut contenir de caractères de ponctuation tels que « # » ou « : ».",
+       "config-project-namespace-help": "Suivant l’exemple de Wikipédia, plusieurs wikis gardent leurs pages de politique séparées de leurs pages de contenu, dans un '''espace de noms de niveau projet''' propre.\nTous les titres de page de cet espace de noms commence par un préfixe défini, que vous pouvez spécifier ici.\nTraditionnellement, ce préfixe est dérivé du nom du wiki, et ne peut contenir de caractères de ponctuation tels que « # » ou « : ».",
        "config-ns-invalid": "L'espace de noms spécifié « <nowiki>$1</nowiki> » n'est pas valide.\nSpécifiez un espace de noms différent pour le projet.",
        "config-ns-conflict": "L'espace de noms spécifié « <nowiki>$1</nowiki> » est en conflit avec un espace de noms par défaut de MediaWiki.\nChoisir un autre espace de noms pour le projet.",
        "config-admin-box": "Compte administrateur",
        "config-skins": "Habillages",
        "config-skins-help": "Les habillages listés ci-dessous ont été détectés dans votre répertoire <code>./skins</code>. Vous devez en activer au moins un, et choisir celui par défaut.",
        "config-skins-use-as-default": "Utiliser cet habillage par défaut",
-       "config-skins-missing": "Aucun habillage trouvé ; MédiaWiki utilisera un habillage de secours jusqu’à ce que vous en installiez un approprié.",
+       "config-skins-missing": "Aucun habillage trouvé ; MediaWiki utilisera un habillage de secours jusqu’à ce que vous en installiez un approprié.",
        "config-skins-must-enable-some": "Vous devez choisir au moins un habillage à activer.",
        "config-skins-must-enable-default": "L’habillage choisi par défaut doit être activé.",
        "config-install-alreadydone": "'''Attention''': Vous semblez avoir déjà installé MediaWiki et tentez de l'installer à nouveau.\nS'il vous plaît, allez à la page suivante.",
        "config-install-extension-tables": "Création de tables pour les extensions activées",
        "config-install-mainpage-failed": "Impossible d’insérer la page principale : $1",
        "config-install-done": "<strong>Félicitations!</strong>\nVous avez installé MediaWiki.\n\nLe programme d'installation a généré un fichier <code>LocalSettings.php</code>. Il contient tous les paramètres de votre configuration.\n\nVous devrez le télécharger et le mettre à la racine de votre installation wiki (dans le même répertoire que index.php). Le téléchargement devrait démarrer automatiquement.\n\nSi le téléchargement n'a pas été proposé, ou que vous l'avez annulé, vous pouvez redémarrer le téléchargement en cliquant ce lien :\n\n$3\n\n<strong>Note :</strong> Si vous ne le faites pas maintenant, ce fichier de configuration généré ne sera pas disponible plus tard si vous quittez l'installation sans le télécharger.\n\nLorsque c'est fait, vous pouvez <strong>[$2 accéder à votre wiki]</strong> .",
-       "config-install-done-path": "<strong>Félicitations !</strong>\nVous avez installé MédiaWiki.\n\nL’installeur a généré un fichier <code>LocalSettings.php</code>.\nIl contient toute votre configuration.\n\nVous devez le télécharger et le mettre dans <code>$4</code>. Le téléchargement devrait avoir démarré automatiquement.\n\nSi le téléchargement n’a pas été proposé ou si vous l’avez annulé, vous pouvez le redémarrer en cliquant sur le lien ci-dessous :\n\n$3\n\n<strong>Note :</strong> Si vous ne le faites pas maintenant, ce fichier de configuration généré ne sera plus disponible ultérieurement si vous quittez l’installation sans le télécharger.\n\nUne fois ceci fait, vous pouvez <strong>[$2 entrer dans votre wiki]</strong>.",
-       "config-install-success": "MédiaWiki a été installé correctement. Vous pouvez maintenant visiter <$1$2> pour voir votre wiki.\nSi vous avez des questions, consultez notre foire aux questions :\n<https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> ou utilisez un des\nforums de support liés sur cette page.",
+       "config-install-done-path": "<strong>Félicitations !</strong>\nVous avez installé MediaWiki.\n\nL’installeur a généré un fichier <code>LocalSettings.php</code>.\nIl contient toute votre configuration.\n\nVous devez le télécharger et le mettre dans <code>$4</code>. Le téléchargement devrait avoir démarré automatiquement.\n\nSi le téléchargement n’a pas été proposé ou si vous l’avez annulé, vous pouvez le redémarrer en cliquant sur le lien ci-dessous :\n\n$3\n\n<strong>Note :</strong> Si vous ne le faites pas maintenant, ce fichier de configuration généré ne sera plus disponible ultérieurement si vous quittez l’installation sans le télécharger.\n\nUne fois ceci fait, vous pouvez <strong>[$2 entrer dans votre wiki]</strong>.",
+       "config-install-success": "MediaWiki a été installé correctement. Vous pouvez maintenant visiter <$1$2> pour voir votre wiki.\nSi vous avez des questions, consultez notre foire aux questions :\n<https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> ou utilisez un des\nforums de support liés sur cette page.",
        "config-install-db-success": "La base de données a été bien installée",
        "config-download-localsettings": "Télécharger <code>LocalSettings.php</code>",
        "config-help": "aide",
        "config-skins-screenshots": "$1 (captures d’écran : $2)",
        "config-extensions-requires": "$1 (nécessite $2)",
        "config-screenshot": "Captures d’écrans",
-       "config-extension-not-found": "Impossible de trouver le fichier d’inscription pour l’extension « $1 »",
-       "config-extension-dependency": "Une erreur de dépendance s’est produite en installant l’extension « $1 » : $2",
+       "config-extension-not-found": "Impossible de trouver le fichier d’inscription pour l’extension « $1 »",
+       "config-extension-dependency": "Une erreur de dépendance s’est produite en installant l’extension « $1 » : $2",
        "mainpagetext": "<strong>MediaWiki a été installé.</strong>",
        "mainpagedocfooter": "Consultez le [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guide de l’utilisateur] pour plus d’informations sur l’utilisation de ce logiciel de wiki.\n\n== Pour démarrer ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste des paramètres de configuration]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/fr Questions courantes sur MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Liste de discussion sur les distributions de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Adaptez MediaWiki dans votre langue]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Apprendre comment lutter contre le pourriel dans votre wiki]"
 }
index cb72d91..f385ab5 100644 (file)
        "config-uploads-not-safe": "<strong>Attenzione:</strong> la directory predefinita per i caricamenti <code>$1</code> è vulnerabile all'esecuzione arbitraria di script.\nAnche se, a difesa della sicurezza, MediaWiki controlla tutti i file caricati, è fortemente raccomandato di [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security chiudere questa minaccia] prima di abilitare i caricamenti.",
        "config-no-cli-uploads-check": "<strong>Attenzione:</strong> la directory predefinita per i caricamenti (<code>$1</code>) non è stata verificata per la vulnerabilità sull'esecuzione arbitraria di script durante l'installazione da linea di comando.",
        "config-brokenlibxml": "Il tuo sistema ha una combinazione di versioni di PHP e libxml2 che è difettosa e che può provocare un danneggiamento non visibile di dati in MediaWiki ed in altre applicazioni per il web.\nAggiorna a libxml2 2.7.3 o successivo ([https://bugs.php.net/bug.php?id=45996 il bug è studiato dal lato PHP]).\nInstallazione interrotta.",
-       "config-suhosin-max-value-length": "Suhosin è installato e limita il parametro GET <code>length</code> a $1 byte.\nIl componente MediaWiki ResourceLoader funzionerà aggirando questo limite, ma riducendo le prestazioni.\nSe possibile, dovresti impostare <code>suhosin.get.max_value_length</code> a 1024 o superiore in <code>php.ini</code>, ed impostare <code>$wgResourceLoaderMaxQueryLength</code> allo stesso valore in <code>LocalSettings.php</code>.",
+       "config-suhosin-max-value-length": "Suhosin è installato e limita il parametro GET <code>length</code> a $1 byte.\nMediaWiki richiede un valore per <code>suhosin.get.max_value_length</code> di almeno $2. Disattiva questa impostazione, o aumenta questo valore a $3 in <code>php.ini</code>.",
        "config-using-32bit": "<strong>Attenzione</strong> sembra che il tuo sistema utilizzi interi a 32-bit, ciò [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit non è raccomandato].",
        "config-db-type": "Tipo di database:",
        "config-db-host": "Host del database:",
index 7a13bde..21ca17c 100644 (file)
        "config-uploads-not-safe": "<strong>Waarschuwing:</strong> uw uploadmap <code>$1</code> kan gebruikt worden voor het arbitrair uitvoeren van scripts.\nHoewel MediaWiki alle toegevoegde bestanden controleert op bedreigingen, is het zeer aan te bevelen het [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security beveiligingslek te verhelpen] alvorens uploads in te schakelen.",
        "config-no-cli-uploads-check": "''Waarschuwing:'' uw standaardmap voor uploads (<code>$1</code>) wordt niet gecontroleerd op kwetsbaarheden voor het uitvoeren van willekeurige scripts gedurende de CLI-installatie.",
        "config-brokenlibxml": "Uw systeem heeft een combinatie van PHP- en libxml2-versies geïnstalleerd die is foutgevoelig is en kan leiden tot onzichtbare beschadiging van gegevens in MediaWiki en andere webapplicaties.\nUpgrade naar libxml2 2.7.3 of hoger([https://bugs.php.net/bug.php?id=45996 bij PHP gerapporteerde fout]).\nDe installatie wordt afgebroken.",
-       "config-suhosin-max-value-length": "Suhosin is geïnstalleerd en beperkt de GET-parameter <code>length</code> tot $1 bytes.\nDe ResourceLoader van MediaWiki omzeilt deze beperking, maar dat is slecht voor de prestaties.\nAls het mogelijk is, moet u de waarde \"<code>suhosin.get.max_value_length</code>\" in <code>php.ini</code> instellen op 1024 of hoger en <code>$wgResourceLoaderMaxQueryLength</code> in LocalSettings.php op dezelfde waarde instellen.",
+       "config-suhosin-max-value-length": "Suhosin is geïnstalleerd en beperkt de GET-parameter <code>length</code> tot $1 bytes.\nMediaWiki vereist dat <code>suhosin.get.max_value_length</code> ten minste $2 is. Schakel deze instelling uit, of verhoog deze in <code>php.ini</code> naar $3.",
        "config-using-32bit": "<strong>Pas op:</strong> uw systeem lijkt met 32-bit integers te werken. Dit is [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit anders dan aangeraden].",
        "config-db-type": "Databasetype:",
        "config-db-host": "Databasehost:",
index 71a0e34..4b381f8 100644 (file)
@@ -93,7 +93,7 @@ class StatusValue {
         *     1 => object(StatusValue) # The StatusValue with warning messages, only
         * ]
         *
-        * @return StatusValue[]
+        * @return static[]
         */
        public function splitByErrorType() {
                $errorsOnlyStatusValue = clone $this;
index c333a5e..5534cbd 100644 (file)
@@ -40,6 +40,7 @@
  * @file
  * @ingroup FileBackend
  */
+use Wikimedia\AtEase\AtEase;
 use Wikimedia\Timestamp\ConvertibleTimestamp;
 
 /**
@@ -63,23 +64,22 @@ class FSFileBackend extends FileBackendStore {
        protected $basePath;
 
        /** @var array Map of container names to root paths for custom container paths */
-       protected $containerPaths = [];
+       protected $containerPaths;
 
+       /** @var int Directory permission mode */
+       protected $dirMode;
        /** @var int File permission mode */
        protected $fileMode;
-       /** @var int File permission mode */
-       protected $dirMode;
-
        /** @var string Required OS username to own files */
        protected $fileOwner;
 
-       /** @var bool */
+       /** @var bool Whether the OS is Windows (otherwise assumed Unix-like)*/
        protected $isWindows;
        /** @var string OS username running this script */
        protected $currentUser;
 
-       /** @var array */
-       protected $hadWarningErrors = [];
+       /** @var bool[] Map of (stack index => whether a warning happened) */
+       private $warningTrapStack = [];
 
        /**
         * @see FileBackendStore::__construct()
@@ -102,11 +102,9 @@ class FSFileBackend extends FileBackendStore {
                        $this->basePath = null; // none; containers must have explicit paths
                }
 
-               if ( isset( $config['containerPaths'] ) ) {
-                       $this->containerPaths = (array)$config['containerPaths'];
-                       foreach ( $this->containerPaths as &$path ) {
-                               $path = rtrim( $path, '/' ); // remove trailing slash
-                       }
+               $this->containerPaths = [];
+               foreach ( ( $config['containerPaths'] ?? [] ) as $container => $path ) {
+                       $this->containerPaths[$container] = rtrim( $path, '/' ); // remove trailing slash
                }
 
                $this->fileMode = $config['fileMode'] ?? 0644;
@@ -124,7 +122,7 @@ class FSFileBackend extends FileBackendStore {
                        // See https://www.php.net/manual/en/migration71.windows-support.php
                        return 0;
                } else {
-                       return FileBackend::ATTR_UNICODE_PATHS;
+                       return self::ATTR_UNICODE_PATHS;
                }
        }
 
@@ -228,20 +226,12 @@ class FSFileBackend extends FileBackendStore {
                }
 
                if ( !empty( $params['async'] ) ) { // deferred
-                       $tempFile = $this->tmpFileFactory->newTempFSFile( 'create_', 'tmp' );
+                       $tempFile = $this->stageContentAsTempFile( $params );
                        if ( !$tempFile ) {
                                $status->fatal( 'backend-fail-create', $params['dst'] );
 
                                return $status;
                        }
-                       $this->trapWarnings();
-                       $bytes = file_put_contents( $tempFile->getPath(), $params['content'] );
-                       $this->untrapWarnings();
-                       if ( $bytes === false ) {
-                               $status->fatal( 'backend-fail-create', $params['dst'] );
-
-                               return $status;
-                       }
                        $cmd = implode( ' ', [
                                $this->isWindows ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
                                escapeshellarg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
@@ -376,34 +366,38 @@ class FSFileBackend extends FileBackendStore {
        protected function doMoveInternal( array $params ) {
                $status = $this->newStatus();
 
-               $source = $this->resolveToFSPath( $params['src'] );
-               if ( $source === null ) {
+               $fsSrcPath = $this->resolveToFSPath( $params['src'] );
+               if ( $fsSrcPath === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 
                        return $status;
                }
 
-               $dest = $this->resolveToFSPath( $params['dst'] );
-               if ( $dest === null ) {
+               $fsDstPath = $this->resolveToFSPath( $params['dst'] );
+               if ( $fsDstPath === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 
                        return $status;
                }
 
-               if ( !is_file( $source ) ) {
-                       if ( empty( $params['ignoreMissingSource'] ) ) {
-                               $status->fatal( 'backend-fail-move', $params['src'] );
-                       }
-
-                       return $status; // do nothing; either OK or bad status
+               if ( $fsSrcPath === $fsDstPath ) {
+                       return $status; // no-op
                }
 
+               $ignoreMissing = !empty( $params['ignoreMissingSource'] );
+
                if ( !empty( $params['async'] ) ) { // deferred
-                       $cmd = implode( ' ', [
-                               $this->isWindows ? 'MOVE /Y' : 'mv', // (overwrite)
-                               escapeshellarg( $this->cleanPathSlashes( $source ) ),
-                               escapeshellarg( $this->cleanPathSlashes( $dest ) )
-                       ] );
+                       // https://manpages.debian.org/buster/coreutils/mv.1.en.html
+                       // https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/move
+                       $encSrc = escapeshellarg( $this->cleanPathSlashes( $fsSrcPath ) );
+                       $encDst = escapeshellarg( $this->cleanPathSlashes( $fsDstPath ) );
+                       if ( $this->isWindows ) {
+                               $writeCmd = "MOVE /Y $encSrc $encDst";
+                               $cmd = $ignoreMissing ? "IF EXIST $encSrc $writeCmd" : $writeCmd;
+                       } else {
+                               $writeCmd = "mv -f $encSrc $encDst";
+                               $cmd = $ignoreMissing ? "test -f $encSrc && $writeCmd" : $writeCmd;
+                       }
                        $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
                                if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
                                        $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
@@ -412,11 +406,13 @@ class FSFileBackend extends FileBackendStore {
                        };
                        $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd );
                } else { // immediate write
-                       $this->trapWarnings();
-                       $ok = ( $source === $dest ) ? true : rename( $source, $dest );
-                       $this->untrapWarnings();
-                       clearstatcache(); // file no longer at source
-                       if ( !$ok ) {
+                       // Use rename() here since (a) this clears xattrs, (b) any threads still reading the
+                       // old inode are unaffected since it writes to a new inode, and (c) this is fast and
+                       // atomic within a file system volume (as is normally the case)
+                       $this->trapWarnings( '/: No such file or directory$/' );
+                       $moved = rename( $fsSrcPath, $fsDstPath );
+                       $hadError = $this->untrapWarnings();
+                       if ( $hadError || ( !$moved && !$ignoreMissing ) ) {
                                $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
 
                                return $status;
@@ -429,26 +425,25 @@ class FSFileBackend extends FileBackendStore {
        protected function doDeleteInternal( array $params ) {
                $status = $this->newStatus();
 
-               $source = $this->resolveToFSPath( $params['src'] );
-               if ( $source === null ) {
+               $fsSrcPath = $this->resolveToFSPath( $params['src'] );
+               if ( $fsSrcPath === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 
                        return $status;
                }
 
-               if ( !is_file( $source ) ) {
-                       if ( empty( $params['ignoreMissingSource'] ) ) {
-                               $status->fatal( 'backend-fail-delete', $params['src'] );
-                       }
-
-                       return $status; // do nothing; either OK or bad status
-               }
+               $ignoreMissing = !empty( $params['ignoreMissingSource'] );
 
                if ( !empty( $params['async'] ) ) { // deferred
-                       $cmd = implode( ' ', [
-                               $this->isWindows ? 'DEL' : 'unlink',
-                               escapeshellarg( $this->cleanPathSlashes( $source ) )
-                       ] );
+                       // https://manpages.debian.org/buster/coreutils/rm.1.en.html
+                       // https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/del
+                       $encSrc = escapeshellarg( $this->cleanPathSlashes( $fsSrcPath ) );
+                       if ( $this->isWindows ) {
+                               $writeCmd = "DEL /Q $encSrc";
+                               $cmd = $ignoreMissing ? "IF EXIST $encSrc $writeCmd" : $writeCmd;
+                       } else {
+                               $cmd = $ignoreMissing ? "rm -f $encSrc" : "rm $encSrc";
+                       }
                        $handler = function ( $errors, StatusValue $status, array $params, $cmd ) {
                                if ( $errors !== '' && !( $this->isWindows && $errors[0] === " " ) ) {
                                        $status->fatal( 'backend-fail-delete', $params['src'] );
@@ -457,10 +452,10 @@ class FSFileBackend extends FileBackendStore {
                        };
                        $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd );
                } else { // immediate write
-                       $this->trapWarnings();
-                       $ok = unlink( $source );
-                       $this->untrapWarnings();
-                       if ( !$ok ) {
+                       $this->trapWarnings( '/: No such file or directory$/' );
+                       $deleted = unlink( $fsSrcPath );
+                       $hadError = $this->untrapWarnings();
+                       if ( $hadError || ( !$deleted && !$ignoreMissing ) ) {
                                $status->fatal( 'backend-fail-delete', $params['src'] );
 
                                return $status;
@@ -483,7 +478,7 @@ class FSFileBackend extends FileBackendStore {
                $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
                $existed = is_dir( $dir ); // already there?
                // Create the directory and its parents as needed...
-               $this->trapWarnings();
+               AtEase::suppressWarnings();
                if ( !$existed && !mkdir( $dir, $this->dirMode, true ) && !is_dir( $dir ) ) {
                        $this->logger->error( __METHOD__ . ": cannot create directory $dir" );
                        $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
@@ -494,7 +489,7 @@ class FSFileBackend extends FileBackendStore {
                        $this->logger->error( __METHOD__ . ": directory $dir is not readable" );
                        $status->fatal( 'directorynotreadableerror', $params['dir'] );
                }
-               $this->untrapWarnings();
+               AtEase::restoreWarnings();
                // Respect any 'noAccess' or 'noListing' flags...
                if ( is_dir( $dir ) && !$existed ) {
                        $status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
@@ -519,9 +514,9 @@ class FSFileBackend extends FileBackendStore {
                }
                // Add a .htaccess file to the root of the container...
                if ( !empty( $params['noAccess'] ) && !file_exists( "{$contRoot}/.htaccess" ) ) {
-                       $this->trapWarnings();
+                       AtEase::suppressWarnings();
                        $bytes = file_put_contents( "{$contRoot}/.htaccess", $this->htaccessPrivate() );
-                       $this->untrapWarnings();
+                       AtEase::restoreWarnings();
                        if ( $bytes === false ) {
                                $storeDir = "mwstore://{$this->name}/{$shortCont}";
                                $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
@@ -539,21 +534,17 @@ class FSFileBackend extends FileBackendStore {
                // Unseed new directories with a blank index.html, to allow crawling...
                if ( !empty( $params['listing'] ) && is_file( "{$dir}/index.html" ) ) {
                        $exists = ( file_get_contents( "{$dir}/index.html" ) === $this->indexHtmlPrivate() );
-                       $this->trapWarnings();
-                       if ( $exists && !unlink( "{$dir}/index.html" ) ) { // reverse secure()
+                       if ( $exists && !$this->unlink( "{$dir}/index.html" ) ) { // reverse secure()
                                $status->fatal( 'backend-fail-delete', $params['dir'] . '/index.html' );
                        }
-                       $this->untrapWarnings();
                }
                // Remove the .htaccess file from the root of the container...
                if ( !empty( $params['access'] ) && is_file( "{$contRoot}/.htaccess" ) ) {
                        $exists = ( file_get_contents( "{$contRoot}/.htaccess" ) === $this->htaccessPrivate() );
-                       $this->trapWarnings();
-                       if ( $exists && !unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
+                       if ( $exists && !$this->unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
                                $storeDir = "mwstore://{$this->name}/{$shortCont}";
                                $status->fatal( 'backend-fail-delete', "{$storeDir}/.htaccess" );
                        }
-                       $this->untrapWarnings();
                }
 
                return $status;
@@ -564,11 +555,11 @@ class FSFileBackend extends FileBackendStore {
                list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
                $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
                $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
-               $this->trapWarnings();
+               AtEase::suppressWarnings();
                if ( is_dir( $dir ) ) {
                        rmdir( $dir ); // remove directory if empty
                }
-               $this->untrapWarnings();
+               AtEase::restoreWarnings();
 
                return $status;
        }
@@ -724,7 +715,7 @@ class FSFileBackend extends FileBackendStore {
 
                        $tmpPath = $tmpFile->getPath();
                        // Copy the source file over the temp file
-                       $this->trapWarnings();
+                       $this->trapWarnings(); // don't trust 'false' if there were errors
                        $isFile = is_file( $source ); // regular files only
                        $copySuccess = $isFile ? copy( $source, $tmpPath ) : false;
                        $hadError = $this->untrapWarnings();
@@ -755,8 +746,19 @@ class FSFileBackend extends FileBackendStore {
                $statuses = [];
 
                $pipes = [];
+               $octalPermissions = '0' . decoct( $this->fileMode );
                foreach ( $fileOpHandles as $index => $fileOpHandle ) {
-                       $pipes[$index] = popen( "{$fileOpHandle->cmd} 2>&1", 'r' );
+                       $cmd = "{$fileOpHandle->cmd} 2>&1";
+                       // Add a post-operation chmod command for permissions cleanup if applicable
+                       if (
+                               !$this->isWindows &&
+                               $fileOpHandle->chmodPath !== null &&
+                               strlen( $octalPermissions ) == 4
+                       ) {
+                               $encPath = escapeshellarg( $fileOpHandle->chmodPath );
+                               $cmd .= " && chmod $octalPermissions $encPath 2>/dev/null";
+                       }
+                       $pipes[$index] = popen( $cmd, 'r' );
                }
 
                $errs = [];
@@ -772,12 +774,10 @@ class FSFileBackend extends FileBackendStore {
                        $function = $fileOpHandle->call;
                        $function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
                        $statuses[$index] = $status;
-                       if ( $status->isOK() && $fileOpHandle->chmodPath ) {
-                               $this->chmod( $fileOpHandle->chmodPath );
-                       }
                }
 
                clearstatcache(); // files changed
+
                return $statuses;
        }
 
@@ -788,13 +788,52 @@ class FSFileBackend extends FileBackendStore {
         * @return bool Success
         */
        protected function chmod( $path ) {
-               $this->trapWarnings();
+               if ( $this->isWindows ) {
+                       return true;
+               }
+
+               AtEase::suppressWarnings();
                $ok = chmod( $path, $this->fileMode );
-               $this->untrapWarnings();
+               AtEase::restoreWarnings();
 
                return $ok;
        }
 
+       /**
+        * Unlink a file, suppressing the warnings
+        *
+        * @param string $path Absolute file system path
+        * @return bool Success
+        */
+       protected function unlink( $path ) {
+               AtEase::suppressWarnings();
+               $ok = unlink( $path );
+               AtEase::restoreWarnings();
+
+               return $ok;
+       }
+
+       /**
+        * @param array $params Operation parameters with 'content' and 'headers' fields
+        * @return TempFSFile|null
+        */
+       protected function stageContentAsTempFile( array $params ) {
+               $content = $params['content'];
+               $tempFile = $this->tmpFileFactory->newTempFSFile( 'create_', 'tmp' );
+               if ( !$tempFile ) {
+                       return null;
+               }
+
+               AtEase::suppressWarnings();
+               $tmpPath = $tempFile->getPath();
+               if ( file_put_contents( $tmpPath, $content ) === false ) {
+                       $tempFile = null;
+               }
+               AtEase::restoreWarnings();
+
+               return $tempFile;
+       }
+
        /**
         * Return the text of an index.html file to hide directory listings
         *
@@ -824,30 +863,29 @@ class FSFileBackend extends FileBackendStore {
        }
 
        /**
-        * Listen for E_WARNING errors and track whether any happen
+        * Listen for E_WARNING errors and track whether any that happen
+        *
+        * @param string|null $regexIgnore Optional regex of errors to ignore
         */
-       protected function trapWarnings() {
-               // push to stack
-               $this->hadWarningErrors[] = false;
-               set_error_handler( function ( $errno, $errstr ) {
-                       // more detailed error logging
-                       $this->logger->error( $errstr );
-                       $this->hadWarningErrors[count( $this->hadWarningErrors ) - 1] = true;
-
-                       // suppress from PHP handler
-                       return true;
+       protected function trapWarnings( $regexIgnore = null ) {
+               $this->warningTrapStack[] = false;
+               set_error_handler( function ( $errno, $errstr ) use ( $regexIgnore ) {
+                       if ( $regexIgnore === null || !preg_match( $regexIgnore, $errstr ) ) {
+                               $this->logger->error( $errstr );
+                               $this->warningTrapStack[count( $this->warningTrapStack ) - 1] = true;
+                       }
+                       return true; // suppress from PHP handler
                }, E_WARNING );
        }
 
        /**
-        * Stop listening for E_WARNING errors and return true if any happened
+        * Stop listening for E_WARNING errors and get whether any happened
         *
-        * @return bool
+        * @return bool Whether any warnings happened
         */
        protected function untrapWarnings() {
-               // restore previous handler
                restore_error_handler();
-               // pop from stack
-               return array_pop( $this->hadWarningErrors );
+
+               return array_pop( $this->warningTrapStack );
        }
 }
index 56a2177..edea75f 100644 (file)
@@ -146,9 +146,9 @@ class SwiftFileBackend extends FileBackendStore {
 
        public function getFeatures() {
                return (
-                       FileBackend::ATTR_UNICODE_PATHS |
-                       FileBackend::ATTR_HEADERS |
-                       FileBackend::ATTR_METADATA
+                       self::ATTR_UNICODE_PATHS |
+                       self::ATTR_HEADERS |
+                       self::ATTR_METADATA
                );
        }
 
@@ -1299,6 +1299,9 @@ class SwiftFileBackend extends FileBackendStore {
         * @return StatusValue[]
         */
        protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
+               /** @var SwiftFileOpHandle[] $fileOpHandles */
+               '@phan-var SwiftFileOpHandle[] $fileOpHandles';
+
                /** @var StatusValue[] $statuses */
                $statuses = [];
 
@@ -1314,7 +1317,6 @@ class SwiftFileBackend extends FileBackendStore {
                // Split the HTTP requests into stages that can be done concurrently
                $httpReqsByStage = []; // map of (stage => index => HTTP request)
                foreach ( $fileOpHandles as $index => $fileOpHandle ) {
-                       /** @var SwiftFileOpHandle $fileOpHandle */
                        $reqs = $fileOpHandle->httpOp;
                        // Convert the 'url' parameter to an actual URL using $auth
                        foreach ( $reqs as $stage => &$req ) {
index 981aeb0..d5f5de3 100644 (file)
@@ -94,8 +94,7 @@ class LogPage {
 
                $dbw = wfGetDB( DB_MASTER );
 
-               // @todo FIXME private/protected/public property?
-               $this->timestamp = $now = wfTimestampNow();
+               $now = wfTimestampNow();
                $data = [
                        'log_type' => $this->type,
                        'log_action' => $this->action,
index 5326705..523a2f8 100644 (file)
@@ -253,8 +253,6 @@ class ManualLogEntry extends LogEntryBase implements Taggable {
         * @throws MWException
         */
        public function insert( IDatabase $dbw = null ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $dbw = $dbw ?: wfGetDB( DB_MASTER );
 
                if ( $this->timestamp === null ) {
@@ -267,31 +265,6 @@ class ManualLogEntry extends LogEntryBase implements Taggable {
                $params = $this->getParameters();
                $relations = $this->relations;
 
-               // Ensure actor relations are set
-               if ( ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) &&
-                       empty( $relations['target_author_actor'] )
-               ) {
-                       $actorIds = [];
-                       if ( !empty( $relations['target_author_id'] ) ) {
-                               foreach ( $relations['target_author_id'] as $id ) {
-                                       $actorIds[] = User::newFromId( $id )->getActorId( $dbw );
-                               }
-                       }
-                       if ( !empty( $relations['target_author_ip'] ) ) {
-                               foreach ( $relations['target_author_ip'] as $ip ) {
-                                       $actorIds[] = User::newFromName( $ip, false )->getActorId( $dbw );
-                               }
-                       }
-                       if ( $actorIds ) {
-                               $relations['target_author_actor'] = $actorIds;
-                               $params['authorActors'] = $actorIds;
-                       }
-               }
-               if ( !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
-                       unset( $relations['target_author_id'], $relations['target_author_ip'] );
-                       unset( $params['authorIds'], $params['authorIPs'] );
-               }
-
                // Additional fields for which there's no space in the database table schema
                $revId = $this->getAssociatedRevId();
                if ( $revId ) {
index 2f6d4da..653e443 100644 (file)
@@ -987,6 +987,7 @@ EOT
                        parent::delete();
                        return;
                }
+               '@phan-var LocalFile $file';
 
                $deleter = new FileDeleteForm( $file );
                $deleter->execute();
index 9c5c4e0..c167f3a 100644 (file)
@@ -2841,7 +2841,7 @@ class WikiPage implements Page, IDBAccessObject {
         */
        protected function archiveRevisions( $dbw, $id, $suppress ) {
                global $wgContentHandlerUseDB, $wgMultiContentRevisionSchemaMigrationStage,
-                       $wgActorTableSchemaMigrationStage, $wgDeleteRevisionsBatchSize;
+                       $wgDeleteRevisionsBatchSize;
 
                // Given the lock above, we can be confident in the title and page ID values
                $namespace = $this->getTitle()->getNamespace();
@@ -2968,9 +2968,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                        $dbw->delete( 'revision', [ 'rev_id' => $revids ], __METHOD__ );
                        $dbw->delete( 'revision_comment_temp', [ 'revcomment_rev' => $revids ], __METHOD__ );
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                               $dbw->delete( 'revision_actor_temp', [ 'revactor_rev' => $revids ], __METHOD__ );
-                       }
+                       $dbw->delete( 'revision_actor_temp', [ 'revactor_rev' => $revids ], __METHOD__ );
 
                        // Also delete records from ip_changes as applicable.
                        if ( count( $ipRevIds ) > 0 ) {
@@ -3147,7 +3145,7 @@ class WikiPage implements Page, IDBAccessObject {
        public function commitRollback( $fromP, $summary, $bot,
                &$resultDetails, User $guser, $tags = null
        ) {
-               global $wgUseRCPatrol;
+               global $wgUseRCPatrol, $wgDisableAnonTalk;
 
                $dbw = wfGetDB( DB_MASTER );
 
@@ -3220,6 +3218,8 @@ class WikiPage implements Page, IDBAccessObject {
                if ( empty( $summary ) ) {
                        if ( !$currentEditorForPublic ) { // no public user name
                                $summary = wfMessage( 'revertpage-nouser' );
+                       } elseif ( $wgDisableAnonTalk && $current->getUser() === 0 ) {
+                               $summary = wfMessage( 'revertpage-anon' );
                        } else {
                                $summary = wfMessage( 'revertpage' );
                        }
index b56527a..6b63a0d 100644 (file)
 
 /**
  * @ingroup Parser
+ *
+ * @property int $eqpos
+ * @property int $commentEnd
+ * @property int $visualEnd
  */
 class PPDPart {
        /**
index 68f1bb2..b9d796d 100644 (file)
@@ -27,6 +27,8 @@ class PPDStack {
        /** @var PPDStackElement[] */
        public $stack;
        public $rootAccum;
+       /** @var string|array */
+       public $accum;
 
        /**
         * @var PPDStackElement|false
index 116244d..fe2b04e 100644 (file)
@@ -21,6 +21,8 @@
 
 /**
  * @ingroup Parser
+ *
+ * @property int $startPos
  */
 class PPDStackElement {
        /**
index 3f147f0..b50fcfc 100644 (file)
@@ -21,6 +21,9 @@
 
 /**
  * @ingroup Parser
+ *
+ * @property int $depth
+ * @property PPFrame $parent
  */
 interface PPFrame {
        const NO_ARGS = 1;
index ac3a266..a0ec326 100644 (file)
@@ -82,7 +82,7 @@ class PPFrame_DOM implements PPFrame {
         * Create a new child frame
         * $args is optionally a multi-root PPNode or array containing the template arguments
         *
-        * @param bool|array $args
+        * @param bool|array|PPNode_DOM $args
         * @param Title|bool $title
         * @param int $indexOffset
         * @return PPTemplateFrame_DOM
@@ -95,11 +95,12 @@ class PPFrame_DOM implements PPFrame {
                }
                if ( $args !== false ) {
                        $xpath = false;
-                       if ( $args instanceof PPNode ) {
+                       if ( $args instanceof PPNode_DOM ) {
                                $args = $args->node;
                        }
+                       // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach
                        foreach ( $args as $arg ) {
-                               if ( $arg instanceof PPNode ) {
+                               if ( $arg instanceof PPNode_DOM ) {
                                        $arg = $arg->node;
                                }
                                if ( !$xpath || $xpath->document !== $arg->ownerDocument ) {
@@ -153,7 +154,7 @@ class PPFrame_DOM implements PPFrame {
 
        /**
         * @throws MWException
-        * @param string|PPNode_DOM|DOMNode $root
+        * @param string|PPNode_DOM|DOMNode|DOMNodeList $root
         * @param int $flags
         * @return string
         */
index df740cf..902e4f1 100644 (file)
@@ -40,6 +40,8 @@ class PPTemplateFrame_Hash extends PPFrame_Hash {
                $namedArgs = [], $title = false
        ) {
                parent::__construct( $preprocessor );
+               /** @var PPFrame_Hash parent */
+               '@phan-var PPFrame_Hash $parent';
 
                $this->parent = $parent;
                $this->numberedArgs = $numberedArgs;
index 99ca1be..19dd96e 100644 (file)
@@ -31,6 +31,11 @@ abstract class Preprocessor {
 
        const CACHE_VERSION = 1;
 
+       /**
+        * @var Parser
+        */
+       public $parser;
+
        /**
         * @var array Brace matching rules.
         */
index 9f4b7c7..7c372ee 100644 (file)
  */
 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
 class Preprocessor_Hash extends Preprocessor {
-
-       /**
-        * @var Parser
-        */
-       public $parser;
-
        const CACHE_PREFIX = 'preprocess-hash';
        const CACHE_VERSION = 2;
 
index d4a34f3..9f583a5 100644 (file)
@@ -121,7 +121,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        'wgCommentCodePointLimit' => CommentStore::COMMENT_CHARACTER_LIMIT,
                ];
 
-               Hooks::run( 'ResourceLoaderGetConfigVars', [ &$vars, $skin ] );
+               Hooks::run( 'ResourceLoaderGetConfigVars', [ &$vars, $skin, $conf ] );
 
                return $vars;
        }
index 74dd7bc..f4ea54f 100644 (file)
@@ -113,8 +113,6 @@ abstract class RevDelList extends RevisionListBase {
         * @since 1.23 Added 'perItemStatus' param
         */
        public function setVisibility( array $params ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $status = Status::newGood();
 
                $bitPars = $params['value'];
@@ -143,7 +141,7 @@ abstract class RevDelList extends RevisionListBase {
                $missing = array_flip( $this->ids );
                $this->clearFileOps();
                $idsForLog = [];
-               $authorIds = $authorIPs = $authorActors = [];
+               $authorActors = [];
 
                if ( $perItemStatus ) {
                        $status->itemStatuses = [];
@@ -225,29 +223,7 @@ abstract class RevDelList extends RevisionListBase {
                                $virtualOldBits |= $removedBits;
 
                                $status->successCount++;
-                               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                                       if ( $item->getAuthorId() > 0 ) {
-                                               $authorIds[] = $item->getAuthorId();
-                                       } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) {
-                                               $authorIPs[] = $item->getAuthorName();
-                                       }
-                                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                                               $actorId = $item->getAuthorActor();
-                                               // During migration, the actor field might be empty. If so, populate
-                                               // it here.
-                                               if ( !$actorId ) {
-                                                       if ( $item->getAuthorId() > 0 ) {
-                                                               $user = User::newFromId( $item->getAuthorId() );
-                                                       } else {
-                                                               $user = User::newFromName( $item->getAuthorName(), false );
-                                                       }
-                                                       $actorId = $user->getActorId( $dbw );
-                                               }
-                                               $authorActors[] = $actorId;
-                                       }
-                               } elseif ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                                       $authorActors[] = $item->getAuthorActor();
-                               }
+                               $authorActors[] = $item->getAuthorActor();
 
                                // Save the old and new bits in $visibilityChangeMap for
                                // later use.
@@ -291,13 +267,7 @@ abstract class RevDelList extends RevisionListBase {
 
                // Log it
                $authorFields = [];
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                       $authorFields['authorIds'] = $authorIds;
-                       $authorFields['authorIPs'] = $authorIPs;
-               }
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $authorFields['authorActors'] = $authorActors;
-               }
+               $authorFields['authorActors'] = $authorActors;
                $this->updateLog(
                        $logType,
                        [
@@ -363,8 +333,6 @@ abstract class RevDelList extends RevisionListBase {
         *     title:           The target title
         *     ids:             The ID list
         *     comment:         The log comment
-        *     authorIds:       The array of the user IDs of the offenders
-        *     authorIPs:       The array of the IP/anon user offenders
         *     authorActors:    The array of the actor IDs of the offenders
         *     tags:            The array of change tags to apply to the log entry
         * @throws MWException
@@ -387,12 +355,6 @@ abstract class RevDelList extends RevisionListBase {
                $relations = [
                        $field => $params['ids'],
                ];
-               if ( isset( $params['authorIds'] ) ) {
-                       $relations += [
-                               'target_author_id' => $params['authorIds'],
-                               'target_author_ip' => $params['authorIPs'],
-                       ];
-               }
                if ( isset( $params['authorActors'] ) ) {
                        $relations += [
                                'target_author_actor' => $params['authorActors'],
index 5644b95..9a9ab19 100644 (file)
@@ -44,8 +44,6 @@ class RevisionDeleteUser {
         * @return bool True on success, false on failure (e.g. invalid user ID)
         */
        private static function setUsernameBitfields( $name, $userId, $op, IDatabase $dbw = null ) {
-               global $wgActorTableSchemaMigrationStage;
-
                if ( !$userId || ( $op !== '|' && $op !== '&' ) ) {
                        return false; // sanity check
                }
@@ -69,19 +67,26 @@ class RevisionDeleteUser {
                $userTitle = Title::makeTitleSafe( NS_USER, $name );
                $userDbKey = $userTitle->getDBkey();
 
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
+               $actorId = $dbw->selectField( 'actor', 'actor_id', [ 'actor_name' => $name ], __METHOD__ );
+               if ( $actorId ) {
                        # Hide name from live edits
-                       $dbw->update(
-                               'revision',
-                               [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
-                               [ 'rev_user' => $userId ],
-                               __METHOD__ );
+                       $ids = $dbw->selectFieldValues(
+                               'revision_actor_temp', 'revactor_rev', [ 'revactor_actor' => $actorId ], __METHOD__
+                       );
+                       if ( $ids ) {
+                               $dbw->update(
+                                       'revision',
+                                       [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
+                                       [ 'rev_id' => $ids ],
+                                       __METHOD__
+                               );
+                       }
 
                        # Hide name from deleted edits
                        $dbw->update(
                                'archive',
                                [ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
-                               [ 'ar_user_text' => $name ],
+                               [ 'ar_actor' => $actorId ],
                                __METHOD__
                        );
 
@@ -89,7 +94,7 @@ class RevisionDeleteUser {
                        $dbw->update(
                                'logging',
                                [ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
-                               [ 'log_user' => $userId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
+                               [ 'log_actor' => $actorId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
                                __METHOD__
                        );
 
@@ -97,7 +102,7 @@ class RevisionDeleteUser {
                        $dbw->update(
                                'recentchanges',
                                [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
-                               [ 'rc_user_text' => $name ],
+                               [ 'rc_actor' => $actorId ],
                                __METHOD__
                        );
 
@@ -105,7 +110,7 @@ class RevisionDeleteUser {
                        $dbw->update(
                                'oldimage',
                                [ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
-                               [ 'oi_user_text' => $name ],
+                               [ 'oi_actor' => $actorId ],
                                __METHOD__
                        );
 
@@ -113,69 +118,11 @@ class RevisionDeleteUser {
                        $dbw->update(
                                'filearchive',
                                [ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
-                               [ 'fa_user_text' => $name ],
+                               [ 'fa_actor' => $actorId ],
                                __METHOD__
                        );
                }
 
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $actorId = $dbw->selectField( 'actor', 'actor_id', [ 'actor_name' => $name ], __METHOD__ );
-                       if ( $actorId ) {
-                               # Hide name from live edits
-                               $ids = $dbw->selectFieldValues(
-                                       'revision_actor_temp', 'revactor_rev', [ 'revactor_actor' => $actorId ], __METHOD__
-                               );
-                               if ( $ids ) {
-                                       $dbw->update(
-                                               'revision',
-                                               [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
-                                               [ 'rev_id' => $ids ],
-                                               __METHOD__
-                                       );
-                               }
-
-                               # Hide name from deleted edits
-                               $dbw->update(
-                                       'archive',
-                                       [ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
-                                       [ 'ar_actor' => $actorId ],
-                                       __METHOD__
-                               );
-
-                               # Hide name from logs
-                               $dbw->update(
-                                       'logging',
-                                       [ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
-                                       [ 'log_actor' => $actorId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
-                                       __METHOD__
-                               );
-
-                               # Hide name from RC
-                               $dbw->update(
-                                       'recentchanges',
-                                       [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
-                                       [ 'rc_actor' => $actorId ],
-                                       __METHOD__
-                               );
-
-                               # Hide name from live images
-                               $dbw->update(
-                                       'oldimage',
-                                       [ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
-                                       [ 'oi_actor' => $actorId ],
-                                       __METHOD__
-                               );
-
-                               # Hide name from deleted images
-                               $dbw->update(
-                                       'filearchive',
-                                       [ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
-                                       [ 'fa_actor' => $actorId ],
-                                       __METHOD__
-                               );
-                       }
-               }
-
                # Hide log entries pointing to the user page
                $dbw->update(
                        'logging',
index 8134c9a..2737e35 100644 (file)
@@ -34,6 +34,7 @@ use RequestContext;
 use SpecialPage;
 use Title;
 use User;
+use Wikimedia\ObjectFactory;
 
 /**
  * Factory for handling the special page list and generating SpecialPage objects.
@@ -221,6 +222,9 @@ class SpecialPageFactory {
        /** @var Language */
        private $contLang;
 
+       /** @var ObjectFactory */
+       private $objectFactory;
+
        /**
         * TODO Make this a const when HHVM support is dropped (T192166)
         *
@@ -241,11 +245,17 @@ class SpecialPageFactory {
        /**
         * @param ServiceOptions $options
         * @param Language $contLang
+        * @param ObjectFactory $objectFactory
         */
-       public function __construct( ServiceOptions $options, Language $contLang ) {
+       public function __construct(
+               ServiceOptions $options,
+               Language $contLang,
+               ObjectFactory $objectFactory
+       ) {
                $options->assertRequiredOptions( self::$constructorOptions );
                $this->options = $options;
                $this->contLang = $contLang;
+               $this->objectFactory = $objectFactory;
        }
 
        /**
@@ -412,14 +422,22 @@ class SpecialPageFactory {
                if ( isset( $specialPageList[$realName] ) ) {
                        $rec = $specialPageList[$realName];
 
-                       if ( is_callable( $rec ) ) {
-                               // Use callback to instantiate the special page
-                               $page = $rec();
-                       } elseif ( is_string( $rec ) ) {
-                               $className = $rec;
-                               $page = new $className;
-                       } elseif ( $rec instanceof SpecialPage ) {
+                       if ( $rec instanceof SpecialPage ) {
+                               wfDeprecated(
+                                       "a SpecialPage instance (for $realName) in " .
+                                       '$wgSpecialPages or from the SpecialPage_initList hook',
+                                       '1.34'
+                               );
+
                                $page = $rec; // XXX: we should deep clone here
+                       } elseif ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
+                               $page = $this->objectFactory->createObject(
+                                       $rec,
+                                       [
+                                               'allowClassName' => true,
+                                               'allowCallable' => true
+                                       ]
+                               );
                        } else {
                                $page = null;
                        }
index 4599b22..40c0edf 100644 (file)
@@ -554,13 +554,9 @@ class SpecialContributions extends IncludableSpecialPage {
                        $filterSelection = Html::rawElement( 'div', [], '' );
                }
 
-               $labelUsername = Xml::radioLabel(
+               $labelUsername = Xml::label(
                        $this->msg( 'sp-contributions-username' )->text(),
-                       'contribs',
-                       'user',
-                       'user',
-                       true,
-                       [ 'class' => 'mw-input' ]
+                       'mw-target-user-or-ip'
                );
                $input = Html::input(
                        'target',
index c6927c1..ac8baa1 100644 (file)
@@ -35,8 +35,6 @@ class SpecialLog extends SpecialPage {
        }
 
        public function execute( $par ) {
-               global $wgActorTableSchemaMigrationStage;
-
                $this->setHeaders();
                $this->outputHeader();
                $out = $this->getOutput();
@@ -107,13 +105,7 @@ class SpecialLog extends SpecialPage {
                        $offenderName = $opts->getValue( 'offender' );
                        $offender = empty( $offenderName ) ? null : User::newFromName( $offenderName, false );
                        if ( $offender ) {
-                               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                                       $qc = [ 'ls_field' => 'target_author_actor', 'ls_value' => $offender->getActorId() ];
-                               } elseif ( $offender->getId() > 0 ) {
-                                       $qc = [ 'ls_field' => 'target_author_id', 'ls_value' => $offender->getId() ];
-                               } else {
-                                       $qc = [ 'ls_field' => 'target_author_ip', 'ls_value' => $offender->getName() ];
-                               }
+                               $qc = [ 'ls_field' => 'target_author_actor', 'ls_value' => $offender->getActorId() ];
                        }
                } else {
                        // Allow extensions to add relations to their search types
index 978efa7..df8bce1 100644 (file)
@@ -25,7 +25,6 @@
  * The web server should generally be configured to make this accessible via a canonical URL/URI,
  * such as <http://my.domain.org/data/main/Foo>.
  *
- * @class
  * @ingroup SpecialPage
  */
 class SpecialPageData extends SpecialPage {
index 8865654..26f3665 100644 (file)
@@ -229,13 +229,7 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                        $sql = $dbr->limitResult( $sql, $limit, false );
                }
 
-               $res = $dbr->query( $sql, __METHOD__ );
-
-               if ( $res->numRows() == 0 ) {
-                       $this->mResultEmpty = true;
-               }
-
-               return $res;
+               return $dbr->query( $sql, __METHOD__ );
        }
 
        function setTopText( FormOptions $opts ) {
index eff8889..83153ae 100644 (file)
@@ -47,7 +47,6 @@ class SpecialStatistics extends SpecialPage {
                $this->total = SiteStats::pages();
                $this->users = SiteStats::users();
                $this->activeUsers = SiteStats::activeUsers();
-               $this->hook = '';
 
                $text = Xml::openElement( 'table', [ 'class' => 'wikitable mw-statistics-table' ] );
 
index 7703e20..0f5355d 100644 (file)
@@ -79,19 +79,16 @@ class ActiveUsersPager extends UsersPager {
        function getQueryInfo( $data = null ) {
                $dbr = $this->getDatabase();
 
-               $useActor = (bool)(
-                       $this->getConfig()->get( 'ActorTableSchemaMigrationStage' ) & SCHEMA_COMPAT_READ_NEW
-               );
-
                $activeUserSeconds = $this->getConfig()->get( 'ActiveUserDays' ) * 86400;
                $timestamp = $dbr->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
                $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
 
                // Inner subselect to pull the active users out of querycachetwo
-               $tables = [ 'querycachetwo', 'user' ];
-               $fields = [ 'qcc_title', 'user_id' ];
+               $tables = [ 'querycachetwo', 'user', 'actor' ];
+               $fields = [ 'qcc_title', 'user_id', 'actor_id' ];
                $jconds = [
                        'user' => [ 'JOIN', 'user_name = qcc_title' ],
+                       'actor' => [ 'JOIN', 'actor_user = user_id' ],
                ];
                $conds = [
                        'qcc_type' => 'activeusers',
@@ -126,20 +123,12 @@ class ActiveUsersPager extends UsersPager {
                                        'ipblocks', '1', [ 'ipb_user=user_id', 'ipb_deleted' => 1 ]
                                ) . ')';
                }
-               if ( $useActor ) {
-                       $tables[] = 'actor';
-                       $jconds['actor'] = [
-                               'JOIN',
-                               'actor_user = user_id',
-                       ];
-                       $fields[] = 'actor_id';
-               }
                $subquery = $dbr->buildSelectSubquery( $tables, $fields, $conds, $fname, $options, $jconds );
 
                // Outer query to select the recent edit counts for the selected active users
                $tables = [ 'qcc_users' => $subquery, 'recentchanges' ];
                $jconds = [ 'recentchanges' => [ 'LEFT JOIN', [
-                       $useActor ? 'rc_actor = actor_id' : 'rc_user_text = qcc_title',
+                       'rc_actor = actor_id',
                        'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Don't count wikidata.
                        'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ), // Don't count categorization changes.
                        'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' ),
index f1b0b9a..be4a1bd 100644 (file)
@@ -97,27 +97,17 @@ class NewFilesPager extends RangeChronologicalPager {
                }
 
                if ( $opts->getValue( 'hidepatrolled' ) ) {
-                       global $wgActorTableSchemaMigrationStage;
-
                        $tables[] = 'recentchanges';
                        $conds['rc_type'] = RC_LOG;
                        $conds['rc_log_type'] = 'upload';
                        $conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
                        $conds['rc_namespace'] = NS_FILE;
 
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                               $jcond = 'rc_actor = ' . $imgQuery['fields']['img_actor'];
-                       } else {
-                               $rcQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
-                               $tables += $rcQuery['tables'];
-                               $jconds += $rcQuery['joins'];
-                               $jcond = $rcQuery['fields']['rc_user'] . ' = ' . $imgQuery['fields']['img_user'];
-                       }
                        $jconds['recentchanges'] = [
                                'JOIN',
                                [
                                        'rc_title = img_name',
-                                       $jcond,
+                                       'rc_actor = ' . $imgQuery['fields']['img_actor'],
                                        'rc_timestamp = img_timestamp'
                                ]
                        ];
index a9f399b..7f4d7c5 100644 (file)
@@ -40,14 +40,11 @@ class UploadFromStash extends UploadBase {
        private $repo;
 
        /**
-        * @param User|bool $user Default: false
+        * @param User|bool $user Default: false Sometimes this won't exist, as when running from cron.
         * @param UploadStash|bool $stash Default: false
         * @param FileRepo|bool $repo Default: false
         */
        public function __construct( $user = false, $stash = false, $repo = false ) {
-               // user object. sometimes this won't exist, as when running from cron.
-               $this->user = $user;
-
                if ( $repo ) {
                        $this->repo = $repo;
                } else {
@@ -63,7 +60,7 @@ class UploadFromStash extends UploadBase {
                                wfDebug( __METHOD__ . " creating new UploadStash instance with no user\n" );
                        }
 
-                       $this->stash = new UploadStash( $this->repo, $this->user );
+                       $this->stash = new UploadStash( $this->repo, $user );
                }
        }
 
index 7068879..666f2b6 100644 (file)
@@ -64,7 +64,7 @@ class User implements IDBAccessObject, UserIdentity {
         * Version number to tag cached versions of serialized User objects. Should be increased when
         * {@link $mCacheVars} or one of it's members changes.
         */
-       const VERSION = 13;
+       const VERSION = 14;
 
        /**
         * Exclude user options that are set to their default value.
@@ -327,22 +327,6 @@ class User implements IDBAccessObject, UserIdentity {
                        case 'defaults':
                                $this->loadDefaults();
                                break;
-                       case 'name':
-                               // Make sure this thread sees its own changes
-                               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
-                               if ( $lb->hasOrMadeRecentMasterChanges() ) {
-                                       $flags |= self::READ_LATEST;
-                                       $this->queryFlagsUsed = $flags;
-                               }
-
-                               $this->mId = self::idFromName( $this->mName, $flags );
-                               if ( !$this->mId ) {
-                                       // Nonexistent user placeholder object
-                                       $this->loadDefaults( $this->mName );
-                               } else {
-                                       $this->loadFromId( $flags );
-                               }
-                               break;
                        case 'id':
                                // Make sure this thread sees its own changes, if the ID isn't 0
                                if ( $this->mId != 0 ) {
@@ -356,6 +340,7 @@ class User implements IDBAccessObject, UserIdentity {
                                $this->loadFromId( $flags );
                                break;
                        case 'actor':
+                       case 'name':
                                // Make sure this thread sees its own changes
                                $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
                                if ( $lb->hasOrMadeRecentMasterChanges() ) {
@@ -366,20 +351,20 @@ class User implements IDBAccessObject, UserIdentity {
                                list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
                                $row = wfGetDB( $index )->selectRow(
                                        'actor',
-                                       [ 'actor_user', 'actor_name' ],
-                                       [ 'actor_id' => $this->mActorId ],
+                                       [ 'actor_id', 'actor_user', 'actor_name' ],
+                                       $this->mFrom === 'name' ? [ 'actor_name' => $this->mName ] : [ 'actor_id' => $this->mActorId ],
                                        __METHOD__,
                                        $options
                                );
 
                                if ( !$row ) {
                                        // Ugh.
-                                       $this->loadDefaults();
+                                       $this->loadDefaults( $this->mFrom === 'name' ? $this->mName : false );
                                } elseif ( $row->actor_user ) {
                                        $this->mId = $row->actor_user;
                                        $this->loadFromId( $flags );
                                } else {
-                                       $this->loadDefaults( $row->actor_name );
+                                       $this->loadDefaults( $row->actor_name, $row->actor_id );
                                }
                                break;
                        case 'session':
@@ -567,17 +552,6 @@ class User implements IDBAccessObject, UserIdentity {
         * @return User The corresponding User object
         */
        public static function newFromActorId( $id ) {
-               global $wgActorTableSchemaMigrationStage;
-
-               // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
-               // but it does little harm and might be needed for write callers loading a User.
-               if ( !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) ) {
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage lacks SCHEMA_COMPAT_NEW'
-                       );
-               }
-
                $u = new User;
                $u->mActorId = $id;
                $u->mFrom = 'actor';
@@ -620,8 +594,6 @@ class User implements IDBAccessObject, UserIdentity {
         * @return User
         */
        public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain = false ) {
-               global $wgActorTableSchemaMigrationStage;
-
                // Stop-gap solution for the problem described in T222212.
                // Force the User ID and Actor ID to zero for users loaded from the database
                // of another wiki, to prevent subtle data corruption and confusing failure modes.
@@ -633,9 +605,7 @@ class User implements IDBAccessObject, UserIdentity {
                $user = new User;
                $user->mFrom = 'defaults';
 
-               // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
-               // but it does little harm and might be needed for write callers loading a User.
-               if ( ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) && $actorId !== null ) {
+               if ( $actorId !== null ) {
                        $user->mActorId = (int)$actorId;
                        if ( $user->mActorId !== 0 ) {
                                $user->mFrom = 'actor';
@@ -1220,11 +1190,12 @@ class User implements IDBAccessObject, UserIdentity {
         *       the constructor does that instead.
         *
         * @param string|bool $name
+        * @param int|null $actorId
         */
-       public function loadDefaults( $name = false ) {
+       public function loadDefaults( $name = false, $actorId = null ) {
                $this->mId = 0;
                $this->mName = $name;
-               $this->mActorId = null;
+               $this->mActorId = $actorId;
                $this->mRealName = '';
                $this->mEmail = '';
                $this->mOptionOverrides = null;
@@ -1375,8 +1346,6 @@ class User implements IDBAccessObject, UserIdentity {
         *  user_properties   Array with properties out of the user_properties table
         */
        protected function loadFromRow( $row, $data = null ) {
-               global $wgActorTableSchemaMigrationStage;
-
                if ( !is_object( $row ) ) {
                        throw new InvalidArgumentException( '$row must be an object' );
                }
@@ -1385,18 +1354,14 @@ class User implements IDBAccessObject, UserIdentity {
 
                $this->mGroupMemberships = null; // deferred
 
-               // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
-               // but it does little harm and might be needed for write callers loading a User.
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
-                       if ( isset( $row->actor_id ) ) {
-                               $this->mActorId = (int)$row->actor_id;
-                               if ( $this->mActorId !== 0 ) {
-                                       $this->mFrom = 'actor';
-                               }
-                               $this->setItemLoaded( 'actor' );
-                       } else {
-                               $all = false;
+               if ( isset( $row->actor_id ) ) {
+                       $this->mActorId = (int)$row->actor_id;
+                       if ( $this->mActorId !== 0 ) {
+                               $this->mFrom = 'actor';
                        }
+                       $this->setItemLoaded( 'actor' );
+               } else {
+                       $all = false;
                }
 
                if ( isset( $row->user_name ) && $row->user_name !== '' ) {
@@ -2214,7 +2179,9 @@ class User implements IDBAccessObject, UserIdentity {
         * @return int The user's ID; 0 if the user is anonymous or nonexistent
         */
        public function getId() {
-               if ( $this->mId === null && $this->mName !== null && self::isIP( $this->mName ) ) {
+               if ( $this->mId === null && $this->mName !== null &&
+                       ( self::isIP( $this->mName ) || ExternalUserNames::isExternal( $this->mName ) )
+               ) {
                        // Special case, we know the user is anonymous
                        return 0;
                }
@@ -2280,62 +2247,43 @@ class User implements IDBAccessObject, UserIdentity {
         * @return int The actor's ID, or 0 if no actor ID exists and $dbw was null
         */
        public function getActorId( IDatabase $dbw = null ) {
-               global $wgActorTableSchemaMigrationStage;
-
-               // Technically we should always return 0 without SCHEMA_COMPAT_READ_NEW,
-               // but it does little harm and might be needed for write callers loading a User.
-               if ( !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) ) {
-                       return 0;
-               }
-
                if ( !$this->isItemLoaded( 'actor' ) ) {
                        $this->load();
                }
 
-               // Currently $this->mActorId might be null if $this was loaded from a
-               // cache entry that was written when $wgActorTableSchemaMigrationStage
-               // was SCHEMA_COMPAT_OLD. Once that is no longer a possibility (i.e. when
-               // User::VERSION is incremented after $wgActorTableSchemaMigrationStage
-               // has been removed), that condition may be removed.
-               if ( $this->mActorId === null || !$this->mActorId && $dbw ) {
+               if ( !$this->mActorId && $dbw ) {
                        $q = [
                                'actor_user' => $this->getId() ?: null,
                                'actor_name' => (string)$this->getName(),
                        ];
-                       if ( $dbw ) {
-                               if ( $q['actor_user'] === null && self::isUsableName( $q['actor_name'] ) ) {
+                       if ( $q['actor_user'] === null && self::isUsableName( $q['actor_name'] ) ) {
+                               throw new CannotCreateActorException(
+                                       'Cannot create an actor for a usable name that is not an existing user'
+                               );
+                       }
+                       if ( $q['actor_name'] === '' ) {
+                               throw new CannotCreateActorException( 'Cannot create an actor for a user with no name' );
+                       }
+                       $dbw->insert( 'actor', $q, __METHOD__, [ 'IGNORE' ] );
+                       if ( $dbw->affectedRows() ) {
+                               $this->mActorId = (int)$dbw->insertId();
+                       } else {
+                               // Outdated cache?
+                               // Use LOCK IN SHARE MODE to bypass any MySQL REPEATABLE-READ snapshot.
+                               $this->mActorId = (int)$dbw->selectField(
+                                       'actor',
+                                       'actor_id',
+                                       $q,
+                                       __METHOD__,
+                                       [ 'LOCK IN SHARE MODE' ]
+                               );
+                               if ( !$this->mActorId ) {
                                        throw new CannotCreateActorException(
-                                               'Cannot create an actor for a usable name that is not an existing user'
-                                       );
-                               }
-                               if ( $q['actor_name'] === '' ) {
-                                       throw new CannotCreateActorException( 'Cannot create an actor for a user with no name' );
-                               }
-                               $dbw->insert( 'actor', $q, __METHOD__, [ 'IGNORE' ] );
-                               if ( $dbw->affectedRows() ) {
-                                       $this->mActorId = (int)$dbw->insertId();
-                               } else {
-                                       // Outdated cache?
-                                       // Use LOCK IN SHARE MODE to bypass any MySQL REPEATABLE-READ snapshot.
-                                       $this->mActorId = (int)$dbw->selectField(
-                                               'actor',
-                                               'actor_id',
-                                               $q,
-                                               __METHOD__,
-                                               [ 'LOCK IN SHARE MODE' ]
+                                               "Cannot create actor ID for user_id={$this->getId()} user_name={$this->getName()}"
                                        );
-                                       if ( !$this->mActorId ) {
-                                               throw new CannotCreateActorException(
-                                                       "Cannot create actor ID for user_id={$this->getId()} user_name={$this->getName()}"
-                                               );
-                                       }
                                }
-                               $this->invalidateCache();
-                       } else {
-                               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $this->queryFlagsUsed );
-                               $db = wfGetDB( $index );
-                               $this->mActorId = (int)$db->selectField( 'actor', 'actor_id', $q, __METHOD__, $options );
                        }
+                       $this->invalidateCache();
                        $this->setItemLoaded( 'actor' );
                }
 
@@ -3979,8 +3927,6 @@ class User implements IDBAccessObject, UserIdentity {
 
                $dbw = wfGetDB( DB_MASTER );
                $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $newTouched ) {
-                       global $wgActorTableSchemaMigrationStage;
-
                        $dbw->update( 'user',
                                [ /* SET */
                                        'user_name' => $this->mName,
@@ -4010,14 +3956,12 @@ class User implements IDBAccessObject, UserIdentity {
                                );
                        }
 
-                       if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                               $dbw->update(
-                                       'actor',
-                                       [ 'actor_name' => $this->mName ],
-                                       [ 'actor_user' => $this->mId ],
-                                       $fname
-                               );
-                       }
+                       $dbw->update(
+                               'actor',
+                               [ 'actor_name' => $this->mName ],
+                               [ 'actor_user' => $this->mId ],
+                               $fname
+                       );
                } );
 
                $this->mTouched = $newTouched;
@@ -4216,16 +4160,12 @@ class User implements IDBAccessObject, UserIdentity {
         * @param IDatabase $dbw Writable database handle
         */
        private function updateActorId( IDatabase $dbw ) {
-               global $wgActorTableSchemaMigrationStage;
-
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $dbw->insert(
-                               'actor',
-                               [ 'actor_user' => $this->mId, 'actor_name' => $this->mName ],
-                               __METHOD__
-                       );
-                       $this->mActorId = (int)$dbw->insertId();
-               }
+               $dbw->insert(
+                       'actor',
+                       [ 'actor_user' => $this->mId, 'actor_name' => $this->mName ],
+                       __METHOD__
+               );
+               $this->mActorId = (int)$dbw->insertId();
        }
 
        /**
@@ -5296,10 +5236,8 @@ class User implements IDBAccessObject, UserIdentity {
         *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
         */
        public static function getQueryInfo() {
-               global $wgActorTableSchemaMigrationStage;
-
                $ret = [
-                       'tables' => [ 'user' ],
+                       'tables' => [ 'user', 'user_actor' => 'actor' ],
                        'fields' => [
                                'user_id',
                                'user_name',
@@ -5312,21 +5250,13 @@ class User implements IDBAccessObject, UserIdentity {
                                'user_email_token_expires',
                                'user_registration',
                                'user_editcount',
+                               'user_actor.actor_id',
+                       ],
+                       'joins' => [
+                               'user_actor' => [ 'JOIN', 'user_actor.actor_user = user_id' ],
                        ],
-                       'joins' => [],
                ];
 
-               // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
-               // but it does little harm and might be needed for write callers loading a User.
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
-                       $ret['tables']['user_actor'] = 'actor';
-                       $ret['fields'][] = 'user_actor.actor_id';
-                       $ret['joins']['user_actor'] = [
-                               ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ? 'JOIN' : 'LEFT JOIN',
-                               [ 'user_actor.actor_user = user_id' ]
-                       ];
-               }
-
                return $ret;
        }
 
index 72f6086..69dcec8 100644 (file)
@@ -57,7 +57,10 @@ class NoWriteWatchedItemStore implements WatchedItemStoreInterface {
        }
 
        public function countWatchersMultiple( array $targets, array $options = [] ) {
-               return $this->actualStore->countVisitingWatchersMultiple( $targets, $options );
+               return $this->actualStore->countVisitingWatchersMultiple(
+                       $targets,
+                       $options['minimumWatchers'] ?? null
+               );
        }
 
        public function countVisitingWatchersMultiple(
index 0df4cd0..5c2d290 100644 (file)
--- a/index.php
+++ b/index.php
@@ -30,6 +30,8 @@
  * @file
  */
 
+define( 'MW_ENTRY_POINT', 'index' );
+
 // Bail on old versions of PHP, or if composer has not been run yet to install
 // dependencies. Using dirname( __FILE__ ) here because __DIR__ is PHP5.3+.
 // phpcs:ignore MediaWiki.Usage.DirUsage.FunctionFound
index 3508483..fc8e5f7 100644 (file)
        "undo-norev": "فشل في الرجوع عن التعديل حيث أنه غير موجود أو تم حذفه.",
        "undo-nochange": "يبدو أن التعديل قد تم التراجع عنه بالفعل.",
        "undo-summary": "الرجوع عن التعديل $1 بواسطة [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]])",
+       "undo-summary-anon": "التراجع عن المراجعة $1 بواسطة [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "الرجوع عن المراجعة $1 التي أجراها مستخدمي مخفي",
        "cantcreateaccount-text": "إنشاء الحسابات من عنوان الأيبي هذا (<strong>$1</strong>) تم منعه بواسطة [[User:$3|$3]].\n\nالسبب المعطى بواسطة $3 هو <em>$2</em>",
        "cantcreateaccount-range-text": "إنشاء الحسابات من عناوين الآيبي في النطاق <strong>$1</strong>، التي تحتوي على الآيبي الخاص بك (<strong>$4</strong>)، قد منعها [[User:$3|$3]].\n\nالسبب المعطى بواسطة $3 هو <em>$2</em>",
        "alreadyrolled": "لم يمكن استرجاع آخر تعديل ل[[$1]] بواسطة [[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": "ملخص التعديل كان:<em>$1</em>.",
        "revertpage": "استرجع تعديلات [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]]) حتى آخر مراجعة ل[[User:$1|$1]]",
+       "revertpage-anon": "استرجع تعديلات [[Special:Contributions/$2|$2]] حتى آخر مراجعة بواسطة [[User:$1|$1]]",
        "revertpage-nouser": "استرجع تعديلات مستخدم مخفي حتى آخر مراجعة ل{{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "تم استرجاع تعديلات {{GENDER:$3|$1}}، حتى آخر نسخة بواسطة {{GENDER:$4|$2}}.",
        "sessionfailure-title": "فشل في الجلسة",
index 469cb7e..c5a76e6 100644 (file)
        "editlink": "editar",
        "viewsourcelink": "ver fonte",
        "editsectionhint": "Editar seición: $1",
-       "toc": "Índiz",
+       "toc": "Conteníu",
        "showtoc": "amosar",
        "hidetoc": "anubrir",
        "collapsible-collapse": "Plegar",
index 1b3950f..37e5947 100644 (file)
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Ida dané madué}} $1 saking {{PLURAL:$3|$3 sang anganggé lianan}} ($2).",
        "youhavenewmessagesmanyusers": "Jero madué $1 saking akéh sang anganggé ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|séwalapatra anyar abesik|999=séwalapatra anyar}}",
+       "newmessagesdifflinkplural": "$1 {{PLURAL:$1|uahan}}",
        "youhavenewmessagesmulti": "Ida dané madué séwalapatra anyar ring $1",
        "editsection": "uah",
        "editold": "uah",
        "template-protected": "(kasaibin)",
        "template-semiprotected": "(semi-kasaibin)",
        "hiddencategories": "lembar niki inggih punika krama saking {{PLURAL:$1|1 golongan sane mengkeb|$1 golongan sane mengkeb}}",
+       "permissionserrors": "Kaiwangan ritatkala ngranjing",
        "permissionserrorstext-withaction": "ida dané nénten madué kuasa ngranjing anggén $2, riantukan {{PLURAL:$1|alasan}} ring sor puniki:",
        "recreate-moveddeleted-warn": "\"pingetan\" ida dane ngawe malih lembar sane naenin maapus.'''\n\nmangda kayunin malih napike pantes lanturang suntingan ida dane. puniki log pengapusan lan pangisidan saking lembar puniki:",
        "moveddeleted-notice": "Kaca puniki sampun kausapin.\nAnggen pewarah, proteksi, lan pengisidan log saking lembar puniki cingakin pustaka beten.",
        "editundo": "nguliang",
        "diff-empty": "(Nénten wénten sané malianan)",
        "diff-multi-sameuser": "({{PLURAL:$1|$1 revisi pantaraning}} olih pangawi sane pateh nenten kacumawisang)",
+       "diff-multi-otherusers": "({{PLURAL:$1|Siki pamecikan pantaraning|$1 pamecikan pantaraning}} olih {{PLURAL:$2|siki pangangge lianan|$2 panggange}} nenten katampilan)",
        "searchresults": "asil pangrereh",
        "searchresults-title": "Asil pangrereh anggén \"$1\"",
        "prevn": "{{PLURAL:$1|$1}} sadurungnyané",
        "group-bot": "Bot",
        "group-sysop": "Prajuru",
        "grouppage-bot": "{{ns:project}}:Bot",
+       "grouppage-sysop": "{{ns:project}}:Prajuru",
        "right-edit": "Uah kaca",
        "right-writeapi": "nganggén API sasuratan",
        "right-delete": "Usap kaca",
        "randompage": "Kaca ulah-aluh",
        "statistics": "Statistik",
        "statistics-articles": "Kaca daging",
+       "double-redirect-fixer": "Pamecikan pengalihan",
        "brokenredirects-edit": "uah",
        "brokenredirects-delete": "usap",
        "withoutinterwiki-submit": "Sinahang",
        "booksources": "Wit buku",
        "booksources-search-legend": "Rereh wit buku",
        "booksources-search": "Rereh",
+       "specialloguserlabel": "Panggange",
        "log": "Log",
        "logeventslist-submit": "Sinahang",
        "all-logs-page": "Makasami log publik",
+       "logempty": "Nenten katemonin entri log sane patut",
        "allpages": "Makasami kaca",
        "allarticles": "Makasami kaca",
        "allinnamespace": "Makasami kaca (genah wastan $1)",
        "listgrouprights-members": "kepahan krama",
        "emailuser": "email sane nganggo niki",
        "emailmessage": "Séwalapatra:",
+       "usermessage-editor": "Séwalapatra sistem",
        "watchlist": "kepahan peninjoan",
        "mywatchlist": "kepahan peninjoan",
        "watchlistfor2": "Anggén $1 $2",
        "pageinfo-robot-index": "Kalugra",
        "pageinfo-robot-noindex": "Tan kalugra",
        "pageinfo-watchers": "Akéh nomér sané negdeg kaca",
+       "pageinfo-few-watchers": "Kirang saking $1 {{PLURAL:$1|tamiu}}",
        "pageinfo-redirects-name": "Akéh nomer sané magingsir ka kaca puniki",
        "pageinfo-subpages-name": "Kapahan kaca saking kaca puniki",
        "pageinfo-firstuser": "Sang makarya kaca",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|pabligbagan]])",
        "duplicate-defaultsort": "pingetan: sereg pangurutan lingga \"$2\" nyampahang sereg pangurutan lingga sadurunge \"$1\"",
        "version-specialpages": "Kaca kusus",
+       "redirect": "Pengalihan manut bacakan ID , panggage, kaca, pamecikan, utawi log",
+       "redirect-summary": "Kaca istimewa puniki kaglingsiran (manut wastan bacakannyane),kaca (manut pamecikan), utawi kacan pangangge (manut ID nomer panganggenyane). Kawigunan:\n[[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], atau [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "Nglanturang",
        "redirect-lookup": "Rereh:",
        "redirect-value": "Aji:",
        "tags-delete-title": "Usap tag",
        "compare-page2": "Kaca 2",
        "logentry-delete-delete": "$1 {{GENDER:$2|ngusapin}} kaca $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|ngwalikan}} kaca $3 ($4)",
+       "logentry-delete-revision": "$1 {{GENDER:$2|mauah}} kaca utama {{PLURAL:$5|$5  pamecikan}} ring kaca $3: $4",
        "revdelete-content-hid": "dagingnyané kaengkebang",
        "logentry-move-move": "$1 {{GENDER:$2|ngingsirang}} kaca $3 ring $4",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|maglisiran}} kaca $3 ka $4 tur nenten ngawe pengalihan",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|maglisiran}} kaca $3 ka $4 tur nenten ngawe pengalihan",
        "logentry-newusers-create": "Akun sang anganggé $1 {{GENDER:$2|kakaryanin}}",
        "logentry-newusers-autocreate": "Akun sang anganggé $1 {{GENDER:$2|kakaryanin}} otomatis",
        "logentry-protect-protect": "$1 {{GENDER:$2|nyaibin}} $3 $4",
        "pagelanguage": "Uah basa ring kaca",
        "pagelang-nonexistent-page": "Kaca $1 nénten wénten.",
        "mw-widgets-abandonedit-keep": "Lanturang nguah",
+       "randomrootpage": "Kaca pinih dasar sane mabrarakan",
        "log-action-filter-protect-protect": "Saiban",
        "log-action-filter-protect-move_prot": "Ngingsirang saiban"
 }
index bc5c403..485c9fe 100644 (file)
        "sessionfailure": "Изглежда има проблем със сесията ви;\nдействието беше отказано като предпазна мярка срещу крадене на сесията.\nМоля, изпратете формуляра повторно.",
        "changecontentmodel": "Промяна на модела на съдържанието на страница",
        "changecontentmodel-legend": "Промяна на модела на съдържанието",
-       "changecontentmodel-title-label": "Заглавие на страницата",
-       "changecontentmodel-model-label": "Нов модел на съдържанието",
+       "changecontentmodel-title-label": "Заглавие на страницата:",
+       "changecontentmodel-model-label": "Нов модел на съдържанието:",
        "changecontentmodel-reason-label": "Причина:",
        "changecontentmodel-submit": "Променяне",
        "changecontentmodel-success-title": "Моделът на съдържанието беше променен",
index a309520..224e4ae 100644 (file)
        "sessionfailure": "আপনার প্রবেশ সেশনে একটি সমস্যা হয়েছে বলে মনে হচ্ছে;\nসেশন হাইজ্যাক প্রতিরোধের উপায় হিসেবে এই কাজটি বাতিল করা হয়েছে।\nদয়া করে ফরমটি পুনরায় জমা দিন।",
        "changecontentmodel": "একটি পাতার বিষয়বস্তুর রূপ পরিবর্তন",
        "changecontentmodel-legend": "বিষয়বস্তুর রূপ পরিবর্তন করুন",
-       "changecontentmodel-title-label": "পাতার শিরোনাম",
+       "changecontentmodel-title-label": "পাতার শিরোনাম:",
        "changecontentmodel-current-label": "বর্তমান বিষয়বস্তুর রূপ:",
-       "changecontentmodel-model-label": "পাতার বিষয়বস্তুর প্রতিরূপ",
+       "changecontentmodel-model-label": "পাতার বিষয়বস্তুর প্রতিরূপ:",
        "changecontentmodel-reason-label": "কারণ:",
        "changecontentmodel-submit": "পরিবর্তন করুন",
        "changecontentmodel-success-title": "বিষয়বস্তুর প্রতিরূপ পরিবর্তিত হয়েছিলো",
        "move-subpages": "উপপাতা স্থানান্তর ($1টি পর্যন্ত)",
        "move-talk-subpages": "উপপাতার আলাপ পাতা স্থানান্তর ($1টি পর্যন্ত)",
        "movepage-page-exists": "$1 পাতাটি ইতিমধ্যেই বিদ্যমান এবং স্বয়ংক্রিয়ভাবে পুনর্লিখন করা সম্ভব নয়।",
+       "movepage-source-doesnt-exist": "$1 পাতাটির অস্তিত্ব নেই এবং স্থানান্তর করা যাবে না।",
        "movepage-page-moved": "$1 পাতাটি $2 পাতায় স্থানান্তর করা হয়েছে।",
        "movepage-page-unmoved": "$1 পাতাটি $2 -এ সরিয়ে নেওয়া সম্ভপর নয়।",
        "movepage-max-pages": "সর্বোচ্চ $1টি {{PLURAL:$1|উপপাতা}}স্থানান্তর করা হয়েছে এবং এর থেকে বেশি সংখ্যক পাতা সয়ংক্রিয়ভাবে স্থানান্তর সম্ভব নয়।",
index 34b49f5..e71e02f 100644 (file)
        "page_first": "یەکەمین",
        "page_last": "دوایین",
        "histlegend": "ھەڵبژاردنی جیاوازی: پیاچوونەوەکان بۆ ھەڵسەنگاندن دیاری بکە و ئینتەر یان دوگمەکەی خوارەوە لێبدە.<br />\nڕێنوێنی: '''({{int:cur}})''' = جیاوازی لەگەڵ دوایین پیاچوونەوە، '''({{int:last}})''' = جیاوازی لەگەڵ پیاچوونەوەی پێشووی، '''{{int:minoreditletter}}''' = دەستکاریی بچووک.",
-       "history-fieldset-title": "گەڕان بەدوای بەسەرداچوونەوەکان",
+       "history-fieldset-title": "پاڵاوتنی بەسەرداچوونەوەکان",
        "history-show-deleted": "تەنیا پێداچوونەوە سڕاوەکان",
        "histfirst": "کۆنترین",
        "histlast": "نوێترین",
        "recentchangeslinked-feed": "گۆڕانکارییە پەیوەندیدارەکان",
        "recentchangeslinked-toolbox": "گۆڕانکارییە پەیوەندیدارەکان",
        "recentchangeslinked-title": "گۆڕانکارییە پەیوەندیدارەکان بە \"$1\" ـەوە",
-       "recentchangeslinked-summary": "ناوی پەڕەک داخل بکە بۆ بینینی گۆڕانکارییەکانی ئەو پەڕانەی کە بەستەریان ھەیە بۆ ئەو پەڕەیە یان لەو پەڕەیەوە پەیوەست کراون. (بۆ بینینی ئەندامەکانی پۆلێک، پۆل:ناوی پۆلەکە داخل بکە). گۆڕانکارییەکانی پەڕەکانی [[Special:Watchlist|لیستی چاودێرییەکەت]] <strong>ئەستوورن</strong>.",
+       "recentchangeslinked-summary": "ناوی پەڕەک داخل بکە بۆ بینینی گۆڕانکارییەکانی ئەو پەڕانەی کە بەستەریان ھەیە بۆ ئەو پەڕەیە یان لەو پەڕەیەوە پەیوەست کراون. (بۆ بینینی ئەندامەکانی پۆلێک، {{ns:category}}:ناوی پۆلەکە داخل بکە). گۆڕانکارییەکانی پەڕەکانی [[Special:Watchlist|لیستی چاودێرییەکەت]] <strong>ئەستوورن</strong>.",
        "recentchangeslinked-page": "ناوی پەڕە:",
        "recentchangeslinked-to": "بەجێگەی ئەوە گۆڕانکارییەکانی ئەو پەڕانە نیشانبدە کە بەستەریان ھەیە بۆ پەڕەی دیاریکراو",
        "recentchanges-page-added-to-category": "[[:$1]] زیادکرا بۆ پۆل",
        "imagelinks": "بەکارھێنانی پەڕگە",
        "linkstoimage": "لەم {{PLURAL:$1|پەڕەی خوارەوە بەستەر دراوە|$1 پەڕەی خوارەوە بەستەر دراوە}} بۆ ئەم پەڕگە:",
        "linkstoimage-more": "زیاتر لە $1 {{PLURAL:$1|بەستەری لاپەڕە|بەستەری لاپەڕە}} بۆ ئەم پەڕگه.\nئەم لیستە {{PLURAL:$1|یەکەم لاپەڕەی بەستەرە|یەکەم لاپەڕە $1 بەستەرە}} بۆ تەنها یەم پەڕگە.\nهەروا [[Special:WhatLinksHere/$2|لیستی تەواو]] ئامادەی کەڵک وەرگرتنە.",
-       "nolinkstoimage": "Ú¾Û\8cÚ\86 Ù¾Û\95Ú\95Û\95Û\8cÛ\95Ú© Ù\86Û\8cÛ\8cÛ\95 Ú©Û\95 Ø¨Û\95ستÛ\95رÛ\8c Ú¾Û\95بÛ\8eت Ø¨Û\86 Ø¦Û\95Ù\85 Ù¾Û\95Ú\95Ú¯Û\95Û\8cÛ\95.",
+       "nolinkstoimage": "Ú¾Û\8cÚ\86 Ù¾Û\95Ú\95Û\95Û\8cÛ\95Ú© Ù\86Û\8cÛ\8cÛ\95 Ú©Û\95 Ø¦Û\95Ù\85 Ù¾Û\95Ú\95Ú¯Û\95Û\8cÛ\95 Ø¨Û\95کاربھÛ\8eÙ\86Û\8eت.",
        "morelinkstoimage": "[[Special:WhatLinksHere/$1|بەستەری زیاتر]] ببینە بۆ ئەم پەڕگە.",
        "linkstoimage-redirect": "$1 (ڕەوانەکەری پەڕگە) $2",
        "duplicatesoffile": "ئەم {{PLURAL:$1|پەڕگە دووبارەکرنەوەیەکی|پەڕگانە دووبارەکردنەوەی}} ئەم پەڕگەن ([[Special:FileDuplicateSearch/$2|وردەکاری زیاتر]]):",
        "uploadnewversion-linktext": "وەشانێکی نوێی ئەم پەڕگەیە بار بکە",
        "shared-repo-from": "لە لایەن $1",
        "shared-repo": "شوێنێکی هاوبەشی",
+       "shared-repo-name-wikimediacommons": "ویکیمیدیا کۆمنز",
        "upload-disallowed-here": "ناتوانی وەشانێکی نوێی ئەم پەڕگەیە بار بکەی.",
        "filerevert": "پێچەوانەکردنەوەی $1",
        "filerevert-legend": "پێچەوانەکردنەوەی پەڕگە",
index e29eb53..9921c13 100644 (file)
@@ -4,7 +4,8 @@
                        "FRANCIS5091",
                        "FRANELYA",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Tofeiku"
                ]
        },
        "tog-underline": "Mangagaris pioputan:",
        "editfont-monospace": "Pimato iso insir",
        "editfont-sansserif": "Pimato San-sorip",
        "editfont-serif": "Pimato Sorip",
-       "sunday": "Dautiwang",
-       "monday": "Dautonlu",
-       "tuesday": "Daumirod",
-       "wednesday": "Daumansa",
-       "thursday": "Dautadru",
-       "friday": "Daurudu",
-       "saturday": "Daukukuak",
-       "sun": "DTiw",
-       "mon": "DTon",
-       "tue": "DMir",
-       "wed": "DMad",
-       "thu": "DTad",
-       "fri": "DRud",
-       "sat": "DKua",
-       "january": "Tumilatok",
-       "february": "Tumansak",
-       "march": "Tugomot",
-       "april": "Tungiop",
-       "may_long": "Tumikat",
-       "june": "Tumahas",
-       "july": "Tumadas",
-       "august": "Tumagus",
-       "september": "Tumanom",
-       "october": "Tugumas",
-       "november": "Tumilau",
-       "december": "Tumomuhau",
+       "sunday": "Tiwang",
+       "monday": "Tontolu'",
+       "tuesday": "Mirod",
+       "wednesday": "Madsa",
+       "thursday": "Tadtaru",
+       "friday": "Kurudu",
+       "saturday": "Kukuak",
+       "sun": "Tiw",
+       "mon": "Ton",
+       "tue": "Mir",
+       "wed": "Mad",
+       "thu": "Tad",
+       "fri": "Kur",
+       "sat": "Kuk",
+       "january": "Milatok",
+       "february": "Mansak",
+       "march": "Gamot",
+       "april": "Ngiop",
+       "may_long": "Mikat",
+       "june": "Mahas",
+       "july": "Madas",
+       "august": "Magus",
+       "september": "Manom",
+       "october": "Gumas",
+       "november": "Milau",
+       "december": "Momuhau",
        "january-gen": "Milatok",
        "february-gen": "Mansak",
        "march-gen": "Gomot",
index 816839c..f6ea46c 100644 (file)
        "undo-norev": "The edit could not be undone because it does not exist or was deleted.",
        "undo-nochange": "The edit appears to have already been undone.",
        "undo-summary": "Undo revision $1 by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])",
+       "undo-summary-anon": "Undo revision $1 by [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "Undo revision $1 by a hidden user",
        "cantcreateaccount-text": "Account creation from this IP address (<strong>$1</strong>) has been blocked by [[User:$3|$3]].\n\nThe reason given by $3 is <em>$2</em>",
        "cantcreateaccount-range-text": "Account creation from IP addresses in the range <strong>$1</strong>, which includes your IP address (<strong>$4</strong>), has been blocked by [[User:$3|$3]].\n\nThe reason given by $3 is <em>$2</em>",
        "alreadyrolled": "Cannot rollback last edit of [[:$1]] by [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nsomeone else has edited or rolled back the page already.\n\nThe last edit to the page was by [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "The edit summary was: <em>$1</em>.",
        "revertpage": "Reverted edits by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) to last revision by [[User:$1|$1]]",
+       "revertpage-anon": "Reverted edits by [[Special:Contributions/$2|$2]] to last revision by [[User:$1|$1]]",
        "revertpage-nouser": "Reverted edits by a hidden user to last revision by {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Reverted edits by {{GENDER:$3|$1}};\nchanged back to last revision by {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Session failure",
index 96cc2a4..9c83390 100644 (file)
        "exif-photometricinterpretation-8": "CIE L*a*b*",
        "exif-photometricinterpretation-9": "CIE L*a*b* (pengkodean ICC)",
        "exif-photometricinterpretation-10": "CIE L*a*b* (pengkodean ITU)",
+       "exif-photometricinterpretation-32803": "Aturan Filter Warna",
+       "exif-photometricinterpretation-34892": "Linear raw",
        "exif-unknowndate": "Tanggal tak diketahui",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Dibalik horisontal",
        "exif-scenetype-1": "Gambar foto langsung",
        "exif-customrendered-0": "Proses normal",
        "exif-customrendered-1": "Proses kustom",
+       "exif-customrendered-2": "HDR (aslinya tidak disimpan)",
+       "exif-customrendered-3": "HDR (aslinya disimpan)",
+       "exif-customrendered-4": "Asli (untuk HDR)",
+       "exif-customrendered-6": "Panorama",
+       "exif-customrendered-7": "Portret HDR",
+       "exif-customrendered-8": "Potret",
        "exif-exposuremode-0": "Pajanan otomatis",
        "exif-exposuremode-1": "Pajanan manual",
        "exif-exposuremode-2": "Braket otomatis",
index bd46ad5..5f82f91 100644 (file)
        "exif-gpsmeasuremode-2": "2-رخي ماپ",
        "exif-gpsmeasuremode-3": "3-رخي ماپ",
        "exif-gpsspeed-k": "ڪلوميٽر في ڪلاڪ",
-       "exif-gpsspeed-m": "ميل في ڪلاڪ",
+       "exif-gpsspeed-m": "ميلَ في ڪلاڪ",
        "exif-gpsspeed-n": "ناٽس",
        "exif-gpsdestdistance-k": "ڪلميٽر",
-       "exif-gpsdestdistance-m": "ميل",
-       "exif-gpsdestdistance-n": "سامونڊي ميل",
+       "exif-gpsdestdistance-m": "ميلَ",
+       "exif-gpsdestdistance-n": "سامونڊي ميلَ",
        "exif-objectcycle-p": "رڳو شام",
        "exif-dc-contributor": "ڀاڱيدار",
        "exif-dc-date": "تاريخون",
index f2fbb2d..5132718 100644 (file)
        "filerenameerror": "Impossible de renommer le fichier « $1 » en « $2 ».",
        "filedeleteerror": "Impossible de supprimer le fichier « $1 ».",
        "directorycreateerror": "Impossible de créer le répertoire « $1 ».",
-       "directoryreadonlyerror": "Le répertoire « $1 » est en lecture seule.",
-       "directorynotreadableerror": "Le répertoire « $1 » n’est pas lisible.",
+       "directoryreadonlyerror": "Le répertoire « $1 » est en lecture seule.",
+       "directorynotreadableerror": "Le répertoire « $1 » n’est pas lisible.",
        "filenotfound": "Impossible de trouver le fichier « $1 ».",
        "unexpected": "Valeur inattendue : « $1 » = « $2 ».",
        "formerror": "Erreur : impossible de soumettre le formulaire.",
        "badarticleerror": "Cette action ne peut pas être effectuée sur cette page.",
        "cannotdelete": "Impossible de supprimer la page ou le fichier « $1 ».\nLa suppression a peut-être déjà été effectuée par quelqu’un d’autre.",
        "cannotdelete-title": "Impossible de supprimer la page « $1 »",
-       "delete-scheduled": "La page « $1 » est programmée pour être supprimée.\nVeuillez patienter.",
+       "delete-scheduled": "La page « $1 » est programmée pour être supprimée.\nVeuillez patienter.",
        "delete-hook-aborted": "Suppression annulée par une extension.\nAucune explication n’a été fournie.",
-       "no-null-revision": "Impossible de créer une nouvelle révision vide pour la page « $1 »",
+       "no-null-revision": "Impossible de créer une nouvelle révision vide pour la page « $1 »",
        "badtitle": "Mauvais titre",
        "badtitletext": "Le titre de la page demandée est non valide, vide, ou mal formé s’il s’agit d’un titre inter-langue ou inter-projet.\nIl contient peut-être un ou plusieurs caractères qui ne peuvent pas être utilisés dans les titres.",
        "title-invalid-empty": "Le titre de la page demandée est vide ou contient seulement le nom d’un espace de noms.",
        "virus-scanfailed": "échec de l’analyse (code $1)",
        "virus-unknownscanner": "antivirus inconnu :",
        "logouttext": "<strong>Vous êtes à présent déconnecté{{GENDER:||e|(e)}}.</strong>\n\nNotez que certaines pages peuvent être encore affichées comme si vous étiez toujours connecté, jusqu’à ce que vous effaciez le cache de votre navigateur.",
-       "logging-out-notify": "Vous allez être déconnecté, veuillez attendre.",
-       "logout-failed": "Impossible de se déconnecter maintenant : $1",
+       "logging-out-notify": "Vous allez être déconnecté, veuillez patienter.",
+       "logout-failed": "Impossible de se déconnecter maintenant: $1",
        "cannotlogoutnow-title": "Impossible de se déconnecter maintenant",
        "cannotlogoutnow-text": "La déconnexion n’est pas possible en utilisant $1.",
        "welcomeuser": "Bienvenue, $1 !",
        "badretype": "Les mots de passe que vous avez saisis ne correspondent pas.",
        "usernameinprogress": "Une création de compte pour ce nom d’utilisateur est déjà en cours.\nVeuillez patienter.",
        "userexists": "Le nom d’utilisateur saisi est déjà utilisé.\nVeuillez choisir un nom différent.",
-       "createacct-normalization": "Votre nom d’utilisateur sera modifié en « $2 » à cause de restrictions techniques.",
+       "createacct-normalization": "Votre nom d’utilisateur sera modifié en « $2 » à cause de restrictions techniques.",
        "loginerror": "Erreur de connexion",
        "createacct-error": "Erreur lors de la création du compte",
        "createaccounterror": "Impossible de créer le compte : $1",
        "noname": "Vous n’avez pas saisi un nom d’utilisateur valide.",
        "loginsuccesstitle": "Connecté",
        "loginsuccess": "<strong>Vous êtes maintenant connecté{{GENDER:$1||e|(e)}} à {{SITENAME}} en tant que « $1 ».</strong>",
-       "nosuchuser": "L’utilisateur « $1 » n’existe pas.\nLes noms d’utilisateur sont sensibles à la casse.\nVérifiez l’orthographe, ou [[Special:CreateAccount|créez un nouveau compte]].",
+       "nosuchuser": "L’utilisateur « $1 » n’existe pas.\nLes noms d’utilisateur sont sensibles à la casse.\nVérifiez l’orthographe, ou [[Special:CreateAccount|créez un nouveau compte]].",
        "nosuchusershort": "Il n’y a pas de contributeur avec le nom « $1 ».\nVeuillez vérifier l’orthographe.",
        "nouserspecified": "Vous devez saisir un nom d’utilisateur.",
        "login-userblocked": "{{GENDER:$1|Cet utilisateur|Cette utilisatrice}} est bloqué{{GENDER:$1||e}}. La connexion n’est pas autorisée.",
        "password-login-forbidden": "L’utilisation de ce nom d’utilisateur ou de ce mot de passe a été interdite.",
        "mailmypassword": "Réinitialiser le mot de passe",
        "passwordremindertitle": "Nouveau mot de passe temporaire pour {{SITENAME}}",
-       "passwordremindertext": "Quelqu’un (depuis l’adresse IP $1) a demandé un nouveau mot de\npasse pour {{SITENAME}} ($4). Un mot de passe temporaire pour l’utilisateur\n« $2 » a été créé et est « $3 ». Si cela était votre intention,\nvous devrez vous connecter et choisir un nouveau mot de passe.\nVotre mot de passe temporaire expirera dans $5 jour{{PLURAL:$5||s}}.\n\nSi vous n’êtes pas l’auteur de cette demande, ou si vous vous avez retrouvé votre mot de passe et ne souhaitez plus en changer, vous pouvez ignorer ce message\net continuer à utiliser votre ancien mot de passe.",
+       "passwordremindertext": "Quelqu’un (depuis l’adresse IP $1) a demandé un nouveau mot de\npasse pour {{SITENAME}} ($4). Un mot de passe temporaire pour l’utilisateur\n« $2 » a été créé et défini comme « $3 ». Si c’était bien votre intention,\nvous devrez vous connecter et choisir un nouveau mot de passe.\nVotre mot de passe temporaire expirera dans $5 jour{{PLURAL:$5||s}}.\n\nSi vous n’êtes pas l’auteur de cette demande, ou si vous vous avez retrouvé votre mot de passe et ne souhaitez plus en changer, vous pouvez ignorer ce message\net continuer à utiliser votre ancien mot de passe.",
        "noemail": "Aucune adresse de courriel n’a été enregistrée pour l’utilisat{{GENDER:$1|eur|rice}} « $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’utilisat{{GENDER:$1|eur|rice}} « $1 ».\nVeuillez vous reconnecter après l’avoir reçu.",
        "botpasswords-label-grants": "Droits applicables :",
        "botpasswords-help-grants": "Les autorisations permettent d’accéder aux droits déjà accordés à votre compte utilisateur. Activer une autorisation ici ne fournit l’accès à aucun droit que votre compte utilisateur n’aurait pas par ailleurs. Voyez le [[Special:ListGrants|tableau des autorisations]] pour plus d’informations.",
        "botpasswords-label-grants-column": "Accordé",
-       "botpasswords-bad-appid": "Le nom de robot « $1 » n’est pas valide.",
-       "botpasswords-insert-failed": "Échec de l’ajout du nom de robot « $1 ». A-t-il déjà été ajouté ?",
-       "botpasswords-update-failed": "Échec à la mise à jour du nom de robot « $1 ». A-t-il déjà été supprimé ?",
+       "botpasswords-bad-appid": "Le nom de robot « $1 » n’est pas valide.",
+       "botpasswords-insert-failed": "Échec de l’ajout du nom de robot « $1 ». A-t-il déjà été ajouté ?",
+       "botpasswords-update-failed": "Échec à la mise à jour du nom de robot « $1 ». A-t-il déjà été supprimé ?",
        "botpasswords-created-title": "Mot de passe de robots créé",
        "botpasswords-created-body": "Le mot de passe pour le robot « $1 » de l’{{GENDER:$2|utilisateur|utilisatrice}} « $2 » a été créé.",
        "botpasswords-updated-title": "Mot de passe de robots mis à jour",
        "botpasswords-newpassword": "Le nouveau mot de passe pour se connecter à <strong>$1</strong> est <strong>$2</strong>. <em>Veuillez l’enregistrer pour y faire référence ultérieurement.</em><br> (Pour les anciens robots qui nécessitent que le nom fourni à la connexion soit le même que le nom d'utilisateur éventuel, vous pouvez aussi utiliser  <strong>$3</strong> comme nom d'utilisateur et <strong>$4</strong> comme mot de passe).",
        "botpasswords-no-provider": "BotPasswordsSessionProvider n’est pas disponible.",
        "botpasswords-restriction-failed": "Les restrictions de mot de passe de robots empêchent cette connexion.",
-       "botpasswords-invalid-name": "Le nom d’utilisateur spécifié ne contient pas de séparateur de mot de passe de robots (« $1 »).",
-       "botpasswords-not-exist": "L’{{GENDER:$1|utilisateur|utilisatrice}} « $1 » n’a pas de mot de passe de robot nommé « $2 ».",
-       "botpasswords-needs-reset": "Le mot de passe du robot de nom « $2 » de l’utilisat{{GENDER:$1|eur|rice}} « $1 » doit être réinitialisé.",
+       "botpasswords-invalid-name": "Le nom d’utilisateur spécifié ne contient pas de séparateur de mot de passe de robots (« $1 »).",
+       "botpasswords-not-exist": "L’utilisat{{GENDER:$1|eur|rice}} « $1 » n’a pas de mot de passe de robot nommé « $2 ».",
+       "botpasswords-needs-reset": "Le mot de passe du robot nommé « $2 » de l’utilisat{{GENDER:$1|eur|rice}} « $1 » doit être réinitialisé.",
        "botpasswords-locked": "Vous ne pouvez pas vous connecter avec un mot de passe de robot, car votre compte est bloqué.",
        "resetpass_forbidden": "Les mots de passe ne peuvent pas être changés",
        "resetpass_forbidden-reason": "Les mots de passe ne peuvent pas être modifiés : $1",
        "resetpass-temp-password": "Mot de passe temporaire :",
        "resetpass-abort-generic": "La modification du mot de passe a été annulée par une extension.",
        "resetpass-expired": "Votre mot de passe a expiré. Veuillez en fournir un nouveau pour vous connecter.",
-       "resetpass-expired-soft": "Votre mot de passe a expiré, et doit être modifié. Veuillez en choisir un nouveau maintenant ou cliquer sur « {{int:authprovider-resetpass-skip-label}} » pour le faire plus tard.",
-       "resetpass-validity": "Votre mot de passe est non valide : $1\n\nVeuillez entrer un nouveau mot de passe pour vous connecter.",
-       "resetpass-validity-soft": "Votre mot de passe n’est pas valide : $1\n\nVeuillez choisir un nouveau mot de passe maintenant, ou cliquez sur « {{int:authprovider-resetpass-skip-label}} » pour le modifier plus tard.",
+       "resetpass-expired-soft": "Votre mot de passe a expiré, et doit être modifié. Veuillez en choisir un nouveau maintenant ou cliquer sur « {{int:authprovider-resetpass-skip-label}} » pour le faire plus tard.",
+       "resetpass-validity": "Votre mot de passe est non valide: $1\n\nVeuillez entrer un nouveau mot de passe pour vous connecter.",
+       "resetpass-validity-soft": "Votre mot de passe n’est pas valide : $1\n\nVeuillez choisir un nouveau mot de passe maintenant, ou cliquez sur « {{int:authprovider-resetpass-skip-label}} » pour le modifier plus tard.",
        "passwordreset": "Réinitialisation du mot de passe",
        "passwordreset-text-one": "Remplissez ce formulaire pour réinitialiser votre mot de passe.",
        "passwordreset-text-many": "{{PLURAL:$1|Remplissez un des champs pour recevoir un mot de passe temporaire par courriel.}}",
        "preview": "Prévisualisation",
        "showpreview": "Prévisualiser",
        "showdiff": "Voir les modifications",
-       "blankarticle": "<strong>Attention :</strong> la page que vous créez est vide.\nSi vous cliquez de nouveau sur « $1 », la page sera créée sans aucun contenu.",
+       "blankarticle": "<strong>Attention :</strong> la page que vous créez est vide.\nSi vous cliquez de nouveau sur « $1 », la page sera créée sans aucun contenu.",
        "anoneditwarning": "<strong>Attention :</strong> vous n’êtes pas connecté(e). Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre propre nom d’utilisateur(rice) et vous aurez d’autres avantages.",
        "anonpreviewwarning": "<em>Vous n’êtes pas connecté(e). Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.</em>",
        "missingsummary": "<strong>Rappel :</strong> vous n’avez pas encore fourni le résumé de votre modification.\nSi vous cliquez de nouveau sur le bouton « $1 », vos modifications seront sauvegardées sans résumé.",
-       "selfredirect": "<strong>Attention :</strong> vous êtes en train de rediriger la page vers elle-même.\nIl se peut que vous ayez spécifié la mauvaise cible pour la redirection, ou que vous modifiez peut-être la mauvaise page.\nSi vous cliquez de nouveau sur « $1 », la redirection sera tout de même créée.",
+       "selfredirect": "<strong>Attention :</strong> vous êtes en train de rediriger la page vers elle-même.\nIl se peut que vous ayez spécifié la mauvaise cible pour la redirection, ou que vous modifiez peut-être la mauvaise page.\nSi vous cliquez de nouveau sur « $1 », la redirection sera tout de même créée.",
        "missingcommenttext": "Veuillez entrer un commentaire.",
-       "missingcommentheader": "<strong>Rappel :</strong> vous n’avez pas fourni de sujet pour ce commentaire.\nSi vous cliquez de nouveau sur « {{int:Savearticle}} », votre modification sera enregistrée sans sujet.",
+       "missingcommentheader": "<strong>Rappel :</strong> vous n’avez pas fourni de sujet pour ce commentaire.\nSi vous cliquez de nouveau sur « $1 », votre modification sera enregistrée sans sujet.",
        "summary-preview": "Aperçu du résumé de modification :",
        "subject-preview": "Aperçu du sujet :",
        "previewerrortext": "Une erreur s’est produite lors de la tentative de prévisualisation de vos modifications.",
        "blockedtitle": "L’utilisateur est bloqué.",
-       "blocked-email-user": "<strong>Votre nom d’utilisateur a été bloqué pour l’envoi de courriels. Vous pouvez toujours modifier d’autres pages sur ce wiki.</strong> Vous pouvez voir tous les détails du blocage sur [[Special:MyContributions|contributions du compte]].\n\nCe blocage a été fait par $1.\n\nLe motif fourni est <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Cible souhaitée du blocage : $7\n* ID du blocage #$5",
-       "blockedtext-partial": "<strong>Votre nom d’utilisateur ou votre adresse IP a été bloqué pour effectuer des modifications sur cette page. Vous pouvez toujours modifier d’autres pages sur ce wiki.</strong> Vous pouvez voir tous les détails sur ce blocage sur [[Special:MyContributions|contributions du compte]].\n\nLe blocage a été effectué par $1.\n\nLe motif fourni est <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Cible souhaitée du blocage : $7\n* ID du blocage #$5",
-       "blockedtext": "<strong>Votre compte utilisateur ou votre adresse IP a été bloqué.</strong>\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité ne vous a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
-       "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n: <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité « {{int:emailuser}} » que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité ne vous a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
+       "blocked-email-user": "<strong>Votre nom d’utilisateur a été bloqué pour l’envoi de courriels. Vous pouvez toujours modifier d’autres pages sur ce wiki.</strong> Vous pouvez voir tous les détails du blocage sur [[Special:MyContributions|contributions du compte]].\n\nCe blocage a été fait par $1.\n\nLe motif fourni est <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Cible souhaitée du blocage : $7\n* ID du blocage nº $5",
+       "blockedtext-partial": "<strong>Votre nom d’utilisateur ou votre adresse IP a été bloqué pour effectuer des modifications sur cette page. Vous pouvez toujours modifier d’autres pages sur ce wiki.</strong> Vous pouvez voir tous les détails sur ce blocage sur [[Special:MyContributions|contributions du compte]].\n\nLe blocage a été effectué par $1.\n\nLe motif fourni est <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Cible souhaitée du blocage : $7\n* ID du blocage nº $5",
+       "blockedtext": "<strong>Votre compte utilisateur ou votre adresse IP a été bloqué.</strong>\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité ne vous a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
+       "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n: <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité « {{int:emailuser}} » que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité ne vous a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
        "systemblockedtext": "Votre nom d'utilisateur ou votre adresse IP ont été bloqués automatiquement par MediaWiki.\nLa raison donnée est la suivante:\n\n: <em>$2</em>.\n\n* Le début du blocage: $8\n* Expiration du délai de blocage: $6\n* Elément concerné: $7\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
        "blockednoreason": "aucune raison donnée",
-       "blockedtext-composite": "<strong>Votre nom d'utilisateur ou votre adresse IP ont été bloqués.</strong>\n\nLa raison invoquées est :\n\n:<em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage le plus long : $6\n\n* $5\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chaque demande que vous ferez.",
-       "blockedtext-composite-ids": "ID de bloc pertinents : $1 (votre adresse IP peut aussi être en liste noire)",
+       "blockedtext-composite": "<strong>Votre nom d'utilisateur ou votre adresse IP ont été bloqués.</strong>\n\nLa raison invoquée est :\n\n: <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage le plus long : $6\n* $5\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chaque demande que vous ferez.",
+       "blockedtext-composite-ids": "ID de bloc pertinents: $1 (votre adresse IP peut aussi être en liste noire)",
        "blockedtext-composite-no-ids": "Votre adresse IP apparaît dans plusieurs listes noires",
        "blockedtext-composite-reason": "Il existe plusieurs blocages sur votre compte et/ou votre adresse IP",
        "whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.",
        "newarticletext": "Vous avez suivi un lien vers une page qui n’existe pas encore. \nAfin de créer cette page, entrez votre texte dans la boîte ci-après (vous pouvez consulter [$1 la page d’aide] pour plus d’informations). \nSi vous êtes arrivé{{GENDER:||e}} ici par erreur, cliquez sur le bouton <strong>Retour</strong> de votre navigateur.",
        "anontalkpagetext": "----\n<em>Vous êtes sur la page de discussion d’un utilisateur anonyme qui n’a pas encore créé de compte ou qui n’en utilise pas</em>.\nPour cette raison, nous devons utiliser son adresse IP pour l'identifier.\nUne telle adresse IP peut être partagée par plusieurs utilisateurs.\nSi vous êtes un{{GENDER:||e|}} utilisat{{GENDER:|eur|rice|eur}} anonyme et si vous constatez que des commentaires qui ne vous concernent pas vous ont été adressés, vous pouvez [[Special:CreateAccount|créer un compte]] ou [[Special:UserLogin|vous connecter]] afin d’éviter toute confusion future avec d’autres contributeurs anonymes.",
        "noarticletext": "Il n’y a pour l’instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|lancer une recherche sur ce titre]] dans les autres pages,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les opérations liées]\nou [{{fullurl:{{FULLPAGENAME}}|action=edit}} créer cette page]</span>.",
-       "noarticletext-nopermission": "Il n'y a pour l'instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>, mais vous n'avez pas la permission de créer cette page.",
-       "missing-revision": "La révision nº $1 de la page intitulée « {{FULLPAGENAME}} » n’existe pas.\n\nCela survient en général en suivant un lien historique désuet vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
+       "noarticletext-nopermission": "Il n’y a pour l’instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>, mais vous n’avez pas la permission de créer cette page.",
+       "missing-revision": "La révision nº $1 de la page intitulée « {{FULLPAGENAME}} » n’existe pas.\n\nCela survient en général en suivant un lien historique désuet vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "userpage-userdoesnotexist": "Le compte utilisateur « <nowiki>$1</nowiki> » n’est pas enregistré. Veuillez vérifier que vous voulez créer cette page.",
        "userpage-userdoesnotexist-view": "Le compte utilisateur « $1 » n'est pas enregistré.",
-       "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
+       "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence:",
        "clearyourcache": "<strong>Note :</strong> après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.\n* <strong>Firefox / Safari :</strong> maintenez la touche <em>Maj</em> (<em>Shift</em>) en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> sur un Mac) \n* <strong>Google Chrome :</strong> appuyez sur <em>Ctrl-Maj-R</em> (<em>⌘-Shift-R</em> sur un Mac) \n* <strong>Internet Explorer :</strong> maintenez la touche <em>Ctrl</em> en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> \n* <strong>Opera :</strong> allez dans <em>Menu → Settings</em> (<em>Opera → Préférences</em> sur un Mac) et ensuite à <em>Confidentialité & sécurité → Effacer les données d’exploration → Images et fichiers en cache</em>.",
        "usercssyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille CSS avant de l’enregistrer.",
-       "userjsonyoucanpreview": "<strong>Conseil :</strong> Utiliser le bouton « {{int:showpreview}} » pour tester votre nouveau JSON avant enregistrement.",
+       "userjsonyoucanpreview": "<strong>Conseil :</strong> Utiliser le bouton « {{int:showpreview}} » pour tester votre nouveau JSON avant enregistrement.",
        "userjsyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille JavaScript avant de l’enregistrer.",
        "usercsspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser votre propre feuille CSS. \nElle n’a pas encore été enregistrée !</strong>",
        "userjsonpreview": "<strong>Rappelez-vous que vous êtes seulement en train de tester/voir un aperçu de votre configuration utilisateur JSON.\nElle n’a pas encore été enregistrée !</strong>",
        "sitecsspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser cette feuille de style. \nElle n’a pas encore été enregistrée !</strong>",
        "sitejsonpreview": "<strong>Souvenez-vous que vous ne faites que regarder un aperçu de cette configuration JSON.\nElle n’a pas encore été enregistrée !</strong>",
        "sitejspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser ce code JavaScript. \nIl n’a pas encore été enregistré !</strong>",
-       "userinvalidconfigtitle": "<strong>Attention :</strong> il n’existe pas d’habillage « $1 ». \nLes pages personnelles avec extensions .css, .json et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Attention :</strong> il n’existe pas d’habillage « $1 ». \nLes pages personnelles avec extensions .css, .json et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.",
        "updated": "(Mis à jour)",
        "note": "<strong>Note :</strong>",
        "previewnote": "<strong>Rappelez-vous que ce n’est qu’une prévisualisation.</strong>\nVos modifications n’ont pas encore été enregistrées !",
        "defaultmessagetext": "Message par défaut",
        "content-failed-to-parse": "Échec de l’analyse syntaxique du contenu de $2 pour le modèle $1 : $3",
        "invalid-content-data": "Données du contenu non valides",
-       "content-not-allowed-here": "Le contenu « $1 » n’est pas autorisé sur la page [[:$2]] dans l’emplacement « $3 »",
-       "editwarning-warning": "Quitter cette page vous fera perdre toutes les modifications que vous avez faites.\nSi vous êtes connecté{{GENDER:||e}}, vous pouvez désactiver cet avertissement dans la section « {{int:prefs-editing}} » de vos préférences.",
+       "content-not-allowed-here": "Le contenu « $1 » n’est pas autorisé sur la page [[:$2]] dans l’emplacement « $3 »",
+       "editwarning-warning": "Quitter cette page vous fera perdre toutes les modifications que vous avez faites.\nSi vous êtes connecté{{GENDER:||e}}, vous pouvez désactiver cet avertissement dans la section « {{int:prefs-editing}} » de vos préférences.",
        "editpage-invalidcontentmodel-title": "Modèle de contenu non pris en charge",
        "editpage-invalidcontentmodel-text": "Le modèle de contenu \"$1\" n'est pas pris en charge.",
        "editpage-notsupportedcontentformat-title": "Format de contenu non pris en charge",
        "undo-norev": "La modification n’a pas pu être défaite parce qu’elle est inexistante ou qu’elle a été supprimée.",
        "undo-nochange": "Il semblerait que la modification ait déjà été annulée.",
        "undo-summary": "Annulation des modifications $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|discussion]])",
+       "undo-summary-anon": "Annuler la modification $1 de [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "Annuler la révision $1 par un utilisateur masqué",
        "cantcreateaccount-text": "La création de compte depuis cette adresse IP (<strong>$1</strong>) a été bloquée par [[User:$3|$3]]. \n\nLa raison donnée par $3 était : <em>$2</em>",
        "cantcreateaccount-range-text": "La création de compte depuis les adresses IP de la plage <strong>$1</strong>, où se trouve votre adresse IP (<strong>$4</strong>), a été bloquée par [[User:$3|$3]].\n\nLe motif fourni par $3 est <em>$2</em>",
        "diff-paragraph-moved-toold": "Paragraphe déplacé. Cliquer pour accéder à l’ancien emplacement.",
        "difference-missing-revision": "{{PLURAL:$2|Une révision|$2 révisions}} de cette différence ($1) {{PLURAL:$2|n’a pas été trouvée|n’ont pas été trouvées}}.\n\nCela survient en général en suivant un lien de différence désuet vers une page qui a été supprimée.\nVous pouvez trouver des détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "searchresults": "Résultats de la recherche",
-       "search-filter-title-prefix": "Recherche seulement les pages dont le titre commence par « $1 »",
+       "search-filter-title-prefix": "Recherche seulement les pages dont le titre commence par « $1 »",
        "search-filter-title-prefix-reset": "Rechercher toutes les pages",
        "searchresults-title": "Résultats de recherche pour « $1 »",
        "titlematches": "Correspondances dans les titres des pages",
        "shown-title": "Afficher $1 résultat{{PLURAL:$1||s}} par page",
        "viewprevnext": "Voir ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "<strong>Il existe une page nommée « [[:$1]] » sur ce wiki.</strong> {{PLURAL:$2|0=|Voyez également les autres résultats de votre recherche.}}",
-       "searchmenu-new": "<strong>Créer la page « [[:$1|$1]] » sur ce wiki !</strong> {{PLURAL:$2|0=Voyez également la page trouvée avec votre recherche.|Voyez également les résultats de votre recherche.}}",
+       "searchmenu-new": "<strong>Créer la page « [[:$1|$1]] » sur ce wiki !</strong> {{PLURAL:$2|0=Voyez également la page trouvée avec votre recherche.|Voyez également les résultats de votre recherche.}}",
        "searchprofile-articles": "Pages de contenu",
        "searchprofile-images": "Multimédia",
        "searchprofile-everything": "Tout",
        "prefs-tokenwatchlist": "Jeton",
        "prefs-diffs": "Différences",
        "prefs-help-prefershttps": "Cette préférence sera effective lors de votre prochaine connexion.",
-       "prefswarning-warning": "Vous avez effectué des modifications dans vos préférences qui n’ont pas encore été enregistrées.\nSi vous quittez cette page sans cliquer sur « $1 », vos préférences ne seront pas mises à jour.",
+       "prefswarning-warning": "Vous avez effectué des modifications dans vos préférences qui n’ont pas encore été enregistrées.\nSi vous quittez cette page sans cliquer sur « $1 », vos préférences ne seront pas mises à jour.",
        "prefs-tabs-navigation-hint": "Astuce : Vous pouvez utiliser les flèches de gauche et de droite pour naviguer entre les onglets.",
        "userrights": "Droits des utilisateurs",
        "userrights-lookup-user": "Sélectionner un utilisateur",
        "userrights-expiry-options": "1 jour:1 day,1 semaine:1 week,1 mois:1 month,3 mois:3 montghs,6 mois:6 month,1 an:1 year",
        "userrights-invalid-expiry": "La date d'expiration pour le groupe « $1 » n'est pas valide.",
        "userrights-expiry-in-past": "La date d'expiration pour le groupe « $1 » est dépassée.",
-       "userrights-cannot-shorten-expiry": "Vous ne pouvez pas raccourcir la durée d’expiration de l’appartenance au groupe « $1 ». Seuls les utilisateurs disposant de l’autorisation d’ajouter et de supprimer ce groupe peuvent raccourcir les durées d’expiration.",
+       "userrights-cannot-shorten-expiry": "Vous ne pouvez pas raccourcir la durée d’expiration de l’appartenance au groupe « $1 ». Seuls les utilisateurs disposant de l’autorisation d’ajouter et de supprimer ce groupe peuvent raccourcir les durées d’expiration.",
        "userrights-conflict": "Conflit de modification des droits utilisateur ! Veuillez relire et confirmer vos modifications.",
        "group": "Groupe :",
        "group-user": "Utilisateurs",
        "right-applychangetags": "Appliquer [[Special:Tags|les balises]] avec ses propres modifications",
        "right-changetags": "Ajouter et supprimer de façon arbitraire [[Special:Tags|des balises]] sur des révisions individuelles et des entrées de journal",
        "right-deletechangetags": "Supprimer des [[Special:Tags|balises]] de la base de données",
-       "grant-generic": "ensemble de droits « $1 »",
+       "grant-generic": "ensemble de droits « $1 »",
        "grant-group-page-interaction": "Interagir avec des pages",
        "grant-group-file-interaction": "Interagir avec des médias",
        "grant-group-watchlist-interaction": "Interagir avec votre liste de suivi",
        "action-bigdelete": "supprimer des pages avec de grands historiques",
        "action-blockemail": "empêcher un utilisateur d’envoyer des courriels",
        "action-bot": "être traité comme un processus automatisé",
-       "action-editprotected": "modifier les pages protégées comme « {{int:protect-level-sysop}} »",
+       "action-editprotected": "modifier les pages protégées comme « {{int:protect-level-sysop}} »",
        "action-editsemiprotected": "modifier des pages protégées avec le statut « {{int:protect-level-autoconfirmed}} »",
        "action-editinterface": "modifier l’interface utilisateur",
        "action-editusercss": "modifier les fichiers CSS d’autres utilisateurs",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nouveaux arrivants",
        "rcfilters-filter-user-experience-level-newcomer-description": "Éditeurs connectés ayant fait moins de 10 modifications ou ayant moins de 4 jours d’activité.",
        "rcfilters-filter-user-experience-level-learner-label": "Apprentis",
-       "rcfilters-filter-user-experience-level-learner-description": "Éditeurs connectés dont l’expérience se situe entre  « Nouveaux arrivants » et  « Utilisateurs expérimentés ».",
+       "rcfilters-filter-user-experience-level-learner-description": "Éditeurs connectés dont l’expérience se situe entre  « Nouveaux arrivants » et  « Utilisateurs expérimentés ».",
        "rcfilters-filter-user-experience-level-experienced-label": "Utilisateurs expérimentés",
        "rcfilters-filter-user-experience-level-experienced-description": "Éditeurs connectés avec plus de 500 modifications et 30 jours d’activité.",
        "rcfilters-filtergroup-automated": "Contributions automatisées",
        "rcfilters-filter-categorization-description": "Enregistrements de pages ajoutées ou supprimées des catégories.",
        "rcfilters-filter-logactions-label": "Actions tracées",
        "rcfilters-filter-logactions-description": "Actions d’administration, créations de compte, suppression de pages, téléchargements…",
-       "rcfilters-hideminor-conflicts-typeofchange-global": "Le filtre « Modifications mineures » est en conflit avec au moins un filtre de Type de modification, parce que certains types de modification ne peuvent être marqués comme « mineurs ». Les filtres en conflit sont marqués dans la zone Filtres actifs ci-dessus.",
-       "rcfilters-hideminor-conflicts-typeofchange": "Certains types de modification ne peuvent pas être qualifiés de « mineurs », donc ce filtre est en conflit avec les filtres de Type de modification suivants : $1",
-       "rcfilters-typeofchange-conflicts-hideminor": "Ce filtre de Type de modification est en conflit avec le filtre « Modifications mineures ». Certains type sde modification ne peuvent pas être indiqués comme « mineurs ».",
+       "rcfilters-hideminor-conflicts-typeofchange-global": "Le filtre « Modifications mineures » est en conflit avec au moins un filtre de Type de modification, parce que certains types de modification ne peuvent être marqués comme « mineurs ». Les filtres en conflit sont marqués dans la zone Filtres actifs ci-dessus.",
+       "rcfilters-hideminor-conflicts-typeofchange": "Certains types de modification ne peuvent pas être qualifiés de « mineurs », donc ce filtre est en conflit avec les filtres de Type de modification suivants : $1",
+       "rcfilters-typeofchange-conflicts-hideminor": "Ce filtre de Type de modification est en conflit avec le filtre « Modifications mineures ». Certains type sde modification ne peuvent pas être indiqués comme « mineurs ».",
        "rcfilters-filtergroup-lastrevision": "Dernières révisions",
        "rcfilters-filter-lastrevision-label": "Dernière révision",
        "rcfilters-filter-lastrevision-description": "Uniquement la dernière modification apportée à une page.",
        "rcfilters-filter-previousrevision-label": "Pas la dernière version",
-       "rcfilters-filter-previousrevision-description": "Toutes les modifications apportées à une page et qui ne concernent pas la « dernière version ».",
+       "rcfilters-filter-previousrevision-description": "Toutes les modifications apportées à une page et qui ne concernent pas la « dernière version ».",
        "rcfilters-filter-excluded": "Exclu",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
        "rcfilters-exclude-button-off": "Exclure les sélectionnés",
        "uploadscripted": "Ce fichier contient du code HTML ou un script qui pourrait être interprété de façon incorrecte par un navigateur web.",
        "upload-scripted-pi-callback": "Impossible de charger un fichier qui contient des instructions de traitement de feuille de style XML.",
        "upload-scripted-dtd": "Impossible de téléverser des fichiers SVG qui contiennent une déclaration de DTD non standard.",
-       "uploaded-script-svg": "Élément scriptable « $1 » trouvé dans le fichier SVG téléversé.",
+       "uploaded-script-svg": "Élément scriptable « $1 » trouvé dans le fichier SVG téléversé.",
        "uploaded-hostile-svg": "CSS non sûr trouvé dans l’élément style d’un fichier SVG téléversé.",
        "uploaded-event-handler-on-svg": "Fixer des attributs de gestionnaire d’événement <code>$1=\"$2\"</code> n’est pas autorisé dans les fichiers SVG.",
        "uploaded-href-attribute-svg": "<a> les éléments ne peuvent être liés (href) qu’aux cibles de données : (fichier inclus), http:// ou https://, ou un fragment (#, même document). Pour les autres éléments, comme <image>, seuls data: et fragment sont autorisés. Essayez les images incluses lors de l’export de votre SVG. <code>&lt;$1 $2=\"$3\"&gt;</code> obtenu.",
        "uploaded-href-unsafe-target-svg": "Un href vers des données non sûres a été trouvé dans le fichier SVG téléversé : URI cible <code>&lt;$1 $2=\"$3\"&gt;</code>.",
-       "uploaded-animate-svg": "Balise « animate » trouvée, qui pourrait modifier le href en utilisant l’attribut « from » <code>&lt;$1 $2=\"$3\"&gt;</code> dans le fichier SVG téléversé.",
+       "uploaded-animate-svg": "Balise « animate » trouvée, qui pourrait modifier le href en utilisant l’attribut « from » <code>&lt;$1 $2=\"$3\"&gt;</code> dans le fichier SVG téléversé.",
        "uploaded-setting-event-handler-svg": "Positionner les attributs du gestionnaire d’événements n'est pas possible, <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé dans le fichier SVG téléversé.",
-       "uploaded-setting-href-svg": "L’utilisation de la balise « set » pour ajouter un attribut « href » à l’élément parent est interdite.",
-       "uploaded-wrong-setting-svg": "L’utilisation de la balise « set » pour ajouter une cible distante, de données ou de type script,  à un attribut quelconque, est interdite. <code>&lt;set to=\"$1\"&gt;</code> a été trouvé dans le fichier SVG téléversé.",
-       "uploaded-setting-handler-svg": "Les SVG qui positionnent l’attribut « handler » avec distant/données/script sont bloqués. <code>$1=\"$2\"</code> a été trouvé dans le fichier SVG téléversé.",
+       "uploaded-setting-href-svg": "L’utilisation de la balise « set » pour ajouter un attribut « href » à l’élément parent est interdite.",
+       "uploaded-wrong-setting-svg": "L’utilisation de la balise « set » pour ajouter une cible distante, de données ou de type script,  à un attribut quelconque, est interdite. <code>&lt;set to=\"$1\"&gt;</code> a été trouvé dans le fichier SVG téléversé.",
+       "uploaded-setting-handler-svg": "Les SVG qui positionnent l’attribut « handler » avec distant/données/script sont bloqués. <code>$1=\"$2\"</code> a été trouvé dans le fichier SVG téléversé.",
        "uploaded-remote-url-svg": "Les SVG qui positionnent un attribut de style avec une URL distante sont bloqués. <code>$1=\"$2\"</code> trouvé dans le fichier SVG téléversé.",
        "uploaded-image-filter-svg": "Filtre d’image avec URL trouvé : <code>&lt;$1 $2=\"$3\"&gt;</code> dans le fichier SVG téléversé.",
        "uploadscriptednamespace": "Ce fichier SVG contient un espace de noms '<nowiki>$1</nowiki>' non autorisé.",
        "backend-fail-read": "Impossible de lire le fichier \"$1\".",
        "backend-fail-create": "Impossible d’écrire le fichier « $1 ».",
        "backend-fail-maxsize": "Impossible d’écrire le fichier « $1 » parce qu’il est plus grand {{PLURAL:$2|qu’un octet|que $2 octets}}.",
-       "backend-fail-readonly": "Le support de stockage « $1 » est actuellement en lecture seule. La raison indiquée est : <em>$2</em>",
+       "backend-fail-readonly": "Le support de stockage « $1 » est actuellement en lecture seule. La raison indiquée est : <em>$2</em>",
        "backend-fail-synced": "Le fichier « $1 » est dans un état incohérent dans les supports de stockage internes",
        "backend-fail-connect": "Impossible de se connecter au média de stockage « $1 ».",
        "backend-fail-internal": "Une erreur inconnue s’est produite dans le média de stockage « $1 ».",
        "backend-fail-contenttype": "Impossible de déterminer le type de contenu du fichier à stocker en « $1 ».",
        "backend-fail-batchsize": "On a fourni au support de stockage un lot de $1 {{PLURAL:$1|opération|opérations}} de fichier; la limite est $2 {{PLURAL:$2|opération|opérations}}.",
        "backend-fail-usable": "Impossible de lire ou d’écrire le fichier « $1 » en raison de droits insuffisants ou de répertoires/conteneurs manquants.",
-       "backend-fail-stat": "Impossible de lire l’état du fichier « $1 ».",
-       "backend-fail-hash": "Impossible de déterminer le hachage cryptographique du fichier « $1 ».",
+       "backend-fail-stat": "Impossible de lire l’état du fichier « $1 ».",
+       "backend-fail-hash": "Impossible de déterminer le hachage cryptographique du fichier « $1 ».",
        "filejournal-fail-dbconnect": "Impossible de se connecter à la base de données du journal pour le terminal de stockage « $1 ».",
        "filejournal-fail-dbquery": "Impossible de mettre à jour la base de données du journal pour le terminal de stockage « $1 ».",
        "lockmanager-notlocked": "Impossible de déverrouiller « $1 » ; elle n'est pas verrouillée.",
        "lockmanager-fail-closelock": "Impossible de fermer le fichier de verrou pour « $1 ».",
        "lockmanager-fail-deletelock": "Impossible de supprimer le fichier de verrou pour « $1 ».",
        "lockmanager-fail-acquirelock": "Impossible d'obtenir le verrou pour « $1 ».",
-       "lockmanager-fail-openlock": "Impossible d’ouvrir le fichier de verrou pour « $1 » . Assurez-vous que votre répertoire de téléchargement est configuré correctement et que votre serveur web a le droit d’y écrire. Voyez https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory pour plus d’information.",
+       "lockmanager-fail-openlock": "Impossible d’ouvrir le fichier de verrou pour « $1 » . Assurez-vous que votre répertoire de téléchargement est configuré correctement et que votre serveur web a le droit d’y écrire. Voyez https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory pour plus d’information.",
        "lockmanager-fail-releaselock": "Impossible de relâcher le verrou pour « $1 ».",
        "lockmanager-fail-db-bucket": "Impossible de contacter suffisamment de bases de données de verrouillage dans le godet $1.",
        "lockmanager-fail-db-release": "Impossible de relâcher les verrous sur la base de données $1.",
        "uploadstash-errclear": "La suppression des fichiers a échoué.",
        "uploadstash-refresh": "Actualiser la liste des fichiers",
        "uploadstash-thumbnail": "afficher la vignette",
-       "uploadstash-exception": "Impossible de stocker le téléversement dans la réserve ($1) : « $2 ».",
+       "uploadstash-exception": "Impossible de stocker le téléversement dans la réserve ($1) : « $2 ».",
        "uploadstash-bad-path": "Le chemin n’existe pas.",
        "uploadstash-bad-path-invalid": "Le chemin n’est pas valide.",
-       "uploadstash-bad-path-unknown-type": "Type « $1 » inconnu.",
+       "uploadstash-bad-path-unknown-type": "Type « $1 » inconnu.",
        "uploadstash-bad-path-unrecognized-thumb-name": "Nom de vignette non reconnu.",
        "uploadstash-bad-path-no-handler": "Aucun gestionnaire trouvé pour le type MIME $1 du fichier $2.",
-       "uploadstash-bad-path-bad-format": "La clé « $1 » n’est pas dans un format correct.",
-       "uploadstash-file-not-found": "La clé « $1 » n’a pas été trouvée dans la réserve.",
+       "uploadstash-bad-path-bad-format": "La clé « $1 » n’est pas dans un format correct.",
+       "uploadstash-file-not-found": "La clé « $1 » n’a pas été trouvée dans la réserve.",
        "uploadstash-file-not-found-no-thumb": "Impossible d’obtenir une vignette.",
        "uploadstash-file-not-found-no-local-path": "Aucun chemin local pour l’élément mis à l’échelle.",
        "uploadstash-file-not-found-no-object": "Impossible de créer l’objet fichier local pour la vignette.",
        "listfiles-delete": "supprimer",
        "listfiles-summary": "Cette page spéciale permet de lister tous les fichiers importés.",
        "listfiles_search_for": "Rechercher un nom de média :",
-       "listfiles-userdoesnotexist": "Le compte utilisateur « $1 » n’est pas enregistré.",
+       "listfiles-userdoesnotexist": "Le compte utilisateur « $1 » n’est pas enregistré.",
        "imgfile": "fichier",
        "listfiles": "Liste de fichiers",
        "listfiles_thumb": "Miniature",
        "randompage": "Page au hasard",
        "randompage-nopages": "Il n'y a aucune page dans {{PLURAL:$2|l'espace de noms|les espaces de noms}} : $1.",
        "randomincategory": "Page au hasard dans la catégorie",
-       "randomincategory-invalidcategory": "« $1 » n’est pas un nom de catégorie valide.",
+       "randomincategory-invalidcategory": "« $1 » n’est pas un nom de catégorie valide.",
        "randomincategory-nopages": "Il n’y a pas de pages dans la catégorie [[:Category:$1|$1]].",
        "randomincategory-category": "Catégorie :",
        "randomincategory-legend": "Page aléatoire dans la catégorie",
        "ancientpages": "Pages les plus anciennement modifiées",
        "move": "Renommer",
        "movethispage": "Renommer cette page",
-       "unusedimagestext": "Les fichiers suivants existent, mais ne sont inclus dans aucune page.\nVeuillez noter que d’autres sites peuvent accéder à ces fichiers à l’aide de liens directs (URLs), et donc qu’un fichier peut être listé ici alors qu’il est utilisé par ces sites.",
+       "unusedimagestext": "Les fichiers suivants existent, mais ne sont inclus dans aucune page.\nVeuillez noter que d’autres sites peuvent accéder à ces fichiers à l’aide de liens directs (URL), et donc qu’un fichier peut être listé ici alors qu’il est utilisé par ces sites.",
        "unusedimagestext-categorizedimgisused": "Les fichiers suivants existent mais ne sont inclus dans aucune page. Les images catégorisées sont considérées comme utilisées malgré qu'elles ne soient pas incluses dans aucune page.\nVeuillez noter que les autres sites web peuvent créer un lien vers un fichier à l'aide d'une URL directe, et donc peuvent encore être listés ici malgré qu'ils soient encore utilisés.",
        "unusedcategoriestext": "Les pages de catégories suivantes existent, mais ne sont utilisées par aucune autre page ni catégorie.",
        "notargettitle": "Pas de cible",
        "suppress": "Supprimer",
        "querypage-disabled": "Cette page spéciale est désactivée pour des raisons de performances.",
        "apihelp": "Aide de l’API",
-       "apihelp-no-such-module": "Le module « $1 » est introuvable.",
+       "apihelp-no-such-module": "Le module « $1 » est introuvable.",
        "apisandbox": "Bac à sable de l'API",
        "apisandbox-jsonly": "Le bac à sable de l'API nécessite JavaScript",
        "apisandbox-intro": "Utilisez cette page pour expérimenter l’<strong>API webservice de MediaWiki</strong>.\nReportez-vous à [[mw:API:Main page|la documentation de l’API]] pour plus de détails sur l’utilisation de l’API. Exemple: [https://www.mediawiki.org/wiki/API#A_simple_example obtenir le contenu d'une page principale]. Choisissez une option pour voir d'autres exemples.",
        "emailccsubject": "Copie de votre message à $1 : $2",
        "emailsent": "Courriel envoyé",
        "emailsenttext": "Votre message a été envoyé par courriel.",
-       "emailuserfooter": "Ce courriel a été {{GENDER:$1|envoyé}} par « $1 » à « {{GENDER:$2|$2}} » par la fonction « {{int:emailuser}} » de {{SITENAME}}. Si {{GENDER:$2|vous}} répondez à ce courriel, {{GENDER:$2|votre}} courriel sera envoyé directement à l’{{GENDER:$1|émetteur initial}}, en {{GENDER:$1|lui}} mentionnant {{GENDER:$2|votre}} adresse courriel .",
+       "emailuserfooter": "Ce courriel a été {{GENDER:$1|envoyé}} par « $1 » à « {{GENDER:$2|$2}} » par la fonction « {{int:emailuser}} » de {{SITENAME}}. Si {{GENDER:$2|vous}} répondez à ce courriel, {{GENDER:$2|votre}} courriel sera envoyé directement à l’{{GENDER:$1|émetteur initial}}, en {{GENDER:$1|lui}} mentionnant {{GENDER:$2|votre}} adresse courriel .",
        "usermessage-summary": "Laisser un message système.",
        "usermessage-editor": "Messager du système",
        "watchlist": "Liste de suivi",
        "watchnologin": "Non connecté",
        "addwatch": "Ajouter à la liste de suivi",
        "addedwatchtext": "La page « [[:$1]] » et sa page de discussion ont été ajoutées à votre [[Special:Watchlist|liste de suivi]].",
-       "addedwatchtext-talk": "« [[:$1]] » et sa page associée ont été ajoutés à votre [[Special:Watchlist|liste de suivi]].",
-       "addedwatchtext-short": "La page « $1 » a été ajoutée à votre liste de suivi.",
+       "addedwatchtext-talk": "« [[:$1]] » et sa page associée ont été ajoutés à votre [[Special:Watchlist|liste de suivi]].",
+       "addedwatchtext-short": "La page « $1 » a été ajoutée à votre liste de suivi.",
        "removewatch": "Supprimer de la liste de suivi",
-       "removedwatchtext": "La page « [[:$1]] » et sa page de discussion ont été retirées de votre [[Special:Watchlist|liste de suivi]].",
-       "removedwatchtext-talk": "« [[:$1]] » et sa page associée ont été supprimés de votre [[Special:Watchlist|liste de suivi]].",
-       "removedwatchtext-short": "La page « $1 » a été supprimée de votre liste de suivi.",
+       "removedwatchtext": "La page « [[:$1]] » et sa page de discussion ont été retirées de votre [[Special:Watchlist|liste de suivi]].",
+       "removedwatchtext-talk": "« [[:$1]] » et sa page associée ont été supprimés de votre [[Special:Watchlist|liste de suivi]].",
+       "removedwatchtext-short": "La page « $1 » a été supprimée de votre liste de suivi.",
        "watch": "Suivre",
        "watchthispage": "Suivre cette page",
        "unwatch": "Ne plus suivre",
        "alreadyrolled": "Impossible de révoquer la dernière modification de la page « [[:$1]] » effectuée par [[User:$2|$2]] ([[User talk:$2|Discuter]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ;\nquelqu'un d'autre a déjà modifié ou révoqué la page.\n\nLa dernière modification de la page a été effectuée par [[User:$3|$3]] ([[User talk:$3|Discuter]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Le résumé de la modification était : <em>$1</em>.",
        "revertpage": "Révocation des modifications de [[Special:Contributions/$2|$2]] ([[User talk:$2|discussion]]) vers la dernière version de [[User:$1|$1]]",
+       "revertpage-anon": "Modifications de [[Special:Contributions/$2|$2]] révoquées vers la dernière version de [[User:$1|$1]]",
        "revertpage-nouser": "Révocation des modifications par un utilisateur masqué à la dernière version par {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Révocation des modifications effectuées par {{GENDER:$3|$1}} ;\nrétablissement de la dernière version par {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Erreur de session",
        "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": "Cette page montre des modifications dans le modèle de contenu des pages, ainsi que les pages créées avec un modèle de contenu différent du contenu par défaut.",
-       "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",
-       "logentry-contentmodel-change": "$1 {{GENDER:$2|a modifié}} le modèle de contenu de la page $3 de « $4 » en « $5 »",
+       "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",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|a modifié}} le modèle de contenu de la page $3 de « $4 » en « $5 »",
        "logentry-contentmodel-change-revertlink": "rétablir",
        "logentry-contentmodel-change-revert": "rétablir",
        "protectlogpage": "Journal des protections",
        "unprotectedarticle": "a supprimé la protection de « [[$1]] »",
        "movedarticleprotection": "a déplacé les paramètres de protection depuis « [[$2]] » vers « [[$1]] »",
        "protectedarticle-comment": "{{GENDER:$2|A protégé}} « [[$1]] »",
-       "modifiedarticleprotection-comment": "{{GENDER:$2|A changé le niveau de protection}} pour « [[$1]] »",
-       "unprotectedarticle-comment": "{{GENDER:$2|A supprimé la protection}} de « [[$1]] »",
+       "modifiedarticleprotection-comment": "{{GENDER:$2|A changé le niveau de protection}} pour « [[$1]] »",
+       "unprotectedarticle-comment": "{{GENDER:$2|A supprimé la protection}} de « [[$1]] »",
        "protect-title": "Changer le niveau de protection pour « $1 »",
        "protect-title-notallowed": "Voir le niveau de protection de « $1 »",
        "prot_1movedto2": "[[$1]] renommé en [[$2]]",
        "anoncontribs": "Contributions",
        "contribsub2": "Pour {{GENDER:$3|$1}} ($2)",
        "contributions-subtitle": "Pour {{GENDER:$3|$1}}",
-       "contributions-userdoesnotexist": "Le compte d’utilisateur « $1 » n’est pas enregistré.",
+       "contributions-userdoesnotexist": "Le compte d’utilisateur « $1 » n’est pas enregistré.",
        "negative-namespace-not-supported": "Les espaces de noms avec des valeurs négatives ne sont pas supportés.",
        "nocontribs": "Aucune modification correspondant à ces critères n'a été trouvée.",
        "uctop": "actuelle",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] a été bloqué{{GENDER:$1||e}}.<br />\nConsultez la [[Special:BlockList|liste des blocages]] pour voir les utilisateurs bloqués.",
        "ipb-blockingself": "Vous êtes sur le point de bloquer votre propre compte ! Êtes-vous certain{{GENDER:||e}} de vouloir faire cela ?",
        "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprimera le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
-       "ipb-confirmaction": "Si vous êtes sûr{{GENDER:||e}} de vraiment vouloir le faire, veuillez cocher le champ « {{int:ipb-confirm}} » en bas.",
+       "ipb-confirmaction": "Si vous êtes sûr{{GENDER:||e}} de vraiment vouloir le faire, veuillez cocher le champ « {{int:ipb-confirm}} » en bas.",
        "ipb-edit-dropdown": "Modifier les motifs de blocage par défaut",
        "ipb-unblock-addr": "Débloquer $1",
        "ipb-unblock": "Débloquer un compte utilisateur ou une adresse IP",
        "empty-username": "(aucun nom d’utilisateur disponible)",
        "contribslink": "contributions",
        "emaillink": "envoyer un courriel",
-       "autoblocker": "Vous avez été bloqué automatiquement parce que votre adresse IP a été récemment utilisée par « [[User:$1|$1]] ».\nLe motif fourni pour le blocage de $1 est « $2 »",
+       "autoblocker": "Vous avez été bloqué automatiquement parce que votre adresse IP a été récemment utilisée par « [[User:$1|$1]] ».\nLe motif fourni pour le blocage de $1 est « $2 »",
        "blocklogpage": "Journal des blocages",
        "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. \nLe journal des blocages est affiché ci-dessous pour référence :",
        "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. \nLe journal des masquages est affiché ci-dessous pour référence :",
        "cant-move-category-page": "Vous n'avez pas la permission de renommer les pages de catégorie.",
        "cant-move-to-category-page": "Vous n'avez pas la permission de renommer une page vers une page de catégorie.",
        "cant-move-subpages": "Vous n’avez pas le droit de renommer des sous-pages.",
-       "namespace-nosubpages": "L’espace de noms « $1 » n’autorise pas les sous-pages.",
+       "namespace-nosubpages": "L’espace de noms « $1 » n’autorise pas les sous-pages.",
        "newtitle": "Nouveau titre :",
        "move-watch": "Suivre les pages originale et nouvelle",
        "movepagebtn": "Renommer la page",
        "movenosubpage": "Cette page n'a aucune sous-page.",
        "movereason": "Motif :",
        "revertmove": "rétablir",
-       "delete_and_move_text": "La page de destination « [[:$1]] » existe déjà.\nÊtes-vous certain{{GENDER:||e|}} de vouloir la supprimer pour permettre ce renommage ?",
+       "delete_and_move_text": "La page de destination « [[:$1]] » existe déjà.\nÊtes-vous certain{{GENDER:||e|}} de vouloir la supprimer pour permettre ce renommage ?",
        "delete_and_move_confirm": "Oui, supprimer la page de destination",
        "delete_and_move_reason": "Page supprimée pour permettre le renommage depuis « [[$1]] »",
        "selfmove": "Le titre est le même ;\nimpossible de renommer une page sur elle-même.",
        "import-upload": "Import de données XML",
        "import-token-mismatch": "Perte des données de session.\n\nVous avez peut-être été déconnecté. '''Veuillez vérifier que vous êtes toujours connecté et réessayez'''.\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et de vous reconnecter, et vérifiez que votre navigateur accepte les témoins (''cookies'') de ce site.",
        "import-invalid-interwiki": "Impossible d'importer depuis le wiki spécifié.",
-       "import-error-edit": "La page « $1 » n’a pas été importée parce que vous n’êtes pas autorisé à la modifier.",
-       "import-error-create": "La page « $1 » n’a pas été importée parce que vous n’êtes pas autorisé à la créer.",
-       "import-error-interwiki": "La page « $1 » n’a pas été importée parce que son nom est réservé pour un lien externe (interwiki).",
-       "import-error-special": "La page « $1 » n’a pas été importée parce qu’elle appartient à un espace de noms spécial qui n’autorise aucune page.",
-       "import-error-invalid": "Page « $1 » n’a pas été importée parce que le nom sous lequel elle aurait été importée n’est pas valide sur ce wiki.",
+       "import-error-edit": "La page « $1 » n’a pas été importée parce que vous n’êtes pas autorisé à la modifier.",
+       "import-error-create": "La page « $1 » n’a pas été importée parce que vous n’êtes pas autorisé à la créer.",
+       "import-error-interwiki": "La page « $1 » n’a pas été importée parce que son nom est réservé pour un lien externe (interwiki).",
+       "import-error-special": "La page « $1 » n’a pas été importée parce qu’elle appartient à un espace de noms spécial qui n’autorise aucune page.",
+       "import-error-invalid": "Page « $1 » n’a pas été importée parce que le nom sous lequel elle aurait été importée n’est pas valide sur ce wiki.",
        "import-error-unserialize": "La révision $2 de la page « $1 » ne peut pas être désérialisée. La révision est indiquée comme utilisant le modèle de contenu $3 sérialisé en $4.",
-       "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stockée sur « $1 » sur ce wiki, car ce modèle n’est pas pris en charge sur cette page.",
+       "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stockée sur « $1 » sur ce wiki, car ce modèle n’est pas pris en charge sur cette page.",
        "import-options-wrong": "{{PLURAL:$2|Mauvaise option|Mauvaises options}} : <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "La page racine fournie est un titre non valide.",
        "import-rootpage-nosubpage": "L'espace de noms « $1 » de la page racine n'autorise pas les sous-pages.",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|révision importée|révisions importées}}",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|révision importée|révisions importées}} depuis $2",
        "javascripttest": "Test de JavaScript",
-       "javascripttest-pagetext-unknownaction": "Action « $1 » inconnue.",
+       "javascripttest-pagetext-unknownaction": "Action « $1 » inconnue.",
        "javascripttest-qunit-intro": "Voir [$1 la documentation de test] sur mediawiki.org.",
        "tooltip-pt-userpage": "Votre page d’{{GENDER:|utilisateur|utilisatrice}}",
        "tooltip-pt-anonuserpage": "La page utilisateur avec l'adresse IP de laquelle vous contribuez",
        "confirmemail_subject": "Confirmation d’adresse de courriel pour {{SITENAME}}",
        "confirmemail_body": "Quelqu’un, probablement vous, à partir de l’adresse IP $1,\na créé un compte « $2 » avec cette adresse de courriel sur le site {{SITENAME}}.\n\nPour confirmer que ce compte vous appartient vraiment et afin\nd’activer les fonctions de messagerie sur {{SITENAME}},\nveuillez suivre ce lien dans votre navigateur :\n\n$3\n\nSi vous n’avez *pas* créé ce compte, suivez le lien ci-dessous \npour annuler la confirmation de votre adresse courriel :\n\n$5\n\nCe code de confirmation expirera le $4.",
        "confirmemail_body_changed": "Quelqu’un, probablement vous, à partir de l’adresse IP $1,\na modifié l’adresse de courriel associée au compte « $2 » de {{SITENAME}}\nen cette adresse.\n\nPour confirmer que ce compte vous appartient vraiment et afin\nde réactiver les fonctions de messagerie sur {{SITENAME}},\nveuillez suivre ce lien dans votre navigateur :\n\n$3\n\nSi ce compte ne vous appartient *pas*, n’ouvrez pas ce lien ;\nvous pouvez suivre l’autre lien ci-dessous pour annuler la\nconfirmation de votre adresse courriel :\n\n$5\n\nCe code de confirmation expirera le $4.",
-       "confirmemail_body_set": "Quelqu’un, probablement vous, depuis l’adresse IP $1, a modifié l’adresse de courriel du compte « $2 » en celle-ci sur {{SITENAME}}.\n\nPour confirmer que ce compte vous appartient et réactiver les fonctions de courriel sur {{SITENAME}}, ouvrez ce lien dans votre navigateur Web :\n\n$3\n\nSi le compte ne vous appartient *pas*, suivez plutôt ce lien pour annuler la confirmation de l’adresse de courriel :\n\n$5\n\nCe code de confirmation expirera le $4.",
+       "confirmemail_body_set": "Quelqu’un, probablement vous, depuis l’adresse IP $1, a modifié l’adresse de courriel du compte « $2 » en celle-ci sur {{SITENAME}}.\n\nPour confirmer que ce compte vous appartient et réactiver les fonctions de courriel sur {{SITENAME}}, ouvrez ce lien dans votre navigateur Web :\n\n$3\n\nSi le compte ne vous appartient *pas*, suivez plutôt ce lien pour annuler la confirmation de l’adresse de courriel :\n\n$5\n\nCe code de confirmation expirera le $4.",
        "confirmemail_invalidated": "Confirmation de l’adresse courriel annulée",
        "invalidateemail": "Annuler la confirmation de l'adresse de courriel",
        "notificationemail_subject_changed": "L’adresse courriel enregistrée sur {{SITENAME}} a été changée",
        "parentheses-start": "(",
        "parentheses-end": ")",
        "brackets": "[$1]",
-       "quotation-marks": "« $1 »",
+       "quotation-marks": "« $1 »",
        "imgmultipageprev": "← page précédente",
        "imgmultipagenext": "page suivante →",
        "imgmultigo": "Accéder !",
        "tags-deactivate-submit": "Désactiver",
        "tags-apply-no-permission": "Vous n’avez pas le droit d’appliquer des balises de changement en même temps que vos modifications.",
        "tags-apply-blocked": "Vous ne pouvez pas appliquer les modifications de balises et vos modifications lorsque vous êtes bloqué{{GENDER:$1||e}}.",
-       "tags-apply-not-allowed-one": "La balise « $1 » n’est pas autorisée à être appliquée manuellement.",
+       "tags-apply-not-allowed-one": "La balise « $1 » n’est pas autorisée à être appliquée manuellement.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|La balise suivante n’est pas autorisée à être appliquée|Les balises suivantes ne sont pas autorisées à être appliquées}} manuellement : $1",
        "tags-update-no-permission": "Vous n’avez pas le droit d’ajouter ou de supprimer \nles balises de modification des révisions individuelles ou des entrées de journal.",
        "tags-update-blocked": "Vous ne pouvez pas ajouter ou supprimer des balises de modifications lorsque vous êtes bloqué{{GENDER:$1||e}}.",
-       "tags-update-add-not-allowed-one": "La balise « $1 » ne peut pas être ajoutée manuellement.",
+       "tags-update-add-not-allowed-one": "La balise « $1 » ne peut pas être ajoutée manuellement.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|La balise suivante ne peut pas être ajoutée|Les balises suivantes ne peuvent pas être ajoutées}} manuellement : $1",
-       "tags-update-remove-not-allowed-one": "La balise « $1 » ne peut pas être enlevée.",
+       "tags-update-remove-not-allowed-one": "La balise « $1 » ne peut pas être enlevée.",
        "tags-update-remove-not-allowed-multi": "{{PLURAL:$2|La balise suivante ne peut pas être enlevée|Les balises suivantes ne peuvent pas être enlevées}} manuellement : $1",
        "tags-edit-title": "Modifier les balises",
        "tags-edit-manage-link": "Gérer les balises",
        "logentry-upload-revert": "$1 {{GENDER:$2|a annulé}} $3 vers une ancienne version",
        "log-name-managetags": "Journal des modifications de balises",
        "log-description-managetags": "Cette page recense les tâches de maintenance liées aux [[Special:Tags|balises]]. Le journal contient uniquement les actions faites manuellement par un administrateur ; les balises peuvent être créées ou supprimées par le logiciel wiki sans que cette action ne soit inscrite dans ce journal.",
-       "logentry-managetags-create": "$1 {{GENDER:$2|a créé}} la balise « $4 ».",
-       "logentry-managetags-delete": "$1 {{GENDER:$2|a supprimé}} la balise « $4 » (retirée {{PLURAL:$5|d'une révision ou entrée de journal|de $5 révisions ou entrées de journal}})",
-       "logentry-managetags-activate": "$1 {{GENDER:$2|a activé}} la balise « $4 » pour l’usage des utilisateurs et des robots",
-       "logentry-managetags-deactivate": "$1 {{GENDER:$2|a désactivé}} la balise « $4 » pour l’usage des utilisateurs et des robots",
+       "logentry-managetags-create": "$1 {{GENDER:$2|a créé}} la balise « $4 ».",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|a supprimé}} la balise « $4 » (retirée {{PLURAL:$5|d'une révision ou entrée de journal|de $5 révisions ou entrées de journal}})",
+       "logentry-managetags-activate": "$1 {{GENDER:$2|a activé}} la balise « $4 » pour l’usage des utilisateurs et des robots",
+       "logentry-managetags-deactivate": "$1 {{GENDER:$2|a désactivé}} la balise « $4 » pour l’usage des utilisateurs et des robots",
        "log-name-tag": "Journal des balises",
        "log-description-tag": "Cette page montre quand des utilisateurs ont ajouté ou supprimé des [[Special:Tags|balises]] de révisions individuelles ou d’entrées de journal. Le journal ne liste pas les actions de marquage quand elles ont lieu au cours d’une modification, d’une suppression, ou d’une action semblable.",
        "logentry-tag-update-add-revision": "$1 {{GENDER:$2|a ajouté}} {{PLURAL:$7|la balise|les balises}} $6 à la révision $4 de la page $3",
        "api-error-emptypage": "Création de pages vide n'est pas autorisée.",
        "api-error-publishfailed": "Erreur interne : Le serveur n'a pas pu publier le fichier temporaire.",
        "api-error-stashfailed": "Erreur interne : le serveur n'a pas pu enregistrer le fichier temporaire.",
-       "api-error-unknown-warning": "Avertissement inconnu : « $1 ».",
+       "api-error-unknown-warning": "Avertissement inconnu : « $1 ».",
        "api-error-unknownerror": "Erreur inconnue : « $1 ».",
        "duration-seconds": "$1 seconde{{PLURAL:$1||s}}",
        "duration-minutes": "$1 minute{{PLURAL:$1||s}}",
        "authmanager-link-not-in-progress": "La liaison de compte n’est pas en cours ou les données de session ont été perdues. Veuillez recommencer depuis le début.",
        "authmanager-autocreate-noperm": "La création automatique de compte n’est pas autorisée.",
        "authmanager-autocreate-exception": "La création automatique de compte est désactivée temporairement, du fait d’erreurs antérieures.",
-       "authmanager-userdoesnotexist": "Le compte utilisateur « $1 » n’est pas inscrit.",
+       "authmanager-userdoesnotexist": "Le compte utilisateur « $1 » n’est pas inscrit.",
        "authmanager-userlogin-remembermypassword-help": "Indique si le mot de passe doit être mémorisé au-delà de la durée de la session.",
        "authmanager-username-help": "Nom d’utilisateur pour l’authentification.",
        "authmanager-password-help": "Mot de passe pour l’authentification.",
        "authprovider-confirmlink-ok-help": "Continuer après l’affichage des messages d’échec de liaison.",
        "authprovider-resetpass-skip-label": "Sauter",
        "authprovider-resetpass-skip-help": "Sauter la réinitialisation du mot de passe.",
-       "authform-nosession-login": "L’authentification a réussi, mais votre navigateur ne peut pas se « souvenir » d’avoir été connecté.\n\n$1",
-       "authform-nosession-signup": "Le compte a été créé, mais votre navigateur ne peut pas se « souvenir » avoir été connecté.\n\n$1",
+       "authform-nosession-login": "L’authentification a réussi, mais votre navigateur ne peut pas se « souvenir » d’avoir été connecté.\n\n$1",
+       "authform-nosession-signup": "Le compte a été créé, mais votre navigateur ne peut pas se « souvenir » avoir été connecté.\n\n$1",
        "authform-newtoken": "Jeton manquant. $1",
        "authform-notoken": "Jeton manquant",
        "authform-wrongtoken": "Mauvais jeton",
index 9c5a899..9faad52 100644 (file)
        "redirectedfrom": "($1 सून पुनर्निर्देशित)",
        "redirectpagesub": "पान परतून निर्देशीत करचें",
        "redirectto": "हांगां पुनर्निर्देशित:",
-       "lastmodifiedat": "हà¥\8dया à¤ªà¤¾à¤¨à¤¾à¤\82त à¤¨à¤¿à¤®à¤¾à¤£à¥\8b à¤¬à¤¦à¤²,$1 à¤µà¥\87र $2 à¤µà¥\87ळार à¤\95à¥\87लà¥\8dलà¥\8b",
+       "lastmodifiedat": "हà¥\87à¤\82 à¤ªà¤¾à¤¨ à¤¶à¥\87वà¤\9fà¥\80à¤\82 $1 à¤¦à¤¿à¤¸à¤¾, $2 à¤µà¥\8bराà¤\82à¤\9aà¥\8bर à¤¬à¤¦à¤²à¥\87लà¥\87à¤\82.",
        "protectedpage": "राखून दवरिल्लें पान",
        "jumpto": "हुपून वचात:",
        "jumptonavigation": "दिशा-नियंत्रण",
        "minoreditletter": "द",
        "newpageletter": "न",
        "boteditletter": "र",
-       "rc-change-size-new": "$1 {{बहुवचन:$1|byte|bytes}}बदल केल्या उपरांत",
+       "rc-change-size-new": "$1 {{PLURAL:$1|बाय्ट|बाय्टी}} बदल केल्या उपरांत",
        "rc-enhanced-expand": "म्हायती दाखय",
        "rc-enhanced-hide": "म्हायती लिपय",
        "recentchangeslinked": "संबंदित बदल",
        "recentchangeslinked-toolbox": "संबंदीत बदल",
        "recentchangeslinked-title": "\"$1\" च्या संबंदातले बदल",
-       "recentchangeslinked-summary": "à¤\96ाशà¥\87लà¥\8dया à¤ªà¤¾à¤¨à¤¾à¤\82 à¤\95डलà¥\8dयान à¤¦à¥\81वà¥\87 à¤®à¥\87ळिलà¥\8dलà¥\8dया à¤ªà¤¾à¤¨à¤¾à¤\82मदà¥\80à¤\82 (वा à¤µà¤¿à¤¶à¤¿à¤¶à¥\8dà¤\9f à¤µà¤°à¥\8dà¤\97ाà¤\82à¤\9aà¥\8dया à¤µà¤¾à¤\82à¤\97डà¥\8dयाà¤\82मदà¥\80à¤\82) à¤¹à¤¾à¤²à¥\80à¤\82à¤\9a à¤\95à¥\87लà¥\8dलà¥\8dया à¤¬à¤¦à¤²à¤¾à¤\82à¤\9aà¥\80 à¤¹à¥\80 à¤µà¤³à¥\87रà¥\80. [[Special:Watchlist|तà¥\81मà¤\9aà¥\8dया à¤¸à¤¾à¤¦à¥\81रवळà¥\87रà¥\80]] à¤ªà¤¾à¤¨à¤¾ '''ठळà¤\95''' à¤¦à¤¾à¤\96यलà¥\8dयात",
+       "recentchangeslinked-summary": "à¤\8fà¤\95ा à¤ªà¤¾à¤¨à¤¾à¤\9aà¥\87à¤\82 à¤¨à¤¾à¤\82व à¤¬à¤°à¤¯ à¤\9cà¥\8dया à¤µà¤°à¥\8dवà¥\80à¤\82 à¤\9cडलà¥\87लà¥\8dया à¤ªà¤¾à¤¨à¤¾à¤\82à¤\9aà¥\87र à¤µà¤¾ à¤ªà¤¾à¤¨à¤¾à¤\82 à¤ªà¤¾à¤¸à¥\82न à¤¬à¤¦à¤² à¤¦à¤¿à¤¸à¤¤à¤²à¥\8b. (à¤\8fà¤\95ा à¤µà¤°à¥\8dà¤\97णाà¤\9aà¥\87 à¤µà¤¾à¤\82à¤\97डà¥\80 à¤ªà¤³à¥\8bवà¤\82à¤\95, à¤¬à¤°à¤¯ {{ns:category}}:वरà¥\8dà¤\97ाà¤\9aà¥\87à¤\82 à¤¨à¤¾à¤\82व). [[Special:Watchlist|तà¥\81à¤\9cà¥\8dया à¤¸à¤¾à¤¦à¥\81रवळà¥\87रà¥\80à¤\82त]] à¤\86सलà¥\87लà¥\8dया à¤ªà¤¾à¤¨à¤¾à¤\9aà¥\87र à¤¬à¤¦à¤² <strong>दाà¤\9f</strong> à¤\86सात.",
        "recentchangeslinked-page": "पानाचें नांव",
        "recentchangeslinked-to": "ह्या पाना बदला दिल्ल्या पानांक जडून आशिल्ल्या पानांचे बदल दाखय",
        "upload": "फायल अपलोड करात",
        "tooltip-t-permalink": "ह्या पानाच्या ह्या पुनर्नियाळाकडे सदांकाळ दुवो",
        "tooltip-ca-nstab-main": "मजकूर पान पळेयात",
        "tooltip-ca-nstab-user": "वापरप्याचें पान दाखय",
-       "tooltip-ca-nstab-special": "हà¥\87à¤\82 à¤\96à¥\87रà¥\80त à¤ªà¤¾à¤¨, à¤¤à¥\81मà¤\9aà¥\8dयाà¤\82नà¥\80à¤\82 à¤\96à¥\81दà¥\8dद à¤¤à¥\8dया à¤ªà¤¾à¤¨à¤¾à¤° à¤¸à¤\82सà¥\8dà¤\95रण à¤\95रà¥\82à¤\82 à¤¨à¤\9cà¥\8b",
+       "tooltip-ca-nstab-special": "हà¥\87à¤\82 à¤\8fà¤\95 à¤\96à¥\87रà¥\80त à¤ªà¤¾à¤¨, à¤\86नà¥\80 à¤¹à¥\87à¤\82 à¤¬à¤¦à¤²à¥\82à¤\82à¤\95 à¤\9cायना",
        "tooltip-ca-nstab-project": "प्रकल्पाचें पान पळेयात",
        "tooltip-ca-nstab-image": "फायलीचें पान पळेयात",
        "tooltip-ca-nstab-template": "सांचो पळेयात",
index 51842f5..1fe99ac 100644 (file)
        "redirectedfrom": "($1 savn punornirdexit)",
        "redirectpagesub": "Punornirdexan pan",
        "redirectto": "Hanga ponornirdeshit:",
-       "lastmodifiedat": "Hem pan xevtim $1 disa, $2 vazta bodolelem.",
+       "lastmodifiedat": "Hem pan xevttim $1 disa, $2 vorancher bodololem.",
        "protectedpage": "Rakhun dovorl'lem pan",
        "jumpto": "Hupun voch",
        "jumptonavigation": "dixa-niontronn",
        "newarticle": "(Novem)",
        "newarticletext": "Tuven ek duveche patlav kelai, zachem pan azun rochunk na.\nPan rochunk, khallchea chovkottan boroi (anik mahitik [$1 adar pan] polloi).\nTu hangasor chukin pavlai zalear tujea internet browser-achi <strong>Fatim</strong> vo <strong>Back</strong> butao dab.",
        "anontalkpagetext": "----\n<em>Hem bhasabhasechem pan ek ninami vaporpeak zannem ozun ek khatem ugddunk na, vo to tem vaporna.</em>\nHea khatir amkam ankddeancho IP pot'to vaprunk podta taka vollkhunk.\nToslo IP pot'to sabar vaporpeamni vaprum ieta.\nTum zor ek ninami vaporpi asa ani tuka dista ki sombondit xere tuje vixim keleat, upkar korun [[Special:CreateAccount|ek khatem roch]] vo [[Special:UserLogin|log in]] fuddle guspop ninami vaporpeanchem tallunk.‎",
-       "noarticletext": "Sodheak hem pan rinte asa.\nTujean dusrea panani [[Special:Search/{{PAGENAME}}|hea panache nanv sodunk zata]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sombondhi sotrani sodunk zata], vo [{{fullurl:{{FULLPAGENAME}}|action=edit}} hem pan rochunk zata]</span>.",
-       "noarticletext-nopermission": "Sodheak hem pan rinte asa.\nTujean dusrea panani [[Special:Search/{{PAGENAME}}|hea panache nanv sodunk zata]], vo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sombondhi sotrani sodunk zata], pun tuka hem pan rochunk porvangi na.",
+       "noarticletext": "Sodheak hem pan ritem asa.\nTujean dusrea panani [[Special:Search/{{PAGENAME}}|hea panache nanv sodunk zata]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sombondhi sotrani sodunk zata], vo [{{fullurl:{{FULLPAGENAME}}|action=edit}} hem pan rochunk zata]</span>.",
+       "noarticletext-nopermission": "Sodheak hem pan ritem asa.\nTujean dusrea panani [[Special:Search/{{PAGENAME}}|hea panache nanv sodunk zata]], vo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sombondhi sotrani sodunk zata], pun tuka hem pan rochunk porvangi na.",
        "userpage-userdoesnotexist-view": "\"$1\" hea vapurpeachea khateachi nondnni korunk na.",
        "clearyourcache": "<strong>Note:</strong> Samball’llea uprant, tuka ghoddiek tujea browseracho cache koddsoravnk poddot bodol pollonvche khatir.\n* <strong>Firefox / Safari:</strong> <em>Shift</em> dhor <em>Reload</em> klik kortana, vo dam <em>Ctrl-F5</em> vo <em>Ctrl-R</em> (<em>⌘-R</em> Mac-acher)\n* <strong>Google Chrome:</strong> <em>Ctrl-Shift-R</em> dam (<em>⌘-Shift-R</em> eka Mac-acher)\n* <strong>Internet Explorer:</strong> <em>Ctrl</em> dhor <em>Refresh</em> klik kortana, vo dam <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Hanga voch: <em>Menu → Settings</em> (<em>Opera → Preferences</em> Mac-acher) ani uprant <em>Privacy & security → Clear browsing data → Cached images and files</em>.‎",
        "previewnote": "<strong>Hem fokot ek zholok mhonn ugddas dhor.</strong>\nTuje bodol azun sambhallun dovrunk nant!",
        "rcfilters-savedqueries-new-name-label": "Nanv",
        "rcfilters-savedqueries-apply-label": "Challnni roch",
        "rcfilters-savedqueries-cancel-label": "Rod'd kor",
+       "rcfilters-savedqueries-add-new-title": "Chalont challnnechi manddavoll samball",
+       "rcfilters-savedqueries-already-saved": "Heo challnneo adinch samball’lloleo asat. Ek novi Samball’lloli Challnni rochunk, tujeo manddavolleo bodol.",
        "rcfilters-restore-default-filters": "Default challnneo porot hadd",
        "rcfilters-clear-all-filters": "Soglleo challnneo nivoll kor",
        "rcfilters-show-new-changes": "$1 savn noveo bodol polloi",
        "rcfilters-filter-editsbyother-label": "Dusreanim kel'le bodol",
        "rcfilters-filter-editsbyother-description": "Tuje khas bhairavn, soglle bodol",
        "rcfilters-filtergroup-user-experience-level": "Vaporpeachi nondnni ani onnbhov",
+       "rcfilters-filter-user-experience-level-registered-description": "Sotrarombh zalole sompadok.",
        "rcfilters-filter-user-experience-level-learner-label": "Xikpi",
+       "rcfilters-filter-user-experience-level-experienced-label": "Onnbhovi vaporpi",
        "rcfilters-filtergroup-automated": "Apoap zalolem iogdan",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Apoap avtamni kelolem sompadon",
        "rcfilters-filter-humans-label": "Monxan kelolem (nhoi robotan)",
        "rcfilters-filter-humans-description": "Monxani kelolem sompadon",
-       "rcfilters-filter-reviewstatus-unpatrolled-description": "Paro kela mhonn khunnavnk naslolem sompadon.",
+       "rcfilters-filter-reviewstatus-unpatrolled-description": "Paro kela mhonn hatan vo apoap khunnavnk naslolem sompadon.",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "Paro korunk naslolem",
        "rcfilters-filtergroup-significance": "Mhotv",
        "rcfilters-filter-minor-label": "Dhakte bodol",
        "rcfilters-filter-major-label": "Dhakte naslolem sompadon",
        "rcfilters-filter-major-description": "Dhaktem mhonn khunne chitt korunk naslolem sompadon",
        "rcfilters-filtergroup-watchlist": "Sadurvollerintlim panam",
-       "rcfilters-filter-watchlist-watchednew-description": "Bodol ghoddlea uprant tuvem bhett divnk na tea sadurvollerintlim panache bodol.",
+       "rcfilters-filter-watchlist-watched-label": "Sadurvollerintlim",
+       "rcfilters-filter-watchlist-watchednew-label": "Sadurvolleriche nove bodol",
+       "rcfilters-filter-watchlist-watchednew-description": "Bodol ghoddleat ten’na savn tuvem bhett divnk nant tea sadurvollerintlim panache bodol.",
+       "rcfilters-filter-watchlist-notwatched-label": "Sadurvollerintlim nhoi",
+       "rcfilters-filter-watchlist-notwatched-description": "Sadurvollerichim panank bodol soddun her sogllem.",
        "rcfilters-filtergroup-watchlistactivity": "Sadurvollerichem kario",
        "rcfilters-filter-watchlistactivity-unseen-label": "Pollovnk naslole bodol",
        "rcfilters-filter-watchlistactivity-unseen-description": "Bodol ghoddlea uprant tuvem bhett divnk na tea panache bodol.",
        "rcshowhidemine": "Mhoje bodol $1",
        "rcshowhidemine-show": "Dakhoi",
        "rcshowhidemine-hide": "Lipoi",
-       "rclinks": "Xevtiche $2 disanim zal'le $1 bodol dakhoi",
+       "rclinks": "Xevottche $2 disamni zal'le $1 bodol dakhoi",
        "diff": "frk",
        "hist": "iti",
        "hide": "Lipoi",
        "recentchangeslinked-feed": "Sombondit bodol",
        "recentchangeslinked-toolbox": "Sombondit bodol",
        "recentchangeslinked-title": "\"$1\"che sombondit bodol",
-       "recentchangeslinked-summary": "Eka panachem nanv boroi jea vorvim zoddlolea panancher vo panam pasun bodol  distolo. (Eka vorgonnache vangddi pollovnk, boroi {{ns:category}}:Vorgonnachem nanv). [[Special:Watchlist|Tujea sadurvollerint]] aslelim panacher bodol <strong>datt</strong> asat.",
+       "recentchangeslinked-summary": "Eka panachem nanv boroi jea vorvim zoddlolea panancher vo panam pasun bodol  distolo. (Eka vorgonnache vangddi pollovnk, boroi {{ns:category}}:Vorgonnachem nanv). [[Special:Watchlist|Tujea sadurvollerint]] aslolea panacher bodol <strong>datt</strong> asat.",
        "recentchangeslinked-page": "Panache nanv:",
        "recentchangeslinked-to": "Dil'em panache bodlek haka zodlelem panank kel'le bodol dakhoi",
        "upload": "Fayl upload kor",
index e9ae01b..37106e5 100644 (file)
        "undo-norev": "A szerkesztés nem állítható vissza, mert nem létezik vagy törölve lett.",
        "undo-nochange": "A szerkesztés már vissza lett állítva.",
        "undo-summary": "Visszavontam [[Special:Contributions/$2|$2]] ([[User talk:$2|vita]]) szerkesztését (oldid: $1)",
+       "undo-summary-anon": "Visszavontam [[Special:Contributions/$2|$2]] szerkesztését (oldid: $1)",
        "undo-summary-username-hidden": "A rejtett felhasználó által végzett $1 változat visszavonása",
        "cantcreateaccount-text": "Erről az IP-címről ('''$1''') nem lehet regisztrálni, mert [[User:$3|$3]] blokkolta az alábbi indokkal:\n\n:''$2''",
        "cantcreateaccount-range-text": "A regisztrációt a(z) <strong>$1</strong> IP-címtartományban, amelybe a te IP-címed (<strong>$4</strong>) is tartozik, [[User:$3|$3]] blokkolta.\n\n$3 a következő indoklást adta: <em>$2</em>",
        "alreadyrolled": "[[:$1]] utolsó, [[User:$2|$2]] ([[User talk:$2|vita]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) általi szerkesztését nem lehet visszavonni:\nidőközben valaki már visszavonta vagy szerkesztette a lapot.\n\nAz utolsó szerkesztést [[User:$3|$3]] ([[User talk:$3|vita]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) végezte.",
        "editcomment": "A szerkesztési összefoglaló <em>$1</em> volt.",
        "revertpage": "Visszaállítottam a lap korábbi változatát [[Special:Contributions/$2|$2]] ([[User talk:$2|vita]]) szerkesztéséről [[User:$1|$1]] szerkesztésére",
+       "revertpage-anon": "Visszaállítottam a lap korábbi változatát [[Special:Contributions/$2|$2]] szerkesztéséről [[User:$1|$1]] szerkesztésére",
        "revertpage-nouser": "Visszaállítottam a lap korábbi változatát (szerkesztőnév eltávolítva) szerkesztéséről [[User:$1|$1]] szerkesztésére",
        "rollback-success": "{{GENDER:$3|$1}} szerkesztéseit visszaállítottam {{GENDER:$4|$2}} utolsó változatára.",
        "sessionfailure-title": "Munkamenethiba",
index 1f35ef6..eb94cd0 100644 (file)
        "undo-main-slot-only": "Le modification non poteva esser disfacite perque illo implica contento foras del cannellatura principal.",
        "undo-norev": "Impossibile annullar le modification proque illo non existe o esseva delite.",
        "undo-nochange": "Pare que iste modification ha jam essite disfacite.",
-       "undo-summary": "Annullava le version $1 per [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussion]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
+       "undo-summary": "Disfacite le version $1 per [[Special:Contributions/$2|$2]] ([[User talk:$2|discussion]])",
+       "undo-summary-anon": "Disfacite le version $1 per [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "Disfacer le revision $1 facite per un usator celate",
        "cantcreateaccount-text": "Le creation de contos desde iste adresse IP ('''$1''') ha essite blocate per [[User:$3|$3]].\n\nLe motivo que $3 dava es ''$2''",
        "cantcreateaccount-range-text": "Le creation de contos ab le adresses IP in le intervallo <strong>$1</strong>, le qual include tu adresse IP (<strong>$4</strong>), ha essite blocate per [[User:$3|$3]].\n\nLe motivo fornite per $3 es <em>$2</em>",
        "cantrollback": "Impossibile revocar le modification;\nle ultime contributor es le sol autor de iste pagina.",
        "alreadyrolled": "Non pote revocar le ultime modification de [[:$1]] per [[User:$2|$2]] ([[User talk:$2|discussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nun altere persona ha ja modificate o revocate le pagina.\n\nLe ultime modification esseva facite per [[User:$3|$3]] ([[User talk:$3|discussion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Le summario del modification esseva: <em>$1</em>.",
-       "revertpage": "Reverteva modificationes per [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussion]]) al ultime version per [[User:$1|$1]]",
+       "revertpage": "Reverteva modificationes per [[Special:Contributions/$2|$2]] ([[User talk:$2|discussion]]) al ultime version per [[User:$1|$1]]",
+       "revertpage-anon": "Reverteva modificationes per [[Special:Contributions/$2|$2]] al ultime version per [[User:$1|$1]]",
        "revertpage-nouser": "Reverteva modificationes per un usator celate al ultime version per {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Revocava modificationes per {{GENDER:$3|$1}};\nretornava al version per {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Error de session",
index d394695..0ba4725 100644 (file)
        "nocreate-loggedin": "Anda tak memiliki hak akses untuk membuat halaman baru.",
        "sectioneditnotsupported-title": "Penyuntingan bagian tidak didukung",
        "sectioneditnotsupported-text": "Penyuntingan bagian tidak didukung di halaman sunting ini.",
+       "modeleditnotsupported-title": "Penyuntingan tidak didukung",
+       "modeleditnotsupported-text": "Penyuntingan tidak didukung untuk model konten $1.",
        "permissionserrors": "Kesalahan Hak Akses",
        "permissionserrorstext": "Anda tak memiliki hak untuk melakukan hal itu karena {{PLURAL:$1|alasan|alasan-alasan}} berikut:",
        "permissionserrorstext-withaction": "Anda tidak memiliki hak akses untuk $2, karena {{PLURAL:$1|alasan|alasan}} berikut:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objek kosong",
        "content-json-empty-array": "Larik kosong",
+       "unsupported-content-model": "<strong> Peringatan: </strong> Model konten $1 tidak didukung di wiki ini.",
+       "unsupported-content-diff": "Beda tidak didukung untuk model konten $1.",
+       "unsupported-content-diff2": "Perbedaan antara model konten $1 dan $2 tidak didukung di wiki ini.",
        "deprecated-self-close-category": "Halaman yang menggunakan tag HTML tertutup-sendiri tidak sah",
        "deprecated-self-close-category-desc": "Halaman ini mengandung tag HTML tertutup-sendiri yang tidak sah, seperti <code>&lt;b/></code> atau <code>&lt;span/></code>.  Perilaku tag seperti ini akan segera berubah agar konsisten dengan spesifikasi HTML5, jadi penggunaannya dalam teks wiki tidak lagi disarankan.",
        "duplicate-args-warning": "<strong>Peringatan:</strong> [[:$1]] memanggil [[:$2]] dengan nilai lebih dari satu untuk parameter \"$3\". Hanya nilai terakhir yang tersedia yang akan digunakan.",
        "backend-fail-contenttype": "Tidak dapat menentukan tipe konten dari berkas yang disimpan di \"$1\".",
        "backend-fail-batchsize": "Penyimpanan backend diberikan batch $1 berkas {{PLURAL:$1||}}operasi; batasnya adalah $2 {{PLURAL:$2||}}operasi.",
        "backend-fail-usable": "Tidak dapat membaca atau menulis berkas \"$1\" karena izin tidak memadai atau direktori/kontainer hilang.",
+       "backend-fail-stat": "Tidak dapat membaca status berkas \"$1\".",
+       "backend-fail-hash": "Tidak dapat menentukan hash kriptografi berkas \"$1\".",
        "filejournal-fail-dbconnect": "Tidak dapat menyambung ke database jurnal untuk penyimpanan backend \"$1\".",
        "filejournal-fail-dbquery": "Tidak bisa update database jurnal untuk penyimpanan backend \"$1\".",
        "lockmanager-notlocked": "Tidak bisa membuka kunci \"$1\" karena \"$1\" tidak terkunci.",
        "sessionfailure": "Sepertinya ada masalah dengan sesi log Anda; log Anda telah dibatalkan sebagai antisipasi untuk mencegah pembajakan. Silakan tekan tombol \"kembali\" dan muat kembali halaman sebelum Anda masuk, lalu coba lagi.",
        "changecontentmodel": "Ubah model isi sebuah halaman",
        "changecontentmodel-legend": "Ubah model isi",
-       "changecontentmodel-title-label": "Judul halaman",
+       "changecontentmodel-title-label": "Judul halaman:",
        "changecontentmodel-current-label": "Model konten saat ini:",
-       "changecontentmodel-model-label": "Model konten baru",
+       "changecontentmodel-model-label": "Model konten baru:",
        "changecontentmodel-reason-label": "Alasan:",
        "changecontentmodel-submit": "Ubah",
        "changecontentmodel-success-title": "Model konten ini telah diubah",
        "move-page-legend": "Pindahkan halaman",
        "movepagetext": "Menggunakan formulir di bawah ini akan mengubah nama suatu halaman dan memindahkan semua data sejarah ke nama baru.\nJudul lama akan menjadi halaman pengalihan ke judul baru.\nAnda dapat memperbarui pengalihan yang menuju ke judul asli secara otomatis.\nJika Anda memilih tidak, pastikan untuk memeriksa\n[[Special:DoubleRedirects|pengalihan ganda]] atau [[Special:BrokenRedirects|pengalihan rusak]].\nAnda bertanggung jawab untuk memastikan bahwa pranala terhubung ke tempat seharusnya.\n\nPerhatikan bahwa halaman '''tidak''' akan dipindah apabila telah ada halaman pada judul yang baru, kecuali bila halaman peralihan dan tidak mempunyai sejarah penyuntingan. \nIni berarti Anda dapat mengubah kembali nama halaman seperti semula apabila Anda membuat kesalahan, dan Anda tidak dapat menimpa halaman yang telah ada.\n\n'''Catatan:'''\nIni dapat mengakibatkan perubahan drastis dan tak terduga bagi halaman yang populer; pastikan Anda mengerti konsekuensinya sebelum melanjutkan.",
        "movepagetext-noredirectfixer": "Formulir di bawah ini digunakan untuk mengubah nama suatu halaman dan memindahkan semua data sejarah ke nama baru.\nJudul yang lama akan menjadi halaman peralihan menuju judul yang baru.\nPastikan untuk memeriksa pengalihan [[Special:DoubleRedirects|ganda]] atau [[Special:BrokenRedirects|rusak]].\nAnda bertanggung jawab untuk memastikan bahwa pranala terus menyambung ke halaman yang seharusnya.\n\nPerhatikan bahwa halaman '''tidak''' akan dipindah apabila telah ada halaman yang menggunakan judul yang baru, kecuali bila halaman tersebut kosong atau merupakan halaman peralihan dan tidak mempunyai sejarah penyuntingan.\nIni berarti Anda dapat mengubah nama halaman kembali seperti semula apabila Anda membuat kesalahan, dan Anda tidak dapat menimpa halaman yang telah ada.\n\n'''Catatan:'''\nHal ini dapat mengakibatkan perubahan yang tak terduga dan drastis bagi halaman yang populer;\nPastikan Anda mengerti konsekuensi dari perbuatan ini sebelum melanjutkan.",
+       "movepagetext-noredirectsupport": "Menggunakan formulir di bawah ini akan mengubah nama halaman, memindahkan semua riwayatnya ke nama baru.\nAnda bertanggung jawab untuk memastikan bahwa pranala akan mengarah kemana seharusnya.\n\nPerhatikan bahwa halaman tersebut <strong> tidak </strong> akan dipindahkan jika sudah ada halaman di judul baru.\nIni berarti bahwa Anda dapat mengganti nama halaman kembali ke tempat namanya diubah jika Anda melakukan kesalahan, dan Anda tidak dapat menimpa halaman yang sudah ada.\n\n<strong> Catatan: </strong>\nIni bisa menjadi perubahan drastis dan tidak terduga untuk halaman populer;\npastikan Anda memahami konsekuensinya sebelum melanjutkan.",
        "movepagetalktext": "Jika Anda mencentang kotak ini, halaman pembicaraan berkaitan akan dipindahkan secara otomatis ke judul baru, kecuali halaman pembicaraan tersebut tidak kosong.\n\nDalam kasus tersebut, Anda harus memindahkan atau menggabungkan halaman secara manual.",
        "moveuserpage-warning": "'''Peringatan:''' Anda tengah memindahkan halaman pengguna. Perlu diketahui bahwa hanya halaman yang akan dipindahkan namun pengguna ''tidak akan'' berganti nama.",
        "movecategorypage-warning": "<strong>Peringatan:</strong> Anda akan memindahkan halaman kategori. Perlu diketahui bahwa hanya halaman yang akan dipindahkan dan setiap halaman dalam kategori lama <em>tidak</em> akan dikategorikan ulang ke yang baru.",
        "delete_and_move_reason": "Dihapus untuk mengantisipasikan pemindahan halaman dari \"[[$1]]\"",
        "selfmove": "Tidak dapat memindahkan halaman, karena judul sumber dan judul tujuan sama.",
        "immobile-source-namespace": "Tidak dapat memindahkan halaman dalam ruang nama \"$1\"",
+       "immobile-source-namespace-iw": "Halaman pada wiki lain tidak dapat dipindahkan dari wiki ini.",
        "immobile-target-namespace": "Tidak dapat memindahkan halaman ke ruang nama \"$1\"",
        "immobile-target-namespace-iw": "Pranala interwiki bukanlah target yang valid untuk pemindahan halaman.",
        "immobile-source-page": "Halaman ini tidak dapat dipindahkan.",
        "immobile-target-page": "Tidak dapat memindahkan ke judul tujuan tersebut.",
+       "movepage-invalid-target-title": "Nama yang diminta tidak valid.",
        "bad-target-model": "Tujuan yang diinginkan menggunakan model konten yang berbeda. Tidak dapat mengkonversi dari  $1  untuk  $2 .",
        "imagenocrossnamespace": "Tidak dapat memindahkan berkas ke ruang nama non-berkas",
        "nonfile-cannot-move-to-file": "Tidak dapat memindahkan non-berkas ke ruang nama berkas",
        "common.json": "/* JSON apa pun yang ada di sini akan dimuat untuk semua pengguna ketika memuat semua halaman. */",
        "common.js": "/* JavaScript yang ada di sini akan diterapkan untuk semua kulit. */",
        "group-autoconfirmed.js": "/* Semua JavaScript di sini hanya dimuatkan untuk pengguna terkonfirmasi otomatis */",
+       "group-user.js": "/* Semua Skrip Java di sini hanya dimuatkan untuk pengguna terdaftar saja */",
        "group-bot.js": "/* Semua JavaScript di sini hanya dimuatkan untuk bot */",
        "group-sysop.js": "/* Semua JavaScript di sini hanya dimuatkan untuk pengurus */",
        "group-bureaucrat.js": "/* Semua JavaScript di sini hanya dimuatkan untuk birokrat */",
        "log-name-pagelang": "Log perubahan bahasa",
        "log-description-pagelang": "Ini adalah log perubahan dalam bahasa halaman.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|mengubah}} bahasa $3 dari $4 menjadi $5.",
+       "default-skin-not-found": "Aduh! Kulit baku untuk wiki Anda, didefinisikan dalam <code dir=\"ltr\">$wgDefaultSkin</code> sebagai <code>$1</code>, tidak tersedia.\n\nInstalasi Anda tampaknya menyertakan {{PLURAL:$4|kulit}} berikut ini. Lihat [https://www.mediawiki.org/wiki/Manual:Skin_configuration manual konfigurasi kulit] untuk informasi cara mengaktifkannya {{PLURAL:$4|dan pilih yang baku}}.\n\n$2\n\n; Jika Anda baru saja menginstal MediaWiki:\n: Anda mungkin menginstal dari git, atau langsung dari kode sumber menggunakan beberapa metode. Ini diharapkan. Coba pasang beberapa kulit dari [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's direktori kulit], dengan:\n: * Mengunduh [https://www.mediawiki.org/wiki/Download tarbal instaler], yang dilengkapi dengan beberapa kulit dan ekstensi. Anda dapat salin tempel direktori <code>kulit/</code> darinya.\n: * Mengunduh tarbal kulit individual dari [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n: * [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Menggunakan Git untuk mengunduh kulit].\n: Melakukan ini seharusnya tidak mengganggu repositori git Anda jika Anda seorang pengembang MediaWiki.\n\n; Jika Anda baru saja upgrade MediaWiki:\n: MediaWiki 1.24 dan yang lebih baru tidak lagi secara otomatis mengaktifkan kulit yang diinstal (lihat [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual kulit autodiscovery]). Anda dapat paste {{PLURAL:$5|baris}} berikut ke <code> LocalSettings.php</code> untuk mengaktifkan {{PLURAL:$5|semua}} {{PLURAL:$5|kulit}} yang terinstal:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Jika Anda baru saja memodifikasi <code>LocalSettings.php</code>:\n: Periksa ulang mana tahi salah ketik.",
+       "default-skin-not-found-no-skins": "Aduh! Kulit baku untuk wiki Anda, didefinisikan dalam <code dir=\"ltr\">$wgDefaultSkin</code> sebagai <code>$1</code>, tidak tersedia.\n\nAnda tidak memiliki skin yang terpasang.\n\n; Jika Anda baru saja menginstal MediaWiki:\n: Anda mungkin menginstal dari git, atau langsung dari kode sumber menggunakan beberapa metode. Ini diharapkan. Coba pasang beberapa kulit dari [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's direktori kulit], dengan:\n: * Mengunduh [https://www.mediawiki.org/wiki/Download tarbal instaler], yang dilengkapi dengan beberapa kulit dan ekstensi. Anda dapat salin tempel direktori <code>kulit/</code> darinya.\n: * Mengunduh tarbal kulit individual dari [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n: * [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Menggunakan Git untuk mengunduh kulit].\n: Melakukan ini seharusnya tidak mengganggu repositori git Anda jika Anda seorang pengembang MediaWiki. Lihat [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Konfigurasi kulit] untuk informasi cara mengaktifkan kulit dan pilihan baku.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (aktif)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>nonaktif</strong>)",
        "mediastatistics": "Statistik media",
index 81ade2a..28cbba6 100644 (file)
        "diff-empty": "(Nula difero)",
        "diff-multi-sameuser": "(ne montresas {{PLURAL:$1|1 meza revizo|$1 meza revizi}} facita da la sama uzero)",
        "diff-multi-otherusers": "(ne montresas {{PLURAL:$1|1 intermeza revizo|$1 intermeza revizi}} facita da {{PLURAL:$2|altra uzero|$2 altra uzeri}})",
+       "difference-missing-revision": "{{PLURAL:$2|Un revizo|$2 revizi}} de ca difero ($1) ne trovesis.\n\nTo ordinare eventas kande vu sequas obsoleta ligilo a pagino eliminata.\nVu povas vidar detali en la 'log' [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}, pri efacinda pagini].",
        "searchresults": "Rezultaji dil sercho",
        "search-filter-title-prefix-reset": "Traserchar pagini",
        "searchresults-title": "Sercho-rezultaji por \"$1\"",
        "unusedimages": "Neuzata imaji",
        "wantedcategories": "Dezirata kategorii",
        "wantedpages": "Dezirata pagini",
+       "wantedpages-summary": "Listo pri neexistanta pagini, organizata segun la quanto di ligili a li, ecepte pagini qui nur havas ridirekti a li. Por kompleta listo pri neexistanta pagini, videz la [[{{#special:BrokenRedirects}}|listo pri ruptita ligili]].",
        "wantedfiles": "Dezirata arkivi",
        "wantedtemplates": "Dezirata shabloni",
        "mostcategories": "Pagini maxime kategorizita",
        "fileduplicatesearch-filename": "Nomo dil arkivo:",
        "fileduplicatesearch-submit": "Serchar",
        "specialpages": "Specala pagini",
+       "specialpages-note-top": "Surskriburo",
        "specialpages-group-maintenance": "Raporti pri manteno",
        "specialpages-group-other": "Altra specala pagini",
        "specialpages-group-login": "Enirar / krear konto",
index 736037e..7310bd3 100644 (file)
        "morenotlisted": "ھیہ فہرست مکمل نو۔",
        "mypage": "مہ صفحہ",
        "mytalk": "مہ مشقولگی",
-       "anontalk": "بچے لو IP ھیہ",
+       "anontalk": "مشقولگی",
        "navigation": "رہنمائی",
        "and": "&#32;وا",
        "faq": "عام معلومات",
        "searcharticle": "Go/بوغے",
        "history": "تاریخچہ ء صفحہ",
        "history_short": "تاریخچہ",
-       "updatedmarker": "مہ آخری گیکا پت نوغ",
+       "history_small": "تاریخچہ",
+       "updatedmarker": "تہ آخری گیکا پت نوغ",
        "printableversion": "قابل طبع نسخہ",
        "permalink": "مستقل لنک",
        "print": "طباعت",
        "talk": "تبادلۂ خیال",
        "views": "خیالات",
        "toolbox": "ٹول بکس",
+       "tool-link-userrights": "حلقہ ہائے {{GENDER:$1|صارف}}ہ تبدیلی",
+       "tool-link-userrights-readonly": "{{GENDER:$1|صارف}} و گروہان لوڑے",
+       "tool-link-emailuser": "ھیہ {{GENDER:$1|صارف}}وت ای میل/بشلی کغاز انزاوے",
        "imagepage": "ھوٹوو صفحو لوڑے",
        "mediawikipage": "پیغامو صفحہو لوڑے",
        "templatepage": "سانچو صفحہو لوڑے",
        "jumptonavigation": "رہنمائی",
        "jumptosearch": "تلاش",
        "view-pool-error": "معذرت: تمام سرورا موجودہ وختہ اِضافی بوجھ شیر.\nبو زیادہ صارفین موجودہ وختہ ھیہ صفحو لاڑینیان \nبرائے مہربانی! صفحو لوڑیکو بچے دوبارہ کوشش کوریکاری پروشٹی پھوکرو انتظار کورے.\n\n$1",
-       "generic-pool-error": "معذرت: تمام سرورا موجودہ وختہ اِضافی بوجھ شیر.\nبو زیادہ صارفین موجودہ وختہ ھیہ صفحو لاڑینیان \nبرائے مہربانی! صفحو لوڑیکو بچے دوبارہ کوشش کوریکاری پروشٹی پھوکرو انتظار کورے.\n\n$1",
+       "generic-pool-error": "معذرت: تمام سرورا موجودہ وختہ اِضافی بوجھ شیر.\nبو زیادہ صارفین موجودہ وختہ ھیہ صفحو لاڑینیان \nبرائے مہربانی! صفحو لوڑیکو بچے دوبارہ کوشش کوریکاری پروشٹی پھوکرو انتظار کورے\n\n$1",
+       "pool-timeout": "مقفل کوریکو بچے انتظارو مہلت ختم",
+       "pool-queuefull": "قطار لیگی شیر",
        "pool-errorunknown": "نامعلوم خطا",
+       "pool-servererror": "پول اݰماریکو خدمت دستیاب نیکی ($1)۔",
        "poolcounter-usage-error": "استعمالہ خامی: $1",
        "aboutsite": "تعارف {{SITENAME}}",
        "aboutpage": "Project:کھوار ویکیپیڈیو تعارف",
        "pagetitle-view-mainpage": "{{SITENAME}}",
        "retrievedfrom": "‘‘$1’’ نقل کاردو",
        "youhavenewmessages": "تہ بچے ای $1 شیر۔ ($2)",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|آپ کے لیے}} {{PLURAL:$3|کسی دوسرے صارف|$3 صارفین}} کی جانب سے $1 ($2)۔",
+       "youhavenewmessagesmanyusers": "تہ بچے متعدد صارفینان وولٹیاری $1 ($2)۔",
        "newmessageslinkplural": "{{PLURAL:$1|نوغ پیغام|999=نوغ پیغاماتs}}",
        "newmessagesdifflinkplural": "آخری {{PLURAL:$1|تبدیلی|تبدیلی}}",
        "youhavenewmessagesmulti": "ء$1 تہ بچے نوغ نوغ پیغامات شینی",
        "databaseerror-text": "ڈیٹا بیس کیوریا خامی پیدا بیتی شیر.\nھیہ سافٹ ویئراای مسئلہ (بگ)و نشاندیکو بوس.",
        "databaseerror-textcl": "ڈیٹا بیس کیوریا خامی پیدا بیتی شیر.",
        "databaseerror-query": "کیوری: $1",
-       "databaseerror-function": "فنکشن: $ 1",
-       "databaseerror-error": "خرابی: $ 1",
+       "databaseerror-function": "فنکشن:$ 1",
+       "databaseerror-error": "نقص: $1",
        "laggedslavemode": "Warning: Page may not contain recent updates.\nخبردار: منکھن شیر کہ صفحہا موجودہ بتاریخہ جات شامل نو بونی",
        "readonly": "ڈیٹابیسا قلف لیگی شیر",
        "enterlockreason": "قلفو بچے کیہ وجہ درج کورے، بشمولِ تخمینہ کہ قلفو کیاوت کھولاو کورونو بوئے",
        "missingarticle-rev": "(نظرثانی#: $1)",
        "missingarticle-diff": "(مختلفات: $1, $2)",
        "readonly_lag": "ڈیٹابیس خودکار طورا قلف کورونو بیتی شیر تاکہ ماتحت ڈیٹابیسی معیلاتن درجہ آقو بوئے",
+       "nonwrite-api-promise-error": "ایچ ٹی ٹی پی سرنامہ 'Promise-Non-Write-API-Action' بھیجا گیا لیکن یہ ایک اے پی آئی نویس ماڈیول کو بھیجی گئی درخواست تھی۔",
        "internalerror": "خطائے اندرونی",
        "internalerror_info": "خطائے اندرونی: $1",
+       "internalerror-fatal-exception": "\"$1\" قسمو تباہ کن استثنا",
        "filecopyerror": "\"$1\" مسلو \"$2\" نقل کورونو نو ھوی",
        "filerenameerror": "مسلو \"$1\" و \"$2\" خور نم دیونو نو ھوی",
        "filedeleteerror": "مسلو \"$1\" حذف کورونو نو ھوی",
        "delete-hook-aborted": "حذف شدگی روکا کورونو ہوئے\nوضاحت کورونو نو ہوئے",
        "badtitle": "خراب عنوان",
        "badtitletext": "'درخاس شدہ صفحہو عنوان ناقص، خالی، یا کیہ غلط ربط شدہ بین لسانی یا بین ویکی عنوان شیر.\nشاید ھیارا ای یا زیات ھݰ حروف موجود شینی کہ ھیت عنوانا استعمال نو بونیان.',",
+       "title-invalid-empty": "درخواست شدہ عنوان خالی شیر یا ھیرا محض نام فضاو نام شیر۔",
+       "title-invalid-utf8": "درخواست شدہ عنوانہ نادرست یونیکوڈ حروف موجود شینی۔",
        "perfcached": "ذیلی ڈیٹا ابطن شدہ شیر وا ھمو بیکا امکان شیر A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.",
        "perfcachedts": "ذیلی ڈیٹا ابطن شدہ شیر وا آخری بار ھمو بتاریخیت $1 کورونو ہوئے. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.",
        "querypage-no-updates": "ھیہ صفحہو بچے بتاریخات فی الحال ناقابل ساوزینو بیتی شینی. \nھمو ڈیٹا ھنیسے تازہ کورونو نو بوئے",
        "viewsource": "مسودو لوڑے",
        "viewsource-title": "$1 و مسودو لوڑے",
+       "actionthrottled": "Action throttled",
        "actionthrottledtext": "بطورِ ای ضدسپم تدبیر، تہ مختصار وختہ کئی دفعہ ھیہ کورومو کوریکو وجھین محدود کورونو ہوئے، وا تو ھیہ حدو پار کوری آسوس.\nبراہِ کرم، ای کما میلیٹ آچہ کھوشش کورے",
        "protectedpagetext": "ھیہ صفحہو تدویناری محفوظ لاکھیکو بچے قلف لیگینو بیتی شیر",
        "viewsourcetext": "تو صرف مضمونو لوڑیکو بوس وا ھو نقل کوریکو بوس:",
        "translateinterface": "تمام ویکیپیڈا تبدیلی یا شامل کوریکو بچے، ھمو استعمال کورے [https://translatewiki.net/ translatewiki.net]، میڈیا ویکی دارالترجمہ.",
        "namespaceprotected": "\"تتے '''$1''' فضائے نامہ صفحاتن تدوینو کوریکو اِجازت نیکی.\",",
        "mycustomcssprotected": "تے ھیہ سی ایس ایس (CSS) صفحہا ترمیم کوریکو اختیار نیکی۔",
+       "mycustomjsonprotected": "تتے ھیہ سی ایس ایس (CSS) صفحہا ترمیم کوریکو اختیار نیکی۔",
        "mycustomjsprotected": "تتے ھیہ جاوا اسکپرٹ (JavaScript) صفحہا ترمیم کوریکو اختیار نیکی۔",
        "myprivateinfoprotected": "تتے ھمی ذاتی معلواتہ (private information) ترمیم کویکو اختیار نیکی۔",
        "mypreferencesprotected": "تتے تان ھمی ترجیحاتہ (preferences) ترمیم کوریکو اختیار نیکی۔",
        "ns-specialprotected": "خاص صفحاتن تدوین کوریکو اجازت نیکی",
        "titleprotected": "ھیہ عنوانو [[User:$1|$1]] تخلیق کوریکاری محفوظ کوری آسور.\nوجہ ھیہ شیر: <em>$2</em>",
+       "filereadonlyerror": "فائل «$1»ہ تبدیلی ممکن نیکی کوریکو کہ خزانہ فائل «$2» فقط خواندگی حالتہ شیر۔\n\nانتظامیو وولٹیاری قلف لیگئیکو حسب ذیل وجہ دیونو بیتی شیر:\n\n«$3»",
+       "invalidtitle": "غلط عنوان",
+       "invalidtitle-knownnamespace": "«$2» نام فضا «$3» متنو سورا مشتمل عنوان نادرست شیر",
+       "invalidtitle-unknownnamespace": "نامعلوم نام فضا عدد «$1» اوچے «$2» متنو سورا مشتمل عنوان نا درست شیر",
+       "exception-nologin": "لاگ ان نو",
+       "exception-nologin-text": "براہ مہربانی! ھیہ صفحا رسائی یا ترمیمو بچے لاگ ان بوس۔",
        "virus-badscanner": "\"خراب وضعیت: نوژان وائرسی مفراس: ''$1''\",",
        "virus-scanfailed": "تفریس ناکام (رمز $1)",
        "virus-unknownscanner": "نوژان ضد وائرس:",
-       "logouttext": "'''ھنیسے تو خارج بیتی آسوس'''<br />\nتو خفی الاسم {{SITENAME}}  استعمال جاری لاکھیکو بوس، یا دوبارہ ھیہ نامو یا مختلف نامان سورا داخل دی بیکو بوس۔  ھیہ یاد آوری کورے کہ ای کما صفحات ھش <span class='plainlinks'>[$1 دوباری لاگن بوس]</span> غیچھی گونی کہ تو ھنیسے خارج نو بیتی آسوس، کلہ پت کہ تو تان تفصحہ (براؤزرو) ابطن (cache) صاف نوکوروس۔\",",
+       "logouttext": "'''ھنیسے تو خارج بیتی آسوس'''<br />\nتو خفی الاسم {{SITENAME}}  استعمال جاری لاکھیکو بوس، یا دوبارہ ھیہ نامو یا مختلف نامان سورا داخل دی بیکو بوس۔  ھیہ یاد آوری کورے کہ ای کما صفحات ھش <span class='plainlinks'>[$1 دوباری لاگن بوس]</span> غیچھی گونی کہ تو ھنیسے خارج نو بیتی آسوس، کلہ پت کہ تو تان تفصحہ (براؤزرو) ابطن (cache) صاف نوکوروس۔\"",
+       "cannotlogoutnow-title": "ھنیسے خارج بیکو نو بوس",
+       "cannotlogoutnow-text": "$1 و استعمالو موژی خارج بیک ممکن نو۔",
+       "welcomeuser": "خوشان گیور، $1!",
+       "welcomecreation-msg": "تہ کھاتہ کھولاو ہوئے۔\nتو [[Special:Preferences|تان ترجیحات]]ہ حسب منشا تبدیلی کوریکو بوس۔",
        "yourname": "اسمِ رکنیت",
        "userlogin-yourname": "اسمِ رکنیت",
        "userlogin-yourname-ph": "تان صارف نام درج کورے",
        "createacct-yourpasswordagain": "کلمۂ اجازتو تصدیق کورے",
        "createacct-yourpasswordagain-ph": "پاس ورڈو وا داخل کورے",
        "userlogin-remembermypassword": "مہ داخل بہچاوے",
+       "userlogin-signwithsecure": "محفوظ رابطہ (کنکشن) استعمال کورے",
+       "cannotlogin-title": "داخل بیکو نو بوس",
+       "cannotlogin-text": "داخل بیک ممکن نو۔",
+       "cannotloginnow-title": "ھنیسے خارج بیکو نو بوس",
+       "cannotloginnow-text": "$1 و استعمالو موژی خارج بیک ممکن نو۔",
+       "cannotcreateaccount-title": "کھاتہ ساوزیکو نو بوس",
+       "cannotcreateaccount-text": "ھیہ ویکیپیڈیا براہ راست کھاتہ سازی فعال نیکی۔",
        "yourdomainname": "تہ ڈومین",
        "password-change-forbidden": "تتے ھیہ ویکیپیڈیا تان پاس روڈو تبدیل کوریکو اختیار نیکی",
        "externaldberror": "یا تھے توثیقی ڈیٹابیسا خطا واقع بیتی شیر یا تتے بیریو کھاتو بتاریخ کوریکو اِجازت نیکی",
        "login": "داخل بوس",
+       "login-security": "تان شناختو تصدیقو کورے",
        "nav-login-createaccount": "کھاتہ کھولاو کورے یا اندراج کورے",
        "logout": "لاگ آوٹ",
        "userlogout": "لاگ آوٹ",
        "createacct-reason-ph": "تو ڈبل کھاتہ کھیوتے ساوزیسان؟",
        "createacct-submit": "کھاتہ ساوزاوے",
        "createacct-another-submit": "کھاتہ ساوزاوے",
+       "createacct-continue-submit": "کھاتہ سازی جاری لاکھے",
+       "createacct-another-continue-submit": "کھاتہ سازی جاری لاکھے",
        "createacct-benefit-heading": "{{SITENAME}} تہ غون روئے ایڈٹ کورونیان.",
        "createacct-benefit-body1": "{{PLURAL:$1|ترمیم|ترامیم}}",
        "createacct-benefit-body2": "$1 {{PLURAL:$1|صفحہ|صفحات}}",
        "wrongpassword": "تو غلط کلمۂ شناخت درج کوری آسوس دوبارہ کو شش کورے",
        "wrongpasswordempty": "کلمۂ شناخت ندارد۔ دوبارہ کوشش کورے",
        "passwordtooshort": "تہ منتخب کردہ پارلوظ(پاسورڈ) مختصار شیر. پارلوظ کم از کم {{PLURAL:$1|1 حرف|$1 حروف}} بیلک.",
+       "passwordtoolong": "تہ منتخب کردہ پارلوظ(پاسورڈ) مختصار شیر. پارلوظ کم از کم {{PLURAL:$1|1 حرف|$1 حروف}} بیلک.",
        "password-name-match": "تہ پارلوظ(پاسورڈ) تہ اسمِ صارفو ساری مختلف بیلک.",
        "mailmypassword": "پاسورڈو ری سیٹ کورے",
        "passwordremindertitle": "نوغ عارضی کلمۂ شناخت برائے {{SITENAME}}",
        "emailconfirmlink": "تان برقی پتو تصدیقو کورے",
        "invalidemailaddress": "تہ بشلی کغاز(ای میل) قبول کورونو نو بویان کوریکو کہ ھیہ غلط شکلا شیر.\nبراہِ کرم! ای برقی پتہ(ای میل ایڈرس) صحیح شکلا درج کورے یا ژاغو خالی پیڅھے.",
        "accountcreated": "تخلیقِ کھاتہ",
-       "accountcreatedtext": "تخیلقِ کھاتۂ ممبار براۓ $1۔",
+       "accountcreatedtext": "[[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|تبادلۂ خیال]]) و صارف کھاتہ ساوز بیتی شیر۔",
        "createaccount-title": "کھاتہ سازی برائے {{SITENAME}}",
        "login-throttled": "تو داخلِ نوشتہ بیکو بچے بو زیادہ کوششیں آرو.\nدوبارہ کوشش کوریکو بچے پھوک مدا انتظار کورے.",
+       "login-abort-generic": "لاگ ان ناکام - منسوخ شد",
        "loginlanguagelabel": "زبان: $1",
        "pt-login": "داخل بوس",
        "pt-login-button": "داخل بوس",
+       "pt-login-continue-button": "داخل بوس",
        "pt-createaccount": "کھاتہ ساوزاوے",
        "pt-userlogout": "لاگ آوٹ",
+       "php-mail-error-unknown": "پی ایچ پیو mail() فنکشنہ نامعلوم نقص۔",
        "changepassword": "پاسورڈو بدیل کورے",
        "resetpass_announce": "تو ای برقی ارسال کردہ عارضی کوڈ ورڈو سوم جستہ داخل بیتی آسوس.\nداخلِ نوشتہ بیکو عملو مکمل کوریکو بچے تہ ھیارا نوغ پاسورڈ متعین کوریلک بوئے جما:",
        "resetpass_header": "کھاتو پاسورڈو تبدیل کورے",
        "newpassword": "نوغ کلمۂ شناخت",
        "retypenew": "نوغ کلمۂ شناخت دوبارہ درج کورے:",
        "resetpass_submit": "پاسورڈ ساوزاوے وا داخل بوس",
+       "botpasswords": "روبہ پاس ورڈ",
+       "botpasswords-disabled": "روبوٹو پاس ورڈ غیر فعال شینی۔",
+       "botpasswords-existing": "روبوٹو موجودہ پاس ورڈ",
+       "botpasswords-createnew": "روبوٹو نوغ پاس ورڈ ساوزاوے",
+       "botpasswords-label-appid": "روبوٹو نام:",
+       "botpasswords-label-create": "ساوزاوے",
+       "botpasswords-label-update": "اپڈیٹ کورے",
+       "botpasswords-label-cancel": "کھینسل",
        "botpasswords-label-delete": "بوغاوے",
        "botpasswords-label-resetpassword": "پاسورڈو ری سیٹ کورے",
+       "botpasswords-label-grants-column": "دیونو ہوئے",
+       "botpasswords-bad-appid": "روبہ نام \"$1\" درست نو۔",
        "resetpass_forbidden": "تتے پاسورڈو چینج کوریکو اجازت نیکی",
        "resetpass-no-info": "ھیہ صفحا براہِ راست رسائیو بچے تہ داخلِ نوشتہ بیک ضروری شیر.",
        "resetpass-submit-loggedin": "پاسورڈو تبدیلی",
        "summary-preview": "نمائش خلاصہ:",
        "subject-preview": "عنوان/شہ سرخیو پیش منظر:",
        "blockedtitle": "صارفو بلاک کورونو بیتی شیر",
+       "blockedtext": "<strong>آپ کے صارف نام یا آئی پی پتہ پر پابندی لگائی جا چکی ہے۔</strong>\n\n$1 نے پابندی عائد کی اور یہ وجہ درج کی: <em>$2</em>\n\n* پابندی کی ابتدا : $8\n* پابندی کا اختتام : $6\n* ممنوع صارف: $7\n\nآپ $1 یا کسی دوسرے [[{{MediaWiki:Grouppage-sysop}}|منتظم]] سے رابطہ کر کے اس پابندی پر گفت و شنید کر سکتے ہیں۔\nواضح رہے کہ آپ «{{int:emailuser}}» کی سہولت اُس وقت تک استعمال نہیں کر سکتے جب تک آپ اپنے [[Special:Preferences|کھاتہ کی ترجیحات]] میں درست برقی ڈاک پتا درج نہ کریں اور آپ کو اِسے استعمال کرنے سے روک نہ دیا گیا ہو۔\nآپ کا موجودہ آئی پی پتہ $3 ہے اور پابندی کا شناختی نمبر #$5 ہے۔\nاگر آپ پابندی سے متعلق کہیں استفسار کریں تو براہِ مہربانی اس میں درج بالا تمام تفصیلات شامل کریں۔",
        "blockednoreason": "کیہ وجہ دیونو نو ھوی",
        "whitelistedittext": "ترمیم کوریکو بچے $1 ضروری شیر.",
        "nosuchsectiontitle": "قطعہ ملاو نو ھوی",
        "accmailtitle": "کلمہ شناخت(پاسورڈ) انځینو ھوی",
        "newarticle": "(نوغ)",
        "newarticletext": "↓تو ای ھݰ صفحو ربطو پیرویو کوری اسوس کہ ھسے ھنیسے موجود نیکی.\nھیہ صفحہو تخلیق کوریکو بچے درج ذیل خانا متنو درج کورے (مزید معلوماتو بچے [$1 صفحۂ معاونت] ملاحظہ کورے).\nاگر تو ھیا غلطیو سورا کہ گیتی اسوس تھے اچھو صفحا آچی بیکو بچے تان کمپیوٹرا '''back''' بٹنو ٹک کورے.",
+       "anontalkpagetext": "----\n<em>یہ تبادلۂ خیال صفحہ ایک ایسے صارف کا ہے جس نے اب تک اپنا کھاتہ نہیں بنایا یا یہ صفحہ اس کے زیر استعمال نہیں۔</em> \nلہٰذا ہمیں اس کی شناخت کے لئے ایک آئی پی پتہ استعمال کرنا پڑ رہا ہے۔ \nاس قسم کا آئی پی پتہ ایک سے زائد صارفین کے درمیان میں مشترک بھی ہوسکتا ہے۔ \nاگر آپ کی موجودہ حیثیت ایک گمنام صارف کی ہے اور آپ محسوس کریں کہ اس صفحہ پر آپ کے متعلق یہ تبصرے غیر متعلق ہیں تو براہ کرم [[Special:CreateAccount|ایک کھاتہ بنا لیں]] یا [[Special:UserLogin|داخل ہو جائیں]] تاکہ مستقبل میں آپ کو گمنام صارفین میں شمار کرنے سے گریز کیا جائے۔",
        "noarticletext": " ھیہ صفحہا فی الحال کیہ متن موجود نیکی.\nتو دیگر صفحاتا [[Special:Search/{{PAGENAME}}|ھیہ صفحہو عنوانو بچے تلاش کوریکو بوس]]، <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}}}} متعلقہ نوشتہ جات تلاش کوریکو بوس],\nیا [{{fullurl:{{FULLPAGENAME}}|action=edit}} تو ھیہ صفحہا ترمیم کوریکو بوس]</span>",
        "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" موجود نیکی۔",
+       "clearyourcache": "<strong>یاددہانی:</strong> محفوظ کرنے کے بعد ان تبدیلیوں کو دیکھنے کے لیے آپ کو اپنے براؤزر کا کیش (cache) صاف کرنا ہوگا۔\n* '''فائرفاکس/ سفاری:''' جب ''Reload'' پر کلک کریں تو ''Shift'' دباکر رکھیں، یا ''Ctrl-F5'' یا ''Ctrl-R'' دبائیں (Mac پر ''R-⌘'')\n* '''گوگل کروم:''' ''Ctrl-Shift-R'' دبائیں (Mac پر ''Shift-R-⌘'')\n* '''انٹرنیٹ ایکسپلورر:''' جب ''Refresh'' پر کلک کریں تو ''Ctrl'' یا ''Ctrl-F5'' دبائیں\n* '''اوپیرا:'''  ''Tools → Preferences'' میں جائیں اور کیش (cache) صاف کریں",
        "updated": "(اپ ڈیٹڈ)",
        "note": "'''نوٹ:'''",
        "previewnote": "'''یاد لاکھے، ھیہ صرفی نمائش شیر، تہ کاردو ترامیم ھنیسے محفوظ کورونو نو بیتی شینی۔'''",
        "post-expand-template-inclusion-category": "ھش صفحات کہ ھتیرا ٹمپلیٹ یعنی سانچو ناپ لوٹ بیتی شیر۔",
        "post-expand-template-argument-warning": "'''خبردار:''' ھیہ صفحا ای سانچہ(ٹمپلیٹ) مشقولگی دیونو بیتی شیر وا ھمو سایز بولوٹ شیر۔\nھمی لوان نیزونو بیتی شیر",
        "post-expand-template-argument-category": "ھش صفحات کہ ھتیرا بوغینو بیرو سانچان یعنی(ٹمپلیٹان) لو شینی۔",
+       "undo-failure": "درمیان میں متنازع ترامیم کی موجودگی کی بنا پر اس ترمیم کو واپس نہیں پھیرا جا سکا۔",
        "viewpagelogs": "ھیہ صفحہو بچے نوشتہ جاتن لوڑے",
        "currentrev-asof": "حالیہ نظرثانی بمطابق $1",
        "revisionasof": "تجدید بمطابق $1",
        "revdel-restore": "ظاہریتو تبدیل کورے",
        "mergehistory-reason": "وجہ:",
        "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
+       "mergelog": "نوشتو انضمام",
        "revertmerge": "غیر ضم",
        "history-title": "تاریخچہ \"$1\"",
        "difference-title": "ایڈٹ کاردوان موژی فرق \"$1\"",
        "editundo": "آچی(Undo)",
        "diff-empty": "(کیہ فرق نیکی)",
        "diff-multi-sameuser": "({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by the same user not shown)",
+       "diff-multi-otherusers": "({{PLURAL:$2|ایک دوسرے صارف|$2 صارفین}} {{PLURAL:$1|کا ایک درمیانی نسخہ نہیں دکھایا گیا|$1 کے درمیانی نسخے نہیں دکھائے گئے}})",
        "searchresults": "تلاشو نتیجہ",
        "searchresults-title": "نتائجِ تلاش برائے \"$1\"",
        "notextmatches": "ھیہ عنوانو سورا کیہ دی صفحہ موجود نیکی",
        "filehist-comment": "تبصرہ",
        "imagelinks": "مسلو روابط",
        "linkstoimage": "ھیہ مسلو سوم درج ذیل {{PLURAL:$1|صفحہ مربوط شیر|$1 صفحات مربوط شینی}}",
+       "linkstoimage-more": ".",
        "nolinkstoimage": "ھیہ کھوار ویکیپیڈیا ھش کیہ صفحات نیکی کہ ھتیت ھیہ مسل (فائلو) متعلقہ شینی",
        "linkstoimage-redirect": "$1 (فائل ری ڈائرکٹ) $2",
        "sharedupload-desc-here": "ھیہ فائل $1 موژاری شیر وا ھیہ خور پرجیکٹہ استعمال بویان۔\nمزید معلومات ھمو [$2 فائل مشقولگی صفحہا]  دیونو بیتی شیر",
        "speciallogtitlelabel": "ہدف (عنوان یا {{ns:user}}:صارفو نام برائے صارف):",
        "log": "نوشتہ جات",
        "all-logs-page": "تمام عوامی نوشتہ جات",
+       "alllogstext": "{{SITENAME}} کے تمام دستیاب لاگز کا پیوستہ ڈسپلے۔\nآپ اور باریکی کے لیے لاگ کی قسم، صارف نام (حساس معاملہ)، یا متاثرہ صفحہ (یہ بھی حساس معاملہ) چن سکتے ہیں۔",
+       "logempty": "نوشتہ میں اس سے مشابہ کوئی اندراج موجود نہیں ہے۔",
        "allpages": "سف صفحات",
        "prevpage": "آچھو صفحہ ($1)",
        "allpagesfrom": "مطلوبہ حرفاری شروع باک صفحاتن نمائش:",
        "watchthispage": "ھیہ صفحو تان نظرا لاکھے",
        "unwatch": "زیرنظرمنسوخ",
        "watchlist-details": "تہ زیرِنظرفہرستا {{PLURAL:$1|$1 صفحہ شیر|$1 صفحات شینی}}، ھیارا تبادلۂ خیالو صفحاتن تعداد شامل نیکی.",
+       "wlheader-showupdated": "آپ کی آخری آمد کے بعد جن صفحات میں تبدیلی ہوئی ہے وہ <strong>جلی حروف</strong> میں نظر آئیں گے۔",
+       "wlnote": "ذیل میں گزشتہ {{PLURAL:$2|گھنٹے|<strong>$2</strong> گھنٹوں}} میں ہونے والی {{PLURAL:$1|تبدیلی|<strong>$1</strong> تبدیلیوں}} کی فہرست درج ہے، تاریخ تجدید $3، $4",
        "watchlist-options": "واچ لسٹ آپشن",
        "watching": "زیر نظر",
        "unwatching": "منسوخ",
        "mycontris": "مہ حصہ",
        "anoncontribs": "آرٹیکلز",
        "contribsub2": "{{GENDER:$3|$1}} ($2)",
+       "nocontribs": "اس معیار کے مطابق کوئی ترمیم دستیاب نہیں ہوئی۔",
        "uctop": "موجودہ نسخہ",
        "month": "مس (وا ھیغاری پروشٹی):",
        "year": "سال (وا ھیغاری پروشٹی):",
        "contribslink": "حصہ داری",
        "blocklogpage": "نوشتۂ پاوبندی",
        "blocklogentry": "بلاک[[$1]] وختہ پت $2 $3",
+       "reblock-logentry": "[[$1]] کی ترتیبات پابندی کو تبدیل کیا، اب میعاد $2 $3 پر ختم ہوگی",
        "unblocklogentry": "بلاک نو کاردو $1",
        "block-log-flags-nocreate": "کھاتہ کھولاو کوریکو سورا پاوپندی شیر",
        "ipb_expiry_invalid": "Expiry ٹیم غلط شیر.",
        "pageinfo-few-watchers": "$1 سے کم {{PLURAL:$1|ناظر|ناظرین}}",
        "pageinfo-redirects-name": "ری ڈائرکٹان تعداد",
        "pageinfo-subpages-name": "ھیہ صفحو ذیلی صفحاتن تعداد",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|رجوع مکرر|رجوع مکررات}}؛ $3 {{PLURAL:$3|غیر رجوع مکرر|غیر رجوع مکررات}})",
        "pageinfo-firstuser": "صفحہ ساوزیاک",
        "pageinfo-firsttime": "صفحہ ساوزیکو تاریخ",
        "pageinfo-lastuser": "آخری ترمیم کوراک",
        "pageinfo-recent-authors": "مختلف مصنفینان حالیہ تعداد",
        "pageinfo-magic-words": "جادوئی {{PLURAL:$1|لوظ|الفاظ}} ($1)",
        "pageinfo-hidden-categories": "کھوشت {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
+       "pageinfo-templates": "زیر استعمال {{PLURAL:$1|سانچہ|سانچے}} ($1)",
        "pageinfo-toolboxlink": "معلومات صفحہ",
+       "pageinfo-contentpage": "شمار بطور صفحہ",
        "pageinfo-contentpage-yes": "دی",
        "patrol-log-page": "پیٹرول لاگ",
        "previousdiff": " ← پرانو تدوین",
        "nextdiff": "صفحہو نم:",
        "widthheightpage": "$1×$2، $3 {{PLURAL:$3|صفحہ|صفحات}}",
        "file-info-size": "$1 × $2 پکسلز, فل سائز: $3, MIME ٹائپ: $4",
+       "file-info-size-pages": "$1 × $2 پکسل، فائل کا حجم: $3، MIME قسم: $4، $5 {{PLURAL:$5|صفحہ|صفحات}}",
        "file-nohires": "ھموغاری لوٹ ریزولیوشن موجود نیکی.",
        "svg-long-desc": "SVG فایل, nominally $1 × $2 پکسلز, فایل سایز: $3",
        "show-big-image": "اصل فائل",
        "watchlisttools-raw": "نوغ واچ لسٹان ایڈیٹ کورے",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|talk]])",
        "duplicate-defaultsort": "'''خبردار:''' ڈیفالٹ تاڑٰ(نغڑی) \"$2\" پروشٹیو ڈیفالٹ تاڑا \"$1\" لیگی شیر۔",
+       "redirect": "فائل، صارف، صفحہ، نسخہ، یا نوشتہو شناختاری رجوع مکرر",
+       "redirect-summary": "ھیہ خصوصی صفحہو ذریعا فائل (درج کاردو فائلو نام)، صفحہ (صفحہ یا نسخو درج کاردو شناختی نمبر)، صفحہ صارف (صارفو درج کاردو شناختی نمبر) یا اندراج نوشتہ (نوشتو درج کاردو شناختی نمبر)و رجوع مکرر حاصل کورونو بوئے۔ طریقہ استعمال: [[{{#Special:Redirect}}/file/Example.jpg]]، \n[[{{#Special:Redirect}}/page/64308]]، [[{{#Special:Redirect}}/revision/328429]] یا [[{{#Special:Redirect}}/user/101]] یا \n[[{{#Special:Redirect}}/logid/186]]۔",
        "redirect-submit": "بوغے لا",
        "redirect-lookup": "تلاش:",
        "redirect-value": "قدر:",
        "compare-page1": "صفحہ 1",
        "logentry-delete-delete": "$1 {{GENDER:$2|حذف کورونو ہوئے}} صفحہ $3",
        "logentry-delete-restore": "$1 صفحو $3 {{GENDER:$2|بحال آریر}}",
+       "logentry-delete-revision": "$1 نے $3 میں موجود {{PLURAL:$5|ایک نسخے|$5 نسخوں}} کی مرئیت کو {{GENDER:$2|تبدیل کیا}}: $4",
        "revdelete-content-hid": "موادو کھوشتئینو بیتی شیر",
        "logentry-move-move": "$1 {{GENDER:$2|moved}} صفحہ $3  پت $4",
        "logentry-move-move-noredirect": "$1 صفحہ $3 و $4 وولٹی ری ڈائرکٹ {{GENDER:$2|آریر}}",
        "logentry-move-move_redir": "$1 ری ڈائرکٹان ہٹاو کوری صفحہ $3 و $4 وولٹی {{GENDER:$2|منتقل آریر}}",
+       "logentry-patrol-patrol-auto": "$1 صفحہ $3 و نسخو $4 خودکار طورا مراجعت شدہ {{GENDER:$2|نشان زد آریر}}",
        "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|ساوزیینو ھوئے}}",
        "logentry-newusers-autocreate": "صارف کھاتہ $1 خودکار طورا {{GENDER:$2|ساوز ہوئے}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|uploaded}} $3",
index 7b8b114..769891d 100644 (file)
        "undo-norev": "문서가 없거나 삭제되었기 때문에 편집을 되돌릴 수 없습니다.",
        "undo-nochange": "편집이 이미 되돌려진 것으로 나타납니다.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|토론]])의 $1판 편집을 되돌림",
+       "undo-summary-anon": "[[Special:Contributions/$2|$2]]의 $1 판을 되돌림",
        "undo-summary-username-hidden": "숨겨진 사용자가 $1 판을 되돌림",
        "cantcreateaccount-text": "현재 IP 주소('''$1''')는 [[User:$3|$3]] 사용자에 의해 계정 만들기가 차단되었습니다.\n\n차단 이유는 다음과 같습니다: $2",
        "cantcreateaccount-range-text": "당신의 IP 주소(<strong>$4</strong>)가 속해 있는 <strong>$1</strong> 대역에서의 계정 만들기를 [[User:$3|$3]]님이 차단했습니다.\n\n$3님이 제시한 이유는 \"$2\"입니다.",
        "search-interwiki-more": "(더 보기)",
        "search-interwiki-more-results": "더 많은 결과",
        "search-relatedarticle": "관련",
+       "search-invalid-sort-order": "$1의 정렬 순서를 인식하지 못했습니다. 기본 정렬이 적용됩니다. 유효한 정렬 순서는 다음과 같습니다: $2",
+       "search-unknown-profile": "$1의 검색 프로파일을 인식하지 못했습니다. 기본 검색 프로파일이 적용됩니다.",
        "searchrelated": "관련",
        "searchall": "모두",
        "showingresults": "'''$2'''번 부터의 {{PLURAL:$1|결과 '''1'''개|결과 '''$1'''개}}입니다.",
        "move": "이동",
        "movethispage": "이 문서 이동하기",
        "unusedimagestext": "다음은 어떠한 문서에서도 사용하지 않는 파일의 목록입니다.\n다른 사이트에서 URL 접근을 통해 파일을 사용할 수 있기 때문에, 아래 목록에 있는 파일도 실제로 사용 중일 가능성이 있다는 점을 주의해주세요.",
+       "unusedimagestext-categorizedimgisused": "다음의 파일이 존재하지만 어느 문서에도 포함되어 있지 않습니다. 분류된 이미지는 문서에 포함되어 있지 않음에도 사용되는 것으로 간주됩니다.\n다른 웹사이트가 파일을 직접 URL에 링크할 수 있으므로 실제 사용되지 않는 경우 여기에 나열될 수 있음을 참고해 주십시오.",
        "unusedcategoriestext": "사용하지 않는 분류 문서의 목록입니다.",
        "notargettitle": "해당하는 문서 없음",
        "notargettext": "기능을 수행할 대상 문서나 사용자를 지정하지 않았습니다.",
        "alreadyrolled": "[[:$1]]에서 [[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": "편집 요약: <em>$1</em>",
        "revertpage": "[[Special:Contributions/$2|$2]]([[User talk:$2|토론]])의 편집을 [[User:$1|$1]]의 마지막 판으로 되돌림",
+       "revertpage-anon": "[[Special:Contributions/$2|$2]]의 편집을 [[User:$1|$1]]의 마지막 판으로 되돌림",
        "revertpage-nouser": "숨긴 사용자의 편집을 {{GENDER:$1|[[User:$1|$1]]}}의 마지막 판으로 되돌림",
        "rollback-success": "{{GENDER:$3|$1}}의 편집을 되돌렸습니다.\n{{GENDER:$4|$2}}의 마지막 판으로 바뀌었습니다.",
        "sessionfailure-title": "세션 실패",
        "contribsub2": "{{GENDER:$3|$1}}($2)의 기여",
        "contributions-subtitle": "{{GENDER:$3|$1}}의 기여",
        "contributions-userdoesnotexist": "\"$1\" 사용자 계정은 등록되어 있지 않습니다.",
+       "negative-namespace-not-supported": "음수값의 이름공간은 지원되지 않습니다.",
        "nocontribs": "지정한 조건과 일치하는 바뀜을 찾을 수 없습니다.",
        "uctop": "최신",
        "month": "월:",
        "permanentlink-revid": "판 ID",
        "permanentlink-submit": "판으로 이동",
        "newsection": "새 문단",
+       "newsection-page": "대상 문서",
        "newsection-submit": "문서로 이동",
        "dberr-problems": "죄송합니다! 이 사이트에 기술적인 문제가 발생하고 있습니다.",
        "dberr-again": "잠시 기다리고 나서 다시 불러오세요.",
        "specialmute-success": "알림 미표시 환경 설정이 업데이트되었습니다. [[Special:Preferences|환경 설정]]에서 알림이 표시되지 않는 모든 사용자를 확인하십시오.",
        "specialmute-submit": "확인",
        "specialmute-label-mute-email": "이 사용자의 이메일 알림을 표시하지 않습니다",
-       "specialmute-header": "<b>{{BIDI:[[User:$1]]}}</b>의 알림 미표시 환경 설정을 선택해 주십시오.",
+       "specialmute-header": "사용자 <b>{{BIDI:[[User:$1]]}}</b>의 알림 미표시 환경 설정을 선택해 주십시오.",
        "specialmute-error-invalid-user": "요청한 사용자 이름을 찾을 수 없습니다.",
-       "specialmute-email-footer": "{{BIDI:$2}}의 이메일 환경 설정을 관리하려면 <$1>을(를) 방문해 주십시오.",
+       "specialmute-error-no-options": "알림 미표시 기능을 사용할 수 없습니다. 다음의 이유일 수 있습니다: 이메일 주소를 인증하지 않았거나 위키 관리자가 이 위키에 이메일 기능 및 이메일 블랙리스트를 비활성화했습니다.",
+       "specialmute-email-footer": "사용자 {{BIDI:$2}}의 이메일 환경 설정을 관리하려면 <$1>을(를) 방문해 주십시오.",
        "specialmute-login-required": "알림 미표시 환경 설정을 변경하려면 로그인해 주십시오.",
        "mute-preferences": "알림 미표시 환경 설정",
        "revid": "$1 판",
        "passwordpolicies-policy-passwordnotinlargeblacklist": "비밀번호는 가장 흔히 쓰이는 비밀번호 100,000개 목록에 속할 수 없습니다.",
        "passwordpolicies-policyflag-forcechange": "로그인 시 변경 필요",
        "passwordpolicies-policyflag-suggestchangeonlogin": "로그인할 때 변경 제안",
+       "mycustomjsredirectprotected": "넘겨주기이면서 사용자 공간 안에 참조하고 있지 않으므로 이 자바스크립트 페이지를 편집할 권한이 없습니다.",
        "easydeflate-invaliddeflate": "주어진 컨텐츠가 적절히 압축되지 않았습니다",
        "unprotected-js": "보안 상의 이유로 자바스크립트는 보호되지 않은 문서로부터 불러올 수 없습니다. 미디어위키: 이름공간이나 사용자의 하위 문서에서만 자바스크립트를 만들어 주십시오.",
        "userlogout-continue": "로그아웃하시겠습니까?"
index 54cc55c..70f259b 100644 (file)
        "editfont-monospace": "فونت هم عرض",
        "editfont-sansserif": "فونت بدون سریف",
        "editfont-serif": "فونت سریف",
-       "sunday": "یە شأمە",
-       "monday": "دوٙشمە",
-       "tuesday": "سەشمە",
-       "wednesday": "چار شأمأھ",
-       "thursday": "پأشأمە",
+       "sunday": "یه شمبد",
+       "monday": "دوشمبد",
+       "tuesday": "سەشمبد",
+       "wednesday": "چار شمبد",
+       "thursday": "پنشمبد",
        "friday": "جمه",
        "saturday": "شمبە",
        "sun": "یە شمبد",
        "mon": "دوشمبد",
-       "tue": "سەشمبد",
+       "tue": "سە شمبد",
        "wed": "چارشمبد",
        "thu": "پیشمبد",
        "fri": "جمَه",
-       "sat": "شأمأھ",
-       "january": "أڤأل قأھارھ",
-       "february": "Ù\84Û\8cرÛ\8cØ´Ú¯Ù\88Ù\99Ù\86",
-       "march": "ئنهزینوٙن",
-       "april": "نۉروٙزماھ",
-       "may_long": "گۉلبارماھ",
-       "june": "جۉریش",
-       "july": "میڤە رأسوٙن",
-       "august": "مە گأرمە",
-       "september": "Ø´Û\8cÙ\86Û\8cارÙ\88Ù\99Ù\86",
-       "october": "مالبارکوٙنوٙن",
-       "november": "ئا سأردکوٙنوٙن",
-       "december": "ئا Ø±Û\8cجÛ\8cÚ©Ù\86Ù\88Ù\99Ù\86",
+       "sat": "شمبد",
+       "january": "ژانویه",
+       "february": "Ù\81Ù\88رÛ\8cÙ\87",
+       "march": "مارس",
+       "april": "آوریل",
+       "may_long": "مه",
+       "june": "ژوئن",
+       "july": "ژوئیه",
+       "august": "اوت",
+       "september": "سپتاÙ\85بر",
+       "october": "اکتبر",
+       "november": "نوامبر",
+       "december": "دساÙ\85بر",
        "january-gen": "أڤأل قأهارھ",
        "february-gen": "لیریشگوٙن",
        "march-gen": "ئنهزینوٙن",
        "october-gen": "مالبارکوٙنوٙن",
        "november-gen": "ئا سأردکوٙنوٙن",
        "december-gen": "ئا ریجیکنوٙن",
-       "jan": "أڤأل قأھارھ",
-       "feb": "Ù\84Û\8cرÛ\8cØ´Ú¯Ù\88Ù\99Ù\86",
-       "mar": "ئنهزینوٙن",
-       "apr": "نۉروٙزماھ",
-       "may": "گوٙلبار ماھ",
-       "jun": "جوٙریش",
-       "jul": "میڤە رأسوٙن",
-       "aug": "مە گأرمە",
-       "sep": "Ø´Û\8cÙ\86Û\8cارÙ\88Ù\99Ù\86",
-       "oct": "مالبارکوٙنوٙن",
-       "nov": "ئا سأردکوٙنوٙن",
+       "jan": "ژانویه",
+       "feb": "Ù\81Ù\88رÛ\8cÙ\87",
+       "mar": "مارس",
+       "apr": "آوریل",
+       "may": "مه",
+       "jun": "ژوئن",
+       "jul": "ژوئیه",
+       "aug": "اوت",
+       "sep": "سپتاÙ\85بر",
+       "oct": "اکتبر",
+       "nov": "نوامبر",
        "dec": "دسامبر",
        "january-date": "ژانویه $1",
        "february-date": "فوریه $1",
        "mypage": "بلگه",
        "mytalk": "گأپ",
        "anontalk": "سی ای آدرس آی پی گپ بزه",
-       "navigation": "هدایت کیردأن",
+       "navigation": "هدایت کردن",
        "and": "&#32;ڤ",
        "faq": "اف آی کیو \" سوالل متداول \"",
        "actions": "عملیه یل",
        "namespaces": "هۉمدیرأنگل",
        "variants": "أنۉاع",
-       "navigation-heading": "مأنۉ ناۉ ۉری",
+       "navigation-heading": "منوی ناوبری",
        "errorpagetitle": "خطا",
        "returnto": "بازگشت ڤە $1.",
        "tagline": "زھ {{SITENAME}}",
-       "help": "Ù\87Ù\88Ù\99میاری",
-       "search": "جۉستأن",
-       "searchbutton": "جۉستأن",
+       "help": "Ù\87Ù\8fمیاری",
+       "search": "جُستن",
+       "searchbutton": "جُستن",
        "go": "رو",
-       "searcharticle": "رÛ\89",
+       "searcharticle": "برÙ\8eÙ\87",
        "history": "ڤیرگار ھ بألگە",
        "history_short": "ڤیرگار",
        "updatedmarker": "بهروز وابی تا موقع آخرین سیل کردن مو",
        "print": "چاپ",
        "view": "نما",
        "view-foreign": "نیما مئن  $1",
-       "edit": "ئÛ\8cصلاح",
+       "edit": "اصلاح",
        "edit-local": "اصلاح توضیحتل محلی",
        "create": "درست کردن",
        "create-local": "ڤأندن توٙضیحأتل مأحألی",
        "talkpagelinktext": "گپ",
        "specialpage": "بلگه مخصوص",
        "personaltools": "ئوزارگل سی خۉتی",
-       "talk": "قسە",
+       "talk": "گپ",
        "views": "نمایل",
-       "toolbox": "ئÙ\88زارگÛ\95",
+       "toolbox": "ابزارئÙ\84",
        "imagepage": "دیئن بلگه فایل",
        "mediawikipage": "دیئن بلگه پیوم",
        "templatepage": "دیئن بلگه قالب",
        "protectedpage": "بلگه حفاظت وابیه",
        "jumpto": "پریدن ڤھ:",
        "jumptonavigation": "هدایت کردن",
-       "jumptosearch": "جۉستأن",
+       "jumptosearch": "جُستن",
        "view-pool-error": "وبشید ، سرور بیش زه حد بارگیری وابیه .\nکارورل زیادی ایخن ای بلگنه سل کنن.\nلطفا یه لحظه واسیت قبلیکه به خیت ای بلگنه مجددا سل کیت.\n$1",
        "generic-pool-error": "وبشید ، سرور بیش زه حد بارگیری وابیه .\nکارورل زیادی ایخن ای منوبنه سل کنن.\nلطفا یه لحظه واسیت قبلیکه به خیت ای منوبنه مجددا سل کیت.",
        "pool-timeout": "پایان زمون اتنظار سی قفل",
        "currentevents": "اتفاقل جاری",
        "currentevents-url": "Project:اتفاقل جاری",
        "disclaimers": "ئینکار کنندھ یل",
-       "disclaimerpage": "Project:ئÛ\8cÙ\86کار Ú©Ø§Ø±Û\89أراÙ\86",
+       "disclaimerpage": "Project:تکذÛ\8cبâ\80\8c Ù\86Ù\88Ù\85Ù\90Û\8c Ø¹Ù\85Ù\88Ù\85Û\8c",
        "edithelp": "هوٙمیاری سی ئیصلاح",
        "mainpage": "ولاگ اصلی",
        "mainpage-description": "بألگە أصلی",
        "policy-url": "Project:خط مشی",
-       "portal": "دأرگاھ کارڤأرل",
-       "portal-url": "Project:دأرگاھ کارڤأرل",
+       "portal": "ورودی کاربرئل",
+       "portal-url": "Project:ورودی کاربرئل",
        "privacy": "خط مأشی رازداری",
-       "privacypage": "Project:خط Ù\85أشÛ\8c Ø±Ø§Ø²Ø¯Ø§Ø±Û\8c",
+       "privacypage": "Project:سÛ\8cاست Ù\85حرÙ\85اÙ\86Ù\87",
        "badaccess": "خطا دسترسی",
        "badaccess-group0": "ایسا اجازه انجام کاری که ایخستیده ندارین",
        "badaccess-groups": "او کاری که ایسا درخواست کردین فقط سی کارورانیه که مئنه ای  گروهن  {{PLURAL:$2|آن گروه|یکی زه گروه یل}}: $1.",
        "versionrequired": "یه نسخه زه نیازمندی یل ویکی مدیا\n$1",
        "versionrequiredtext": "یه نسخه زه ویکی مدیا($1) نیازمند ه وه استفاده زه ای بلگه\nبویین :[[مخصوص:نسخه|نسخه مخصوص]].",
        "ok": "خووه",
-       "retrievedfrom": "بازÛ\8cاÙ\81ت Ø²Ú¾ \"$1\"",
+       "retrievedfrom": "برگرÙ\81تÙ\87 Ù\88Ù\87 Â«$1»",
        "youhavenewmessages": "پیوم نو داری $1 ($2).",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|ایشا داریت}} $1 زه {{PLURAL:$3|یه کارور دیه|$3 کارورل}} ($2).",
        "youhavenewmessagesmanyusers": "ایشا $1 زه کارورل دیه داریت ($2).",
        "createaccount-title": "اکانت سازی سی {{SITENAME}}",
        "login-abort-generic": "ورود ایشا ناموفق وابی - سقط وابی",
        "loginlanguagelabel": "زوٙوٙن:$1",
-       "pt-login": "ئÛ\89Û\8cدأÙ\86 Ú¤Ú¾ Ø³Û\8cستÙ\85",
+       "pt-login": "داخÙ\8fÙ\84 Ù\88ابÛ\8cدÙ\86",
        "pt-login-button": "ئۉیدن ۉھ سیستم",
-       "pt-createaccount": "راس Ú©Û\8cردأÙ\86 Ø­Û\8cسآÛ\89",
+       "pt-createaccount": "دÙ\8fرÙ\8fس Ú©Ù\90ردÙ\8eÙ\86 Ø­Ø³Ø§Ø¨",
        "pt-userlogout": "رأتن زھ سیستم",
        "changepassword": "تغییر رمز",
        "resetpass_announce": "سی پایان ورود ، ایشا واسی یه رمز جدید سیخوت به ونی.",
        "rev-delundel": "قابلیأت تأغییر دائن",
        "history-title": "تاریخچه اصلاحل $1",
        "difference-title": "فرخ ۉا بین تجدید نطرل \"$1\"",
-       "lineno": "سأطر $1:",
+       "lineno": "سطر $1:",
        "editundo": "لأغڤ",
        "diff-multi-sameuser": "({{PLURAL:$1|یه ۉرزن متۉسط|$1 ۉرژنل متۉسط}} تۉسط کارۉر مشابه نشۉ نۉابیه)",
        "searchresults": "نأتیجل جۉستأن",
        "right-writeapi": "ئیستفادھ د نڤشتن ڤە صوٙرأت API",
        "newuserlogpage": "سیاهە راس کیردأن حیسآۉ",
        "enhancedrc-history": "ڤیرگار",
-       "recentchanges": "تأغÛ\8cÛ\8cرÙ\84 Ù\86Û\89",
+       "recentchanges": "تغÛ\8cÛ\8cرئÙ\84 Ù\86Ù\88",
        "recentchanges-legend": "گۉزینە یل تأغییرل أخیر",
        "recentchanges-summary": "شیار تأغییرل أخیر مئنە ئی بألگە ڤە ڤیکی .",
        "recentchanges-label-newpage": "ئی ئیصلاح یە بألگە نوٙ ئیسازھ",
        "nolinkstoimage": "ای پرونده مِن هیچ صفحه‌ای و کار نرته.",
        "sharedupload-desc-here": "ئی فایل ز $1 ئوٙمائە ڤ شاید د پۉرۉجە یل دیە مورد ئیستفادھ ڤابین.\nتوٙضیحتل ری [$2 بألگە تۉضیح فایل] دوٙمین نیشۉ ڤابیە .",
        "upload-disallowed-here": "ئیشا نیتأریت ئی فایلنە بینڤیسیت",
-       "randompage": "بألگە بأختە کی",
+       "randompage": "ولاگ شانسی",
        "nbytes": "$1 {{PLURAL:$1|بایت|بایتل}}",
        "nmembers": "$1 {{PLURAL:$1|عضۉ|اعضۉل}}",
        "newpages": "بألگە یل نوٙ",
        "month": "مئنھ ای ماھ (ۉ قبل زھ ھۉ):",
        "year": "مئنھ ای سال (ۉ قبل زھ ھۉ):",
        "sp-contributions-submit": "جُستن",
-       "whatlinkshere": "لینکل ئی بألگە",
+       "whatlinkshere": "لینکئل ای ولاگ",
        "whatlinkshere-title": "بألگل کە لینک دائنە ڤە \"$1\"",
        "whatlinkshere-page": "بألگە:",
        "linkshere": "لینک ھ بألگل دوٙمین الذیکر ڤە '''$2''':",
        "tooltip-pt-preferences": "ترجیحئل {{GENDER:|ایشا}}",
        "tooltip-pt-watchlist": "لیست بألگلی کە ئیشا تأغییرل هۉنۉنە  دۉنبال ئیکۉنین",
        "tooltip-pt-mycontris": "لیست سأھمیل ئیشا",
-       "tooltip-pt-login": "توٙصیە ڤابوٙھ کە ڤە سیستم داخل بوٙین. أما ئیجباری نیسس",
+       "tooltip-pt-login": "توصیه ایکنیم که وه سیستم وارد وابید، گرچه اجباری نیسی",
        "tooltip-pt-logout": "رأتن زھ سیستم",
        "tooltip-pt-createaccount": "توٙصیە ڤابوٙھ کە حسآڤ کارڤأری راس بکنیت یا ڤە سیستم داخل بوٙین. اما ئیجباری نیسس",
-       "tooltip-ca-talk": "قسە د بألگە مۉحتڤا",
+       "tooltip-ca-talk": "گپئل دربارهٔ محتوِی ولاگ",
        "tooltip-ca-edit": "ایسھ ترین ای بلگھ نھ اصلاح کنیت.لطفا قبل اصلاح ای بلگھ ز دۉکمه پیش نمایش استفاده کنیت",
        "tooltip-ca-addsection": "ئاغاز کیردأن یە قسمت نوٙ",
        "tooltip-ca-viewsource": "ئی بألگە دوٙمین حیمایأتە. \nئیشا تأرین سأرچیشمە سە بیخوٙنیت",
        "tooltip-ca-history": "ڤیرگار",
        "tooltip-ca-move": "جابجاکردن ای بلگه",
        "tooltip-ca-watch": "ئیضاف کردن ئی بألگە ڤە لیست پیگیری یل ئیشا",
-       "tooltip-search": "جۉستأن {{SITENAME}}",
-       "tooltip-search-go": "رÛ\89 Ù\85أئÙ\86Ù\87 Ø¨Ø£Ù\84Ú¯Û\95 Ø¦Û\8c Ú¤Ø§ Ø¦Û\8c Ù\86Û\89Ù\85 Ø£Ø± Ù\87Û\8cسس",
-       "tooltip-search-fulltext": "جۉستأن بألگە یل سی ئی مأتن",
+       "tooltip-search": "جُستن {{SITENAME}}",
+       "tooltip-search-go": "أر Ø§Û\8cترÛ\8cد Ù\88Ù\87 Ù\88Ù\84اگÛ\8c Ø¨Û\8c Ù\87Ù\85Û\8c Ù\86Ù\88Ù\88Ù\85 Ø¨Ø±Û\8cد.",
+       "tooltip-search-fulltext": "جُستن ای جمله مِن ولاگئل",
        "tooltip-p-logo": "رۉ د بألگە أصلی",
        "tooltip-n-mainpage": "رۉ د بألگە أصلی",
        "tooltip-n-mainpage-description": "رۉ د بألگە أصلی",
        "tooltip-n-recentchanges": "سیائل تأغییرل آخر مئن ئی ڤیکی",
        "tooltip-n-randompage": "سڤار کردن یە بألگە بأختە کی",
        "tooltip-n-help": "ینە جا سی سیل کردن",
-       "tooltip-t-whatlinkshere": "فهرست همە بألگە یل ڤیکی کە ئیچوٙ لینک دارن",
+       "tooltip-t-whatlinkshere": "فهرست همِی صفحئلی که وه ای صفحه پیوند ایخرن",
        "tooltip-t-recentchangeslinked": "تأغییرل آخر مئن بألگە کە لینک دانە ڤە ئی بألگە",
        "tooltip-feed-atom": "تأغذیە کچک تأرین جۉزء  ئی بألگە",
        "tooltip-t-contributions": "لیست مشارکتئل توسط {{GENDER:$1|این کاربر}}",
        "tooltip-t-upload": "بلم گیر کردن فایلل",
-       "tooltip-t-specialpages": "بألگە یل ڤیجە",
-       "tooltip-t-print": "Ù\88Û\8cرÚ\98Ù\86 Ø³Û\8c Ú\86اپ Ø¦Û\8c Ø¨Ø£Ù\84Ú¯Û\95",
+       "tooltip-t-specialpages": "فهرستی وه همی ولاگئل ویژه",
+       "tooltip-t-print": "Ù\86سخÙ\90Û\8c Ù\82ابÙ\84 Ú\86اپ Ø§Û\8c ØµÙ\81Ø­Ù\87",
        "tooltip-t-permalink": "لینکل دائمی ڤە ئی ۉیرژن ئی بألگە",
        "tooltip-ca-nstab-main": "دیئن بألگە مۉحتڤا",
        "tooltip-ca-nstab-user": "دیئن بألگە کارڤأر",
        "monthsall": "همه",
        "semicolon-separator": "؛&#32;",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|گأپ]])",
-       "specialpages": "بألگە یل ڤیجە",
+       "specialpages": "ولاگئل ویژه",
        "tag-filter": "[[Special:Tags|بأرچأسب]] فیلتر:",
        "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|بأرچسب|بأرچسبل}}]]: $2",
        "logentry-delete-delete": "$1 {{GENDER:$2|حأذف ڤابیدھ}} بألگە $3",
        "logentry-move-move": "$1 {{GENDER:$2|انتقال دادھ بیه}} بلگه $3 ۉھ $4",
        "logentry-newusers-create": "حسآۉ کارڤأر $1 ڤابیە {{GENDER:$2|راس ڤیدھ }}",
        "logentry-upload-upload": "$1 {{GENDER:$2|بلم گیر کردھ ۉابی}} $3",
-       "searchsuggest-search": "جÙ\8fستÙ\8eÙ\86Ù\92",
+       "searchsuggest-search": "جÙ\8fستÙ\86",
        "specialmute": "بی‌صدا",
        "userlogout-continue": "ایخیت برِیِتو وَدَر"
 }
index 12f69f0..b56f065 100644 (file)
@@ -64,6 +64,7 @@
        "tog-norollbackdiff": "Jan tampilan pabedoan sasudah malakukan pangambalian",
        "tog-useeditwarning": "Ingekan denai jikok maninggakan laman suntiang sabalun manyimpan parubahan",
        "tog-prefershttps": "Selalu gunokan koneksi aman katiko masuak log",
+       "tog-showrollbackconfirmation": "Tampilkan konfirmasi katiko mangklik pautan pangambalian",
        "underline-always": "Taruih",
        "underline-never": "Indak pernah",
        "underline-default": "Kulik atau pangaturan paramban web",
        "returnto": "Baliak ka $1",
        "tagline": "Dari {{SITENAME}}",
        "help": "Bantuan",
+       "help-mediawiki": "Bantuan pakaro MediaWiki",
        "search": "Cari",
        "searchbutton": "Cari",
        "go": "Tuju",
        "searcharticle": "Tuju",
        "history": "Riwayaik laman",
-       "history_short": "Riwayaik",
-       "history_small": "riwayaik",
+       "history_short": "Versi",
+       "history_small": "versi",
        "updatedmarker": "alah diubah samanjak kunjuangan tarakhia Sanak",
        "printableversion": "Versi cetak",
        "permalink": "Pautan parmanen",
        "nospecialpagetext": "<strong>Sanak mamintak laman istimewa nan indak sah.</strong>\n\nDaftar laman istimewa nan sah dapek dicaliak di [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Kasalahan",
        "databaseerror": "Kasalahan basis data",
+       "databaseerror-text": "Ado gangguan pado kueri basis data. Iko mungkin manunjuakkan adonyo kasalahan pado parangkek lunak.",
+       "databaseerror-textcl": "Ado gangguan pado kueri basis data.",
+       "databaseerror-query": "Kueri: $1",
        "databaseerror-function": "Fungsi: $1",
        "databaseerror-error": "تېروتنه: $1",
        "transaction-duration-limit-exceeded": "Untuak mancagah panundoan replikasi nan tinggi, pangiriman ko dibatalan karano lamo panulihan $1 malabiahi bateh $2.\nJiko sanak nio maubah banyak hal dalam sakali ubah, cubo lakuan dalam operasi nan labiah ketek.",
        "cannotdelete-title": "Indak dapek mangapuih laman \"$1\"",
        "delete-scheduled": "Laman \"$1\" dijadwalan untuak diapuih. Mohon basaba.",
        "delete-hook-aborted": "Pengapusan batal jo hook.\nIndak ado keterangan.",
+       "no-null-revision": "Indak dapek mambuek paubahan kosong nan baru untuak laman \"$1\"",
        "badtitle": "Judul indak sah",
        "badtitletext": "Pamintaan judul laman indak sah, kosong, atau antarbaso atau antarwiki nan salah sambuang. Mungkin juo ado kandungan karakter nan indak buliah digunoan untuak judul.",
        "title-invalid-empty": "Judul laman nan dimintak kosong atau hanyo barisi namo sabuah ruang namo.",
        "botpasswords-label-cancel": "Batalan",
        "botpasswords-label-delete": "Hapuih",
        "botpasswords-label-resetpassword": "Setel ulang kato sandi",
+       "botpasswords-label-grants-column": "Izin diagiah",
        "botpasswords-bad-appid": "Namo bot \"$1\" indak sah.",
        "botpasswords-insert-failed": "Gagal manambah namo bot \"$1\". Alah ditambahan sabalun iko?",
        "botpasswords-update-failed": "Gagal mampabarui namo bot \"$1\". Pernah dihapuih sabalunnyo?",
        "botpasswords-updated-body": "Kato sandi untuak bot \"$1\" dari {{GENDER:$2|user}} \"$2\" alah dipabarui.",
        "botpasswords-deleted-title": "Kato sandi bot dihapuih",
        "botpasswords-deleted-body": "Kato sandi untuak bot \"$1\" dari {{GENDER:$2|user}} \"$2\" berhasil dihapuih.",
+       "botpasswords-newpassword": "Kato sandi baru untuak masuak log jo <strong>$1</strong> adolah <strong>$2</strong>. <em>Cataiklah kato sandi ko untuak rujuakan ka muko.</em> <br> (Untuak bot lamo nan mamaralukan namo masuak log nan samo jo namo pangguno, dapek manggunokan <strong>$3</strong> sabagai namo pangguno jo <strong>$4</strong> sabagai kato sandi.)",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider indak tasadio.",
        "botpasswords-restriction-failed": "Bateh dalam kato sandi mangahalangi masuak log ko.",
        "botpasswords-invalid-name": "Namo pangguno nan diagiah indak manganduang pamisah kato sandi bot (\"$1\").",
        "botpasswords-not-exist": "Pangguno \"$1\" indak mampunyoi kato sandi bot banamo \"$2\".",
        "passwordreset-emailelement": "Namo pangguno: \n$1\n\nSandi samantaro: \n$2",
        "passwordreset-emailsentemail": "Jiko alamaik surel ko bahubuangan jo akun Sanak, surel parubahan kato sandi akan dikirim.",
        "passwordreset-emailsentusername": "Jiko ado alamaik surel nan bahubuangan jo namo pangguno ko, surel untuak mangatua ulang kato sandi akan dikirim.",
+       "passwordreset-nocaller": "Paimbau musti disadiokan",
+       "passwordreset-nosuchcaller": "Paimbau indak ado: $1",
+       "passwordreset-ignored": "Pamuliahan kato sandi indak tatangani. Mungkin panyadio indak diatua?",
        "passwordreset-invalidemail": "Alamaik surel indak sah",
        "passwordreset-nodata": "Namo pangguno ataupun alamai surel indak diaagiahan",
        "changeemail": "Tuka atau hapuih alamaik surel.",
        "changeemail-throttled": "Sanak alah acok bana mancubo masuak log. Mohon tunggu $1 sabalun mancubo baliak.",
        "changeemail-nochange": "Mohon masuakan alamaik surel nan lain.",
        "resettokens": "Ubah token",
+       "resettokens-text": "Sanak dapek ma-reset Token nan mamungkinkan akses ka data pribadi tatantu nan takaik jo akun Sanak di siko.\n\nSanak musti malakukannyo kok Sanak sacaro indak sangajo babagi jo urang lain atau kok akun Sanak alah disalinoki.",
+       "resettokens-no-tokens": "Indak ado token untuak di-reset.",
+       "resettokens-tokens": "Token:",
        "resettokens-token-label": "$1 (nilai saat ini:$2)",
+       "resettokens-watchlist-token": "Token untuak sindikasi web (Atom/RSS) dari [[Special:Watchlist|parubahan di daftar pantauan Sanak]]",
+       "resettokens-done": "Reset token.",
+       "resettokens-resetbutton": "Reset token nan dipiliah",
        "bold_sample": "Teks taba",
        "bold_tip": "Teks taba",
        "italic_sample": "Teks miriang",
        "previewerrortext": "Ado nan salah wakatu manunjuakan pratinjau parubahan Sanak.",
        "blockedtitle": "Pangguno diblokir",
        "blocked-email-user": "<strong>Namo pangguno Sanak diblokir untuak mangirim surel. Sanak masih bisa manyuntiang laman lain di wiki ko. </strong> Sanak bisa mancaliak parincian sakek pado [[Special:MyContributions|jariah pangguno]].\n\nSakek dilakuan dek $1.\n\nAlasannyo adolah <em>$2</em>.\n\n* Disakek sajak: $8\n* Sakek kadaluarsa pado: $6\n* Sasaran panyakek: $7\n* ID sakek #$5",
+       "blockedtext-partial": "<strong>Namo pangguno Sanak disakek untuak mangirim surel. Sanak masih bisa manyuntiang laman lain di wiki ko. </strong> Sanak bisa mancaliak parincian sakek pado [[Special:MyContributions|jariah pangguno]].\n\nSakek dilakuan dek $1.\n\nAlasannyo adolah <em>$2</em>.\n\n* Disakek sajak: $8\n* Sakek kadaluarsa pado: $6\n* Sasaran panyakek: $7\n* ID sakek #$5",
        "blockedtext": "'''Namo pangguno atau alamaik IP Sanak alah kanai sakek.'''\n\nSakek dibuek dek $1.\nAlasan nan diagiahan adolah ''$2''.\n\n* Kanai sakek sajak: $8\n* Maso sakek habih pado: $6\n* Sasaran nan disakek: $7\n\nSanak dapek maubungi $1 atau [[{{MediaWiki:Grouppage-sysop}}|panguruih lainnyo]] untuak marundiangan hal ko.\n\nSanak indak dapek manggunoan fitua 'Kirim surel ka pangguno ko' kacuali Sanak alah mamasuakan alamaik surel nan sah di [[Special:Preferences|pangaturan akun]] dan Sanak indak sadang disakek untuak manggunoannyo.\n\nAlamaik IP Sanak adolah $3, dan ID panyakek adolah $5.\nTolong saratoan informasi di ateh pado satiok patanyoan nan Sanak buek.",
        "autoblockedtext": "Alamaik IP Sanak alah kanai sakek sacaro otomatih dek dipakai jo pangguno lain, nan alah disakek dek $1. Alasannyo dek:\n\n:<em>$2</em>\n\n* Kanai sakek sajak: $8\n* Maso sakek habih pado: $6\n* Sasaran nan disakek: $7\n\nSanak dapek maubuangi $1 atau [[{{MediaWiki:Grouppage-sysop}}|panguruih lainnya]] untuak marundiangan pakaro ko.\n\nSanak indak dapek manggunoan pakakeh \"{{int:emailuser}}\" kacuali Sanak alah mamasuakan alamaik surel nan sah pado [[Special:Preferences|pangaturan akun]] dan Sanak indak sadang disakek untuak manggunoannyo.\n\nAlamaik IP Sanak adolah $3, dan ID panyakekan adolah $5.\nTolong saratoan informasi di ateh pado satiok patanyaan nan Sanak buek.",
+       "systemblockedtext": "Namo pangguno atau alamaik IP Sanak alah disakek sacaro otomatis dek MediaWiki.\nAlasan nan diagiah adolah:\n\n:<em>$2</em>\n\n* Disakek sajak: $8\n* Sakek kadaluwarsa pado: $6\n* Sasaran panyakekan: $7\n\nAlamaik IP Sanak kini adolah $3\nMohon saratokan sadoalah parincian di ateh dalam satiok patanyoan nan Sanak ajukan.",
        "blockednoreason": "indak ado alasan nan diagiah.",
+       "blockedtext-composite-ids": "Panyakekan ID relevan: $1 (alamaik IP Sanak juo dapek dicekal)",
+       "blockedtext-composite-no-ids": "Alamaik IP Sanak muncua dalam daftar itam gando",
+       "blockedtext-composite-reason": "Ado panyakekan bagando ka bakeh akun Sanak dan/atau alamaik IP Sanak.",
        "whitelistedittext": "Sanak musti $1 untuak manyuntiang laman.",
        "confirmedittext": "Sanak musti mangkonfirmasian alamaik surel sabalun manyuntiang laman.\nMasuakan dan validasian alamaik surel Sanak pado [[Special:Preferences|pangaturan pangguno]] Sanak.",
        "nosuchsectiontitle": "Bagian indak ditamuan",
        "userjsonyoucanpreview": "<strong>Tip:</strong> Gunoan tombol \"{{int:showpreview}}\" untuak mauji JSON baharu Sanak sabalun manyimpannyo.",
        "userjsyoucanpreview": "'''Tips:''' Gunoan tombol \"{{int:showpreview}}\" untuak mauji JS baharu Sanak sabalun manyimpannyo.",
        "usercsspreview": "<strong>Ingeklah bahawa Sanak sadang manampilan pratinjau dari CSS Sanak.\nPratinjau ko alun disimpan!</strong>",
+       "userjsonpreview": "<strong>Ingeklah baso nan Sanak caliak anyolah pratonton JavaScript Sanak, dan pratonton tasabuik alun disimpan!</strong>",
        "userjspreview": "<strong>Ingeklah bahawa nan Sanak liek hanyolah pratinjau JavaScript Sanak, dan pratinjau tasabuik alun disimpan!</strong>",
        "sitecsspreview": "<strong>Ingeklah Sanak hanyo manampilan pratinjau dari CSS ko. Parubahan alun disimpan!</strong>",
        "sitejsonpreview": "<strong>Ingeklah bahwa Sanak hanyo manampilan pratinjau konfigurasi JSON ko. Parubahan alun basimpan!</strong>",
        "yourtext": "Teks Sanak",
        "storedversion": "Versi tasimpan",
        "editingold": "'''Paringatan:\nSanak manyuntiang revisi lamo suatu laman.\nJikok Sanak manyimpannyo, parubahan-parubahan nan dibuek sajak revisi ko akan hilang.'''",
+       "unicode-support-fail": "Tampaknyo masin pancari Sanak indak mandukuang Unicode, nan manjadi syaraik panyuntiangan laman. Jadi suntiangan Sanak indak disimpan.",
        "yourdiff": "Pambedoan",
        "copyrightwarning": "Untuak diingek bahaso apo nan disumbang kapado {{SITENAME}} dianggap lah dilapeh di bawah $2 (caliak $1 untuak langkoknyo).\nJikok awak indak ingin apo nan ditulih tu disuntiang dan disebaran, jan dikirim tulisan tu ka siko.<br />\nAwak musti bajanji juo bahaso iko adolah asia karya awak surang, atau disalin dari sumber miliak basamo atau sumber bebas lainnyo.\n'''Jan dikirim karya bahak cipta nan indak baizin!'''",
        "copyrightwarning2": "Parhatikan sadoalah jariah tahadok {{SITENAME}} dapek disuntiang, diubah, atau dihapuih dek panyumbang lainnyo. Jikok Sanak indak ingin tulisan Sanak disuntiang urang lain, jan kiriman ka siko.<br />Sanak juo bajanji iko adolah hasil karya Sanak surang, atau disalin dari sumber miliak umum atau sumber bebas nan lain (liek $1 untuak informasi labiah lanjuik). '''JAN KIRIMAN KARYA NAN DILINDUNGI HAK CIPTA TANPA IJIN!'''",
        "nocreate-loggedin": "Sanak ndak mampunyoi hak akses untuak mambuek laman baharu.",
        "sectioneditnotsupported-title": "Panyuntiangan bagian indak didukuang",
        "sectioneditnotsupported-text": "Panyuntiangan bagian indak didukuang di laman suntiang iko.",
+       "modeleditnotsupported-title": "Panyuntiangan indak didukuang",
+       "modeleditnotsupported-text": "Panyuntiangan indak didukuang untuak model konten $1.",
        "permissionserrors": "Kasalahan Hak Akses",
        "permissionserrorstext": "Sanak indak ado hak untuak malakuannyo dek {{PLURAL:$1|alasan}} barikuik:",
        "permissionserrorstext-withaction": "Sanak indak punyo hak akses untuak $2, dek {{PLURAL:$1|alasan}} barikuik:",
        "edit-no-change": "Suntiangan sanak ditulak, karano indak ado parubahan nan tajadi ka teks.",
        "edit-slots-cannot-add": "{{PLURAL:$1|slot is|slots are}} barikuik indak didukuang disiko: $2.",
        "edit-slots-cannot-remove": "{{PLURAL:$1|slot is|slots are}} wajib dan indak  buliah dihapuih: $2.",
+       "edit-slots-missing": "{{PLURAL:$1|slot is|slots are}} barikuik indak didukuang disiko: $2.",
        "postedit-confirmation-created": "Laman alah dibuek.",
        "postedit-confirmation-restored": "Laman alah dipuliahan.",
        "postedit-confirmation-saved": "Suntiangan Sanak alah tasimpan.",
        "invalid-content-data": "Data kanduangan indak valid.",
        "content-not-allowed-here": "Isi \"$1\" indak diizinkan di laman [[:$2]] pado slot \"$3\"",
        "editwarning-warning": "Maninggakan laman ko dapek maakibaikan parubahan nan dibuek hilang. Jikok Sanak lah masuak log, dapek mamatian pasan ko malalui bagian \"{{int:prefs-editing}}\" pado laman Pangaturan.",
+       "editpage-invalidcontentmodel-title": "Model konten indak didukuang",
+       "editpage-invalidcontentmodel-text": "Model konten \"$1\" indak didukuang.",
+       "editpage-notsupportedcontentformat-title": "Format konten indak didukuang",
+       "editpage-notsupportedcontentformat-text": "Format konten $1 indak didukuang dek model konten $2.",
        "slot-name-main": "Utamo",
        "content-model-wikitext": "Teks wiki",
        "content-model-text": "Teks kosong",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objek kosong",
        "content-json-empty-array": "Lariak kosong",
+       "unsupported-content-model": "<strong> Paringatan: </strong> Model konten $1 indak didukuang di wiki ko.",
+       "unsupported-content-diff": "Panyuntiangan indak didukuang untuak model konten $1.",
+       "unsupported-content-diff2": "Pabedaan antaro model konten $1 jo $2 indak didukuang di wiki ko.",
        "deprecated-self-close-category": "Laman nan menggunoan tag HTML tatutuik-surang indak sah",
+       "deprecated-self-close-category-desc": "Laman ko manganduang tag HTML tatutuik-surang nan indak sah, sarupo <code>&lt;b/></code> atau <code>&lt;span/></code>.  Parilaku tag sarupo ko ka sagiro barubah supayo konsisten jo spesifikasi HTML5, jadi panggunoannyo dalam teks wiki indak lai disarankan.",
        "expensive-parserfunction-warning": "'''Paringatan:''' Laman ko manganduang talalu banyak panggilan fungsi parser.\n\nSeharusnyo kurang dari $2 {{PLURAL:$2|panggilan}}, tapi {{PLURAL:$1|kini ado $1 panggilan}}.",
        "expensive-parserfunction-category": "Laman nan talalu banyak panggilan fungsi parser",
        "post-expand-template-inclusion-warning": "'''Peringatan:''' Ukuran templat talalu gadang.\nBabarapo templat akan diabaikan.",
        "undo-failure": "Suntiangan ko indak dapek dibatalan dek konflik panyuntiangan antaro.",
        "undo-norev": "Suntiangan ko indak dapek dibatalan dek laman indak ditamukan atau lah dihapuih.",
        "undo-summary": "Mambatalan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|maota]])",
+       "undo-summary-anon": "Baliakan revisi $1 dek [[Special:Contributions/$2|$2]]",
        "cantcreateaccount-text": "Mambuek akun dari alamat IP ko ('''$1''') alah diblok jo [[User:$3|$3]].\n\nAlasan nan diagiah jo $3 adolah ''$2''",
        "viewpagelogs": "Caliak log untuak laman ko",
        "nohistory": "Indak ado sajarah panyuntiangan untuak laman ko",
        "action-writeapi": "manggunoan panulisan API",
        "action-import": "impor laman dari wiki lain",
        "nchanges": "$1 {{PLURAL:$1|parubahan}}",
-       "enhancedrc-history": "riwayaik",
+       "enhancedrc-history": "versi",
        "recentchanges": "Parubahan baru",
        "recentchanges-legend": "Piliahan parubahan baru",
        "recentchanges-summary": "Caliak parubahan baru di wiki pado laman ko.<br />\n;Patunjuak:(<span style=\"color:blue;\">bedo</span>) parubahan, (<span style=\"color:blue;\">sijarah</span>) riwayaik parubahan, '''B''' laman baru, '''b''' suntiangan bot, '''k''' suntiangan ketek, <span class=\"unpatrolled\">!</span> parubahan alun dipatroli,<br /><span style=\"color:green;\">'''(+ ''bita'')'''</span> isi laman batambah, <span style=\"color:red;\">(- ''bita'')</span> isi laman bakurang, (← Ikhtisar otomatih), (→ <span style=\"color:grey;\">Suntiangan bagian</span>)",
index 7540d5c..17a46a4 100644 (file)
        "ok": "ꯌꯥꯔꯦ",
        "retrievedfrom": "\"$1\" ꯃꯐꯝꯗꯨꯗꯒꯤ ꯑꯣꯏꯔꯛꯄꯥ",
        "youhavenewmessages": "{{PLURAL:$3|ꯅꯪꯉꯣꯟꯗ ꯂꯩ}} $1 ($2) ꯫",
-       "youhavenewmessagesfromusers": "{{PLURAL:$4|You have}} $1 from {{PLURAL:$3|another user|$3 users}} ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|ꯅꯪꯅꯥ}} $1 ꯗꯒꯤ {{PLURAL:$3|ꯑꯇꯣꯞꯄ ꯁꯤꯖꯤꯟꯅꯔꯤꯕ|$3 ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯁꯤꯡ}} ($2).",
        "youhavenewmessagesmanyusers": "ꯅꯪ $1 ꯂꯩꯔꯦ $2 ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ ꯃꯌꯥꯝꯗꯒꯤ ꯫",
        "newmessageslinkplural": "{{PLURAL:$1|ꯑꯅꯧꯕ ꯄꯥꯎꯖꯦꯜ ꯱|꯹꯹꯹=ꯑꯅꯧꯕ ꯄꯥꯎꯖꯦꯜꯁꯤꯡ}}",
        "newmessagesdifflinkplural": "ꯑꯔꯣꯏꯕꯥ {{PLURAL:$1|ꯑꯍꯣꯡꯕ|꯹꯹꯹=ꯑꯍꯣꯡꯕꯁꯤꯡ}}",
        "showdiff": "ꯑꯍꯣꯡꯕꯗꯨ ꯎꯨꯠꯂꯨ",
        "blankarticle": "<strong>Warning:</strong> The page you are creating is blank.\nIf you click \"$1\" again, the page will be created without any content.",
        "anoneditwarning": "<strong>Warning:</strong> ꯅꯪ ꯃꯅꯨꯡ ꯆꯪꯗꯔꯤ꯬꯬ ꯫ Your IP address will be publicly visible if you make any edits. If you <strong>[$1 log in]</strong> or <strong>[$2 create an account]</strong>, your edits will be attributed to your username, along with other benefits.",
-       "loginreqlink": "Chang Sinba",
+       "blockedtext": "<strong>ꯅꯪꯒꯤ ꯁꯤꯖꯤꯟꯅꯔꯤꯕ-ꯃꯃꯤꯡ ꯅꯠꯇꯔꯒ ꯑꯥꯏꯄꯤ ꯑꯦꯗꯔꯦꯁ ꯑꯁꯤ ꯊꯤꯡꯖꯤꯟꯈꯔꯦ ꯫ </strong>\n\nꯑꯊꯤꯡꯕ ꯑꯁꯤ $1ꯅꯥ ꯁꯦꯝꯕꯅꯤ ꯫\nꯃꯔꯝꯗꯨ ꯃꯁꯤꯗ ꯄꯤꯔꯦ<em>$2</em>.\n\n* ꯊꯤꯡꯕ ꯍꯧꯕ: $8\n* ꯊꯤꯡꯕ ꯂꯣꯏꯕ ꯃꯇꯝ: $6\n* ꯑꯇꯝꯅꯅ ꯊꯤꯡꯕ: $7\n\nꯅꯪꯅꯥ $1 ꯄꯥꯎ ꯐꯥꯎꯅꯕ ꯌꯥꯅꯤ ꯅꯠꯇꯔꯒ ꯑꯇꯣꯞꯄ  [[{{MediaWiki:Grouppage-sysop}}|ꯆꯨꯞꯂꯤꯄꯥꯏꯔꯤꯕꯁꯤꯡ]]ꯗ ꯑꯊꯤꯡꯕꯗꯨꯒꯤ ꯃꯇꯥꯡꯗ ꯈꯟꯅ ꯅꯩꯅꯕ ꯫\nꯅꯪꯅꯥ ꯁꯤꯖꯤꯟꯅꯕ ꯌꯥꯔꯣꯏ \"{{int:emailuser}}\" ꯅꯪꯒꯤ ꯑꯦꯀꯥꯎꯟꯗꯨꯒꯤ ꯏꯃꯦꯜ ꯑꯦꯗꯔꯦꯁ ꯑꯃꯥ ꯍꯥꯞꯇ꯭ꯔꯤꯈꯩ [[Special:Preferences|ꯑꯦꯀꯥꯎꯟ ꯀꯔꯝꯕꯗ ꯈꯟꯒꯅꯤ]] ꯑꯃꯁꯨꯡ ꯃꯗꯨꯗꯗꯤ ꯁꯤꯖꯤꯟꯅꯕꯗ ꯊꯤꯡꯗꯦ ꯫\nꯅꯪꯒꯤ ꯍꯧꯖꯤꯛ ꯑꯥꯏꯄꯤ ꯑꯦꯗꯔꯦꯁ ꯁꯤ $3ꯅꯤ, ꯑꯃꯁꯨꯡ ꯊꯤꯡꯈꯤꯕ ꯑꯥꯏꯗꯤ ꯅꯥ #$5ꯅꯤ ꯫\nꯆꯥꯟꯕꯤꯗꯨꯅꯥ ꯃꯊꯛꯀꯤ ꯑꯀꯨꯞꯄ ꯃꯔꯣꯜꯁꯤ ꯌꯥꯎꯍꯟꯂꯨ ꯅꯪꯅꯥ ꯆꯪꯕ ꯈꯨꯗꯤꯡꯗ ꯫",
+       "loginreqlink": "ꯆꯪꯕ",
        "accmailtitle": "ꯄꯥꯁꯋ꯭ꯔꯇ ꯊꯥꯕ",
        "newarticle": "(ꯑꯅꯧꯕꯥ)",
        "newarticletext": "You have followed a link to a page that does not exist yet.\nTo create the page, start typing in the box below (see the [$1 help page] for more info).\nIf you are here by mistake, click your browser's <strong>ꯍꯟꯕ</strong> button.",
        "noarticletext-nopermission": "There is currently no text in this page.\nYou can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages, or <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span>, but you do not have permission to create this page.",
        "missing-revision": "The revision #$1 of the page named \"{{FULLPAGENAME}}\" does not exist.\n\nThis is usually caused by following an outdated history link to a page that has been deleted.\nDetails can be found in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
        "userpage-userdoesnotexist-view": "$1 ꯁꯤꯖꯤꯟꯅꯔꯤꯕ ꯑꯦꯀꯥꯎꯅ ꯁꯤ ꯃꯤꯡ ꯆꯟꯗꯔꯤ ꯫",
+       "clearyourcache": "<strong>ꯏꯁꯤꯟꯒꯗꯕ:</strong> ꯇꯨꯡꯁꯤꯟꯂꯕ ꯃꯇꯨꯡ, ꯅꯪꯅꯥ ꯑꯍꯣꯡꯕ ꯎꯅꯕ ꯅꯪꯒꯤ ꯕꯔꯥꯎꯁꯔ ꯀꯥꯆꯦ ꯕꯥꯏꯄꯥꯁ ꯇꯧꯔꯣ ꯫\n* <strong>ꯐꯥꯌꯥꯔꯐꯣꯛꯁ / ꯁꯐꯥꯔꯤ:</strong> ꯄꯥꯏꯁꯤꯟꯕ<em>ꯊꯥꯡꯇꯣꯛꯄ</em> ꯅꯝꯃꯤꯉꯩ ꯃꯅꯨꯡꯗ<em>ꯑꯃꯨꯛꯍꯟꯅ-ꯆꯤꯡꯉꯣ</em>, ꯅꯠꯇꯔꯒ ꯅꯝꯃꯣ <em>Ctrl-F5</em> ꯑꯃꯥ ꯍꯦꯛꯇꯥ ꯅꯠꯃꯣ <em>Ctrl-R</em> (<em>⌘-R</em> ꯃꯦꯛ ꯱ ꯇꯥ)\n* <strong>ꯒꯨꯒꯜ ꯀꯔꯣꯝ:</strong> ꯅꯝꯃꯣ <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> ꯃꯦꯛ ꯱ ꯗ)\n* <strong>ꯏꯟꯇꯔꯅꯦꯠ ꯑꯦꯛꯁꯄ꯭ꯂꯣꯔꯔ:</strong> ꯄꯥꯏꯁꯤꯟꯕ <em>Ctrl</em> ꯅꯝꯃꯤꯉꯩ ꯃꯅꯨꯡꯗ <em>ꯇꯦꯈꯠꯍꯟꯂꯨ</em>, ꯅꯠꯇꯔꯒr ꯅꯝꯃꯨ <em>Ctrl-F5</em>\n* <strong>ꯑꯣꯄꯦꯔꯥ:</strong> ꯑꯗꯨꯗ ꯆꯠꯂꯨ <em>ꯃꯤꯅꯨ → ꯁꯦꯝꯐꯝ</em> (<em>ꯑꯣꯄꯦꯔꯥ → ꯀꯔꯝꯕꯗ ꯄꯤꯒꯅꯤ</em> ꯃꯦꯛ ꯱ ꯗꯥ) ꯑꯃꯁꯨꯡ ꯑꯗꯨꯒꯥ <em>ꯑꯔꯣꯟꯕ ꯱ꯁꯨꯡ ꯉꯥꯛꯁꯦꯟ → ꯕꯔꯥꯎꯁꯔ ꯗꯥꯇꯥ ꯀꯣꯛꯊꯣꯛꯄ → ꯀꯥꯆꯦ ꯃꯤꯔꯦꯜꯁꯤꯡ ꯑꯃꯁꯨꯡ ꯐꯥꯏꯜꯁꯤꯡ</em> ꯗꯥ ꯫",
        "updated": "(ꯅꯧꯊꯣꯛꯍꯟꯂꯦ)",
        "note": "<strong>ꯏꯁꯤꯟꯒꯗꯕ:</strong>",
        "continue-editing": "ꯁꯦꯝꯒꯠꯄꯒꯤ ꯃꯐꯝꯗꯨꯗꯥ ꯆꯠꯂꯨ",
        "yourdiff": "ꯈꯦꯠꯅꯕꯥꯁꯤꯡ",
        "copyrightwarning": "Please note that all contributions to {{SITENAME}} are considered to be released under the $2 (see $1 for details).\nIf you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.<br />\nYou are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.\n<strong>Do not submit copyrighted work without permission!</strong>",
        "templatesused": "ꯃꯁꯤꯒꯤ ꯂꯃꯥꯏꯁꯤꯗ ꯁꯤꯖꯤꯟꯅꯕ {{PLURAL:$1|ꯇꯦꯝꯄꯂꯦꯠ|ꯇꯦꯝꯄꯂꯦꯠꯁꯤꯡ}}:",
-       "templatesusedpreview": "{{PLURAL:$1|ꯇꯦꯝꯄꯂꯦꯠ|ꯇꯦꯝꯄꯂꯦꯠꯁꯤꯡ}} ꯄꯔꯤꯕꯤꯌꯨ ꯗ ꯁꯤꯖꯤꯟꯅꯕ:",
+       "templatesusedpreview": "{{PLURAL:$1|ê¯\87ꯦê¯\9dê¯\84ê¯\82ꯦꯠ|ê¯\87ꯦê¯\9dê¯\84ê¯\82ꯦꯠê¯\81ꯤꯡ}} ê¯\83ê¯\81ꯤê¯\92ꯤ ê¯\84ê¯\94ꯤê¯\95ꯤê¯\8cꯨ ê¯\97 ê¯\81ꯤê¯\96ꯤê¯\9fê¯\85ê¯\95:",
        "template-protected": "(ꯉꯥꯛꯊꯣꯛꯂꯕꯥ)",
        "template-semiprotected": "(ꯇꯪꯈꯥꯏ ꯉꯥꯛꯊꯣꯛꯂꯕꯥ)",
        "hiddencategories": "This page is a member of {{PLURAL:$1|1 hidden category|$1 hidden categories}}:",
        "histlast": "ꯑꯅꯧꯕꯥ",
        "historyempty": "(ꯑꯍꯥꯡꯕ)",
        "history-feed-title": "ꯄꯨꯋꯥꯔꯤ ꯑꯃꯨꯛ ꯍꯟꯅ ꯌꯦꯡꯕ",
+       "history-feed-description": "ꯋꯤꯀꯤꯗ ꯃꯁꯤꯒꯤ ꯂꯃꯥꯏꯁꯤꯒꯤ ꯑꯃꯨꯛꯍꯟꯅ-ꯌꯦꯡꯕ ꯄꯨꯋꯥꯔꯤ",
        "history-feed-item-nocomment": "$2 ꯗ$1",
        "rev-delundel": "ꯑꯍꯣꯡꯕꯥ ꯎꯍꯟꯂꯤꯕꯥ",
        "rev-showdeleted": "ꯎꯨꯠꯂꯨ",
        "nextn": "ꯃꯥꯊꯪ{{PLURAL:$1|$1}}",
        "prev-page": "ꯃꯃꯥꯡꯒꯤ ꯂꯃꯥꯏ",
        "next-page": "ꯃꯊꯪ ꯂꯃꯥꯏ",
-       "prevn-title": "ꯃꯃꯥꯡꯒꯤ $1 {{PLURAL:$1|result|results}}",
+       "prevn-title": "ꯃꯃꯥꯡꯒꯤ $1 {{PLURAL:$1|ꯑꯣꯏꯅꯥ ꯐꯪꯕ|ꯑꯣꯏꯅꯥ ꯐꯪꯉꯦ}}",
        "nextn-title": "ꯃꯊꯪ $1 {{PLURAL:$1|ꯐꯣꯜ|ꯐꯣꯜꯁꯤꯡ}}",
        "shown-title": "ꯎꯠꯂꯨ $1 {{PLURAL:$1|result|results}} ꯂꯃꯥꯏ ꯑꯃꯝ ꯑꯃꯝꯒꯤ ꯑꯣꯏꯅꯥ",
        "viewprevnext": "ꯎꯨꯇꯂꯨ ($1 {{int:pipe-separator}} $2) ($3)",
        "search-relatedarticle": "ꯃꯔꯤꯂꯩꯅꯔꯦ",
        "searchrelated": "ꯃꯔꯤꯂꯩꯅꯔꯦ",
        "searchall": "ꯄꯨꯂꯞ",
-       "search-showingresults": "{{PLURAL:$4|Result <strong>$1</strong> of <strong>$3</strong>|Results <strong>$1 – $2</strong> of <strong>$3</strong>}}",
+       "search-showingresults": "{{PLURAL:$4| ꯑꯣꯏꯅꯥ ꯐꯪꯕ <strong>$1</strong> ꯒꯤ <strong>$3</strong>|ꯑꯣꯏꯅꯥ ꯐꯪꯕꯁꯤꯡ<strong>$1 – $2</strong> ꯒꯤ <strong>$3</strong>}}",
        "search-nonefound": "ꯃꯁꯤꯒꯤ ꯐꯣꯜꯁꯤꯒꯥ ꯆꯥꯟꯅꯕꯥ ꯂꯩꯇꯦ ꯫",
        "powersearch-legend": "ꯈꯨꯃꯥꯡ ꯆꯥꯎꯁꯤꯟꯅ ꯊꯤꯕꯥ",
        "powersearch-togglelabel": "ꯑꯁꯣꯏ ꯑꯔꯥꯟ ꯌꯥꯎꯕꯔ ꯌꯦꯡꯕ:",
        "right-read": "ꯂꯃꯥꯏꯁꯤꯡ ꯄꯥꯕꯥ",
        "right-edit": "ꯂꯃꯥꯏꯁꯤꯡ ꯁꯦꯝꯒꯠꯄ",
        "right-writeapi": "API sijinaduna eba",
-       "newuserlogpage": "ꯁꯤꯖꯤꯅꯅꯔꯤꯕ creation log",
+       "newuserlogpage": "ꯁꯤꯖꯤꯟꯅꯔꯤꯕ ꯆꯪꯕꯗꯨ ꯁꯥꯕ",
+       "rightslog": "ꯁꯤꯖꯤꯟꯅꯔꯤꯕ ꯆꯪꯕ ꯍꯛꯁꯤꯡ",
        "action-edit": "ꯃꯁꯤꯒꯤ ꯂꯃꯥꯏꯁꯤ ꯁꯦꯝꯒꯠꯂꯨ",
        "action-createaccount": "ꯃꯁꯤ ꯁꯤꯖꯤꯟꯅꯔꯤꯕ ꯑꯦꯀꯥꯎꯟ ꯁꯤ ꯁꯦꯝꯃꯨ",
        "enhancedrc-history": "ꯄꯨꯋꯥꯔꯤ",
        "recentchanges-label-unpatrolled": "ꯃꯁꯤꯒꯤ ꯁꯦꯝꯒꯠꯄꯁꯤ ꯍꯧꯖꯤꯛꯐꯥꯎ ꯌꯦꯡꯁꯤꯟꯗ꯭ꯔꯤ",
        "recentchanges-label-plusminus": "ꯕꯥꯏꯠꯀꯤ ꯑꯍꯣꯡꯕꯒꯤ ꯃꯇꯪ ꯏꯟꯅꯥ ꯂꯥꯃꯥꯏꯁꯤꯒꯤ ꯑꯆꯧꯕꯥ ꯂꯦꯞꯄꯤ",
        "recentchanges-legend-heading": "<strong>ꯊꯥꯏꯅꯗꯒꯤ</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (also see [[Special:NewPages|ꯑꯅꯧꯕ ꯂꯃꯥꯏꯁꯤꯡ ꯄꯔꯤꯡ]])",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ꯁꯤꯁꯨ ꯌꯦꯡꯉꯨ [[Special:NewPages|ꯑꯅꯧꯕ ꯂꯃꯥꯏꯁꯤꯡ ꯄꯔꯤꯡ]])",
        "rcnotefrom": "ꯃꯈꯥ {{PLURAL:$5|is the change|are the changes}} since <strong>$3, $4</strong> (up to <strong>$1</strong> shown).",
        "rclistfrom": "$2$3 ꯁꯤꯗꯒꯤ ꯍꯧꯔꯒꯥ ꯑꯅꯧꯕꯥ ꯑꯍꯣꯡꯕꯗꯨ ꯎꯨꯇꯂꯨ",
        "rcshowhideminor": "$1 ꯄꯤꯛꯅꯥ ꯁꯦꯝꯒꯠꯄꯁꯤꯡ",
        "filehist-comment": "ꯑꯄꯥꯝꯕꯥ ꯐꯣꯡꯗꯣꯛ ꯎ",
        "imagelinks": "ꯐꯥꯏꯜꯒꯤ ꯁꯤꯖꯤꯟꯅꯐꯝ",
        "linkstoimage": "ꯃꯇꯨꯡ ꯏꯟꯕ {{PLURAL:$1|ꯂꯃꯥꯏꯁꯤꯖꯤꯟꯅꯕ|$1ꯂꯃꯥꯏ ꯁꯤꯖꯤꯟꯅꯕ}} ꯃꯁꯤꯒꯤ ꯐꯥꯏꯜ:",
-       "linkstoimage-more": "$1 ê¯\97ê¯\92ꯤ ê¯\8dꯦê¯\9fê¯\85 {{PLURAL:$1|ê¯\82ê¯\83ꯥê¯\8f ê¯\81ꯤê¯\96ꯤê¯\9fê¯\85ê¯\90ê¯\9d|page use}} ê¯\83ê¯\81ꯤ ê¯\90ꯥê¯\8fê¯\9c ê¯«\nThe following list shows the {{PLURAL:$1|ê¯\91ê¯\8dꯥê¯\9fê¯\95 ê¯\82ê¯\83ꯥê¯\8f|first $1 pages}} that use this file only.\nA [[Special:WhatLinksHere/$2|ꯄꯔꯤꯡ ꯄꯨꯂꯞ]] ꯁꯤ ꯐꯪꯉꯦ ꯫",
+       "linkstoimage-more": "$1 ê¯\97ê¯\92ꯤ ê¯\8dꯦê¯\9fê¯\85 {{PLURAL:$1|ê¯\82ê¯\83ꯥê¯\8f ê¯\81ꯤê¯\96ꯤê¯\9fê¯\85ê¯\95ê¯\81ꯤꯡ|ê¯\82ê¯\83ꯥê¯\8f ê¯\81ꯤê¯\96ꯤê¯\9fê¯\85ê¯\95}} ê¯\83ê¯\81ꯤê¯\92ꯤ ê¯\90ꯥê¯\8fê¯\9cê¯\81ꯤê¯\97 ê¯«\nê¯\83ê¯\88ꯥê¯\92ꯤê¯\81ꯤê¯\85ꯥ ê¯\84ê¯\94ꯤꯡê¯\81ꯤê¯\85ꯥ ê¯\8eꯠê¯\82ꯤê¯\95ê¯\81ꯤ {{PLURAL:$1|ê¯\91ê¯\8dꯥê¯\9fê¯\95 ê¯\82ê¯\83ꯥê¯\8f|ê¯\91ê¯\8dꯥê¯\9fê¯\95 $1 ê¯\82ê¯\83ꯥê¯\8fê¯\81ꯤꯡ}} ê¯\90ꯥê¯\8fê¯\9c ê¯\81ꯤê¯\96ꯤê¯\9fê¯\85ê¯\95 ê¯\88ꯧê¯\87ê¯\85ꯤ ê¯«\n[[Special:WhatLinksHere/$2|ꯄꯔꯤꯡ ꯄꯨꯂꯞ]] ꯁꯤ ꯐꯪꯉꯦ ꯫",
        "nolinkstoimage": "ꯃꯁꯤꯒꯤ ꯐꯥꯏꯜ ꯁꯤ ꯁꯤꯖꯤꯟꯅꯕ ꯂꯃꯥꯏꯁꯤꯡ ꯂꯩꯇꯦ ꯫",
        "linkstoimage-redirect": "$1 (ꯐꯥꯏꯜ ꯱ꯗꯒꯤ ꯱ ꯗ ꯂꯥꯛꯍꯟꯕ) $2",
        "sharedupload-desc-here": "ꯃꯁꯤꯒꯤ ꯐꯥꯏꯜ ꯑꯁꯤ  $1 ꯗꯒꯤꯅꯤ ꯑꯃꯁꯨꯡ ꯑꯇꯩ ꯊꯧꯔꯥꯁꯁꯤꯡꯅꯥ ꯁꯤꯖꯤꯟꯅꯩ ꯫ ꯃꯁꯤꯗ ꯁꯟꯗꯣꯛꯅꯥ ꯇꯥꯛꯄ ꯑꯁꯤ  [$2 ꯐꯥꯏꯜ ꯁꯟꯗꯣꯛꯅꯥ ꯍꯥꯏꯕ ꯂꯃꯥꯏ] ꯃꯈꯥꯒꯤ ꯁꯤꯗ ꯎꯨꯠꯂꯦ ꯫",
        "booksources-search": "ꯊꯤꯕꯥ",
        "specialloguserlabel": "ꯄꯥꯡꯊꯣꯛꯂꯤꯕ ꯃꯤ",
        "log": "ꯆꯪꯕꯥ",
+       "logempty": "ꯃꯁꯤꯒ ꯆꯥꯟꯅꯕ ꯄꯣꯠꯂꯝꯁꯤꯡ ꯆꯪꯗꯦ",
        "allpages": "ꯂꯃꯥꯏꯁꯤꯡ ꯂꯣꯏꯅꯥ",
        "allarticles": "ꯂꯃꯥꯏꯁꯤꯡ ꯂꯣꯏꯅꯥ",
        "allpagessubmit": "ꯆꯠꯂꯨ",
        "allpages-hide-redirects": "ꯃꯥꯏꯀꯩ ꯄꯤꯔꯧꯄꯗꯨ ꯂꯣꯌꯂꯨ",
        "categories": "ꯃꯊꯪ ꯃꯅꯥꯎ ꯈꯥꯏꯗꯣꯛꯄꯥ",
+       "listgrouprights-members": "(ꯊꯧꯃꯤꯁꯤꯡ ꯄꯔꯤꯡ)",
        "emailuser": "ꯃꯁꯤ ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯁꯤ ꯏ-ꯃꯦꯜ ꯊꯥꯖꯤꯟꯂꯨ",
        "watchlist": "ꯌꯦꯡꯂꯤꯕ ꯄꯥꯥꯔꯦꯡ",
        "mywatchlist": "ꯌꯦꯡꯅꯕ ꯄꯔꯤꯡ",
        "namespace": "ꯃꯥꯃꯤꯡꯒꯤ ꯃꯐꯝ:",
        "invert": "ꯈꯟꯂꯤꯕꯗꯨ ꯃꯀꯣꯛꯇꯒꯤ ꯂꯥꯛꯍꯟꯕ",
        "tooltip-invert": "Akhannaba maming gi manungda page tungi ahongba lotnaba oopu du yeng ngoo",
-       "namespace_association": "Maming eefam ga marileinaba",
+       "namespace_association": "ꯃꯃꯤꯡ ꯏꯐꯝꯒ ꯃꯔꯤꯂꯩꯅꯕ",
        "tooltip-namespace_association": "Oopu du yengoo maming eefam gi hiramga mari leinaba khangatlaba maming eefam amadi wa ngangfam manung channaba",
        "blanknamespace": "(ꯃꯔꯨꯑꯣꯏꯕ)",
        "contributions": "{{GENDER:$1|ꯁꯤꯖꯤꯟꯅꯔꯤꯕ}} ꯈꯣꯝꯒꯠꯂꯛꯄꯁꯤꯡ",
        "tooltip-t-recentchangeslinked": "ꯃꯁꯤꯒꯤ ꯂꯃꯥꯏꯁꯤꯒꯥ ꯃꯔꯤ ꯂꯩꯅꯕꯥ ꯍꯧꯖꯤꯛꯀꯤ ꯑꯍꯣꯡꯕꯥ ꯂꯥꯃꯥꯏꯁꯤꯡ",
        "tooltip-feed-atom": "ꯂꯃꯥꯏꯁꯤꯒꯤ ꯃꯁꯥ ꯃꯇꯣꯝꯇꯥ ꯌꯣꯛꯈꯠꯂꯛꯄꯥ",
        "tooltip-t-contributions": " {{GENDER:$1|ꯃꯁꯤꯒꯤ ꯁꯤꯖꯤꯟꯅꯔꯤꯕ}} ꯑꯁꯤ ꯅꯥ ꯈꯣꯝꯖꯤꯟꯂꯛꯂꯤꯕꯥ ꯄꯥꯔꯦꯡ ꯱",
+       "tooltip-t-emailuser": "ꯏꯃꯦꯜ ꯱ ꯊꯥꯎ {{GENDER:$1|ꯃꯁꯤꯒꯤ ꯁꯤꯖꯤꯟꯔꯤꯕ}}ꯁꯤꯗ",
        "tooltip-t-upload": "ꯐꯥꯏꯜꯁꯤꯡ ꯊꯥꯒꯠꯂꯨ",
        "tooltip-t-specialpages": "ꯑꯈꯟꯅꯕ ꯂꯥꯃꯥꯏꯁꯤꯡꯒꯤ ꯄꯥꯔꯦꯡ ꯱",
        "tooltip-t-print": "ꯅꯝꯕ ꯌꯥꯕ ꯃꯑꯣꯡꯒꯤ ꯂꯃꯥꯏ",
-       "tooltip-t-permalink": "Amuk han na yengba lamaisigi Lengdaba Samnafam",
+       "tooltip-t-permalink": "ꯃꯃꯨꯛ ꯍꯟꯅ ꯌꯦꯡꯕ ꯂꯃꯥꯏꯒꯤ ꯂꯦꯡꯗꯕ ꯁꯝꯅꯐꯝ",
        "tooltip-ca-nstab-main": "ꯂꯃꯥꯏꯁꯤꯒꯤ ꯑꯌꯥꯎꯕꯁꯤꯡꯗꯨ ꯎꯨꯇꯂꯨ",
        "tooltip-ca-nstab-user": "ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ ꯂꯥꯃꯥꯏꯁꯤ ꯌꯦꯡꯕꯥ",
        "tooltip-ca-nstab-special": "ꯃꯁꯤ ꯑꯈꯟꯅꯕꯥ ꯂꯃꯥꯏꯅꯤ, ꯁꯦꯝꯒꯠꯄꯥ ꯌꯥꯔꯣꯏ",
        "tooltip-ca-nstab-mediawiki": "ꯊꯧꯁꯤꯜꯒꯤ ꯑꯣꯏꯕ ꯄꯥꯎꯖꯦꯜꯗꯨ ꯎꯨꯠꯂꯨ",
        "tooltip-ca-nstab-template": "ꯇꯦꯝꯄꯂꯦꯠ ꯇꯨ ꯎꯨꯠꯂꯨ",
        "tooltip-ca-nstab-category": "ꯃꯆꯥꯈꯥꯏꯕ ꯂꯃꯥꯏꯗꯨ ꯎꯨꯠꯂꯨ",
+       "tooltip-minoredit": "ꯃꯁꯤꯗꯤ ꯑꯄꯤꯛꯄ ꯁꯦꯝꯒꯠꯄꯅꯤ",
        "tooltip-save": "ꯅꯪꯒꯤ ꯑꯍꯣꯡꯕꯗꯨ ꯇꯨꯡꯁꯤꯟꯂꯨ",
        "tooltip-preview": "ꯅꯪꯒꯤ ꯑꯍꯣꯡꯕꯗꯨ ꯑꯃꯨꯛ ꯍꯟꯅꯥ ꯎꯠꯂꯨ. ꯆꯥꯟꯕꯤꯗꯨꯅꯥ ꯃꯁꯤ ꯍꯥꯟꯅꯥ ꯁꯤꯖꯤꯅꯧ ꯇꯪꯨꯁꯤꯟꯗ꯭ꯔꯤꯉꯧꯗꯥ ꯫",
        "tooltip-diff": "ꯅꯪꯅꯥ ꯏꯔꯤꯕꯥ ꯄꯥꯔꯦꯡꯗꯨꯗꯥ ꯑꯍꯣꯡꯕꯥ ꯎꯠꯂꯨ",
        "pageinfo-lasttime": "ꯅꯧꯔꯤꯕ ꯁꯦꯝꯒꯠꯄꯒꯤ ꯆꯩꯆꯠ",
        "pageinfo-edits": "ꯑꯄꯨꯟꯕ ꯁꯦꯝꯒꯠꯄꯒꯤ ꯃꯁꯤꯡ",
        "pageinfo-authors": "ꯑꯄꯨꯟꯕ ꯑꯈꯟꯅꯕ ꯑꯌꯤꯕꯁꯤꯡꯒꯤ ꯃꯁꯤꯡ",
+       "pageinfo-recent-edits": "ꯀꯨꯏꯗꯔꯤꯕ ꯁꯦꯝꯒꯠꯄ ꯃꯁꯤꯡ($1 ꯐꯥꯎ ꯃꯅꯨꯡꯗ)",
        "pageinfo-magic-words": "ꯃꯦꯖꯤꯛ {{PLURAL:$1|ꯋꯥꯍꯩ|ꯋꯥꯍꯩꯁꯤꯡ}} ($1)",
        "pageinfo-hidden-categories": "ꯂꯣꯠꯍꯟꯕ {{PLURAL:$1|category|ꯃꯆꯥꯛꯈꯥꯏꯕ}} ($1)",
        "pageinfo-templates": "ꯇ꯭ꯔꯥꯟꯁꯀꯂꯨꯗꯦꯗ {{PLURAL:$1|ꯇꯦꯝꯄꯂꯦꯠ|ꯇꯦꯝꯄꯂꯦꯠꯁꯤꯡ}} ($1)",
index b6fa2af..cd64fb3 100644 (file)
        "blocklist-timestamp": "အချိန်တံဆိပ်",
        "blocklist-target": "ပစ်မှတ်",
        "blocklist-expiry": "သက်တမ်းကုန်လွန်မည်",
-       "blocklist-by": "á\80\95á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80·á\80º á\80¡á\80\80á\80ºá\80\92á\80\99á\80­á\80\94á\80º",
+       "blocklist-by": "á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80·á\80º á\80\85á\80®á\80\99á\80¶á\80\81á\80\94á\80·á\80ºá\80\81á\80½á\80²á\80\9eá\80°",
        "blocklist-params": "ပိတ်ပင်မှု ပါရာမီတာများ",
        "blocklist-reason": "အကြောင်းပြချက်",
        "ipblocklist-submit": "ရှာဖွေရန်",
index dd6aa2e..3365b11 100644 (file)
        "redirectedfrom": "(Redirect 'a $1)",
        "redirectpagesub": "Paggena 'e redirect",
        "redirectto": "Reindirizza a:",
-       "lastmodifiedat": "Sta paggena fuje cagnàta ll'urdema vota 'o $1, 'e $2.",
+       "lastmodifiedat": "Sta paggena venette cagnàta ll'urdema vota 'o $1, 'e $2.",
        "viewcount": "Chesta paggena è stata liggiùta {{PLURAL:$1|una vòta|$1 vòte}}.",
        "protectedpage": "Paggena prutetta",
        "jumpto": "Vaje a:",
        "externaldberror": "Ce sta n'errore ch' 'e server d'autenticazione esterno, o pure nun v'è permesso accedere all'aghiurnamento d' 'o cunto sterno vuosto.",
        "login": "Tràse",
        "login-security": "Cunferma l'identità",
-       "nav-login-createaccount": "Trasite o criate n'acciesso nuovo",
+       "nav-login-createaccount": "Trasite o criate n'acciesso novo",
        "logout": "Jèsce",
        "userlogout": "Jèsce",
        "notloggedin": "Acciesso nun affettuato",
-       "userlogin-noaccount": "Nun tenite perzine n'acciesso?",
+       "userlogin-noaccount": "Perzine nun tenite n'acciesso?",
        "userlogin-joinproject": "Facite 'o riggistro ncopp'a {{SITENAME}}",
        "createaccount": "Crèa nu cunto nuovo",
        "userlogin-resetpassword-link": "Te sì scurdat' 'a password?",
        "newarticle": "(Nuovo)",
        "newarticletext": "Site ghiuto/a addò nu link 'e na paggena ca nun esiste ancora.\nPe crià sta paggena, accummenciate a scrivere dint'<nowiki/>'a cascia ccà abbascio (vedite 'a [$1 paggena d'aiuto] pe vedè cchiù 'nfurmazziune).\nSi site venuto/a ccà pe sbaglio, vedite 'e sprémmere 'o buttòne '''Arreto''' d'<nowiki/>o navigatóre.",
        "anontalkpagetext": "----\n''Chest'è 'a paggena 'e discussione 'e n'utente anonimo ca ancora nun s'è fatt' n'utenza o ca nun 'a sta ausanno.''\n\nPe' l'identificà avite 'e truvà 'o nummero d' 'o ndirizzo IP d' 'o sujo. L'indirizze IP se ponno spartì però sempe ausanno cunte differente.\n\nSi site n'utente anonimo e penzate ca 'e cummente ccà dint'a sta paggena nun parlano 'e vuje, allora [[Special:CreateAccount|criate n'utenza nnova]] o [[Special:UserLogin|trasite cu chella ca tenite già]] pe' nun sta' mmescato mmiez'a l'ati utente anonime n futuro.",
-       "noarticletext": "Mo' mo' 'a paggena richiesta è abbacante. Se pò [[Special:Search/{{PAGENAME}}|ascià stu titolo]] dint'a l'ati paggene d' 'o sito, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ascià dint'e riggistre azzeccate] o pure [{{fullurl:{{FULLPAGENAME}}|action=edit}} crià 'a paggena mo']</span>.",
-       "noarticletext-nopermission": "Mo' mo' 'a paggena richiesta è abbacante. Se pò [[Special:Search/{{PAGENAME}}|ascià stu titolo]] dint'a l'ati paggene d' 'o sito, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ascià dint'e riggistre azzeccate]</span>, però nun tenite 'o permesso 'a crià sta paggena.",
+       "noarticletext": "Mo mmo 'a paggena richiesta è abbacante. Può [[Special:Search/{{PAGENAME}}|ascià stu titolo]] dint' 'a ll'ati paggene d' 'o sito, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ascià dint'e riggistre azzeccate] o pure [{{fullurl:{{FULLPAGENAME}}|action=edit}} crià 'a paggena 'a via 'e subbeto]</span>.",
+       "noarticletext-nopermission": "Mo mmo 'a paggena richiesta è abbacante. Se pò [[Special:Search/{{PAGENAME}}|ascià stu titolo]] dint' 'a ll'ati paggene d' 'o sito, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ascià dint'e riggistre azzeccate]</span>, però nun tenite 'o permesso 'e crià sta paggena.",
        "missing-revision": "'A verziona #$1 d' 'a paggena \"{{FULLPAGENAME}}\" nun esiste.\n\nChest'è causato quanno se và dint'a nu link a na paggena ch'è stata scancellata.\n'E dettaglie se ponno truvà dint'a [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 'o riggistro 'e scancellamiente].",
        "userpage-userdoesnotexist": "'O cunto utente \"<nowiki>$1</nowiki>\" nun è riggistrato. Cuntrolla ca si buò overo crià o cagnà sta paggena.",
        "userpage-userdoesnotexist-view": "'O cunto utente \"$1\" nun è riggistrato.",
        "blocked-notice-logextract": "St'utente è bloccato mò.\nL'urdemo elemento d' 'o riggistro 'e blocche è ripurtato ccà abbascio p'avé nu riferimento:",
-       "clearyourcache": "<strong>Nota:</strong> aroppo sarvate putisse necessità 'e pulezzà 'a caché d' 'o navigatóre pe' vedé 'e cagnamiente. \n*<strong>Firefox / Safari</strong>: sprémme 'o buttóne maiuscole e ffà clic ncopp'a ''Recarreca'', o pure spremme ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' ncopp'a Mac)\n*<strong>Google Chrome''': spremme ''Ctrl-Shift-R'' (''⌘-Shift-R'' ncopp'a nu Mac)\n*<strong>Internet Explorer</strong>: spremme 'o buttóne ''Ctrl'' pe' tramente ca faie click ncopp'a ''Refresh'', o pure spremmere ''Ctrl-F5''\n* <strong>Opera:</strong> Vaje addò 'o <em>Menu → Mpustaziune</em> (<em>Opera → Mpustaziune</em> ncopp' 'o Mac) e po' ncopp'a <em>Privacy & sicurezza → Pulezza date d' 'o browser → Immaggene e file d' 'a cache</em>.",
+       "clearyourcache": "<strong>Notarella:</strong> aroppo sarvate putisse necessità 'e pulezzà 'a caché d' 'o navigatóre pe vedé 'e cagnamienti. \n*<strong>Firefox / Safari</strong>: sprémme 'o buttóne maiuscole e ffà clic ncopp'a ''Recarreca'', o pure spremme ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' ncopp'a Mac)\n*<strong>Google Chrome''': spremme ''Ctrl-Shift-R'' (''⌘-Shift-R'' ncopp'a nu Mac)\n*<strong>Internet Explorer</strong>: spremme 'o buttóne ''Ctrl'' pe' tramente ca faie click ncopp'a ''Refresh'', o pure spremmere ''Ctrl-F5''\n* <strong>Opera:</strong> Vaje addò 'o <em>Menu → Mpustaziune</em> (<em>Opera → Mpustaziune</em> ncopp' 'o Mac) e po' ncopp'a <em>Privacy & sicurezza → Pulezza date d' 'o browser → Immaggene e file d' 'a cache</em>.",
        "usercssyoucanpreview": "'''Cunziglio:''' spremme 'o buttone 'Vide anteprimma' pe' pruvà 'o CSS nuovo apprimma d' 'o sarvà.",
        "userjsonyoucanpreview": "<strong>Cunziglio:</strong> premme 'o buttone \"{{int:showpreview}}\" pe' pruvà 'o JSON nuovo apprimma d' 'o sarvà.",
        "userjsyoucanpreview": "'''Cunziglio:''' spremme 'o buttone 'Vide anteprimma' pe' pruvà 'o JavaScript nuovo apprimma d' 'o sarvà.",
        "protect-existing-expiry-infinity": "Tiempo d'ammaturamiento: infinito",
        "protect-otherreason": "Ati/cchiù ragiune:",
        "protect-otherreason-op": "Ati ragiune",
-       "protect-dropdown": "*Mutive 'e prutezione comune\n** Vandalisme eccessive\n** Spam eccessivo\n** 'Uerre 'e cagnamiente controproducente\n** Paggena cu troppo traffeco",
+       "protect-dropdown": "*Mutive 'e prutezione comune\n** Vandalisme eccessive\n** Spam eccessivo\n** 'Uerra 'e cagnamienti controproducente\n** Paggena cu assaje traffeco\n** Paggena 'e sistema ausata assai",
        "protect-edit-reasonlist": "Càgna 'e mutive 'e prutezione",
        "protect-expiry-options": "1 ore:1 hour,1 juorno:1 day,1 semmana:1 week,2 semmane:2 weeks,1 mese:1 month,3 mise:3 months,6 mise:6 months,1 anno:1 year,infinito:infinite",
        "restriction-type": "Permesse:",
        "specialpages-note-restricted": "* Paggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Paggene speciale ch' 'e restriziune.</span>",
        "specialpages-group-maintenance": "Report 'e manutenzione",
        "specialpages-group-other": "Ati paggene speciale",
-       "specialpages-group-login": "Trasite o criate n'acciesso nuovo",
+       "specialpages-group-login": "Trasite o criate n'acciesso novo",
        "specialpages-group-changes": "Urdeme cagnamiénte e riggistre",
        "specialpages-group-media": "Riepileghe 'e media e carreche",
        "specialpages-group-users": "Utente e deritte",
index dec927a..4bf2965 100644 (file)
@@ -94,7 +94,7 @@
        "october": "oktober",
        "november": "november",
        "december": "december",
-       "january-gen": "jannewaori",
+       "january-gen": "janri",
        "february-gen": "februåri",
        "march-gen": "meert",
        "april-gen": "april",
        "november-date": "$1 november",
        "december-date": "$1 desember",
        "pagecategories": "{{PLURAL:$1|Kategory|Kategoryen}}",
-       "category_header": "Artikels in kategorie $1",
+       "category_header": "Artikels in kategory $1",
        "subcategories": "Subkategorieën",
        "category-media-header": "Media in kategorie \"$1\"",
        "category-empty": "''In disse kategoria staon op t moment nog gien artikels of media.''",
        "actions": "Haandeling",
        "namespaces": "Naamruumdes",
        "variants": "Varianten",
-       "navigation-heading": "Navigatymenu",
+       "navigation-heading": "Navigatymenü",
        "errorpagetitle": "Foutmelding",
        "returnto": "Weerumme naor $1.",
        "tagline": "Van {{SITENAME}}",
        "permalink": "Vaste verwysing",
        "print": "Aofdrokken",
        "view": "Leasen",
-       "view-foreign": "Bekieken op $1",
+       "view-foreign": "Bekyken up $1",
        "edit": "Bewarken",
        "edit-local": "Lokale beschrieving bewarken",
        "create": "Anmaken",
        "aboutsite": "Oaver {{SITENAME}}",
        "aboutpage": "Project:Info",
        "copyright": "De inhoud is beschikbaor onder de $1 as der niks aanders an-egeven is.",
-       "copyrightpage": "{{ns:project}}:Auteursrechten",
+       "copyrightpage": "{{ns:project}}:Autöörsrechten",
        "currentevents": "In et nys",
        "currentevents-url": "Project:In et nys",
        "disclaimers": "Vöärbehold",
        "youhavenewmessagesmanyusers": "Je hebben $1 van n bulte gebrukers ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|n niej bericht|999=nieje berichten}}",
        "newmessagesdifflinkplural": "leste {{PLURAL:$1|wieziging|999=wiezigingen}}",
-       "youhavenewmessagesmulti": "Jy hebben nye berichten up $1",
+       "youhavenewmessagesmulti": "Jy hebbet nye berichten up $1",
        "editsection": "bewark",
        "editold": "bewark",
        "viewsourceold": "brontekste bekyken",
        "perfcached": "Disse gegevens koemen uut t tussengeheugen en bin misschien niet aktueel. Der {{PLURAL:$1|is hooguut een resultaot|bin hooguut $1 resultaoten}} beschikbaor in t tussengeheugen.",
        "perfcachedts": "Disse gegevens koemen uut t tussengeheugen die veur t lest bie-ewörken is op $2 um $3. Der {{PLURAL:$4|is hooguut een resultaot|bin hooguut $4 resultaoten}} beschikbaor in t tussengeheugen.",
        "querypage-no-updates": "'''Disse zied wördt niet meer bie-ewörken.'''",
-       "viewsource": "Brontekste bekyken",
+       "viewsource": "Brontekst bekyken",
        "viewsource-title": "Bron bekieken van $1",
        "actionthrottled": "Haandeling tegenehöllen",
        "actionthrottledtext": "As maotregel tegen t plaotsen van alderhaande moek, is t antal keren da'j disse haandeling in n korte tied uutvoeren kunnen beteund. Je hebben de limiet overschrejen. Probeer t over n antal minuten weer.",
        "logout": "Afmelden",
        "userlogout": "Aofmelden",
        "notloggedin": "Neet an-emelded",
-       "userlogin-noaccount": "Heb jy noch geen gebrukersname?",
+       "userlogin-noaccount": "Heb jy noch geen brukersname?",
        "userlogin-joinproject": "Wörd lid van {{SITENAME}}",
        "createaccount": "Inskryven",
        "userlogin-resetpassword-link": "Juuw wachtwoord vergeaten?",
        "pt-login": "Anmelden",
        "pt-login-button": "Anmelden",
        "pt-createaccount": "Inskryven",
-       "pt-userlogout": "Ofmelden",
+       "pt-userlogout": "Afmelden",
        "php-mail-error-unknown": "Der was n onbekende fout mit de mail()-funksie van PHP",
        "user-mail-no-addy": "Eprobeerd n berichjen te versturen zonder n netpostadres",
        "user-mail-no-body": "Der is eprobeerd n netbreef zonder tekste of mit n biester korte tekste te versturen.",
        "subject": "Onderwarp:",
        "minoredit": "kleine wysiging",
        "watchthis": "volg disse syde",
-       "savearticle": "Syde upslån",
-       "savechanges": "Wysigingen upslån",
+       "savearticle": "Syde uutgeaven",
+       "savechanges": "Wysigingen uutgeaven",
        "publishpage": "Zied uutbrengen",
-       "publishchanges": "Wysigingen üütbrengen",
+       "publishchanges": "Wysigingen uutgeaven",
        "preview": "Naokieken",
        "showpreview": "Bewarking nåkyken",
        "showdiff": "Verskil bekyken",
        "summary-preview": "Samenvatting naokieken:",
        "subject-preview": "Onderwarp naokieken:",
        "blockedtitle": "Gebruker is eblokkeerd",
-       "blockedtext": "'''Joew gebrukersnaam of IP-adres is eblokkeerd.'''\n\nJe bin eblokkeerd deur: $1.\nDe op-egeven reden is: ''$2''.\n\n* Eblokkeerd vanaof: $8\n* Eblokkeerd tot: $6\n* Bedoeld um te blokkeren: $7\n\nJe kunnen kontakt opnemen mit $1 of n aandere [[{{MediaWiki:Grouppage-sysop}}|beheerder]] um de blokkering te bepraoten.\nJe kunnen gien gebruukmaken van de funksie 'een bericht sturen', behalven a'j n geldig netpostadres op-egeven hebben in joew [[Special:Preferences|veurkeuren]] en t gebruuk van disse funksie niet eblokkeerd is.\nt IP-adres da'j noen gebruken is $3 en t blokkeringsnummer is #$5.\nVermeld t allebeie a'j argens op disse blokkering reageren.",
+       "blockedtext": "<strong>Juw brukersname of IP-adres is blokkeerd.</strong>\n\nJy binnet blokkeerd döär $1.\nDe upgeaven readen is <em>$2</em>.\n\n* Blokkeerd vanaf: $8\n* Blokkeerd tot: $6\n* Bedoold üm te blokkeren: $7\n\nJy künnet kontakt upneamen mid $1 of een andere [[{{MediaWiki:Grouppage-sysop}}|beheyrder]] üm de blokkering te bepråten.\nJy künnet de funkty \"{{int:emailuser}}\" neet bruken, behalven as jy een geldig e-postadres in juw [[Special:Preferences|instellingen]] upgeaven hebbet en et gebruuk van disse funkty neet blokkeerd is.\nEt IP-adres wat jy nu bruket is $3, en et blokkeringsnummer is #$5.\nVermeld de gegeavens dee hyrboaven stån as jy argens up disse blokkering reageren.",
        "autoblockedtext": "Joew IP-adres is automaties eblokkeerd umdat t gebruukt wördt deur n aandere gebruker, die eblokkeerd wördt deur $1.\nDe reden hierveur was:\n\n:''$2''\n\n* Begint: $8\n* Löp of nao: $6\n* Wee eblokkeerd wördt: $7\n\nJe kunnen kontakt opnemen mit $1 of n van de aandere\n[[{{MediaWiki:Grouppage-sysop}}|beheerders]] um de blokkering te bepraoten.\n\nNB: je kunnen de opsie \"n bericht sturen\" niet gebruken, behalven a'j n geldig netpostadres op-egeven hebben in de [[Special:Preferences|gebrukersveurkeuren]] en je niet eblokkeerd bin.\n\nJoew IP-adres is $3 en joew blokkeernummer is $5.\nGeef disse nummers deur a'j kontakt mit ene opnemen over de blokkering.",
        "blockednoreason": "gien reden op-egeven",
        "whitelistedittext": "Um ziejen te kunnen wiezigen, mu'j $1 ween",
        "accmailtext": "Der is n willekeurig wachtwoord veur [[User talk:$1|$1]] verstuurd naor $2. t Kan ewiezigd wörden op de zied ''[[Special:ChangePassword|wachtwoord wiezigen]]'' naoda'j an-emeld bin.",
        "newarticle": "(Niej)",
        "newarticletext": "Disse zied besteet nog niet.\nIn t veld hieronder ku'j wat schrieven um disse zied an te maken (meer informasie vie'j op de [$1 hulpzied]).\nA'j hier per ongelok terechtekeumen bin gebruuk dan de knoppe '''veurige''' um weerumme te gaon.",
-       "anontalkpagetext": "---- ''Disse overlegzied heurt bie n anonieme gebruker die nog gien gebrukersnaam hef, of t niet gebruukt. We gebruken daorumme t IP-adres um hum of heur te herkennen, mer t kan oek ween dat meerdere personen t zelfde IP-adres gebruken, en da'j hiermee berichten ontvangen die niet veur joe bedoeld bin. A'j dit veurkoemen willen, dan ku'j t best [[Special:CreateAccount|n gebrukersnaam anmaken]] of [[Special:UserLogin|anmelden]].''",
+       "anontalkpagetext": "----\n<em>Disse oaverlegsyde höyrt by een anonyme bruker dee noch geen brukersname hevt, of et neet bruukt.</em>\nDårümme bruken wy et IP-adresse ter identifikaty. Een IP-adresse kan döär meyrere lüde bruked wörden. As jy een anonyme bruker binnet, en et gevööl hebbet dat jy berichten kryget dee neet vöär ju bedoold binnet [[Special:CreateAccount|skryv ju eigen dan in]] of [[Special:UserLogin|meld ju eigen an]] üm verwarring mid andere anonyme brukers in de tokumst te vöärkommen.''",
        "noarticletext": "Der steyt nun geen tekst up disse syde.\nJy künnet [[Special:Search/{{PAGENAME}}|de titel upsöken]] in andere syden,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} söken in de logboken],\nof [{{fullurl:{{FULLPAGENAME}}|action=edit}} disse syde anmaken]</span>.",
        "noarticletext-nopermission": "Op disse zied steet gien tekste.\nJe kunnen [[Special:Search/{{PAGENAME}}|zeuken naor disse term]] in aandere ziejen of\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de logboeken deurzeuken]</span>, mer je hebben gien rechten um disse zied an te maken.",
        "missing-revision": "De versie #$1 van de zied \"{{FULLPAGENAME}} besteet niet.\n\nDit kömp meestentieds deur t volgen van n verouwerde verwiezing naor n zied die vortedaon is.\nWaorschienlik ku'j der meer gegevens over vienen in t [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} vortdologboek].",
        "userpage-userdoesnotexist": "Je bewarken n gebrukerszied van n gebruker die niet besteet (gebruker \"<nowiki>$1</nowiki>\"). Kiek effen nao o'j disse zied wel anmaken/bewarken willen.",
        "userpage-userdoesnotexist-view": "Gebruker \"$1\" steet hier niet in-eschreven",
        "blocked-notice-logextract": "Disse gebruker is op t moment eblokkeerd.\nDe leste regel uut t blokkeerlogboek steet hieronder as referensie:",
-       "clearyourcache": "'''Waort je:''' naodat de wiezigingen op-esleugen bin, mut t tussengeheugen van de webkieker nog eleegd wörden um t te kunnen zien. \n*'''Firefox / Safari:''' drok op ''Shift'' terwiel je op ''verniejen'' klikken, of gebruuk ''Ctrl-F5'' of ''Ctrl-R'' (''⌘-R'' op n knipperkiste van Mac)\n* '''Google Chrome:''' drok op ''Ctrl-Shift-R'' (''⌘-Shift-R'' op n knipperkiste van Mac)\n*'''Internet Explorer:''' drok op ''Ctrl'' terwiel je op ''verniejen'' klikken of drok op ''Ctrl-F5''\n*'''Opera:''' leeg t tussengeheugen in ''Extra → Voorkeuren\"",
+       "clearyourcache": "<strong>Upmarking:</strong> nå et seakeren, sül jy lichtkans et tüskengehöägen van juw webkyker mütten leadigen üm de wysigingen te seen.\n* <strong>Firefox / Safari:</strong> hold <em>Shift</em> indrükked terwyl jy up <em>Herladen</em> klikket, of drük up <em>Ctrl-F5</em> of <em>Ctrl-R</em> (<em>⌘-R</em> up een Mac)\n* <strong>Google Chrome:</strong> drük up <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> up een Mac)\n* <strong>Internet Explorer:</strong> hold <em>Ctrl</em> indrükked terwyl jy up <em>Herladen</em> klikket, of drük up <em>Ctrl-F5</em>\n* <strong>Opera:</strong> gå nå <em>Menü → Instellingen</em> (<em>Opera → Vöärköären</em> up een Mac) en dan nå <em>Privaatheid & beveiliging → Söökgeskydenisse wisken → Tydelike afbealdingen en bestanden</em>.",
        "usercssyoucanpreview": "'''Tip:''' gebruuk de knoppe \"{{int:showpreview}}\" um joew nieje css/js nao te kieken veurda'j t opslaon.",
        "userjsyoucanpreview": "'''Tip:''' gebruuk de knoppe \"{{int:showpreview}}\" um joew nieje css/js nao te kieken veurda'j t opslaon.",
        "usercsspreview": "'''Dit is allinnig n naokieksel van joew persoonlike CSS.'''\n'''t Is nog niet op-esleugen!'''",
        "templatesusedpreview": "{{PLURAL:$1|Mal|Mallen}} die in disse bewarking gebruukt wörden:",
        "templatesusedsection": "{{PLURAL:$1|Mal|Mallen}} die in dit subkopjen gebruukt wörden:",
        "template-protected": "(beveiligd)",
-       "template-semiprotected": "(half-beveiligd)",
+       "template-semiprotected": "(halv-beveiligd)",
        "hiddencategories": "Disse zied völt in de volgende verbörgen {{PLURAL:$1|kategorie|kategorieën}}:",
        "edittools": "<!-- Disse tekste steet onder de bewarkings- en bestaandinlaodformulieren. -->",
        "nocreatetext": "Disse webstee hef de meugelikheid um nieje ziejen an te maken beteund. Je kunnen ziejen die al bestaon wiezigen of je kunnen je [[Special:UserLogin|anmelden of n gebrukerszied anmaken]].",
        "prevn-title": "{{PLURAL:$1|Veurig resultaot|Veurige $1 resultaoten}}",
        "nextn-title": "{{PLURAL:$1|Volgend resultaot|Volgende $1 resultaoten}}",
        "shown-title": "Låt $1 {{PLURAL:$1|resultaat|resultaten}} per syde seen",
-       "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3)",
+       "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) bekyken.",
        "searchmenu-exists": "'''Der is n zied mit de naam \"[[:$1]]\" op disse wiki.'''",
        "searchmenu-new": "<strong>De zied \"[[:$1]]\" op disse wiki anmaken!</strong> \n{{PLURAL:$2|0=|Zie oek de zied mit joew zeukresultaoten.|Zie oek de lieste mit evunnen zeukresultaoten.}}",
        "searchprofile-articles": "Artikels",
        "searchprofile-advanced-tooltip": "Söken in de angeaven naamruumden",
        "search-result-size": "$1 ({{PLURAL:$2|1 woord|$2 woorden}})",
        "search-result-category-size": "{{PLURAL:$1|1 kategorielid|$1 kategorielejen}} ({{PLURAL:$2|1 onderkategorie|$2 onderkategorieën}}, {{PLURAL:$3|1 bestaand|$3 bestaanden}})",
-       "search-redirect": "(deurverwiezing vanaof $1)",
+       "search-redirect": "(döärverwysing vanaf $1)",
        "search-section": "(onderwarp $1)",
        "search-file-match": "(kümt oavereyne mid de inhold van et bestand)",
        "search-suggest": "Bedoelden je: $1",
        "searchall": "alles",
        "showingresults": "Hieronder {{PLURAL:$1|steet '''1''' resultaot|staon '''$1''' resultaoten}}  <b>$1</b> vanaof nummer <b>$2</b>.",
        "search-showingresults": "{{PLURAL:$4|Resultaot <strong>$1</strong> van <strong>$2</strong>|Resultaoten <strong>$1 - $2</strong> van <strong>$3</strong>}}",
-       "search-nonefound": "Der bin gien resultaoten veur de zeukopdrachte.",
+       "search-nonefound": "Der binnet geen resultaten vöär de söökupdrachte.",
        "powersearch-legend": "Uutgebreid zeuken",
        "powersearch-ns": "Zeuken in naamruumten:",
        "powersearch-togglelabel": "Selekteren:",
        "action-editmyprivateinfo": "joew eigen priveegegevens bewarken",
        "nchanges": "$1 {{PLURAL:$1|wieziging|wiezigingen}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sinds joew leste bezeuk}}",
-       "enhancedrc-history": "geschiedenisse",
+       "enhancedrc-history": "geskydenisse",
        "recentchanges": "Lätste wysigingen",
        "recentchanges-legend": "Optys vöär lätste wysigingen",
        "recentchanges-summary": "Up disse syde kün jy de lätste wysigingen van disse wiki bekyken.",
        "rcfilters-quickfilters-placeholder-description": "Üm juuw filterinstellingen up te slån en et låter te gebruken, klik up et bladwyserikoon underan by \"Aktive filters\".",
        "rcfilters-savedqueries-apply-label": "Instellingen opslaon",
        "rcfilters-savedqueries-cancel-label": "Aofbreken",
-       "rcfilters-savedqueries-add-new-title": "Filterinstellingen upslån",
+       "rcfilters-savedqueries-add-new-title": "Filterinstellingen seakeren",
        "rcfilters-restore-default-filters": "Standardfilters weerummezetten",
        "rcfilters-clear-all-filters": "Alle filters vortdoon",
        "rcfilters-show-new-changes": "Låt nyste wysigingen seen",
-       "rcfilters-search-placeholder": "Filter wysigingen (gebrüük et menü of söök up filtername)",
+       "rcfilters-search-placeholder": "Filter wysigingen (bruuk et menü of söök up filtername)",
        "rcfilters-filterlist-feedbacklink": "Låt uns weaten wat jy van disse (nye) filterhülpmiddels vinden",
        "rcfilters-highlightbutton-title": "Resultåten markeren",
        "rcfilters-highlightmenu-title": "Kies n kleur",
        "rcfilters-filter-user-experience-level-newcomer-label": "Anwas",
        "rcfilters-filter-user-experience-level-newcomer-description": "An-emelden bewarkers dee minder as 10 bewarkingen edån hebben of 4 dagen aktiv ewesd hebben.",
        "rcfilters-filter-user-experience-level-learner-label": "Leyrlingen",
-       "rcfilters-filter-user-experience-level-learner-description": "An-emelde bewarkers mid meyr ervåring as \"anwas\", mär minder as \"ervåren gebrukers\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Anmeldede bewarkers mid meyr ervaring as \"anwas\", mär minder as \"ervaren brukers\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Ervåren gebrukers",
        "rcfilters-filter-user-experience-level-experienced-description": "An-emelde bewarkers mid meyr as 500 bewarkingen en 30 dagen van aktiviteit.",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-pageedits-label": "Sydbewarkingen",
        "rcfilters-filter-pageedits-description": "Wysigingen an de wiki-inhold, diskussys, kategorybeskryvingen…",
        "rcfilters-filter-newpages-label": "Nye syden",
-       "rcfilters-filter-newpages-description": "Bewarkingen wårmead jy een nye syde anmaken.",
+       "rcfilters-filter-newpages-description": "Bewarkingen wårmead jy een nye syde anmaket.",
        "rcfilters-filter-categorization-label": "Kategorywysigingen",
        "rcfilters-filter-categorization-description": "Upgave van syden dee to-evoogd of vordedån wörden uut kategoryen.",
        "rcfilters-filter-logactions-label": "Eregistreerde aktys",
        "rclistfrom": "Bekyk wysigingen vanaf $3 $2",
        "rcshowhideminor": "$1 kleine wysigingen",
        "rcshowhideminor-show": "Bekiek",
-       "rcshowhideminor-hide": "Verbarg",
+       "rcshowhideminor-hide": "verbargen",
        "rcshowhidebots": "$1 botbrukers",
-       "rcshowhidebots-show": "Bekiek",
+       "rcshowhidebots-show": "bekyken",
        "rcshowhidebots-hide": "Verbarg",
        "rcshowhideliu": "$1 registreerde brukers",
        "rcshowhideliu-show": "Bekiek",
-       "rcshowhideliu-hide": "Verbarg",
-       "rcshowhideanons": "$1 anonime brukers",
+       "rcshowhideliu-hide": "verbargen",
+       "rcshowhideanons": "$1 anonyme brukers",
        "rcshowhideanons-show": "Bekiek",
-       "rcshowhideanons-hide": "Verbarg",
+       "rcshowhideanons-hide": "verbargen",
        "rcshowhidepatr": "$1 nao-ekeken bewarkingen",
        "rcshowhidepatr-show": "Bekiek",
        "rcshowhidepatr-hide": "Verbarg",
        "rcshowhidemine": "$1 myn bewarkingen",
        "rcshowhidemine-show": "Bekiek",
-       "rcshowhidemine-hide": "Verbarg",
+       "rcshowhidemine-hide": "verbargen",
        "rcshowhidecategorization": "$1 kategorisering van ziejen",
        "rcshowhidecategorization-show": "Bekiek",
        "rcshowhidecategorization-hide": "Verbarg",
        "recentchangeslinked-feed": "Volg verwysingen",
        "recentchangeslinked-toolbox": "Volg verwysingen",
        "recentchangeslinked-title": "Wiezigingen verwaant an $1",
-       "recentchangeslinked-summary": "Op disse spesiale zied steet n lieste mit de leste wieziginen op ziejen waornaor verwezen wördt. Ziejen op [[Special:Watchlist|joew volglieste]] staon '''vet'''.",
+       "recentchangeslinked-summary": "Voor een sydname in üm bewarkingen te seen up syden wårnå verweasen wördt of dårnå verwysen. (Voor {{ns:category}}:kategoryname in üm leaden van een kategory te seen). Bewarkingen van syden up [[Special:Watchlist|juw volglyste]] binnet <strong>vetdrükked</strong>.",
        "recentchangeslinked-page": "Ziednaam:",
        "recentchangeslinked-to": "Bekiek wiezigingen op ziejen mit verwiezingen naor disse zied",
        "recentchanges-page-added-to-category": "[[:$1]] bie kategorie ezet",
        "namespace_association": "Bybehöyrende naamruumde",
        "tooltip-namespace_association": "Vink dit vakjen an um ouk de oaverleg- en underwarpnaamruumde in te slüten dee by de selekteerde naamruumde höyret.",
        "blanknamespace": "(Höyvdnaamruumde)",
-       "contributions": "{{GENDER:$1|Gebrukersbydragen}}",
+       "contributions": "{{GENDER:$1|Brukersbydragen}}",
        "contributions-title": "Biedragen van $1",
        "mycontris": "Myn bydragen",
        "anoncontribs": "Bydragen",
        "tooltip-pt-preferences": "{{GENDER:|Miene}} vuurkeuren",
        "tooltip-pt-watchlist": "Lieste van zieden die op miene volglieste stoan",
        "tooltip-pt-mycontris": "Oaverzicht van {{GENDER:|oew}} biejdreagen",
-       "tooltip-pt-login": "Jy wördt van harte uutnöygd üm ju an te melden as bruker, mar et is neet verplicht",
+       "tooltip-pt-login": "Jy wördet van harte uutnöygd üm ju an te melden as bruker, mär et is neet verplicht",
        "tooltip-pt-logout": "Ofmaelden",
        "tooltip-pt-createaccount": "Skryv juw eigen vöäral in en meld juw eigen an. Dit is lykewels neet verplicht.",
        "tooltip-ca-talk": "Låt een oaverlegtekst oaver disse syde seen",
        "yesterday-at": "Gisteren um $1",
        "bad_image_list": "De opmaak is as volgt:\n\nAllinnig regels in n lieste (regels die beginnen mit *) wörden verwarkt.\nDe eerste verwiezing op n regel mut n verwiezing ween naor n ongewunst bestaand.\nAlle volgende verwiezingen die op de zelfde regel staon, wörden behaandeld as uutzundering, zo as ziejen waorop t bestaand in te tekste op-eneumen is.",
        "metadata": "Metadata",
-       "metadata-help": "In dit bestaand zit metadata mit EXIF-informasie, die deur n fotokamera, inleesapparaot of fotobewarkingsprogramma op-estuurd kan ween.",
+       "metadata-help": "Dit bestand bevat ekstra informaty, dee wårskynlik döär een fotokamera, skänner of fotobewarkingsprogramma upladen binnet.\nAs dit betand bewarked is, kommen möägelik bepålde details neet oavereyne mid et ewysigde bestand.",
        "metadata-expand": "Bekiek uutebreiden gegevens",
        "metadata-collapse": "Verbarg uutebreiden gegevens",
        "metadata-fields": "De afbealdingsmetadatavelden in dit bericht ståt ouk up een afbealdingssyde as de metadatatabel inklapped is.\nAndere velden wördet verbörgen.\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",
        "confirm-purge-title": "Herny disse syde",
        "confirm_purge_button": "Bevestig",
        "confirm-purge-top": "Klik up 'bevestig' üm et tüskengehöägen van disse syde te leagen.",
-       "confirm-purge-bottom": "Et leagmaken van et tüskengehöägen sörgt dervöär dat jy de lätste versy van een syde te seen krygen.",
+       "confirm-purge-bottom": "Et leadigmaken van et tüskengehöägen sörgt dervöär dat jy de lätste versy van een syde te seen kryget.",
        "confirm-watch-button": "Oké",
        "confirm-watch-top": "Disse zied op joew volglieste zetten?",
        "confirm-unwatch-button": "Oké",
index eb7b6d5..2ea7cdc 100644 (file)
        "undo-norev": "De bewerking kon niet ongedaan gemaakt worden, omdat die niet bestaat of is verwijderd.",
        "undo-nochange": "De bewerking lijkt al ongedaan gemaakt te zijn.",
        "undo-summary": "Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|overleg]]) ongedaan gemaakt",
+       "undo-summary-anon": "Versie $1 van [[Special:Contributions/$2|$2]] ongedaan gemaakt",
        "undo-summary-username-hidden": "Versie $1 door een verborgen gebruiker ongedaan gemaakt",
        "cantcreateaccount-text": "Registreren vanaf dit IP-adres ('''$1''') is geblokkeerd door [[User:$3|$3]].\n\nDe door $3 opgegeven reden is ''$2''",
        "cantcreateaccount-range-text": "Het aanmaken van gebruikers vanaf IP-adressen in de range <strong>$1</strong> is niet mogelijk doordat dit is ingesteld door [[User:$3|$3]]. Uw IP-adres $4 bevindt zich in deze range.\n\nDe reden voor de blokkade is <em>$2</em>",
        "alreadyrolled": "Het is niet mogelijk om de bewerking van de pagina [[:$1]] door [[User:$2|$2]] ([[User talk:$2|overleg]]{{int:pipe-separator}}[[Special:Contributions/$2|bijdragen]]) ongedaan te maken.\nIemand anders heeft deze pagina al bewerkt of hersteld naar een eerdere versie.\n\nDe meest recente bewerking is gemaakt door [[User:$3|$3]] ([[User talk:$3|overleg]]{{int:pipe-separator}}[[Special:Contributions/$3|bijdragen]]).",
        "editcomment": "De bewerkingssamenvatting was: <em>$1</em>.",
        "revertpage": "Wijzigingen door [[Special:Contributions/$2|$2]] ([[User talk:$2|Overleg]]) hersteld tot de laatste versie door [[User:$1|$1]]",
+       "revertpage-anon": "Wijzigingen door [[Special:Contributions/$2|$2]] teruggedraaid naar de laatste versie door [[User:$1|$1]]",
        "revertpage-nouser": "Wijzigingen door een verborgen gebruiker teruggedraaid naar de laatste versie door {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Wijzigingen door {{GENDER:$3|$1}} ongedaan gemaakt;\nlaatste versie van {{GENDER:$4|$2}} hersteld.",
        "sessionfailure-title": "Sessiefout",
index 75e75f4..e55b753 100644 (file)
        "tog-usenewrc": "ߞߙߎ ߡߊߝߊ߬ߟߋ߲߬ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߐ߯ߟߕߊ ߟߎ߬ ߣߌ߫ ߜߋ߬ߟߎ߲߬ߠߌ߲߬ ߛߙߍߘߍ ߘߐ߫.",
        "tog-numberheadings": "ߕߍ߰ߟߌ ߡߐ߬ߟߐ߲ ߠߎ߬ ߝߙߍߕߍ߫ ߞߍ߲ߖߘߍߡߊߓߟߏߡߊ߬",
        "tog-editondblclick": "ߞߏߜߍ ߟߎ߬ ߡߊߦߟߍ߬ߡߊ߲߫ ߛߐ߲߬ߞߌ߲߬ߠߌ߲߬ߞߏ ߝߌ߬ߟߊ߬ ߟߊ߫",
+       "tog-editsectiononrightclick": "ߕߍߕߎ߲߮ ߞߌߣߌ߲ߝߍ߫ ߡߊߦߟߍߡߊ߲ߠߌ߲ ߕߍ߫ ߕߍߕߎ߲߮ ߞߎ߲߬ߕߐ߮ ߟߊ߫.",
        "tog-watchcreations": "ߒ ߠߊ߫ ߞߐߜߍ߫ ߛߌ߲ߘߌ߫ ߣߍ߲ ߠߎ߬ ߣߌ߫ ߞߐߕߐ߯ ߟߊߦߟߍ߬ߣߍ߲ ߠߎ߬ ߓߌ߬ߟߊ߬ ߒ ߠߊ߫ ߜߋ߬ߟߎ߬ߠߌ߲߬ ߛߙߍߘߍ ߘߐ߫.",
        "tog-watchdefault": "ߒ ߠߊ߫ ߞߐߜߍ߫ ߣߌ߫ ߞߐߕߐ߯ ߡߊߦߟߍ߬ߡߊ߲߬ߣߍ߲ ߠߎ߬ ߓߌ߬ߟߊ߬ ߒ ߠߊ߫ ߜߋ߬ߟߎ߬ߠߌ߲߬ ߛߙߍߘߍ ߘߐ߫.",
        "tog-watchmoves": "ߒ ߠߊ߫ ߞߐߜߍ ߣߌ߫ ߞߐߕߐ߯ ߟߊߕߐߣߍ߲ ߠߎ߬ ߓߌ߬ߟߊ߬ ߒ ߠߊ߫ ߜߋ߬ߟߎ߬ߠߌ߲߬ ߛߙߍߘߍ ߘߐ߫.",
        "tog-watchdeletion": "ߒ ߠߊ߫ ߞߐߜߍ ߣߌ߫ ߞߐߕߐ߯ ߖߏ߬ߛߌ߬ߣߍ߲ ߠߎ߬ ߓߌ߬ߟߊ߬ ߒ ߠߊ߫ ߜߋ߬ߟߎ߬ߠߌ߲߬ ߛߙߍߘߍ ߘߐ߫.",
        "tog-watchuploads": "ߒ ߠߊ߫ ߞߐߕߐ߯ ߟߊߦߟߍ߬ߣߍ߲ ߠߎ߬ ߓߌ߬ߟߊ߬ ߒ ߠߊ߫ ߜߋ߬ߟߎ߬ߠߌ߲߬ ߛߙߍߘߍ ߘߐ߫.",
+       "tog-watchrollback": "ߞߐߜߍ ߟߎ߬ ߟߊߘߏ߲߬ ߒ ߠߊ߫ ߦߟߌߡߊߛߙߋ ߟߛߊ߬ߦߌ߲߬ߣߍ߲߬ ߣߐ߬ߡߊߢߌ߲߬ߧߊ߬ߣߍ߲ ߠߎ߬ ߘߐ߫.",
        "tog-minordefault": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߘߋ߬ߣߍ߲ ߠߎ߬ ߣߐ߬ߣߐ߬ ߓߐߛߎ߲ ߘߌ߫",
        "tog-previewontop": "ߢߍߦߋߟߌ ߦߌ߬ߘߊ߬ ߡߊ߰ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߏ߲ߘߏ ߢߍ߫.",
        "tog-previewonfirst": "ߢߍߦߋߟߌ ߦߌ߬ߘߊ߬ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߝߟߐ ߘߐ߫.",
@@ -57,6 +59,8 @@
        "underline-default": "ߝߊ߬ߘߌ ߥߟߊ߫ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫ ߝߍ߭",
        "editfont-style": "ߕߌ߲߬ߞߎߘߎ߲ ߛߓߍߛߎ߲ ߛߎ߯ߦߊ ߢߊߓߐ߫:",
        "editfont-monospace": "ߞߣߍߙߋ߲ ߛߓߍߛߎ߲",
+       "editfont-sansserif": "ߛߊ߲-ߛߋ߬ߙߌߝ ߞߟߏߜߍ",
+       "editfont-serif": "ߛߋ߬ߙߌߝ ߞߟߏߜߍ",
        "sunday": "ߞߊ߯ߙߌߟߏ߲",
        "monday": "ߞߐ߬ߓߊ߬ߟߏ߲",
        "tuesday": "ߞߐ߬ߟߏ߲",
        "privacy": "ߘߎ߲߬ߘߎ߬ߡߊ߬ ߓߘߍߓߘߍߟߌ",
        "privacypage": "Project:ߘߎ߲߬ߘߎ߬ߡߊ߬ ߓߘߍ߬ߓߘߍ߬ߟߌ",
        "badaccess": "ߟߊ߬ߘߌ߬ߢߍ߬ߟߌ ߝߎ߬ߕߎ߲߬ߕߌ",
+       "badaccess-group0": "ߌ ߣߊ߬ ߞߏ ߡߍ߲ ߡߊߢߌ߬ߣߌ߲߬ߞߊ߬ ߟߴߏ߬ ߘߌ߫߸ ߌ ߟߊߘߌ߬ߢߍ߬ߣߍ߲߬ ߕߍ߫ ߞߵߏ߬ ߞߍ߫.",
        "badaccess-groups": "ߌ ߣߊ߬ ߞߍߢߊ ߡߍ߲ ߡߊߢߌ߬ߣߌ߲߬ߞߊ߬ ߟߊ߫ ߣߌ߲߬߸ ߏ߬ ߟߊߓߊ߯ߙߊ ߡߊߓߍ߲߬ߣߍ߲߫ ߠߋ߫ ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߡߍ߲ ߠߎ߬ ߦߋ߫ {{PLURAL:$2|ߞߙߎ ߘߐ߫|ߞߙߎ ߘߏ߫ ߘߐ߫}}: $1.",
        "versionrequired": "ߡߊߘߌߦߊ߫-ߥߞߌ߫ ߓߊߦߟߍߡߊ߲ߠߌ߲ $1 ߞߊ߬ߣߌ߲߬ ߣߍ߲߫",
        "versionrequiredtext": "ߡߊߘߌߦߊ߫-ߥߞߌ߫ ߘߟߊߡߌߘߊߟߌ $1 ߞߊ߬ߣߌ߲߬ ߣߍ߲߫ ߞߊ߬ ߞߐߜߍ ߣߌ߲߬ ߠߊߓߊ߯ߙߊ߫.\n[[Special:Version|version page]] ߦߋ߫.",
        "nstab-category": "ߦߌߟߡߊ",
        "mainpage-nstab": "ߓߏ߬ߟߏ߲߬ߘߊ",
        "nosuchaction": "ߞߍߟߌ߫ ߛߎ߮ ߏ߬ ߕߴߦߋ߲߬",
+       "nosuchactiontext": "ߞߏ ߡߍ߲ ߞߙߍߞߙߍߣߍ߲ ߦߋ߫ URL ߓߟߏ߫߸ ߏ߬ ߓߍ߲߬ ߣߍ߲߬ ߕߍ߫.\nߌ ߝߟߌ߬ߣߍ߲߫ ߘߌ߫ ߞߍ߫ URL ߛߓߍ߫ ߕߎߡߊ ߟߊ߫߸ ߥߟߊ߫ ߛߘߌ߬ߜߋ߲ ߓߍ߲߬ߓߊߟߌ ߟߋ߬ ߟߊߓߊ߬ߕߏ߬ߣߍ߲߬ ߦߵߌ ߓߟߏ߫.\nߕߎ߬ߡߊ߬ߘߐ߫ ߏ߬ ߝߣߊ߫ ߘߴߊ߬ ߦߌ߬ߘߊ߬ ߞߏ߫ ߝߏ߬ߘߏ ߦߋ߫ ߛߎ߲ߝߘߍ߫ ߟߊߓߊ߯ߙߊߣߍ߲ ߘߐ߫  {{SITENAME}} ߓߟߏ߫.",
        "nosuchspecialpage": "ߘߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲߬ ߛߎ߮ ߏ߬ ߝߋ߲߫ ߕߍ߫ ߦߊ߲߬",
        "nospecialpagetext": "<strong>ߊߟߎ߫ ߓߘߊ߫ ߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߘߏ߫ ߢߌߣߌ߲߫ ߡߍ߲ ߕߺߴߦߋ߲߬.</strong>\nߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲߫ ߓߘߍ߬ߡߊ ߟߎ߬ ߛߙߍߘߍ ߦߋ߫ ߢߌ߲߬ ߠߋ߫ ߞߊ߲߬ [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "ߝߎ߬ߕߎ߲߬ߕߌ",
        "databaseerror": "ߓߟߏߡߟߊ ߟߎ߬ ߝߊ߲ ߝߎ߬ߕߎ߲߬ߕߌ",
+       "databaseerror-text": "ߓߟߏߡߟߊߝߊ߲ ߢߌ߬ߣߌ߲߬ߞߊ߬ߟߌ ߝߎ߬ߕߎ߲߬ߕߌ ߘߏ߫ ߓߘߴߊ߬ ߓߌ߬ߟߵߊ߬ ߘߐ߫.\nߏ߬ ߦߴߊ߬ ߦߌ߬ߘߊ߬ ߟߊ߫ ߟߋ߬ ߞߏ߫ ߝߏ߬ߘߏ ߦߋ߫ ߛߎ߲ߝߘߍ ߘߐ߫.",
        "databaseerror-textcl": "ߓߟߏߡߟߊߝߊ߲ ߢߌ߬ߣߌ߲߬ߞߊ߬ߟߌ ߝߎ߬ߕߎ߲߬ߕߌ ߘߏ߫ ߓߘߊ߫ ߓߌ߬ߟߵߊ߬ ߘߐ߫.",
        "databaseerror-query": "ߢߌ߬ߣߌ߲߬ߞߊ߬ߟߌ $1",
        "databaseerror-function": "ߗߋߘߊ $1",
        "databaseerror-error": "ߝߎ߬ߕߎ߲߬ߕߌ: $1",
+       "laggedslavemode": "<strong>ߖߊ߲߬ߓߌ߬ߟߊ߬ߟߌ</strong> ߟߏ߲ߘߐߦߊߟߌ߫ ߞߎߘߊ߫ ߛߌ߫ ߕߍ߫ ߞߐߜߍ ߘߐ߫.",
        "readonly": "ߓߟߏߡߟߊ ߝߊ߲ ߛߐ߰ߣߍ߲߫",
+       "enterlockreason": "ߛߐ߰ߟߌ ߞߎ߲߭ ߠߊߘߏ߲߬߸ ߞߵߏ߬ ߟߊ߫ ߛߐ߰ߟߌ ߓߊ߲߫ ߕߎߡߊ߫ ߖߊ߬ߕߋߡߌ߬ߘߊ߬ߣߍ߲ ߞߊ߲߬.",
        "missingarticle-rev": "(ߡߛߊ߬ߦߌ߲߬ߠߌ߲#:$1)",
        "missingarticle-diff": "(Diff: $1, $2)",
        "readonly_lag": "ߓߟߏߡߟߊ ߝߊ߲ ߓߘߊ߫ ߛߐ߰ ߞߍߒߖߘߍߦߋ߫ ߓߟߏߡߊ߬߸ ߞߊ߬ߦߌ߯ ߖߐ߲߬ ߓߟߏߡߟߊ ߝߊ߲ ߡߊߛߐߓߊ߮ ߡߌ߬ߣߊ߬ ߘߊ߫ ߞߙߊ߬ߡߐ߮ ߓߟߏ߫.",
        "editpage-cannot-use-custom-model": "ߞߐߜߍ ߣߌ߲߬ ߞߣߐߘߐ ߛߎ߮ߦߊ ߕߍߣߊ߬ ߛߐ߲߬ ߠߊ߫ ߡߊߦߟߍ߬ߡߊ߲߫ ߠߊ߫.",
        "templatesused": "{{PLURAL:$1|ߞߙߊߞߏ|ߞߙߊߞߏ ߟߎ߫}} ߟߎ߫ ߟߊߓߊ߯ߙߊ߫ ߘߊ߫ ߞߐߜߍ ߣߌ߲߬ ߘߐ߫",
        "templatesusedpreview": "{{PLURAL:$1|ߞߙߊߞߏ|ߞߙߊߞߏ ߟߎ߬}} ߟߋ߬ ߟߊߓߊ߯ߙߊ߫ ߣߍ߲߫ ߢߍߦߋߟߌ ߣߌ߲߬ ߘߐ߫",
+       "templatesusedsection": "{{PLURAL:$1|ߞߙߊߞߏ|ߞߙߊߞߏ ߟߎ߬}} ߟߋ߬ ߟߊߓߊ߯ߙߊ߫ ߣߍ߲߫ ߕߍߕߍ߮ ߣߌ߲߬ ߘߐ߫:",
        "template-protected": "(ߊ߬ ߡߊߞߊ߲ߞߊ߲ߣߍ߲߫ ߠߋ߬)",
        "template-semiprotected": "(ߟߊ߬ߞߊ߲߬ߘߊ߬ߟߌ-ߝߊ߲߬ߞߋ߬ߟߋ߲߬ߡߊ)",
        "hiddencategories": "ߞߐߜߍ ߣߌ߲߬ ߦߋ߫ ߢߌ߲߬ ߠߎ߫ ߛߌ߲߬ߝߏ߲ ߠߋ߬ ߘߌ߫{{PLURAL:$1|}}",
+       "nocreatetext": "{{SITENAME}} ߓߘߴߊ߬ ߛߋߞߏߦߊ ߟߊߞߎ߲߬ߛߌ߲߫ ߞߊ߬ ߞߐߜߍ߫ ߞߎߘߊ߫ ߟߊߘߊ߲߫.\nߌ ߘߌ߫ ߛߴߌ ߞߐߛߊ߬ߦߌ߬ ߟߊ߫ ߞߊ߬ ߛߋ߲߬ߠߊ߬ ߞߐߜߍ ߡߊߦߟߍ߬ߡߊ߲߫ ߸ ߥߟߊ߫  [[Special:UserLogin|log in or create an account]].",
        "nocreate-loggedin": "ߞߐߜߍ߫ ߞߎߘߊ߫ ߛߌ߲ߘߌ߫ ߞߏ ߟߊߘߌ߬ߢߍ߬ߣߍ߲߬ ߕߴߌ ߦߋ߫.",
+       "sectioneditnotsupported-title": "ߘߌ߬ߢߍ߬ ߞߍߣߍ߲߫ ߕߍ߫ ߕߍߕߍ߮ ߡߊߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߡߊ߬.",
        "sectioneditnotsupported-text": "ߛߌ߰ߘߊ ߡߊߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߠߊߘߤߊ߬ߣߍ߲߬ ߕߍ߫ ߞߐߜߍ ߣߌ߲߬ ߠߊ߫ ߕߊ߲߬.",
+       "modeleditnotsupported-title": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߟߊߘߌ߬ߢߍ߬ߣߍ߲߬ ߕߍ߫",
+       "modeleditnotsupported-text": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߟߊߘߌ߬ߢߍ߬ߣߍ߲߬ ߕߍ߫ ߞߣߐߘߐ ߛߎ߮ߦߊ $1 ߦߋ߫.",
        "permissionserrors": "ߝߌ߬ߟߌ߫ ߘߌ߬ߢߍ߬ߒߧߋ",
        "permissionserrorstext": "ߌ ߟߊߘߌ߬ߢߍ߬ߣߍ߲߬ ߕߍ߫ ߞߵߏ߬ ߞߍ߫߸ ߣߌ߲߬ ߠߊ߫ {{PLURAL:$1|ߛߊߓߎ|ߛߊߓߎ ߟߎ߬}}:",
        "permissionserrorstext-withaction": "ߟߊ߬ߘߌ߬ߢߍ߬ߟߌ߬ ߛߌ߫ ߕߴߌ ߦߋ߫ ߞߊ߬ $2߸ {{PLURAL:$1|ߞߏߛߐ߲߬|ߟߎ߬ ߞߏߛߐ߲߬}}",
        "contentmodelediterror": "ߌ ߕߍߣߊ߬ ߛߋ߫ ߟߊ߫ ߛߌ߰ߘߊ ߣߌ߲߬ ߡߊߦߟߍ߬ߡߊ߲߬ ߠߊ߫߸ ߓߊߏ߬ ߞߣߐߘߐ ߛߎ߯ߦߊ ߦߋ߫ <code>$1</code> ߟߋ߬ ߘߌ߫߸ ߡߍ߲ ߦߋ߫ ߕߋ߲߬ߕߋ߲߬ ߞߣߐߘߐ ߛߎ߯ߦߊ ߝߘߏ߬ ߟߊ߫ ߞߐߜߍ <code>$2</code> ߘߐ߫.",
        "recreate-moveddeleted-warn": "<strong>ߌ ߖߊ߲߬ߕߏ߫: ߌ ߦߋ߫ ߞߐߜߍ ߘߏ߫ ߟߋ߬ ߟߊߘߊ߲߫ ߞߏ ߘߐ߫ ߣߌ߲߬߸ ߡߍ߲ ߖߏ߬ߛߌ߬ߣߍ߲߬ ߡߎߣߎ߲߬.</strong> \nߌ ߓߛߌ߬ߞߌ߬ ߕߐ߫ ߟߋ߬ ߛߍ߲߸ ߣߴߌ ߘߌ߫ ߛߋ߫ ߞߐߜߍ ߣߌ߲߬ ߡߊߦߟߍ߬ߡߊ߲ ߘߊߓߊ߲߫ ߠߊ߫. \nߞߐߜߍ ߣߌ߲߬ ߦߟߌߣߐ ߖߏ߬ߛߌ߬ߣߍ߲ ߣߴߊ߬ ߛߋ߲߬ߓߐ߬ߣߍ߲ ߠߎ߬ ߡߊߘߊ߲ߣߍ߲߫ ߦߊ߲߬ ߠߋ ߟߊ߬ߣߐ߰ߦߊ߬ߟߌ ߘߌ߫:",
        "moveddeleted-notice": "ߞߐߜߍ ߣߌ߲߬ ߓߘߊ߫ ߖߏ߬ߛߌ߬.\nߖߏ߬ߛߌ߬ߟߌ߸ ߟߊ߬ߞߊ߲߬ߘߊ߬ߟߌ߸ ߊ߬ ߣߌ߫ ߞߐߜߍ ߛߓߍߟߌ ߟߎ߬ ߛߋ߲߬ߓߐ߸ ߏ߬ ߟߎ߫ ߓߍ߯ ߡߊߛߐߣߍ߲߫ ߦߋ߫ ߘߎ߰ߟߊ ߘߐ߫.",
+       "moveddeleted-notice-recent": "ߤߊߞߍ߬ߕߏ߫߸ ߞߐߜߍ ߣߌ߲߬ ߓߊ߲߫ ߛߊ߲߮ ߠߋ߬ ߦߋ߫ ߖߏ߬ߛߌ߬ ߟߊ߫ (ߕߎ߬ߡߊ߬ߙߋ߲߫ ߂߄ ߕߊ߬ߡߌ߲߬ߣߍ߲) ߣߌ߲߬ ߞߘߐ߫.\nߞߐߜߍ ߖߏ߰ߛߌ߬ߟߌ߸ ߟߊ߬ߞߊ߲߬ߘߊ߬ߟߌ߸ ߊ߬ ߣߌ߫ ߘߊ߲ߖߐ ߛߋ߲߬ߓߐ ߓߍ߯ ߡߊߛߐߣߍ߲߫ ߦߋ߫ ߘߎ߰ߟߊ ߘߐ߫ ߦߟߌߡߊߛߙߋ߫ ߞߏ ߘߐ߫.",
        "log-fulllog": "ߘߎ߲ߛߓߍ ߘߝߊߣߍ߲ ߦߋ߫",
+       "edit-hook-aborted": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߓߘߊ߫ ߘߐߛߊ߬ ߛߏ߲߭ߓߊ߬ߟߌ ߓߟߏ߫. \nߊ߬ ߘߏ߲߬ ߡߊ߫ ߘߊ߲߬ߕߍ߰ߟߍ߬ ߛߌ߫ ߞߍ߫.",
+       "edit-gone-missing": "ߞߐߜߍ ߕߍ߫ ߛߐ߲߬ ߟߏ߲ߘߐߦߊ߫ ߟߊ߫.\nߊ߬ ߦߌ߬ߘߊ߬ߣߍ߲߬ ߦߋ߫ ߞߴߊ߬ ߓߘߊ߫ ߖߏ߰ߛߌ߬.",
        "edit-conflict": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߝߐߢߐ߲߯ߞߐ.",
        "edit-no-change": "ߌ ߟߊ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߕߎ߲߬ ߓߘߊ߫ ߡߊߓߌ߬ߟߊ߬߸ ߓߊߏ߬ ߡߝߊ߬ߟߋ߲߬ߠߌ߲߬ ߛߌ߫ ߕߎ߲߬ ߡߊ߫ ߞߍ߫ ߛߓߍߟߌ ߘߐ߫.",
        "postedit-confirmation-created": "ߞߐߜߍ ߓߘߊ߫ ߓߊ߲߫ ߛߌ߲ߘߌ߫ ߟߊ߫.",
        "right-minoredit": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߣߐ߬ߣߐ߬ ߡߌ߬ߛߍ߬ߡߊ߲ ߘߌ߫",
        "right-move": "ߞߐߜߍ ߟߎ߬ ߛߋ߲߬ߓߐ߫",
        "right-move-subpages": "ߞߐߜߍ ߛߋ߲߬ߓߐ߫ ߊ߬ߟߎ߬ ߟߊ߫ ߞߐߜߍߙߋ߲ ߠߎ߬ ߘߐ߫",
+       "right-move-rootuserpages": "ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ߫ ߟߌ߯ߟߌ߲ߖߌ߰ߣߍ߲ ߞߐߜߍ ߛߋ߲߬ߓߐ߫",
        "right-move-categorypages": "ߦߌߟߡߊ߫ ߞߐߜߍ ߟߎ߬ ߛߋ߲߬ߓߐ߫",
        "right-movefile": "ߞߐߕߐ߮ ߟߎ߬ ߛߋ߲߬ߓߐ߫",
        "right-upload": "ߞߐߕߐ߮ ߟߎ߬ ߟߊߦߟߍ߬",
        "right-sendemail": "ߢߎߡߍߙߋ߲ ߗߋ߫ ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߘߏ ߟߎ߬ ߡߊ߬",
        "grant-generic": "\"$1\" ߞߌߣߌ߲߫ ߝߍ߫ ߝߎߝߎ",
        "grant-group-page-interaction": "ߞߐߜߍ ߟߎ߬ ߟߊ߫ ߞߏߢߐ߲߯ߦߊ",
+       "grant-group-watchlist-interaction": "ߌ ߟߊ߫ ߦߟߌߡߊߛߙߋ ߞߏߢߐ߲߯ߦߊ",
        "grant-group-email": "ߢߎߡߍߙߋ߲ ߗߋ߫",
+       "grant-group-customization": "ߘߎ߲߬ߘߎ߬ߡߦߊ߬ߟߌ ߣߌ߫ ߦߟߌߡߊߛߙߋ",
        "grant-blockusers": "ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߟߎ߬ ߓߊ߬ߟߌ ߣߴߊ߬ߟߎ߬ ߓߊ߬ߟߌ߬ߣߍ߲ ߓߐߟߌ",
        "grant-createaccount": "ߖߊ߬ߕߋ߬ߘߊ ߘߏ߫ ߛߌ߲ߘߌ߫",
        "grant-createeditmovepage": "ߞߐߜߍ ߛߌ߲ߘߌ߫߸ ߡߊߦߟߍ߬ߡߊ߲߫߸ ߊ߬ ߣߌ߫ ߞߵߊ߬ ߛߋ߲߬ߓߐ߫",
        "backend-fail-read": "ߞߐߕߐ߮ ߕߴߛߋ߫ ߘߐߞߊ߬ߙߊ߲߬ ߠߊ߫   \"$1\".",
        "backend-fail-create": "ߊ߬ ߕߍ߫ ߣߊ߬ ߛߐ߲߬ ߠߊ߫ ߞߐߕߐ߮  \"$1\" ߛߓߍ߫ ߟߊ߫.",
        "backend-fail-maxsize": "ߊ߫ ߕߍ߫ ߣߊ߬ ߞߐߕߐ߮  \"$1\" ߛߓߍ߫ ߟߊ߫߸ ߓߊߏ߬ ߊ߬ ߓߏ߲߬ߓߊ߫ ߞߊ߬ ߕߊ߬ߡߌ߲߬ {{PLURAL:$2|ߝߙߐ߬ߢߐ߬ ߞߋߟߋ߲߫|ߝߙߐ߬ߢߐ ߟߎ߬ $2}}.",
+       "backend-fail-stat": "ߊ߬ ߕߴߛߋ߫ ߞߐߕߐ߮ \"$1\" ߟߌ߬ߤߟߊ ߞߊ߬ߙߊ߲߬ ߠߊ߫.",
        "lockmanager-notlocked": "ߊ߬ ߕߍ߫ ߣߊ߬ ߛߐ߲߬ ߠߊ߫ \"$1\" ߟߊߞߊ߬ ߟߊ߫߸ ߊ߬ ߛߐ߰ߣߍ߲߬ ߕߍ߫.",
        "lockmanager-fail-closelock": "ߊ߬ ߕߍ߫ ߣߊ߬ ߛߋ߫ ߟߊ߫ \"$1\" ߞߐߕߐ߮ ߛߐ߰ߣߍ߲ ߘߊߕߎ߲߯ ߠߊ߫.",
        "lockmanager-fail-deletelock": "ߊ߬ ߕߍ߫ ߣߊ߬ ߛߋ߫ ߟߊ߫ \"$1\" ߞߐߕߐ߯ ߛߐ߰ߣߍ߲ ߖߏ߰ߛߌ߬ ߟߊ߫.",
        "license": "ߟߊ߬ߘߌ߬ߢߍ߬ߟߌ ߦߴߌ ߘߐ߫:",
        "license-header": "ߟߊ߬ߘߌ߬ߢߍ߬ߟߌ ߦߴߌ ߘߐ߫",
        "nolicense": "ߊ߬ ߡߊ߫ ߓߊߕߐ߬ߡߐ߲߬",
+       "licenses-edit": "ߕߌ߰ߦߊ ߢߣߊߕߊߟߌ ߡߊߦߟߍ߬ߡߊ߲߫",
        "upload_source_file": "(ߌ ߟߊ߫ ߞߐߕߐ߯ ߛߎߥߊ߲ߘߌߣߍ߲ ߠߎ߬ ߞߊ߬ ߝߘߊ߫ ߌ ߟߊ߫ ߕߟߋ߬ߓߊ߮ ߟߊ߫)",
        "listfiles-delete": "ߊ߬ ߖߏ߬ߛߌ߬",
        "listfiles-summary": "ߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߣߌ߲߬ ߦߴߌ ߟߊ߫ ߞߐߕߐ߯ ߟߊߦߟߍ߬ߣߍ߲ ߠߎ߬ ߓߍ߯ ߟߋ߬ ߦߌ߬ߘߊ߬ ߟߊ߫",
        "protect-cantedit": "ߌ ߕߍ߫ ߣߊ߬ ߛߋ߫ ߟߊ߫ ߞߐߜߍ ߣߌ߲߬ ߟߊ߬ߞߊ߲߬ߘߊ߬ߟߌ ߞߊߓߋ ߡߊߝߊ߬ߟߋ߲߬ ߠߊ߫߸ ߞߵߊ ߞߵߊ߬ ߡߊߛߐ߬ߘߐ߲߬ ߊ߬ ߡߊߦߟߍ߬ߡߊ߲ ߠߊߘߤߊ߬ߣߍ߲߬ ߕߴߌ ߦߋ߫.",
        "protect-othertime": "ߕߎ߬ߡߊ߬ ߜߘߍ:",
        "protect-othertime-op": "ߕߎ߬ߡߊ߬ ߜߘߍ߫",
+       "protect-existing-expiry": "ߕߋ߲߭ߕߋ߲߭ ߛߕߊߝߊ߫ ߕߎߡߊ: $3߸ $2",
        "protect-existing-expiry-infinity": "ߕߋ߲߭ߕߋ߲߭ ߛߕߊߝߊ߫ ߕߎߡߊ: ߘߊ߲߬ߓߊߟߌ",
        "protect-otherreason": "ߞߎ߲߬ ߡߊߞߊ߬ߝߏ߬ߕߊ߬/ߜߘߍ:",
        "protect-otherreason-op": "ߞߎ߲߬ ߜߘߍ ߟߎ߬",
        "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": "ߘߌ߬ߢߍ߬ߟߊ߬ߢߌ߬ߣߌ߲:",
        "minimum-size": "ߘߎ߰ߟߊ߬ߘߐ߬ ߢߊ߲ߞߊ߲",
        "maximum-size": "ߢߊ߲ߞߊ߲ ߞߐߘߊ߲:",
        "restriction-edit": "ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߬",
        "version-ext-colheader-description": "ߕߐ߯ߟߊߘߏ߲",
        "version-ext-colheader-credits": "ߛߓߍߦߟߊ",
        "version-license-title": "$1 ߕߌ߰ߦߊ",
+       "version-poweredby-others": "ߘߏ߫ ߜߘߍ ߟߎ߬",
+       "version-poweredby-translators": "translatewiki.net ߘߟߊߡߌߘߊߟߊ߲",
+       "version-credits-summary": "ߊ߲ ߧߴߊ߬ ߝߍ߬ ߞߊ߬ ߡߐ߱ ߢߌ߲߬ ߠߎ߬ ߡߊߟߐ߲߫ ߞߊ߬ ߓߍ߲߬ ߊ߬ߟߎ߬ ߟߊ߫ ߓߟߏߓߌߟߊߢߐ߲߯ߞߊ߲ ߡߊ߬ [[Special:Version|MediaWiki]] ߘߐ߫.",
        "version-software": "ߛߎ߲ߝߘߍ ߓߘߊ߫ ߡߊߞߍ߫",
        "version-software-product": "ߥߟߏߒߘߐ",
        "version-software-version": "ߦߌߟߡߊ",
        "fileduplicatesearch-summary": "ߞߐߕߐ߯ ߓߊߟߌߣߍ߲ ߠߎ߬ ߢߌߣߌ߲߫ ߡߍ߲ ߠߎ߬ ߓߌ߲ߓߌ߲ߣߍ߲߫ ߦߋ߫ ߢߋߙߋ߲ߞߎߟߌ ߡߐ߬ߟߐ߲ ߡߊ߬.",
        "fileduplicatesearch-filename": "ߞߐߕߐ߮ ߕߐ߮:",
        "fileduplicatesearch-submit": "ߢߌߣߌ߲ߠߌ߲",
+       "fileduplicatesearch-result-1": "ߓߊߟߌߟߌ߫ ߡߊߟߐ߲ߣߍ߲߫ ߛߌ߫ ߕߍ߫ ߞߐߕߐ߮  \"$1\" ߡߊ߬.",
+       "fileduplicatesearch-noresults": "ߞߐߕߐ߯ ߛߌ߫ ߕߐ߯ ߡߊ߫ ߦߋ߫ ߞߏ߫  \"$1\".",
        "specialpages": "ߘߎ߲߬ߘߎ߬ߡߊ߬ ߞߐߜߍ ߟߎ߬",
+       "specialpages-group-other": "ߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߕߐ߭ ߟߎ߬",
+       "specialpages-group-login": "ߌ ߜߊ߲߬ߞߎ߲߬/ߖߊ߬ߕߋ߬ߘߊ ߘߏ߫ ߟߊߞߊ߬",
+       "specialpages-group-users": "ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߟߎ߬ ߣߌ߫ ߤߊߞߍ",
+       "specialpages-group-highuse": "ߞߐߜߍ߫ ߟߊߓߊ߯ߙߕߊ ߛߊ߲ߘߐߕߊ ߟߎ߬",
+       "specialpages-group-pages": "ߞߐߜߍ ߟߎ߬ ߛߙߍߘߍ",
+       "specialpages-group-pagetools": "ߞߐߜߍ ߖߐ߯ߙߊ߲ ߠߎ߬",
+       "specialpages-group-wiki": "ߕߎ߬ߡߊ߬ߘߊ ߣߌ߫ ߖߐ߯ߙߊ߲ ߠߎ߬",
+       "specialpages-group-redirects": "ߞߐߜߍ߫ ߞߙߍߞߙߍߣߍ߲ ߠߎ߬ ߟߊߞߎ߲߬ߛߌ߲ ߦߴߌ ߘߐ߫",
+       "specialpages-group-spam": "ߞߏ߲߬ߘߏ ߖߐ߯ߙߊ߲ ߠߎ߬",
+       "specialpages-group-developer": "ߟߊ߬ߥߙߌ߬ߞߌ߬ߟߌ ߖߐ߯ߙߊ߲ ߠߎ߬",
+       "blankpage": "ߞߐߜߍ߫ ߘߐߞߏߟߏ߲",
        "tag-filter": "[[Special:Tags|ߞߊ߲ߠߊߛߓߍ]] ߢߡߊߘߏ߲߰ߣߍ߲",
        "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|ߡߊ߬ߛߙߋ |ߡߊ߬ߛߙߋ ߟߎ߬ }}]]: $2",
        "tags-active-yes": "ߐ߲߬ߐ߲߬ߐ߲߫",
index 384e7ae..25d7bf1 100644 (file)
        "undo-norev": "Edycja nie może być cofnięta, ponieważ nie istnieje lub została usunięta.",
        "undo-nochange": "Wygląda na to, że edycja została już anulowana.",
        "undo-summary": "Anulowanie wersji $1 autorstwa [[Special:Contributions/$2|$2]] ([[User talk:$2|dyskusja]])",
+       "undo-summary-anon": "Anulowanie wersji $1 autorstwa [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "Anulowanie wersji $1 autorstwa ukrytego użytkownika",
        "cantcreateaccount-text": "Tworzenie konta z tego adresu IP ('''$1''') zostało zablokowane przez [[User:$3|$3]].\n\nPodany przez $3 powód to ''$2''",
        "cantcreateaccount-range-text": "Tworzenie konta z adresów IP w zakresie <strong>$1</strong>, zawierającego twój adres IP (<strong>$4</strong>), zostało zablokowane przez [[User:$3|$3]].\n\nPodany przez $3 powód to <em>$2</em>",
        "alreadyrolled": "Nie można dla strony [[:$1|$1]] cofnąć ostatniej zmiany, którą wykonał [[User:$2|$2]] ([[User talk:$2|dyskusja]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]).\nKtoś inny zdążył już to zrobić lub wprowadził własne poprawki do treści strony.\n\nAutorem ostatniej zmiany jest teraz [[User:$3|$3]] ([[User talk:$3|dyskusja]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Edycję opisał: <em>$1</em>.",
        "revertpage": "Wycofano edycje użytkownika [[Special:Contributions/$2|$2]] ([[User talk:$2|dyskusja]]). Autor przywróconej wersji to [[User:$1|$1]].",
+       "revertpage-anon": "Wycofano edycje użytkownika [[Special:Contributions/$2|$2]]. Autor przywróconej wersji to [[User:$1|$1]].",
        "revertpage-nouser": "Wycofano edycje ukrytego użytkownika. Autor przywróconej wersji to {{GENDER:$1|[[User:$1|$1]]}}.",
        "rollback-success": "Wycofano edycje {{GENDER:$3|użytkownika|użytkowniczki}} $1;\nprzywrócono ostatnią wersję autorstwa {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Błąd sesji",
index 150fa14..1106f5f 100644 (file)
        "undo-norev": "A edição não pôde ser desfeita porque não existe ou foi apagada.",
        "undo-nochange": "Parece que a edição já foi desfeita.",
        "undo-summary": "Desfeita a edição $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussão]])",
+       "undo-summary-anon": "Desfazer revisão $1 por [[Special:Contributions/$2|$2]]",
        "undo-summary-username-hidden": "Desfazer a revisão $1 de um usuário oculto",
        "cantcreateaccount-text": "Este IP ('''$1''') foi bloqueado de criar novas contas por [[User:$3|$3]].\n\nA justificativa apresentada por $3 foi ''$2''",
        "cantcreateaccount-range-text": "A criação de conta a partir dos endereços IP no intervalo <strong>$1</strong>, que inclui o seu endereço IP (<strong>$4</strong>), foi bloqueada por [[User:$3|$3]].\n\nA razão dada por $3 é <em>$2</em>",
        "alreadyrolled": "Não foi possível reverter a última edição de [[:$1]] por [[User:$2|$2]] ([[User talk:$2|discussão]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nalguém já editou ou reverteu a página.\n\nA última edição da página foi feita por [[User:$3|$3]] ([[User talk:$3|discussão]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "O sumário de edição era: <em>$1</em>.",
        "revertpage": "Foram revertidas as edições de [[Special:Contributions/$2|$2]] ([[User talk:$2|disc]]) para a última versão por [[User:$1|$1]]",
+       "revertpage-anon": "Foram revertidas as edições de [[Special:Contributions/$2|$2]] para a última versão por [[User:$1|$1]]",
        "revertpage-nouser": "Revertidas as edições de um usuário oculto para a última revisão de {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Edições revertidas por {{GENDER:$3|$1}};\nalterado para a última revisão por {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Erro de sessão",
index 5f0b966..478a52e 100644 (file)
        "blockednoreason": "Substituted with <code>$2</code> in the following message if the reason is not given:\n* {{msg-mw|cantcreateaccount-text}}.\n{{Identical|No reason given}}",
        "blockedtext-composite": "Text displayed to requests blocked by more than one block.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - details of the individual blocks that this block is made from\n* $6 - the expiry of the block with the longest duration\n* $7 - (Unused) the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Systemblockedtext|notext=1}}",
        "blockedtext-composite-ids": "Text displayed when a user is blocked by multiple blocks, if at least one block comes from the database.\n\nParameters:\n* $1 - IDs of the blocks from the database",
-       "blockedtext-composite-no-ids": "IP가 블랙리스트에 올라간 경우 여러 차단으로 사용자를 차단할 때 표시되는 텍스트 입니다.",
+       "blockedtext-composite-no-ids": "Text displayed when a user is blocked by multiple blocks, if all the blocks are due to the IP being blacklisted.",
        "blockedtext-composite-reason": "Reason given to blocked users who are affected by more than one block.\n\nSee also:\n* {{msg-mw|blockedtext-composite}}",
        "whitelistedittext": "Used as error message. Parameters:\n* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description\n* $2 - an URL to the same\n\nSee also:\n* {{msg-mw|Nocreatetext}}\n* {{msg-mw|Uploadnologintext}}\n* {{msg-mw|Loginreqpagetext}}",
        "confirmedittext": "Used as error message.",
        "undo-main-slot-only": "Message appears if an attempt to revert an edit by clicking the \"undo\" link on the page history fails because it involves content outside the page's main slot, which is not yet supported.\nThis message is temporary, see https://phabricator.wikimedia.org/T189808\n\nSee also:\n* {{msg-mw|Undo-failure}}\n{{Identical|Undo}}",
        "undo-norev": "Message appears if an attempt to revert an edit by clicking the \"undo\" link on the page history fails.\n\nSee also:\n* {{msg-mw|Undo-failure}}\n* {{msg-mw|Undo-nochange}}\n{{Identical|Undo}}",
        "undo-nochange": "Message appears if an attempt to revert an edit by clicking the \"undo\" link results in an edit making no change to the current version of the page.\n\nSee also:\n* {{msg-mw|Undo-failure}}\n* {{msg-mw|Undo-norev}}",
-       "undo-summary": "Edit summary for an undo action. Parameters:\n* $1 - revision ID\n* $2 - username\n{{Identical|Undo}}",
+       "undo-summary": "Edit summary for an undo action. Parameters:\n* $1 - revision ID\n* $2 - username\n{{Identical|Undo}}\nSee also:\n* {{msg-mw|Undo-summary-anon}}",
+       "undo-summary-anon": "Edit summary for an undo action, when undoing an edit by an anonymous user and $wgDisableAnonTalk is activated. Parameters:\n* $1 - revision ID\n* $2 - username\nSee also:\n* {{msg-mw|Undo-summary}}",
        "undo-summary-username-hidden": "Edit summary for an undo action where the username of the old revision is hidden.\n\nParameters:\n* $1 - the revision ID being undone\nSee also:\n* {{msg-mw|Undo-summary}}",
        "cantcreateaccount-text": "Used as error message when account creation is prevented by an IP block.\n* $1 - target IP address\n* $2 - reason or {{msg-mw|Blockednoreason}}\n* $3 - username\nSee also:\n* {{msg-mw|Cantcreateaccount-range-text}}",
        "cantcreateaccount-range-text": "Used instead of the {{msg-mw|Cantcreateaccount-text}} when the block is a range block.\n* $1 - target IP address range\n* $2 - reason or {{msg-mw|Blockednoreason}}\n* $3 - username\n* $4 - current user's IP address",
        "cantrollback": "Used as error message when rollback fails due to there not being a valid revision to revert back to.\n\nSee also:\n* {{msg-mw|Notvisiblerev}}\n{{Identical|Revert}}\n{{Identical|Rollback}}",
        "alreadyrolled": "Appear when there's rollback and/or edit collision.\n\nRefers to:\n* {{msg-mw|Pipe-separator}}\n* {{msg-mw|Contribslink}}\nParameters:\n* $1 - the page to be rolled back\n* $2 - the editor to be rolled-back of that page\n* $3 - the editor that cause collision\n{{Identical|Rollback}}",
        "editcomment": "Only shown if there is an edit {{msg-mw|Summary}}. Parameters:\n* $1 - the edit summary",
-       "revertpage": "Parameters:\n* $1 - username 1\n* $2 - username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from\nSee also:\n* {{msg-mw|Revertpage-nouser}}\n{{Identical|Revert}}",
-       "revertpage-nouser": "This is a confirmation message a user sees after reverting, when the username of the version is hidden with RevisionDelete.\n\nIn other cases the message {{msg-mw|Revertpage}} is used.\n\nParameters:\n* $1 - username 1, can be used for GENDER\n* $2 - (Optional) username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from",
+       "revertpage": "Parameters:\n* $1 - username 1\n* $2 - username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from\nSee also:\n* {{msg-mw|Revertpage-anon}}\n* {{msg-mw|Revertpage-nouser}}\n{{Identical|Revert}}",
+       "revertpage-anon": "Parameters:\n* $1 - username 1\n* $2 - username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from\nSee also:\n* {{msg-mw|Revertpage}}\n* {{msg-mw|Revertpage-nouser}}\n{{Identical|Revert}}",
+       "revertpage-nouser": "This is a confirmation message a user sees after reverting, when the username of the version is hidden with RevisionDelete.\n\nIn other cases the message {{msg-mw|Revertpage}} or {{msg-mw|Revertpage-anon}} is used.\n\nParameters:\n* $1 - username 1, can be used for GENDER\n* $2 - (Optional) username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from",
        "rollback-success": "This message shows up on screen after successful revert (generally visible only to admins). Parameters:\n* $1 - user whose changes have been reverted\n* $2 - user who produced version, which replaces reverted version\n* $3 - the first user's name, can be used for GENDER\n* $4 - the second user's name, can be used for GENDER\n{{Identical|Revert}}\n{{Identical|Rollback}}",
        "sessionfailure-title": "Used as title of the error message {{msg-mw|Sessionfailure}}.",
        "sessionfailure": "Used as error message.\n\nThe title for this error message is {{msg-mw|Sessionfailure-title}}.",
index d5a801c..332a02e 100644 (file)
                ]
        },
        "tog-underline": "ڳنڍڻي هيٺان لڪير:",
-       "tog-hideminor": "تازين تبديلين منجھہ معمولي تبديليون لڪايو",
+       "tog-hideminor": "تازين تبديلين ۾ معمولي سنوارون لڪايو",
        "tog-hidepatrolled": "تازيون نگرانيل تبديليون لڪايو",
-       "tog-newpageshidepatrolled": "نَوَن صفحن واري فهرست مان نگرانيل صفحا لڪايو",
+       "tog-newpageshidepatrolled": "نَوَن صفحن جي فھرست مان گشت-ڪيل صفحا لڪايو",
        "tog-hidecategorization": "صفحن جا زمرا لڪايو",
-       "tog-extendwatchlist": "تازÙ\87 ØªØ±Ù\8aÙ\86 Ø¨Ø¯Ø±Ø§Ù\86 Ø³Ù\85Ù\88رÙ\8aÙ\88Ù\86 ØªØ¨Ø¯Ù\8aÙ\84Ù\8aÙ\88Ù\86 Ú\8fÙ\8aکارڻ Ù\84اءÙ\90 Ø²Ù\8aر Ù\86ظر Ù\81Ù\87رست Ú©Ù\8a Ù\88سÙ\8aع ÚªØ±Ù\8aÙ\88.",
+       "tog-extendwatchlist": "رڳÙ\88 ØªØ§Ø²Û\81 ØªØ±Ù\8aÙ\86 Ù\86Û\81Ø\8c Ø³Ù\85Ù\88رÙ\8aÙ\88Ù\86 ØªØ¨Ø¯Ù\8aÙ\84Ù\8aÙ\88Ù\86 Ú\8fÙ\8aکارڻ Ù\84اءÙ\90 Ù\86ظر Û¾ Ù\81ھرست Ú©Ù\8a Ú¦Ú¾Ù\84اÙ\8aÙ\88",
        "tog-numberheadings": "سُرخين کي خودڪاراً نمبر ڏيو",
        "tog-editondblclick": "ٻٽي ڪلڪ تي صفحا سنواريو",
        "tog-editsectiononrightclick": "ڀاڱن جي عنوان کي رائيٽ ڪلڪ ڪري سنوارڻ واري سھولت کي ڪارگر بڻايو",
        "tog-watchmoves": "جيڪي صفحا ۽ فائيل آءُٗ چوريان، سي منهنجي نظر ۾ فھرست ۾ شامل ڪريو",
        "tog-watchdeletion": "آءُٗ جيڪي صفحا ۽ فائيل  ڊاهيان، سي منهنجي نظر ۾ فھرست تي رکو",
        "tog-watchuploads": "منھنجا نوان چاڙهيل فائيلس نظر ۾ فھرست ۾ شامل ڪريو",
-       "tog-watchrollback": "انهن صفحن کي منهنجي نظر ۾ فھرست تي رکو، جن ۾ تبديلين کي مون واپس ورايو آهي",
+       "tog-watchrollback": "انھن صفحن کي منھنجي نظر ۾ فھرست تي رکو، جتي مون واپس ورائڻ جو عمل آهي.",
        "tog-minordefault": "سمورين سنوارن کي پاڻمرادو معمولي طور نشان لڳايو",
        "tog-previewontop": "سنوار دٻيءَ مٿان پيش-نگاھ ڏيکاريو",
        "tog-previewonfirst": "پھرين سنوار تي پيش-نگاھ ڏيکاريو",
-       "tog-enotifwatchlistpages": "منهنجي نظر ۾ فھرست اندر شامل ڪنهن صفحي يا فائيل ۾ تبديل پيش اچي مون کي برقٽپال اماڻيو",
+       "tog-enotifwatchlistpages": "منھنجي نظر ۾ فھرست ۾ شامل ڪو صفحو يا فائيل بدلايو وڃي تہ مون کي برقٽپال اماڻيو",
        "tog-enotifusertalkpages": "منهنجي مباحثي صفحي ۾ تبديليءَ جي صورت ۾ مون کي برقٽپال اماڻيو",
        "tog-enotifminoredits": "صفحن ۽ فائيلن جي معمولي سنوارن بابت بہ مون کي برقٽپال ڪريو",
        "tog-enotifrevealaddr": "پڌراين ۾ منهنجو برقٽپال پتو ظاهر ڪريو",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|توھان کي}} {{PLURAL:$3|ٻي واپرائيندڙ|$3 واپرائيندڙن}} ($2) کان $1 آھن.",
        "youhavenewmessagesmanyusers": "توهان لاءِ ڪيترن ئي واپرائيندڙن ($2) طرفان $1 آهن.",
        "newmessageslinkplural": "{{PLURAL:$1|ھڪ نئون پيغام|999=نوان پيغام}}",
-       "newmessagesdifflinkplural": "آخرÙ\8a {{PLURAL:$1|تبدÙ\8aÙ\84Ù\8a|999=تبدÙ\8aÙ\84Ù\8aÙ\88Ù\86}}",
+       "newmessagesdifflinkplural": "آخرÙ\8a {{PLURAL:$1|بدÙ\84اءÙ\8f|999=بدÙ\84اءÙ\8e}}",
        "youhavenewmessagesmulti": "$1 تي توهان لاءِ نوان نياپا آهن",
        "editsection": "سنواريو",
        "editold": "سنواريو",
        "databaseerror-query": "استفسار: $1",
        "databaseerror-function": "ڪاڄ: $1",
        "databaseerror-error": "چُڪَ: $1",
-       "laggedslavemode": "<strong>Ú\86تاءÙ\8f:</strong> ØµÙ\81Ø­Ù\8a Û¾ Ú¾Ø§Ú»Ù\88ÚªÙ\8aÙ\88Ù\86 ØªØ¨Ø¯Ù\8aÙ\84Ù\8aÙ\88Ù\86 Ù\86Ù\87 Ú¾Ø¬Ú» Ø¬Ù\88 Ø§Ù\85ڪاÙ\86 Ø¢Ú¾ي.",
+       "laggedslavemode": "<strong>Ú\86تاءÙ\8f:</strong> ØµÙ\81Ø­Ù\88 Ø´Ø§Ù\8aد Ú¾Ø§Ú»Ù\88ÚªÙ\8aÙ\88Ù\86 Ø¬Ø¯ØªÙ\88Ù\86 Ù\86Û\81 Ø±Ú©Ù\86دÙ\88 Ú¾Ø¬ي.",
        "readonly": "اعدادخانو بنديل",
        "missingarticle-rev": "(ڀيرو#: $1)",
        "missingarticle-diff": "(تفاوت: $1، $2)",
        "protectedpagetext": "هيءُ صفحو سوارڻ ۽ ٻين عملن کان بچائڻ لاءِ تحفظيو ويو آهي.",
        "viewsourcetext": "توهان هن صفحي جو ڪوڊ ڏسي ۽ نقل ڪري سگھو ٿا.",
        "viewyourtext": "توھان ھاڻي ھن صفحي ۾ <strong>پنھنجي سنوارن</strong> جو ذريعو ڏسي ۽ نقل ڪري سگھو ٿا.",
-       "protectedinterface": "هي صفحو سافٽ ويئر جو انٽرفيس متعين ڪري ٿو ۽ غلط استعال کان بچڻ لاءِ ان کي تحفظيو ويو آهي.\nتمام وڪي ۾ ترجمو شامل ڪرڻ لاءِ يا هن ۾ تبديلي ڪرڻ لاءِ ميڊياوڪي ترجمو [https://translatewiki.net/ translatewiki.net] استعمال ڪيو.",
+       "protectedinterface": "هي صفحو سافٽ ويئر جو انٽرفيس متعين ڪري ٿو ۽ غلط استعال کان بچڻ لاءِ ان کي تحفظيو ويو آهي.\nتمام وڪين ۾ ترجمو وجھڻ يا بدلائڻ لاءِ، مھرباني ڪري [https://translatewiki.net/ translatewiki.net] استعمال ڪريو، ميڊياوڪي جي لوڪلائيزيشڻ رٿا.",
        "namespaceprotected": "توهان کي نانءُپولار <strong>$1</strong> جا صفحا سنوارڻ جا اختيار ناهن.",
        "sitecssprotected": "اوهان وٽ ھن سيايسايس صفحي کي سنوارڻ جي اجازت ناھي، ڇو تہ ان سان سڀني گھمندڙ متاثر ٿي سگھن ٿا.",
        "mycustomcssprotected": "توهان کي هيءُ CSS صفحو سنوارڻ جي اجازت نہ آهي.",
        "password-name-match": "توهان جو ڳجھولفظ توهان جي واپرائيندڙ-نانءَ کان مختلف هجڻ لازمي آھي.",
        "mailmypassword": "ڳجھولفظ ٻيھر مقرر ڪريو",
        "passwordremindertitle": "{{SITENAME}} لاءِ نئون عارضي ڳجھولفظ",
-       "passwordremindertext": "ÚªÙ\86Ú¾Ù\86 (آءÙ\90Ù\8a Ù¾ØªÙ\8a $1 ØªØ§Ù\86) Ø§Ø³Ø§Ù\86 Ú©Ù\8a {{SITENAME}} ($4) Ù\84اءÙ\90 Ù\86ئÙ\88Ù\86 Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ø§Ù\85اڻڻ Ø¬Ù\8a Ú¯Ù\8fھرÙ\8e ÚªØ¦Ù\8a.\"$2\" Ù\88اپرائÙ\8aÙ\86دÚ\99 Ù\84اءÙ\90 Ú¾Úª Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ø³Ø±Ø¬Ù\8aÙ\88 Ù\88Ù\8aع Ø¢Ù\87Ù\8a \"$3\" ØªÙ\8a ØªØ±ØªÙ\8aب Ú\8fÙ\86Ù\88 Ù\88Ù\8aÙ\88 Ú¾Ù\88. Ø¬Ù\8aÚªÚ\8fÚ¾Ù\86 Ø§Ú¾Ù\88 ØªÙ\88ھاÙ\86 Ø¬Ù\88 Ø§Ø±Ø§Ø¯Ù\88 Ú¾Ù\8aÙ\88Ø\8c ØªÛ\81 Ú¾Ø§Ú»Ù\8a ØªÙ\88ھاÙ\86 Ú©Ù\8a Ú¾Ù\8aÙ\86ئر Ø¦Ù\8a Ø¯Ø§Ø®Ù\84 Ù¿Ù\8a Ù¾Ù\86Ú¾Ù\86جÙ\88 Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ ØªØ¨Ø¯Ù\8aÙ\84 ÚªØ±Ú» Ú¯Ú¾Ø±Ø¬Ù\8a.\nتÙ\88ھاÙ\86 Ø¬Ù\88 Ø¹Ø§Ø±Ø¶Ù\8a Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ {{PLURAL:$5|Ù\87Úª Ú\8fÙ\8aÙ\86Ú¾Ù\8fÙ\86|$5 Ú\8fÙ\8aÙ\86Ú¾Ù\8eÙ\86}} Û¾ Ø®ØªÙ\85 Ù¿Ù\8aÙ\86دÙ\88.\n\nجÙ\8aÚªÚ\8fÚ¾Ù\86 Ø§Ú¾Ø§ Ú¯Ù\8fھرÙ\8e Ø§Ù\88ھاÙ\86 Ù\86Û\81 ÚªØ¦Ù\8a Ú¾Ø¦Ù\8aØ\8c Ù\8aا Ú¾Ø§Ú»Ù\8a Ø§Ù\88ھاÙ\86 Ú©Ù\8a Ù¾Ù\86Ú¾Ù\86جÙ\88 Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ù\8aاد Ø§Ú\86Ù\8a Ù\88Ù\8aÙ\88 Ø¢Ú¾Ù\8a Û½ ØªÙ\88ھاÙ\86 Ø§Ù\86 Ú©Ù\8a ØªØ¨Ø¯Ù\8aÙ\84 ÚªØ±ڻ نٿا چاھيو، تہ توھان ھن نياپي کي نظر انداز ڪندي پنھنجو پراڻو ڳجھولفظ ئي استعمال ڪري سگھو ٿا.",
+       "passwordremindertext": "ÚªÙ\86Ú¾Ù\86 (آئÙ\90Ù¾Ù\8a Ù¾ØªÙ\8a $1 ØªØ§Ù\86) Ø§Ø³Ø§Ù\86 Ú©Ù\8a {{SITENAME}} ($4) Ù\84اءÙ\90 Ù\86ئÙ\88Ù\86 Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ø§Ù\85اڻڻ Ø¬Ù\8a Ú¯Ù\8fھرÙ\8e ÚªØ¦Ù\8a.\"$2\" Ù\88اپرائÙ\8aÙ\86دÚ\99 Ù\84اءÙ\90 Ú¾Úª Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ø³Ø±Ø¬Ù\8aÙ\88 Ù\88Ù\8aÙ\88 Ø¢Ù\87Ù\8a \"$3\" ØªÙ\8a ØªØ±ØªÙ\8aب Ú\8fÙ\86Ù\88 Ù\88Ù\8aÙ\88 Ú¾Ù\88. Ø¬Ù\8aÚªÚ\8fÚ¾Ù\86 Ø§Ú¾Ù\88 ØªÙ\88ھاÙ\86 Ø¬Ù\88 Ø§Ø±Ø§Ø¯Ù\88 Ú¾Ù\8aÙ\88Ø\8c ØªÛ\81 Ú¾Ø§Ú»Ù\8a ØªÙ\88ھاÙ\86 Ú©Ù\8a Ú¾Ù\8aÙ\86ئر Ø¦Ù\8a Ø¯Ø§Ø®Ù\84 Ù¿Ù\8a Ù¾Ù\86Ú¾Ù\86جÙ\88 Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ø¨Ø¯Ù\84ائڻ Ú¯Ú¾Ø±Ø¬Ù\8a.\nتÙ\88ھاÙ\86 Ø¬Ù\88 Ø¹Ø§Ø±Ø¶Ù\8a Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ {{PLURAL:$5|Ù\87Úª Ú\8fÙ\8aÙ\86Ú¾Ù\8fÙ\86|$5 Ú\8fÙ\8aÙ\86Ú¾Ù\8eÙ\86}} Û¾ Ø®ØªÙ\85 Ù¿Ù\8aÙ\86دÙ\88.\n\nجÙ\8aÚªÚ\8fÚ¾Ù\86 Ø§Ú¾Ø§ Ú¯Ù\8fھرÙ\8e Ø§Ù\88ھاÙ\86 Ù\86Û\81 ÚªØ¦Ù\8a Ú¾Ø¦Ù\8aØ\8c Ù\8aا Ú¾Ø§Ú»Ù\8a Ø§Ù\88ھاÙ\86 Ú©Ù\8a Ù¾Ù\86Ú¾Ù\86جÙ\88 Ú³Ø¬Ú¾Ù\88Ù\84Ù\81ظ Ù\8aاد Ø§Ú\86Ù\8a Ù\88Ù\8aÙ\88 Ø¢Ú¾Ù\8a Û½ ØªÙ\88ھاÙ\86 Ø§Ù\86 Ú©Ù\8a Ø¨Ø¯Ù\84ائڻ نٿا چاھيو، تہ توھان ھن نياپي کي نظر انداز ڪندي پنھنجو پراڻو ڳجھولفظ ئي استعمال ڪري سگھو ٿا.",
        "noemail": "واپرائيندڙ \"$1\" جو ڪو بہ برقٽپال پتو درج ٿيل ناهي.",
        "noemailcreate": "توھان کي قابلڪار برقٽپال پتو مھيا ڪرڻو پوندو.",
        "passwordsent": "واپرائيندڙ \"$1\" لاءِ ھڪ نئون ڳجھولفظ برقٽپال ذريعي اماڻيو ويو آهي.\nمھرباني ڪري اھو حاصل ڪرڻ بعد داخل ٿيندا.",
        "pt-userlogout": "خارج ٿيو",
        "php-mail-error-unknown": "پي ايڇ پي جي  ڪاڄ() اندر اڻڄاتل چُڪَ.",
        "user-mail-no-addy": "برقٽپال پتو ڄاڻائڻ کان سواءِ برقٽپال اماڻڻ جي ڪوشش ڪئي وئي.",
-       "changepassword": "ڳجھÙ\88Ù\84Ù\81ظ ØªØ¨Ø¯Ù\8aÙ\84 ÚªØ±يو",
+       "changepassword": "ڳجھÙ\88Ù\84Ù\81ظ Ø¨Ø¯Ù\84ايو",
        "resetpass_announce": "داخل ٿيڻ جو عمل پورو ڪرڻ لاءِ، توهان کي نئون ڳجھولفظ اختيار مقرر ڪرڻو پوندو.",
        "resetpass_header": "کاتي جو ڳجھولفظ بدلايو",
        "oldpassword": "اڳوڻو ڳجھولفظ:",
        "showdiff": "تبديليون ڏيکاريو",
        "blankarticle": "<strong>چِتاءُ:</strong> اوهان خالي صفحو سرجي رهيا آهيو.\nجيڪڏهن اوهان «$1» تي ٻيھر ڪلڪ ڪيو، تہ هي صفحو بغير ڪنھن مواد جي سرجيو ويندو.",
        "anoneditwarning": "<strong>چِتاءُ:</strong> توھان داخل ٿيل نہ آھيو. توھان جو آءِپي پتو عوامي طور ظاھر ٿيندو جي توھان ڪي سنوارون ڪريو ٿا. جيڪڏھن توھان <strong>[$1 داخل ٿيو]</strong> ٿا يا <strong>[$2 کاتو کوليو]</strong> ٿا، تہ ٻين فائدن سان گڏ توھان جون سنوارون توھان جي واپرائيندڙ-نانءَ سان منسوب ڪيون وينديون.",
-       "anonpreviewwarning": "توهان داخل ٿيل نہ آهيو. جيڪڏهن توهان صفحي ۾ تبديليون سانڍيون تہ اهڙين تبديلين ساڻ توهان جو آءِپي پتو درج ڪيو ويندو.",
-       "missingcommenttext": "براءِ مھرباني ڪو تاثر درج ڪندا.",
+       "anonpreviewwarning": "<em>توهان داخل ٿيل نہ آهيو. سانڍڻ سان توهان جو آئِپي پتو ھن صفحي جي سنوار سوانح ۾ درج ٿيندو.</em>",
+       "missingcommenttext": "مھرباني ڪري ڪو تاثر داخل ڪريو.",
        "summary-preview": "تت جي پيش نگاھ:",
        "subject-preview": "موضوع جي پيش نگاھ:",
        "blockedtitle": "واپرائيندڙ بندشيل آهي",
        "rev-deleted-event": "(لاگ تفصيل هٽايا ويا)",
        "rev-deleted-user-contribs": "[واپرائيندڙ-نانءُ يا آءِپِي پتو مِٽايو ويو - ڀاڱيدارين مان سنوار لڪائي وئي]",
        "rev-suppressed-no-diff": "توهان اهو تفاوت نٿا ڏسي سگھو، ڇاڪاڻ تہ ورجائن مان ڪو ھڪ <strong>ڊاھيو ويو آھي</strong>.",
-       "rev-delundel": "نمائش تبديل ڪريو",
+       "rev-delundel": "ظاھريت بدلايو",
        "rev-showdeleted": "ڏيکاريو",
        "revisiondelete": "ورجاءَ ڊاهيو/اڻ‌ڊاهيو",
        "revdelete-no-file": "ڄاڻايل فائيل وجود نٿو رکي.",
        "revdelete-failure": "ورجاءُ ظاھريت نہ جديدي سگھجي:\n$1",
        "logdelete-success": "لاگ ظاھريت مرتب ٿي.",
        "logdelete-failure": "لاگ ظاھريت مرتب نہ ٿي سگھجي:\n$1",
-       "revdel-restore": "نمائش تبديل ڪريو",
+       "revdel-restore": "ظاھريت بدلايو",
        "pagehist": "صفحي جي سوانح",
        "deletedhist": "ڊاٿل سوانح",
        "revdelete-otherreason": "ٻيا/اضافي ڪارڻ:",
        "prefs-editwatchlist-clear": "پنهنجي نظر ۾ فھرست ڊاهيو",
        "prefs-watchlist-days": "نظر ۾ فھرست ۾ ڏيکارڻ لاءِ ڏينهن:",
        "prefs-watchlist-days-max": "وڌ ۾ وڌ $1 {{PLURAL:$1|ڏينھن}}",
-       "prefs-watchlist-edits": "نظر ۾ فھرست ز۾ ڏيکارڻ جي لاءِ تبديلين جو وڌ-۾-وڌ انگ:",
+       "prefs-watchlist-edits": "نظر ۾ فھرست ۾ ڏيکارڻ جي لاءِ تبديلين جو وڌ-۾-وڌ انگ:",
        "prefs-watchlist-edits-max": "وڌ ۾ وڌ تعداد: 1000",
        "prefs-watchlist-token": "نظر ۾ فھرست جو ٽوڪن:",
        "prefs-misc": "متفرق",
        "searchresultshead": "ڳولا",
        "stub-threshold-sample-link": "نمونو",
        "stub-threshold-disabled": "اڻ-فعايل",
-       "recentchangesdays": "تازين تبديلين ۾ ڏيکارڻ جي لاءِ ڏينهن:",
+       "recentchangesdays": "تازين تبديلين ۾ ڏيکارڻ جي لاءِ ڏينھن:",
        "recentchangesdays-max": "وڌ ۾ وڌ $1 {{PLURAL:$1|ڏينهن}}",
-       "recentchangescount": "تازين تبديلين، صفحن جي سوانح، ۽ لاگس ۾ ڏيکارڻ لاءِ سنوارن جو خودڪار ڏنل انگ:",
+       "recentchangescount": "تازين تبديلين، صفحن جي سوانح، ۽ لاگس ۾ ڏيکارڻ لاءِ سنوارن جو ڏنل انگ:",
        "prefs-help-recentchangescount": "وڌ ۾ وڌ انگ: 1000",
        "savedprefs": "توھان جون ترجيحون سانڍجي چڪيون آھن.",
        "savedrights": "{{GENDER:$1|$1}} جا واپرائيندڙ گروھ سانڍجي چڪا آھن.",
        "yourrealname": "اصل نالو:",
        "yourlanguage": "ٻولي:",
        "yournick": "نئون دستخط",
-       "prefs-help-signature": "بحث ØµÙ\81Ø­Ù\8a ØªÙ\8a Ø±Ø§Ù\8aا Ú\8fÙ\8aÚ» Ù\88Ù\82ت Ù\87Ù\86 Ù\86شاÙ\86Ù\8aÙ\86 Ø°Ø±Ù\8aعÙ\8a \"<nowiki>~~~~</nowiki>\" Ø¯Ø³ØªØ®Ø· ÚªÙ\8aÙ\88Ø\8c Ø¬Ù\8aÚªÙ\8a Ù¾Ø§Ú»Ù\85رادÙ\88 ØªÙ\88Ù\87اÙ\86 Ø¬Ù\8a Ø¯Ø³ØªØ®Ø· Û½ Ù\88Ù\82ت Û¾ ØªØ¨Ø¯Ù\8aÙ\84 Ù¿ي ويندا.",
+       "prefs-help-signature": "بحث ØµÙ\81Ø­Ù\86 ØªÙ\8a Ø±Ø§Ù\8aا Ù\87Ù\86 \"<nowiki>~~~~</nowiki>\" Ø°Ø±Ù\8aعÙ\8a Ø¯Ø³ØªØ®Ø· ÚªØ±Ú» Ú¯Ú¾Ø±Ø¬Ù\86Ø\8c Ø¬Ù\8aÚªÙ\8a Ù¾Ø§Ú»Ù\85رادÙ\88 ØªÙ\88Ù\87اÙ\86 Ø¬Ù\8a Ø¯Ø³ØªØ®Ø· Û½ Ù\88Ù\82ت-ٺپÙ\8a Û¾ Ù\85ٽجي ويندا.",
        "badsiglength": "اهو درتخط هيڪاندو ڊگھو آهي.\nاها وڌ ۾ وڌ $1 {{PLURAL:$1|اکر|اکرن}} تي ٻڌل هجڻ گھرجي.",
        "yourgender": "توھان ڪيئن بيان ٿيڻ چاھيندا؟",
        "gender-unknown": "توهان جو ذڪر ڪندي، جيترو ٿي سگھيو، منطقگري بي جنس لفظن جو استعمال ڪندي.",
        "userrights-reason": "سبب:",
        "userrights-no-interwiki": "توهان کي ٻين وڪين تي واپرائيندڙ حق سنوارڻ جي اجازت ناھي.",
        "userrights-nodatabase": "اعداخانو $1 يا تہ وجود نٿو رکي يا تہ اهو مقامي اعدادخانو نہ آهي.",
-       "userrights-changeable-col": "گروپَ جيڪي توهان تبديل ڪري سگھو ٿا",
-       "userrights-unchangeable-col": "گروپَ جيڪي توهان تبديل نٿا ڪري سگھو",
+       "userrights-changeable-col": "گروھَ جيڪي توهان بدلائي سگھو ٿا",
+       "userrights-unchangeable-col": "گروھَ جيڪي توهان بدلائي نٿا سگھو",
        "userrights-irreversible-marker": "$1*",
        "userrights-no-shorten-expiry-marker": "$1#",
        "userrights-expiry-current": "مدو پورو $1",
        "userrights-expiry-options": "1 ڏينھن:1 ڏينھن،1 ھفتو:1 ھفتا،1 مھينو:1 مھينو،3 مھينا:3 مھينا،6 مھينا:6 مھينا،1 سال:1 سال",
        "group": "گروھ:",
        "group-user": "واپرائيندڙَ",
-       "group-autoconfirmed": "خودبخود پڪ ڪيل واپرائيندڙَ",
+       "group-autoconfirmed": "پاڻمرادو-پڪ-ڪيل واپرائيندڙَ",
        "group-bot": "بوٽس",
        "group-sysop": "منتظم",
        "group-interface-admin": "منتظم براءِ حليو",
        "action-editinterface": "واپرائيندڙ انٽرفيس سنواريو",
        "action-editusercss": "واپرائيندڙن جا سي.ايس.ايس فائيل سنواريو",
        "action-unblockself": "ڪنھن جي بندش ختم ڪريو",
-       "nchanges": "$1 {{PLURAL:$1|تبدÙ\8aÙ\84Ù\8a|تبدÙ\8aÙ\84Ù\8aÙ\88Ù\86}}",
+       "nchanges": "$1 {{PLURAL:$1|بدÙ\84اءÙ\8f|بدÙ\84اءÙ\8e}}",
        "ntimes": "$1ڀيرا",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|آخري ڦيري کان}}",
        "enhancedrc-history": "سوانح",
        "rcfilters-activefilters-show-tooltip": "سرگرم ڇاڻين جي ايراضي ڏيکاريو",
        "rcfilters-advancedfilters": "متقدم ڇاڻيون",
        "rcfilters-limit-title": "ڏيکارڻ لاءِ نتيجا",
-       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|تبدÙ\8aÙ\84Ù\8a|$1 ØªØ¨Ø¯Ù\8aÙ\84Ù\8aÙ\88Ù\86}}، $2",
+       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|بدÙ\84اءÙ\8f|$1 Ø¨Ø¯Ù\84اءÙ\8e}}، $2",
        "rcfilters-date-popup-title": "ڳولڻ لاءِ وقت جو دورانيو",
        "rcfilters-days-title": "ھاڻوڪا ڏينھن",
        "rcfilters-hours-title": "ھاڻوڪا ڪلاڪَ",
        "rcfilters-filter-logactions-label": "لاگڊ عمل",
        "rcfilters-filtergroup-lastrevision": "تازا-ترين ورجاءَ",
        "rcfilters-filter-lastrevision-label": "تازو-ترين ورجاءُ",
-       "rcfilters-filter-lastrevision-description": "ÚªÙ\86Ú¾Ù\86 ØµÙ\81Ø­Ù\8a Û¾ Ø±Ú³Ù\88 ØªØ§Ø²Ù\8a-ترÙ\8aÙ\86 ØªØ¨Ø¯Ù\8aÙ\84Ù\8a.",
+       "rcfilters-filter-lastrevision-description": "ÚªÙ\86Ú¾Ù\86 ØµÙ\81Ø­Ù\8a Û¾ Ø±Ú³Ù\88 ØªØ§Ø²Ù\88-ترÙ\8aÙ\86 Ø¨Ø¯Ù\84اءÙ\8f.",
        "rcfilters-filter-previousrevision-label": "تازو-ترين ورجاءُ نہ",
        "rcfilters-filter-previousrevision-description": "سڀ تبديليون جيڪي \"تازو-ترين ورجاءُ\" ناھن.",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:نہ</strong> $1",
        "rcfilters-watchlist-markseen-button": "سڀ تبديلين کي ڏٺل طور نشان لڳايو",
        "rcfilters-watchlist-edit-watchlist-button": "پنھنجي نظر ۾ صفحن جي فھرست سنواريو",
        "rcfilters-alldiscussions-label": "سڀ گفتگوئون",
-       "rcnotefrom": "Ù\87Ù\8aÙº {{PLURAL:$5|تبدÙ\8aÙ\84Ù\8a Ø¢Ù\87Ù\8a|تبدÙ\8aÙ\84Ù\8aÙ\88Ù\86 Ø¢Ù\87Ù\86}} Ú©Ø§Ù\86 <strong>$3, $4</strong> (تائين <strong>$1</strong> ) ڏيکاريل آهن.",
+       "rcnotefrom": "Ù\87Ù\8aÙº {{PLURAL:$5|بدÙ\84اءÙ\8f Ø¢Ù\87Ù\8a|بدÙ\84اءÙ\8e Ø¢Ù\87Ù\86}} Ú©Ø§Ù\86 <strong>$3Ø\8c $4</strong> (تائين <strong>$1</strong> ) ڏيکاريل آهن.",
        "rclistfromreset": "تاريخ چونڊڻ ٻيھر مرتب ڪريو",
        "rclistfrom": "$2، $3 کان شروع ٿيندڙ نيون تبديليون ڏيکاريو",
        "rcshowhideminor": "$1 معمولي سنوارون",
        "uploaded-hostile-svg": "چاڙھيل ايس.وي.جي فائيل جو غير محفوظ سي.ايس.ايس اسٽائيل ايلمينٽ ۾ مليو.",
        "uploaded-event-handler-on-svg": "ايس وي جي فائيل ۾ ايوينٽ هينڊلر خصوصيتون <code>$1=\"$2\"</code> مقرر ڪرڻ جي اجازت نہ آهي.",
        "uploaded-href-unsafe-target-svg": "href جو غير محفوظ ڊيٽا: يوآرآءِ نشانو مليو آهي <code>&lt;$1 $2=\"$3\"&gt;</code> چاڙھيل اَيسوِيجِي فائيل ۾",
-       "uploaded-animate-svg": "”اينيميٽ“ ٽيگ ڳوليو جيڪو ٿي سگھي ٿو href کي تبديل ڪري رهي هجي، چاڙھيل ايس.وي.جي فائيل ۾ \"form\" وصف استعمال ڪندي <code>&lt;$1 $2=\"$3\"&gt;</code>",
+       "uploaded-animate-svg": "”اينيميٽ“ ٽيگ لڌو جيڪو ٿي سگھي ٿو href کي بدلائي رهيو هجي، چاڙھيل ايسوِيجِي فائيل ۾ \"form\" وصف استعمال ڪندي <code>&lt;$1 $2=\"$3\"&gt;</code>",
        "uploaded-setting-event-handler-svg": "موقعو-سنڀاليندڙ جا انتساب ترتيبڻ بندشيل آهن، <code>&lt;$1 $2=\"$3\"&gt;</code> چاڙھيل ايس.وي.جي فائيل ۾ مليو",
        "uploaded-setting-href-svg": "\"set\"  ٽيگ کي \"href\" وصف استعمال ڪندي بنيادي عنصر کي بندشيو ويو آھي.",
        "uploaded-wrong-setting-svg": "\"set\" ٽيگ کي استعمال ڪندي رموٽ/ڊيٽا/اسڪرپٽ ٽارگيٽ کي ڪنھڻ وصف سان جوڙڻ کي بلاڪ ڪيو ويو آهي. \n<code>&lt;set to=\"$1\"&gt;</code> چاڙھيل ايس.وي.جي فائيل ۾ مليو آهي.",
        "randomincategory-submit": "هلو",
        "randomredirect": "بلاترتيب چورڻو",
        "statistics": "انگ-اکر",
-       "statistics-header-pages": "صفحي انگ اکر",
+       "statistics-header-pages": "صفحي انگ-اکر",
        "statistics-header-edits": "سنوار جا انگ-اکر",
-       "statistics-header-users": "واپرائيندڙن جا انگ اکر",
+       "statistics-header-users": "واپرائيندڙن جا انگ-اکر",
        "statistics-header-hooks": "ٻيا انگ-اکر",
        "statistics-articles": "موادي صفحا",
        "statistics-pages": "صفحا",
        "protectedpages-unknown-performer": "اڻڄاتل واپرائيندڙ",
        "protectedtitles": "تحفظيل عنوان",
        "protectedtitles-submit": "عنوان ڏيکاريو",
-       "listusers": "واپرائيندڙن جي فهرست",
+       "listusers": "واپرائيندڙن جي فھرست",
        "listusers-editsonly": "رڳو سنوارن وارا واپرائيندڙ ڏيکاريو",
        "listusers-temporarygroupsonly": "صرف عارضي واپرائيندڙ گروھن ۾ واپرائيندڙ ڏيکاريو",
        "listusers-creationsort": "سرجڻ جي تاريخ سان مرتب ڪريو",
        "unwatching": "نظر مان ڪڍندي...",
        "enotif_reset": "سڀ گھميل صفحن تي نشان لڳايو",
        "enotif_impersonal_salutation": "{{SITENAME}} واپرائيندڙ",
-       "enotif_lastdiff": "Ù\87Ù\8a ØªØ¨Ø¯Ù\8aÙ\84Ù\8a ڏسڻ لاءِ، $1 ڏسو",
+       "enotif_lastdiff": "Ù\87Ù\8a Ø¨Ø¯Ù\84اءÙ\8f ڏسڻ لاءِ، $1 ڏسو",
        "enotif_anon_editor": "گمنام واپرائيندڙ $1",
        "enotif_minoredit": "هيءَ ننڍڙي سنوار آهي",
        "created": "ٺهي چڪو",
-       "changed": "تبدÙ\8aÙ\84 Ù¿ي ويو",
+       "changed": "بدÙ\84جي ويو",
        "deletepage": "صفحو ڊاهيو",
        "confirm": "پڪ ڪريو",
        "delete-confirm": "\"$1\" ڊهي چڪو",
        "confirmdeletetext": "توهان هڪ صفحي کي ان جي سموري سوانح سميت ڊاهڻ وارا آهيو. مهرباني ڪري پڪ ڪندا ته توهان اهو ئي ڪرڻ گھرو ٿا، ۽ اهو ته توهان ان جي نتيجن کان واقف آهيو، ۽ اهو پڻ ته توهان اهو ڪم [[{{MediaWiki:Policy-url}}|پاليسي]]ءَ مطابق ڪري رهيا آهيو.",
        "actioncomplete": "ڪم پُورو",
        "actionfailed": "عمل ناڪام",
-       "deletedtext": "\"$1\" ڊهي چڪو آهي.\nتازو ڊاٺل صفحن جي فهرست لاءِ $2 ڏسندا.",
+       "deletedtext": "\"$1\" ڊھي چڪو آھي.\nتازين ڊاھن صفحن جي رڪارڊ لاءِ $2 ڏسو.",
        "dellogpage": "ڊاھ لاگ",
        "deletionlog": "ڊاٺ لاگ",
        "deletecomment": "سبب:",
        "logentry-contentmodel-change-revert": "واپس ورايو",
        "protectlogpage": "تحفظ لاگ",
        "protectedarticle": "محفوظ ٿيل \"[[$1]]\"",
-       "modifiedarticleprotection": "\"[[$1]]\" جي تحفظ جي سطح تبديل ڪئي",
+       "modifiedarticleprotection": "\"[[$1]]\" لاءِ تحفظي سطح بدلائي",
        "unprotectedarticle": "\"[[$1]]\" تان تحفظ ھٽايو ويو",
        "movedarticleprotection": "\"[[$2]]\" جو حفاظت درجو \"[[$1]]\" جي طرف منتقل ڪيو",
        "unprotectedarticle-comment": "\"[[$1]]\" تان {{GENDER:$2|تحفظ ھٽايو}}",
        "protect_expiry_invalid": "انجامي مدو ناقابلڪار آهي.",
        "protect_expiry_old": "انجامي مدو ماضيءَ ۾ آهي.",
        "protect-text": "توهان '''$1''' صفحي جي تحفظاتي سطح ڏسي ۽ بدلائي سگھو ٿا.",
-       "protect-locked-access": "تÙ\88Ù\87اÙ\86 Ø¬Ù\88 Ú©Ø§ØªÙ\88 ØµÙ\81Ø­Ù\86 Ø¬Ù\8a ØªØ­Ù\81ظاتÙ\8a Ø³Ø·Ø­ ØªØ¨Ø¯Ù\8aÙ\84Ù\8a ÚªØ±Ú» Ø¬Ø§ Ø§Ø®ØªÙ\8aار Ù\86Ù\87 Ù¿Ù\88 Ø±Ú©Ù\8a. Ù\87Ù\8aÙº ØµÙ\81Ø­Ù\8a Ø¬Ù\88Ù\86 Ù\88Ù\82Ù\88عات (سÙ\8aٽڱس) Ù¾Ù\8aØ´ ÚªØ¬Ù\86 Ù¿Ù\8aÙ\88Ù\86 '''$1''':",
+       "protect-locked-access": "تÙ\88Ù\87اÙ\86 Ø¬Ù\8a Ú©Ø§ØªÙ\8a Ú©Ù\8a ØµÙ\81Ø­Ù\86 Ø¬Ù\8a ØªØ­Ù\81ظ Ø³Ø·Ø­ Ø¨Ø¯Ù\84ائڻ Ø¬Ù\8a Ø§Ø¬Ø§Ø²Øª Ù\86Û\81 Ø¢Ú¾Ù\8a.\nھتÙ\8a ØµÙ\81Ø­Ù\8a Ù\84اءÙ\90 Ú¾Ø§Ú»Ù\88ÚªÙ\8aÙ\88Ù\86 ØªØ±ØªÙ\8aبÙ\88Ù\86 Ø¢Ú¾Ù\86 <strong>$1</strong>:",
        "protect-cascadeon": "هيءُ صفحو في الوقت تحفظيل آهي، ڇاڪاڻ ته اهو هيٺين {{PLURAL:$1|صفحي|صفحن}} جو حصو آهي، جنهن تي تحفظ در تحفظ لاڳو ٿيل آهي.\nChanges to this page's protection level will not affect the cascading protection.",
        "protect-default": "سڀ واپرائيندڙن کي اجازت ڏيو",
        "protect-fallback": "\"$1\" جي اجازت وارن واپرائيندڙن کي اجازت ڏيو",
        "unlockbtn": "اعدادخاني کي کوليو",
        "move-page": "$1 چوريو",
        "move-page-legend": "صفحو چوريو",
-       "movepagetext": "هيٺيون فارم استعمال ڪندي ڪنھن صفحي کي نئون عنوان ڏئي سگھجي ٿو، جنھن سان سمورو صفحو نئين عنوان ڏانھن هليو ويندو. \nاڳوڻو عنوان نئين عنوان ڏانھن چورڻو بڻجي ويندو. \nتوهان  چورڻن کي سنواري سگھو ٿا جيڪي اصل عنوان ڏانهن خودبخود اشارو ڪن ٿا.\nانهي ڳالھ جي پڪ ڪري وٺو تہ [[Special:BrokenRedirects|ٽٽل چورڻا]] يا [[Special:DoubleRedirects|ٻٽا چورڻا]] نہ هجن.\nان ڳالھ جي پڪ ڪرڻ ذميواري توهان تي آهي تہ ڳنڍڻا اتي ئي وٺي وڃن ٿا جتي انھن کي وٺي وڃڻ گھرجي.\n\nياد رکندا تہ جيڪڏهن نئين عنوان سان اڳي ئي ڪو مضمون موجود آهي ته پوءِ صفحو '''نہ''' چوريو ويندو، سواءِ ان جي تہ موجوده صفحو محظ خالي آهي يا ڪا بہ سوانح نہ رکندڙ ڪو چورڻو آهي.\n\n<strong>نوٽ!</strong>\nاها هڪ مقبول صفحي لاءِ ڪا غير متوقع ۽ انتھائي اڻوڻندڙ تبديلي ثابت ٿي سگھي ٿي؛ براءِ مھرباني اڳتي وڌڻ کان اڳ پڪ ڪندا تہ توهان اها تبديلي آڻڻ جي نتيجن کان چڱيءَ ريت واقف آهيو.",
-       "movepagetext-noredirectfixer": "هيٺيون فارم استعمال ڪندي ڪنھن صفحي کي نئون عنوان ڏئي سگھجي ٿو، جنھن سان سمورو صفحو نئين عنوان ڏانھن هليو ويندو. \nاڳوڻو عنوان نئين عنوان ڏانھن چورڻو بڻجي ويندو. \nتوهان  چورڻن کي سنواري سگھو ٿا جيڪي اصل عنوان ڏانھن خودبخود اشارو ڪن ٿا.\nانهي ڳالھ جي پڪ ڪري وٺو تہ [[Special:BrokenRedirects|ٽٽل چورڻا]] يا [[Special:DoubleRedirects|ٻٽا چورڻا]] نہ هجن.\nان ڳالھ جي پڪ ڪرڻ ذميواري توهان تي آهي تہ ڳنڍڻا اتي ئي وٺي وڃن ٿا جتي انھن کي وٺي وڃڻ گھرجي.\n\nياد رکندا تہ جيڪڏهن نئين عنوان سان اڳي ئي ڪو مضمون موجود آهي ته پوءِ صفحو '''نہ''' چوريو ويندو، سوا ان جي تہ موجوده صفحو محظ خالي آهي يا ڪا بہ سوانح نہ رکندڙ ڪو چورڻو آهي.\n\n<strong>نوٽ!</strong>\nاها هڪ مقبول صفحي لاءِ ڪا غير متوقع ۽ انتھائي اڻوڻندڙ تبديلي ثابت ٿي سگھي ٿي؛ مھرباني ڪري اڳتي وڌڻ کان اڳ پڪ ڪندا تہ توهان اها تبديلي آڻڻ جي نتيجن کان چڱيءَ ريت واقف آهيو.",
+       "movepagetext": "هيٺيون فارم استعمال ڪندي ڪنھن صفحي کي نئون عنوان ڏئي سگھجي ٿو، جنھن سان سمورو صفحو نئين عنوان ڏانھن هليو ويندو. \nاڳوڻو عنوان نئين عنوان ڏانھن چورڻو بڻجي ويندو. \nتوهان  چورڻن کي سنواري سگھو ٿا جيڪي اصل عنوان ڏانهن پاڻمرادو اشارو ڪن ٿا.\nانهي ڳالھ جي پڪ ڪري وٺو تہ [[Special:BrokenRedirects|ٽٽل چورڻا]] يا [[Special:DoubleRedirects|ٻٽا چورڻا]] نہ هجن.\nان ڳالھ جي پڪ ڪرڻ ذميواري توهان تي آهي تہ ڳنڍڻا اتي ئي وٺي وڃن ٿا جتي انھن کي وٺي وڃڻ گھرجي.\n\nياد رکندا تہ جيڪڏهن نئين عنوان سان اڳي ئي ڪو مضمون موجود آهي ته پوءِ صفحو '''نہ''' چوريو ويندو، سواءِ ان جي تہ موجوده صفحو محظ خالي آهي يا ڪا بہ سوانح نہ رکندڙ ڪو چورڻو آهي.\n\n<strong>نوٽ!</strong>\nاھو ھڪ مقبول صفحي لاءِ ڪو غير متوقع ۽ انتھائي اڻوڻندڙ بدلاءُ ثابت ٿي سگھي ٿو؛ مھرباني ڪري اڳتي وڌڻ کان اڳ پڪ ڪندا تہ توهان اھو بدلاءُ آڻڻ جي نتيجن کان چڱيءَ ريت واقف آهيو.",
+       "movepagetext-noredirectfixer": "هيٺيون فارم استعمال ڪندي ڪنھن صفحي کي نئون عنوان ڏئي سگھجي ٿو، جنھن سان سمورو صفحو نئين عنوان ڏانھن هليو ويندو. \nاڳوڻو عنوان نئين عنوان ڏانھن چورڻو بڻجي ويندو. \nتوهان  چورڻن کي سنواري سگھو ٿا جيڪي اصل عنوان ڏانھن پاڻمرادو اشارو ڪن ٿا.\nانهي ڳالھ جي پڪ ڪري وٺو تہ [[Special:BrokenRedirects|ٽٽل چورڻا]] يا [[Special:DoubleRedirects|ٻٽا چورڻا]] نہ هجن.\nان ڳالھ جي پڪ ڪرڻ ذميواري توهان تي آهي تہ ڳنڍڻا اتي ئي وٺي وڃن ٿا جتي انھن کي وٺي وڃڻ گھرجي.\n\nياد رکندا تہ جيڪڏهن نئين عنوان سان اڳي ئي ڪو مضمون موجود آهي ته پوءِ صفحو '''نہ''' چوريو ويندو، سوا ان جي تہ ھاڻوڪو صفحو محظ خالي آهي يا ڪا بہ سوانح نہ رکندڙ ڪو چورڻو آهي.\n\n<strong>نوٽ!</strong>\nاھو هڪ مقبول صفحي لاءِ ڪو غير متوقع ۽ انتھائي اڻوڻندڙ بدلاءُ ثابت ٿي سگھي ٿو؛ مھرباني ڪري اڳتي وڌڻ کان اڳ پڪ ڪندا تہ توهان اهو بدلاءُ آڻڻ جي نتيجن کان چڱيءَ ريت واقف آهيو.",
        "movepagetalktext": "جيڪڏهن توهان هن خاني کي نشان لڳائيندئو، واسطيدار مباحثي صفحو پاڻ ئي چوريو ويندو ماسواءِ اتي ڪو اڳ ئي ڪو غيرخالي مباحثي صفحو موجود هجي.\n\nان صورت ۾، جيڪڏهن توهان چاهيو ته صفحي کي پاڻ چوري يا ضم ڪري سگھو ٿا.",
        "movecategorypage-warning": "<strong>چتاءُ:</strong> اوهان زمري واري صفحي کي چورڻ وڃي رهيا آهيو. ياد رکو رڳو صفحو چرندو، پراڻي زمري ۾ ڪن بہ صفحن جي نئين صفحي ۾ ٻيھر-زمراڪاري <em>نہ</em> ڪئي ويندي.",
        "movenotallowed": "توهان کي صفحا چورڻ جي اجازت حاصل ڪانهي.",
        "tooltip-minoredit": "ھن کي هڪ معمولي سنوار طور نشان لڳايو",
        "tooltip-save": "پنھنجون تبديليون سانڍيو",
        "tooltip-publish": "پنهنجيون تبديليون ڇاپيو",
-       "tooltip-preview": "پنھنجي تبديلين تي نگاھ وجھو. براءِ مھرباني اھو سانڍڻ کان اڳ ڪندا.",
+       "tooltip-preview": "پنھنجي تبديلين تي نگاھ وجھو. مھرباني ڪري اھو سانڍڻ کان اڳ ڪندا.",
        "tooltip-diff": "لکت ۾ ڪيل پنھنجون تبديليون ڏسو",
        "tooltip-compareselectedversions": "هن صفحي جن وچ ۾ چونڊيل ورجائن وچ ۾ تفاوت ڏسو",
        "tooltip-watch": "هيءُ صفحو پنهنجي نظر ۾ فھرست ۾ شامل ڪريو",
        "confirm-watch-button": "ٺيڪ",
        "confirm-watch-top": "هيءُ صفحو پنهنجي نظر ۾ فھرست ۾ شامل ڪندا؟",
        "confirm-unwatch-button": "ٺيڪ",
-       "confirm-unwatch-top": "هيءُ صفحو پنهنجي نظر ۾ فهرست مان هٽائيندا؟",
+       "confirm-unwatch-top": "هيءُ صفحو پنھنجي نظر ۾ فھرست مان هٽائيندا؟",
        "confirm-rollback-top": "ھن صفحي ۾ ڪيل سنوارون واپس ورايون؟",
        "semicolon-separator": "؛&#32;",
        "comma-separator": "،&#32;",
        "watchlistedit-clear-titles": "عنوانَ:",
        "watchlisttools-clear": "نظر ۾ فھرست صاف ڪريو",
        "watchlisttools-view": "لاڳاپيل تبديليون ڏسو",
-       "watchlisttools-edit": "نظر ۾ فهرست ڏسو ۽ سنواريو",
+       "watchlisttools-edit": "نظر ۾ فھرست ڏسو ۽ سنواريو",
        "watchlisttools-raw": "ڪچي نظر ۾ فھرست سنواريو",
        "hijri-calendar-m1": "محرم",
        "hijri-calendar-m2": "صفر",
        "specialpages-group-media": "ميڊيا رپورٽ ۽ چاڙهيل",
        "specialpages-group-users": "واپرائيندڙَ ۽ حق",
        "specialpages-group-highuse": "وڌيڪ استعمال وارا صفحا",
-       "specialpages-group-pages": "صفحن جي فهرست",
+       "specialpages-group-pages": "صفحن جي فھرست",
        "specialpages-group-pagetools": "صفحن جا اوزار",
        "blankpage": "خالي صفحو",
        "intentionallyblankpage": "هيءُ صفحو ڄاڻي خالي ڇڏيو ويو آهي.",
        "tags-delete": "ڊاهيو",
        "tags-activate": "فعال بڻايو",
        "tags-deactivate": "غير فعال بڻايو",
-       "tags-hitcount": "$1 {{PLURAL:$1|تبدÙ\8aÙ\84Ù\8a|تبدÙ\8aÙ\84Ù\8aÙ\88Ù\86}}",
+       "tags-hitcount": "$1 {{PLURAL:$1|بدÙ\84اءÙ\8f|بدÙ\84اءÙ\8e}}",
        "tags-create-tag-name": "ٽيگ نانءُ:",
        "tags-create-reason": "سبب:",
        "tags-create-submit": "سرجيو",
        "htmlform-title-not-exists": "$1 وجود نٿو رکي.",
        "logentry-delete-delete": "$1 {{GENDER:$2|ڊاٿو}} صفحو $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|بحاليو}} صفحو $3 ($4)",
-       "logentry-delete-revision": "$1 $3: $4 ØµÙ\81Ø­Ù\8a ØªÙ\8a {{PLURAL:$5|Ú¾Úª Ù\88رجاءÙ\8e|$5 Ù\88رجائÙ\86}} Ø¬Ù\8a Ø¸Ø§Ú¾Ø±Ù\8aت {{GENDER:$2|تبدÙ\8aÙ\84 Úªئي}}",
+       "logentry-delete-revision": "$1 $3: $4 ØµÙ\81Ø­Ù\8a ØªÙ\8a {{PLURAL:$5|Ú¾Úª Ù\88رجاءÙ\8e|$5 Ù\88رجائÙ\86}} Ø¬Ù\8a Ø¸Ø§Ú¾Ø±Ù\8aت {{GENDER:$2|بدÙ\84ائي}}",
        "revdelete-content-hid": "مواد لڪيل",
        "revdelete-uname-hid": "واپرائيندڙ-نانءُ لڪل",
        "revdelete-unrestricted": "منتظمن تان پابنديون ھٽايون ويون",
index cd4e8dd..10882ad 100644 (file)
@@ -71,7 +71,7 @@
        "tog-shownumberswatching": "Zobraziť počet používateľov sledujúcich stránku",
        "tog-oldsig": "Váš súčasný podpis:",
        "tog-fancysig": "Považovať podpisy za wikitext (bez automatických odkazov)",
-       "tog-uselivepreview": "Používať živý náhľad",
+       "tog-uselivepreview": "Zobrazovať náhľady bez obnovenia stránky",
        "tog-forceeditsummary": "Upozoriť ma, keď nevyplním zhrnutie úprav",
        "tog-watchlisthideown": "Skryť moje úpravy zo zoznamu sledovaných",
        "tog-watchlisthidebots": "Skryť úpravy botov zo zoznamu sledovaných",
        "createacct-another-submit": "Vytvoriť účet",
        "createacct-continue-submit": "Pokračovať vo vytváraní účtu",
        "createacct-another-continue-submit": "Pokračovať vo vytváraní účtu",
-       "createacct-benefit-heading": "{{GRAMMAR:akuzatív|{{SITENAME}}}} tvoria ľudia ako ste vy.",
+       "createacct-benefit-heading": "{{GRAMMAR:akuzatív|{{SITENAME}}}} tvoria ľudia ako vy.",
        "createacct-benefit-body1": "{{PLURAL:$1|úprava|úpravy|úprav}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stránka|stránky|stránok}}",
        "createacct-benefit-body3": "{{PLURAL:$1|nedávny prispievateľ|nedávni prispievatelia|nedávnych prispievateľov}}",
        "rcfilters-empty-filter": "Žiadne aktívne filtre. Všetky príspevky sú zobrazené.",
        "rcfilters-filterlist-title": "Filtre",
        "rcfilters-filterlist-whatsthis": "Ako to funguje?",
-       "rcfilters-filterlist-feedbacklink": "Povedzte nám, čo si myslíte o týchto (nových) filtroch",
+       "rcfilters-filterlist-feedbacklink": "Povedzte nám, čo si myslíte o týchto filtroch",
        "rcfilters-highlightbutton-title": "Zvýrazniť výsledky",
        "rcfilters-highlightmenu-title": "Vybrať farbu",
        "rcfilters-highlightmenu-help": "Vyberte farbu pre zvýraznenie tejto vlastnosti",
index 8a7289f..cd35ab2 100644 (file)
        "longpageerror": "''Feler: Tekst kery żeś sam wćepywoł mo {{PLURAL:$1|jedyn kilobajt|$1 kilobajtůw}}. Maksymalno dugość tekstu ńy może być srogszo kej {{PLURAL:$2|jedyn kilobajt|$2 kilobajtůw}}. Twůj tekst ńy bydźe sam naszkryflany.'''",
        "readonlywarning": "'''Dej pozůr: Baza danych zostoua filowo zawarto skuli potřeb admińistracyjnych. Bestůž ńy do śe terozki naškryflać Twojich pomjyńań. Radzymy přećepać nowy tekst kajś do plika tekstowego (wytnij/wklej) a wćepać sam zaś po uodymkńyńću bazy.'''\n\nAdmińistrator kery zawar baza dou take wyjaśńyńe: $1",
        "protectedpagewarning": "'''Dej pozůr: Sprowjańe tyj zajty zostoło zawarte. Mogům jům sprowjać ino użytkowńicy ze uprawńyńami admińistratora.'''\nUostatńy wpis w rejerze je poniżej.",
-       "semiprotectedpagewarning": "'''Pozůr:''' Ta zajta zostoÅ\82a zawarto a ino zaregiszterowani użytkownicy mogům jům sprowjaÄ\87.\nUostotÅ\84y wpis w rejerze je Å\84yżej.",
+       "semiprotectedpagewarning": "'''PozÅ\8dr:''' Ta strÅ\8dna je zawartÅ\8f i ino zaregistrowani używÅ\8fcze mogÅ\8dm jÅ\8dm edytowaÄ\87.\nÃ\94statni wpis ze regestu je niżyj.",
        "cascadeprotectedwarning": "'''Dej pozůr:''' Ta zajta zostoła zawarto a ino użytkowńicy ze uprawńyńami admińistratora mogům jům sprowjać. Zajta ta je podpjynto pod {{PLURAL:$1|nastympujůnco zajta, kero zostoła zawarto|nastympujůncych zajtach, kere zostouy zawarte}} ze załůnczonům uopcjům dźedźiczyńo:",
        "titleprotectedwarning": "'''Dej pozůr: Zajta uo tym titlu zostoła zawarto a ino [[Special:ListGroupRights|ńykerzi użytkowńicy]] mogům jům wćepać.'''\nUostatńy wpis z rejera je ńyżej.",
        "templatesused": "{{PLURAL:$1|Muster użyty|Mustry użyte}} na tyj strōnie:",
        "permissionserrorstext-withaction": "Niy mŏsz przizwolyniŏ na $2, skuli {{PLURAL:$1|takigo powodu|takich powodōw}}:",
        "recreate-moveddeleted-warn": "<strong>Pozōr: Prziwrŏcŏsz strōnã, co była przōdzij skasowanŏ.</strong>\n\nDej pozōr, czy prziwrōcynie tyj strōny je nŏleżne.\nRegesty kasowań i pōnkniyńć tyj strōny idzie ôbejzdrzeć niżyj.",
        "moveddeleted-notice": "Ta strōna była skasowanŏ.\nRegest skasowań, zabezpieczyń i pōnkniyńć tyj strōny je pokŏzany niżyj.",
-       "log-fulllog": "Ukoż rejer",
+       "log-fulllog": "Pokoż cołki regest",
        "edit-hook-aborted": "Sprowjyńy sztopńynte skiż hoka.\nŃy je wjadůme pů jakymu.",
        "edit-gone-missing": "Ńy idźe zaktualizować zajty.\nZdowo śe, co zostoła wyćepano.",
        "edit-conflict": "Kůnflikt sprowjyń.",
index e64f8fa..f9c80aa 100644 (file)
        "protectedpagetext": "ఈ పేజీలో మార్పులు వగైరాలు చెయ్యకుండా ఉండేందుకు గాను, సంరక్షించబడింది.",
        "viewsourcetext": "మీరీ పేజీ సోర్సును చూడవచ్చు, కాపీ చేసుకోవచ్చు.",
        "viewyourtext": "ఈ పేజీలో <strong>మీరు చేసిన మార్పుల</strong> మూలాన్ని చూడవచ్చు, కాపీ చేసుకోవచ్చు.",
-       "protectedinterface": "à°\88 à°ªà±\87à°\9cà±\80, à°\88 à°µà°¿à°\95à±\80 à°¯à±\8aà°\95à±\8dà°\95 à°¸à°¾à°«à±\8dà°\9fà±\81à°µà±\87à°°à±\81 à°\87à°\82à°\9fà°°à±\81à°«à±\87à°¸à±\81à°\95à±\81 à°\9aà±\86à°\82దిన à°\9fà±\86à°\95à±\8dà°¸à±\8dà°\9fà±\81à°¨à±\81 à°\85à°\82దిసà±\8dà°¤à±\81à°\82ది. à°¦à±\81à°¶à±\8dà°\9aà°°à±\8dయల à°¨à°¿à°µà°¾à°°à°£ à°\95à±\8bసమà±\88 à°¦à±\80à°¨à±\8dని à°¸à°\82à°°à°\95à±\8dà°·à°¿à°\82à°\9aà°¾à°\82. à°µà°¿à°\95à±\80లనà±\8dనిà°\9fà°¿à°²à±\8bà°¨à±\81 అనువాదాలను చేర్చాలన్నా, మార్చాలన్నా మీడియావికీ స్థానికీకరణ ప్రాజెక్టైన [https://translatewiki.net/ translatewiki.net] ను వాడండి.",
+       "protectedinterface": "à°\88 à°ªà±\87à°\9cà±\80, à°\88 à°µà°¿à°\95à±\80 à°¯à±\8aà°\95à±\8dà°\95 à°\87à°\82à°\9fà°°à±\81à°«à±\87à°¸à±\81à°\95à±\81 à°\9aà±\86à°\82దిన à°\9fà±\86à°\95à±\8dà°¸à±\8dà°\9fà±\81à°¨à±\81 à°\85à°\82దిసà±\8dà°¤à±\81à°\82ది. à°¦à±\81à°¶à±\8dà°\9aà°°à±\8dయలనà±\81 à°¨à°¿à°µà°¾à°°à°¿à°\82à°\9aà±\87à°\82à°¦à±\81à°\95à±\88 à°¦à±\80à°¨à±\8dని à°¸à°\82à°°à°\95à±\8dà°·à°¿à°\82à°\9aà°¾à°\82. à°µà°¿à°\95à±\80లనà±\8dనిà°\9fà°¿à°²à±\8b అనువాదాలను చేర్చాలన్నా, మార్చాలన్నా మీడియావికీ స్థానికీకరణ ప్రాజెక్టైన [https://translatewiki.net/ translatewiki.net] ను వాడండి.",
        "editinginterface": "<strong>హెచ్చరిక:</strong> సాఫ్టువేరుకు ఇంటరుఫేసు టెక్స్టును అందించేందుకు పనికొచ్చే పేజీని మీరు సరిదిద్దుతున్నారు.\nఈ పేజీలో చేసే మార్పుల వల్ల ఇతర వాడుకరులకు కనబడే ఇంటరుఫేసు ప్రభావితమౌతుంది.",
        "translateinterface": "అన్ని వికీలలో కనిపించేలా అనువాదాలు చేర్చాలన్నా, మార్చాలన్నా, దయచేసి [https://translatewiki.net/ translatewiki.net] ను వాడండి. ఇది మీడియావికీ స్థానికీకరణ ప్రాజెక్టు.",
        "cascadeprotected": "కింది {{PLURAL:$1|పేజీని|పేజీలను}} కాస్కేడింగు ఆప్షనుతో సంరక్షించబడింది. ప్రస్తుత పేజీ, ఈ పేజీల్లో ట్రాన్స్‌క్లూడు అయి ఉంది కాబట్టి, దిద్దుబాటు చేసే వీలు లేకుండా ఇది కూడా రక్షణలో ఉంది:\n$2",
index 4735c4c..821ce9d 100644 (file)
        "nocreate-loggedin": "Yeni sayfalar oluşturmaya yetkiniz yok.",
        "sectioneditnotsupported-title": "Bölüm değiştirmesi desteklenmiyor",
        "sectioneditnotsupported-text": "Bölüm değiştirmesi bu sayfada desteklenmiyor.",
+       "modeleditnotsupported-title": "Düzenleme desteklenmemektedir",
        "permissionserrors": "İzin hatası",
        "permissionserrorstext": "Aşağıdaki {{PLURAL:$1|sebep|sebepler}}den dolayı, bunu yapmaya yetkiniz yok:",
        "permissionserrorstext-withaction": "Aşağıdaki {{PLURAL:$1|neden|nedenler}}den dolayı $2 yetkiniz yok:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Boş nesne",
        "content-json-empty-array": "Boş dizi",
+       "unsupported-content-model": "<strong>Uyarı:</strong> İçerik modeli $1 desteklenmemektedir",
+       "unsupported-content-diff": "İçerik modeli $1 için değişiklikler desteklenmemektedir.",
+       "unsupported-content-diff2": "İçerik modeli  $1 ve $2 arasındaki değişiklikler bu vikide desteklenmemektedir.",
        "deprecated-self-close-category": "Kendiliğinden geçersiz HTML etiketlerini kullanan sayfalar",
        "deprecated-self-close-category-desc": "Sayfa, <code>&lt;b/></code> veya <code>&lt;span/></code> gibi kendiliğinden geçersiz HTML etiketleri içeriyor. Bunların davranışları yakında HTML5 belirtimiyle tutarlı olacak şekilde değişecektir, bu nedenle wikitext'deki kullanımları, kullanımdan kaldırılır.",
        "duplicate-args-warning": "<strong>Uyarı:</strong>[[:$1]] [[:$2]] şablonunu \"$3\" parametresi için birden fazla değerle çağırıyor. Sadece sağlanan son değer kullanılacak.",
        "undo-norev": "Değişiklik geri alınamaz çünkü ya silinmiş ya da varolmamaktadır.",
        "undo-nochange": "Düzeltme zaten geri alınmış.",
        "undo-summary": "$1 değişikliği [[Special:Contributions/$2|$2]] ([[User talk:$2|mesaj]]) tarafından geri alındı.",
+       "undo-summary-anon": "[[Special:Contributions/$2|$2]] tarafından yapılan değişiklik $1'i geri al",
        "undo-summary-username-hidden": "Gizli bir kullanıcı tarafından $1 sürümü geri alınıyor",
        "cantcreateaccount-text": "Bu IP adresinden ('''$1''') kullanıcı hesabı oluşturulması [[User:$3|$3]] tarafından engellenmiştir.\n\n$3 tarafından verilen sebep ''$2''",
        "cantcreateaccount-range-text": "<strong>$1</strong> aralığındaki IP'ler için hesap oluşturma [[User:$3|$3]] tarafından engellendi, bu sizin IP adresinizi de (<strong>$4</strong>) içeriyor.\n\n$3 tarafından verilen gerekçe <em>$2</em>",
        "backend-fail-contenttype": "\"$1\" konumunda saklanan dosyanın içerik türü belirlenemiyor.",
        "backend-fail-batchsize": "Depolama arkaplan uygulamasına $1 dosya {{PLURAL:$1|işlemi|işlemi}} yığını verildi; sınır $2 {{PLURAL:$1|işlem|işlem}}.",
        "backend-fail-usable": "Yetersiz izinlerden ya da eksik dizin/konteynerlerden dolayı \"$1\" dosyası okunup yazılamıyor.",
+       "backend-fail-stat": "Dosya \"$1\" durumu okunamıyor.",
+       "backend-fail-hash": "Dosya \"$1\"e ait kriptografi belirlenemedi.",
        "filejournal-fail-dbconnect": "\"$1\" depolama arkaplan uygulaması için günlük veri tabanına erişilemiyor.",
        "filejournal-fail-dbquery": "\"$1\" depolama arkaplan uygulaması için günlük veri tabanı güncellenemiyor.",
        "lockmanager-notlocked": "\"$1\" kilidi açılamıyor; kilitli değil.",
index 59e9c86..55bf54c 100644 (file)
@@ -9,3 +9,7 @@
  */
 
 $fallback = 'hy';
+
+$magicWords['redirect'] = [ '0', '#ՎԵՐԱՅՂՈՒՄ', '#ՎԵՐԱՀՂՈՒՄ', '#REDIRECT' ];
+
+$namespaceNames[NS_CATEGORY] = 'Ստորոգութիւն';
index 6edb7ec..3f0c3df 100644 (file)
--- a/load.php
+++ b/load.php
@@ -28,6 +28,8 @@ use MediaWiki\MediaWikiServices;
 // details of the session. Enforce this constraint with respect to session use.
 define( 'MW_NO_SESSION', 1 );
 
+define( 'MW_ENTRY_POINT', 'load' );
+
 require __DIR__ . '/includes/WebStart.php';
 
 // URL safety checks
index 6d6dbe5..f89fa62 100644 (file)
@@ -20,6 +20,8 @@
  * @defgroup Maintenance Maintenance
  */
 
+define( 'MW_ENTRY_POINT', 'cli' );
+
 // Bail on old versions of PHP, or if composer has not been run yet to install
 // dependencies.
 require_once __DIR__ . '/../includes/PHPVersionCheck.php';
diff --git a/maintenance/archives/patch-drop-user-fields.sql b/maintenance/archives/patch-drop-user-fields.sql
new file mode 100644 (file)
index 0000000..7faa593
--- /dev/null
@@ -0,0 +1,50 @@
+--
+-- patch-drop-user-fields.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+ALTER TABLE /*_*/archive
+  DROP INDEX /*i*/ar_usertext_timestamp,
+  DROP COLUMN ar_user,
+  DROP COLUMN ar_user_text,
+  ALTER COLUMN ar_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/ipblocks
+  DROP COLUMN ipb_by,
+  DROP COLUMN ipb_by_text,
+  ALTER COLUMN ipb_by_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/image
+  DROP INDEX /*i*/img_user_timestamp,
+  DROP INDEX /*i*/img_usertext_timestamp,
+  DROP COLUMN img_user,
+  DROP COLUMN img_user_text,
+  ALTER COLUMN img_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/oldimage
+  DROP INDEX /*i*/oi_usertext_timestamp,
+  DROP COLUMN oi_user,
+  DROP COLUMN oi_user_text,
+  ALTER COLUMN oi_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/filearchive
+  DROP INDEX /*i*/fa_user_timestamp,
+  DROP COLUMN fa_user,
+  DROP COLUMN fa_user_text,
+  ALTER COLUMN fa_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/recentchanges
+  DROP INDEX /*i*/rc_ns_usertext,
+  DROP INDEX /*i*/rc_user_text,
+  DROP COLUMN rc_user,
+  DROP COLUMN rc_user_text,
+  ALTER COLUMN rc_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/logging
+  DROP INDEX /*i*/user_time,
+  DROP INDEX /*i*/log_user_type_time,
+  DROP INDEX /*i*/log_user_text_type_time,
+  DROP INDEX /*i*/log_user_text_time,
+  DROP COLUMN log_user,
+  DROP COLUMN log_user_text,
+  ALTER COLUMN log_actor DROP DEFAULT;
index 305a41d..6928181 100644 (file)
@@ -42,6 +42,9 @@ class CleanupImages extends TableCleanup {
                'callback' => 'processRow',
        ];
 
+       /** @var LocalRepo|null */
+       private $repo;
+
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Script to clean up broken, unparseable upload filenames' );
@@ -111,8 +114,12 @@ class CleanupImages extends TableCleanup {
                }
        }
 
+       /**
+        * @param string $name
+        * @return string
+        */
        private function filePath( $name ) {
-               if ( !isset( $this->repo ) ) {
+               if ( $this->repo === null ) {
                        $this->repo = RepoGroup::singleton()->getLocalRepo();
                }
 
index 3f55878..0d4b7b1 100644 (file)
@@ -39,6 +39,18 @@ require_once __DIR__ . '/dumpIterator.php';
 class CompareParsers extends DumpIterator {
 
        private $count = 0;
+       /** @var bool */
+       private $saveFailed;
+       /** @var bool */
+       private $stripParametersEnabled;
+       /** @var bool */
+       private $showParsedOutput;
+       /** @var bool */
+       private $showDiff;
+       /** @var ParserOptions */
+       private $options;
+       /** @var int */
+       private $failed;
 
        public function __construct() {
                parent::__construct();
index 751932d..20e9459 100644 (file)
@@ -34,9 +34,10 @@ require_once __DIR__ . '/Maintenance.php';
  * @ingroup Maintenance
  */
 abstract class DumpIterator extends Maintenance {
-
        private $count = 0;
        private $startTime;
+       /** @var string|bool|null */
+       private $from;
 
        public function __construct() {
                parent::__construct();
@@ -60,6 +61,7 @@ abstract class DumpIterator extends Maintenance {
                        $revision->setTitle( Title::newFromText(
                                rawurldecode( basename( $this->getOption( 'file' ), '.txt' ) )
                        ) );
+                       $this->from = false;
                        $this->handleRevision( $revision );
 
                        return;
@@ -131,7 +133,7 @@ abstract class DumpIterator extends Maintenance {
                }
 
                $this->count++;
-               if ( isset( $this->from ) ) {
+               if ( $this->from !== false ) {
                        if ( $this->from != $title ) {
                                return;
                        }
index c4ca056..40cac1b 100644 (file)
@@ -32,6 +32,9 @@ require_once __DIR__ . '/Maintenance.php';
  * @ingroup Maintenance
  */
 class DumpUploads extends Maintenance {
+       /** @var string */
+       private $mBasePath;
+
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Generates list of uploaded files which can be fed to tar or similar.
@@ -44,30 +47,29 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
 
        public function execute() {
                global $IP;
-               $this->mAction = 'fetchLocal';
                $this->mBasePath = $this->getOption( 'base', $IP );
-               $this->mShared = false;
-               $this->mSharedSupplement = false;
-
-               if ( $this->hasOption( 'local' ) ) {
-                       $this->mAction = 'fetchLocal';
-               }
-
-               if ( $this->hasOption( 'used' ) ) {
-                       $this->mAction = 'fetchUsed';
-               }
+               $shared = false;
+               $sharedSupplement = false;
 
                if ( $this->hasOption( 'shared' ) ) {
                        if ( $this->hasOption( 'used' ) ) {
                                // Include shared-repo files in the used check
-                               $this->mShared = true;
+                               $shared = true;
                        } else {
                                // Grab all local *plus* used shared
-                               $this->mSharedSupplement = true;
+                               $sharedSupplement = true;
                        }
                }
-               $this->{$this->mAction} ( $this->mShared );
-               if ( $this->mSharedSupplement ) {
+
+               if ( $this->hasOption( 'local' ) ) {
+                       $this->fetchLocal( $shared );
+               } elseif ( $this->hasOption( 'used' ) ) {
+                       $this->fetchUsed( $shared );
+               } else {
+                       $this->fetchLocal( $shared );
+               }
+
+               if ( $sharedSupplement ) {
                        $this->fetchUsed( true );
                }
        }
index d861348..7e9104c 100644 (file)
@@ -38,9 +38,7 @@ class GetReplicaServer extends Maintenance {
        }
 
        public function execute() {
-               if ( $this->getConfig()->get( 'AllDBsAreLocalhost' ) ) {
-                       $host = 'localhost';
-               } elseif ( $this->hasOption( 'group' ) ) {
+               if ( $this->hasOption( 'group' ) ) {
                        $db = $this->getDB( DB_REPLICA, $this->getOption( 'group' ) );
                        $host = $db->getServer();
                } else {
index cda16fe..d5f94ad 100644 (file)
@@ -43,6 +43,16 @@ class BackupReader extends Maintenance {
        public $imageBasePath = false;
        /** @var array|false */
        public $nsFilter = false;
+       /** @var bool|resource */
+       public $stderr;
+       /** @var callable|null */
+       protected $importCallback;
+       /** @var callable|null */
+       protected $logItemCallback;
+       /** @var callable|null */
+       protected $uploadCallback;
+       /** @var int */
+       protected $startTime;
 
        function __construct() {
                parent::__construct();
index 358dc21..6c1c083 100644 (file)
@@ -49,6 +49,8 @@ abstract class BackupDumper extends Maintenance {
        public $dumpUploadFileContents = false;
        public $orderRevs = false;
        public $limitNamespaces = [];
+       /** @var bool|resource */
+       public $stderr;
 
        protected $reportingInterval = 100;
        protected $pageCount = 0;
@@ -65,6 +67,33 @@ abstract class BackupDumper extends Maintenance {
 
        protected $ID = 0;
 
+       /** @var int */
+       protected $startTime;
+       /** @var int */
+       protected $pageCountPart;
+       /** @var int */
+       protected $revCountPart;
+       /** @var int */
+       protected $maxCount;
+       /** @var int */
+       protected $timeOfCheckpoint;
+       /** @var ExportProgressFilter */
+       protected $egress;
+       /** @var string */
+       protected $buffer;
+       /** @var array|false */
+       protected $openElement;
+       /** @var bool */
+       protected $atStart;
+       /** @var string|null */
+       protected $thisRevModel;
+       /** @var string|null */
+       protected $thisRevFormat;
+       /** @var string */
+       protected $lastName;
+       /** @var string */
+       protected $state;
+
        /**
         * The dependency-injected database to use.
         *
index 1b35a20..48dcf8d 100644 (file)
@@ -51,15 +51,6 @@ class MigrateActors extends LoggedUpdateMaintenance {
        }
 
        protected function doDBUpdates() {
-               $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
-               if ( !( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) ) {
-                       $this->output(
-                               "...cannot update while \$wgActorTableSchemaMigrationStage lacks SCHEMA_COMPAT_WRITE_NEW\n"
-                       );
-                       return false;
-               }
-
                $tables = $this->getOption( 'tables' );
                if ( $tables !== null ) {
                        $this->tables = explode( ',', $tables );
@@ -265,6 +256,12 @@ class MigrateActors extends LoggedUpdateMaintenance {
                        return 0;
                }
 
+               $dbw = $this->getDB( DB_MASTER );
+               if ( !$dbw->fieldExists( $table, $userField, __METHOD__ ) ) {
+                       $this->output( "No need to migrate $table.$userField, field does not exist\n" );
+                       return 0;
+               }
+
                $complainedAboutUsers = [];
 
                $primaryKey = (array)$primaryKey;
@@ -274,7 +271,6 @@ class MigrateActors extends LoggedUpdateMaintenance {
                );
                wfWaitForSlaves();
 
-               $dbw = $this->getDB( DB_MASTER );
                $actorIdSubquery = $this->makeActorIdSubquery( $dbw, $userField, $nameField );
                $next = '1=1';
                $countUpdated = 0;
@@ -357,6 +353,7 @@ class MigrateActors extends LoggedUpdateMaintenance {
         * @param string $nameField User name field name
         * @param string $newPrimaryKey Primary key of the new table.
         * @param string $actorField Actor field name
+        * @return int Number of errors
         */
        protected function migrateToTemp(
                $table, $primaryKey, $extra, $userField, $nameField, $newPrimaryKey, $actorField
@@ -366,6 +363,12 @@ class MigrateActors extends LoggedUpdateMaintenance {
                        return 0;
                }
 
+               $dbw = $this->getDB( DB_MASTER );
+               if ( !$dbw->fieldExists( $table, $userField, __METHOD__ ) ) {
+                       $this->output( "No need to migrate $table.$userField, field does not exist\n" );
+                       return 0;
+               }
+
                $complainedAboutUsers = [];
 
                $newTable = $table . '_actor_temp';
@@ -374,7 +377,6 @@ class MigrateActors extends LoggedUpdateMaintenance {
                );
                wfWaitForSlaves();
 
-               $dbw = $this->getDB( DB_MASTER );
                $actorIdSubquery = $this->makeActorIdSubquery( $dbw, $userField, $nameField );
                $next = [];
                $countUpdated = 0;
index 54c1d38..b19420f 100644 (file)
@@ -21,7 +21,6 @@
                                "classes": [
                                        "mw.Title",
                                        "mw.Uri",
-                                       "mw.RegExp",
                                        "mw.String",
                                        "mw.messagePoster.*",
                                        "mw.notification",
index 48a6666..d78c5a0 100644 (file)
@@ -129,7 +129,6 @@ class MergeMessageFileList extends Maintenance {
                $files = [];
                $fileLines = file( $fileName );
                if ( $fileLines === false ) {
-                       $this->hasError = true;
                        $this->error( "Unable to open list file $fileName." );
 
                        return $files;
@@ -144,7 +143,6 @@ class MergeMessageFileList extends Maintenance {
                                if ( file_exists( $extension ) ) {
                                        $files[] = $extension;
                                } else {
-                                       $this->hasError = true;
                                        $this->error( "Extension {$extension} doesn't exist" );
                                }
                        }
index 4a50cc5..f600f13 100644 (file)
@@ -42,6 +42,26 @@ require_once __DIR__ . '/Maintenance.php';
  * @ingroup Maintenance
  */
 class MWDocGen extends Maintenance {
+       /** @var string */
+       private $doxygen;
+       /** @var string */
+       private $mwVersion;
+       /** @var string */
+       private $output;
+       /** @var string */
+       private $input;
+       /** @var string */
+       private $inputFilter;
+       /** @var string */
+       private $template;
+       /** @var string[] */
+       private $excludes;
+       /** @var string[] */
+       private $excludePatterns;
+       /** @var bool */
+       private $doDot;
+       /** @var bool */
+       private $doMan;
 
        /**
         * Prepare Maintenance class
index 87fb396..20096a2 100644 (file)
@@ -244,9 +244,7 @@ CREATE TABLE archive (
   ar_parent_id      INTEGER          NULL,
   ar_sha1           TEXT         NOT NULL DEFAULT '',
   ar_comment_id     INTEGER      NOT NULL,
-  ar_user           INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  ar_user_text      TEXT         NOT NULL DEFAULT '',
-  ar_actor          INTEGER      NOT NULL DEFAULT 0,
+  ar_actor          INTEGER      NOT NULL,
   ar_timestamp      TIMESTAMPTZ  NOT NULL,
   ar_minor_edit     SMALLINT     NOT NULL  DEFAULT 0,
   ar_rev_id         INTEGER      NOT NULL,
@@ -258,7 +256,6 @@ CREATE TABLE archive (
 );
 ALTER SEQUENCE archive_ar_id_seq OWNED BY archive.ar_id;
 CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
-CREATE INDEX archive_user_text            ON archive (ar_user_text);
 CREATE INDEX archive_actor                ON archive (ar_actor);
 CREATE UNIQUE INDEX ar_revid_uniq ON archive (ar_rev_id);
 
@@ -404,9 +401,7 @@ CREATE TABLE ipblocks (
   ipb_id                INTEGER      NOT NULL  PRIMARY KEY DEFAULT nextval('ipblocks_ipb_id_seq'),
   ipb_address           TEXT             NULL,
   ipb_user              INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  ipb_by                INTEGER      NOT NULL  DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-  ipb_by_text           TEXT         NOT NULL  DEFAULT '',
-  ipb_by_actor          INTEGER      NOT NULL  DEFAULT 0,
+  ipb_by_actor          INTEGER      NOT NULL,
   ipb_reason_id         INTEGER      NOT NULL,
   ipb_timestamp         TIMESTAMPTZ  NOT NULL,
   ipb_auto              SMALLINT     NOT NULL  DEFAULT 0,
@@ -448,9 +443,7 @@ CREATE TABLE image (
   img_major_mime   TEXT                DEFAULT 'unknown',
   img_minor_mime   TEXT                DEFAULT 'unknown',
   img_description_id INTEGER NOT NULL,
-  img_user         INTEGER   NOT NULL  DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  img_user_text    TEXT      NOT NULL  DEFAULT '',
-  img_actor        INTEGER   NOT NULL  DEFAULT 0,
+  img_actor        INTEGER   NOT NULL,
   img_timestamp    TIMESTAMPTZ,
   img_sha1         TEXT      NOT NULL  DEFAULT ''
 );
@@ -466,9 +459,7 @@ CREATE TABLE oldimage (
   oi_height        INTEGER      NOT NULL,
   oi_bits          SMALLINT         NULL,
   oi_description_id INTEGER     NOT NULL,
-  oi_user          INTEGER      NOT NULL DEFAULT 0  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  oi_user_text     TEXT         NOT NULL DEFAULT '',
-  oi_actor         INTEGER      NOT NULL DEFAULT 0,
+  oi_actor         INTEGER      NOT NULL,
   oi_timestamp     TIMESTAMPTZ      NULL,
   oi_metadata      BYTEA        NOT NULL DEFAULT '',
   oi_media_type    TEXT             NULL,
@@ -502,9 +493,7 @@ CREATE TABLE filearchive (
   fa_major_mime         TEXT                   DEFAULT 'unknown',
   fa_minor_mime         TEXT                   DEFAULT 'unknown',
   fa_description_id     INTEGER      NOT NULL,
-  fa_user               INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  fa_user_text          TEXT         NOT NULL DEFAULT '',
-  fa_actor              INTEGER      NOT NULL DEFAULT 0,
+  fa_actor              INTEGER      NOT NULL,
   fa_timestamp          TIMESTAMPTZ,
   fa_deleted            SMALLINT     NOT NULL DEFAULT 0,
   fa_sha1               TEXT         NOT NULL DEFAULT ''
@@ -548,9 +537,7 @@ CREATE SEQUENCE recentchanges_rc_id_seq;
 CREATE TABLE recentchanges (
   rc_id              INTEGER      NOT NULL  PRIMARY KEY DEFAULT nextval('recentchanges_rc_id_seq'),
   rc_timestamp       TIMESTAMPTZ  NOT NULL,
-  rc_user            INTEGER      NOT NULL  DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  rc_user_text       TEXT         NOT NULL  DEFAULT '',
-  rc_actor           INTEGER      NOT NULL  DEFAULT 0,
+  rc_actor           INTEGER      NOT NULL,
   rc_namespace       SMALLINT     NOT NULL,
   rc_title           TEXT         NOT NULL,
   rc_comment_id      INTEGER      NOT NULL,
@@ -646,27 +633,21 @@ CREATE TABLE logging (
   log_type        TEXT         NOT NULL,
   log_action      TEXT         NOT NULL,
   log_timestamp   TIMESTAMPTZ  NOT NULL,
-  log_user        INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  log_actor       INTEGER      NOT NULL DEFAULT 0,
+  log_actor       INTEGER      NOT NULL,
   log_namespace   SMALLINT     NOT NULL,
   log_title       TEXT         NOT NULL,
   log_comment_id  INTEGER      NOT NULL,
   log_params      TEXT,
   log_deleted     SMALLINT     NOT NULL DEFAULT 0,
-  log_user_text   TEXT         NOT NULL DEFAULT '',
   log_page        INTEGER
 );
 ALTER SEQUENCE logging_log_id_seq OWNED BY logging.log_id;
 CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);
-CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);
 CREATE INDEX logging_actor_time_backwards ON logging (log_timestamp, log_actor);
 CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp);
 CREATE INDEX logging_times ON logging (log_timestamp);
-CREATE INDEX logging_user_type_time ON logging (log_user, log_type, log_timestamp);
 CREATE INDEX logging_actor_type_time ON logging (log_actor, log_type, log_timestamp);
 CREATE INDEX logging_page_id_time ON logging (log_page, log_timestamp);
-CREATE INDEX logging_user_text_type_time ON logging (log_user_text, log_type, log_timestamp);
-CREATE INDEX logging_user_text_time ON logging (log_user_text, log_timestamp);
 CREATE INDEX logging_actor_time ON logging (log_actor, log_timestamp);
 CREATE INDEX logging_type_action ON logging (log_type, log_action, log_timestamp);
 
index 963bfec..fddac8a 100644 (file)
@@ -41,6 +41,8 @@ class PreprocessDump extends DumpIterator {
        /* Variables for dressing up as a parser */
        public $mTitle = 'PreprocessDump';
        public $mPPNodeCount = 0;
+       /** @var Preprocessor */
+       public $mPreprocessor;
 
        public function getStripList() {
                $parser = MediaWikiServices::getInstance()->getParser();
@@ -73,8 +75,9 @@ class PreprocessDump extends DumpIterator {
                        $name = Preprocessor_DOM::class;
                }
 
-               MediaWikiServices::getInstance()->getParser()->firstCallInit();
-               $this->mPreprocessor = new $name( $this );
+               $parser = MediaWikiServices::getInstance()->getParser();
+               $parser->firstCallInit();
+               $this->mPreprocessor = new $name( $parser );
        }
 
        /**
index 68fb643..3a43ae8 100644 (file)
@@ -150,6 +150,16 @@ class PPFuzzTester {
 class PPFuzzTest {
        public $templates, $mainText, $title, $entryPoint, $output;
 
+       /** @var PPFuzzTester */
+       private $parent;
+       /** @var string */
+       public $nickname;
+       /** @var bool */
+       public $fancySig;
+
+       /**
+        * @param PPFuzzTester $tester
+        */
        function __construct( $tester ) {
                global $wgMaxSigChars;
                $this->parent = $tester;
index 54f1862..f3b856c 100644 (file)
@@ -23,8 +23,6 @@
  * @license GPL-2.0-or-later
  */
 
-use Wikimedia\Rdbms\IDatabase;
-
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -76,8 +74,6 @@ class ReassignEdits extends Maintenance {
         * @return int Number of entries changed, or that would be changed
         */
        private function doReassignEdits( &$from, &$to, $rc = false, $report = false ) {
-               $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
                $dbw = $this->getDB( DB_MASTER );
                $this->beginTransaction( $dbw, __METHOD__ );
 
@@ -136,36 +132,22 @@ class ReassignEdits extends Maintenance {
                        if ( $total ) {
                                # Reassign edits
                                $this->output( "\nReassigning current edits..." );
-                               if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                                       $dbw->update(
-                                               'revision',
-                                               [
-                                                       'rev_user' => $to->getId(),
-                                                       'rev_user_text' => $to->getName(),
-                                               ],
-                                               $from->isLoggedIn()
-                                                       ? [ 'rev_user' => $from->getId() ] : [ 'rev_user_text' => $from->getName() ],
-                                               __METHOD__
-                                       );
-                               }
-                               if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                                       $dbw->update(
-                                               'revision_actor_temp',
-                                               [ 'revactor_actor' => $to->getActorId( $dbw ) ],
-                                               [ 'revactor_actor' => $from->getActorId() ],
-                                               __METHOD__
-                                       );
-                               }
+                               $dbw->update(
+                                       'revision_actor_temp',
+                                       [ 'revactor_actor' => $to->getActorId( $dbw ) ],
+                                       [ 'revactor_actor' => $from->getActorId() ],
+                                       __METHOD__
+                               );
                                $this->output( "done.\nReassigning deleted edits..." );
                                $dbw->update( 'archive',
-                                       $this->userSpecification( $dbw, $to, 'ar_user', 'ar_user_text', 'ar_actor' ),
+                                       [ 'ar_actor' => $to->getActorId( $dbw ) ],
                                        [ $arQueryInfo['conds'] ], __METHOD__ );
                                $this->output( "done.\n" );
                                # Update recent changes if required
                                if ( $rc ) {
                                        $this->output( "Updating recent changes..." );
                                        $dbw->update( 'recentchanges',
-                                               $this->userSpecification( $dbw, $to, 'rc_user', 'rc_user_text', 'rc_actor' ),
+                                               [ 'rc_actor' => $to->getActorId( $dbw ) ],
                                                [ $rcQueryInfo['conds'] ], __METHOD__ );
                                        $this->output( "done.\n" );
                                }
@@ -177,33 +159,6 @@ class ReassignEdits extends Maintenance {
                return (int)$total;
        }
 
-       /**
-        * Return user specifications for an UPDATE
-        * i.e. user => id, user_text => text
-        *
-        * @param IDatabase $dbw Database handle
-        * @param User $user User for the spec
-        * @param string $idfield Field name containing the identifier
-        * @param string $utfield Field name containing the user text
-        * @param string $acfield Field name containing the actor ID
-        * @return array
-        */
-       private function userSpecification( IDatabase $dbw, &$user, $idfield, $utfield, $acfield ) {
-               $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
-               $ret = [];
-               if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                       $ret += [
-                               $idfield => $user->getId(),
-                               $utfield => $user->getName(),
-                       ];
-               }
-               if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $ret += [ $acfield => $user->getActorId( $dbw ) ];
-               }
-               return $ret;
-       }
-
        /**
         * Initialise the user object
         *
index bfbee9b..df2ab04 100644 (file)
@@ -41,12 +41,32 @@ use Wikimedia\Rdbms\IMaintainableDatabase;
  * @ingroup Maintenance
  */
 class ImageBuilder extends Maintenance {
-
        /**
         * @var IMaintainableDatabase
         */
        protected $dbw;
 
+       /** @var bool */
+       private $dryrun;
+
+       /** @var LocalRepo|null */
+       private $repo;
+
+       /** @var int */
+       private $updated;
+
+       /** @var int */
+       private $processed;
+
+       /** @var int */
+       private $count;
+
+       /** @var int */
+       private $startTime;
+
+       /** @var string */
+       private $table;
+
        function __construct() {
                parent::__construct();
 
@@ -79,7 +99,7 @@ class ImageBuilder extends Maintenance {
         * @return LocalRepo
         */
        function getRepo() {
-               if ( !isset( $this->repo ) ) {
+               if ( $this->repo === null ) {
                        $this->repo = RepoGroup::singleton()->getLocalRepo();
                }
 
@@ -91,6 +111,10 @@ class ImageBuilder extends Maintenance {
                $this->buildOldImage();
        }
 
+       /**
+        * @param int $count
+        * @param string $table
+        */
        function init( $count, $table ) {
                $this->processed = 0;
                $this->updated = 0;
index 7e8f063..8ec648f 100644 (file)
@@ -36,6 +36,12 @@ use MediaWiki\MediaWikiServices;
  * @ingroup Maintenance
  */
 class RecountCategories extends Maintenance {
+       /** @var string */
+       private $mode;
+
+       /** @var int */
+       private $minimumId;
+
        public function __construct() {
                parent::__construct();
                $this->addDescription( <<<'TEXT'
index 16a7346..ca90468 100644 (file)
@@ -39,8 +39,6 @@ class RemoveUnusedAccounts extends Maintenance {
        }
 
        public function execute() {
-               $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
                $this->output( "Remove unused accounts\n\n" );
 
                # Do an initial scan for inactive accounts and report the result
@@ -48,18 +46,14 @@ class RemoveUnusedAccounts extends Maintenance {
                $delUser = [];
                $delActor = [];
                $dbr = $this->getDB( DB_REPLICA );
-               if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $res = $dbr->select(
-                               [ 'user', 'actor' ],
-                               [ 'user_id', 'user_name', 'user_touched', 'actor_id' ],
-                               '',
-                               __METHOD__,
-                               [],
-                               [ 'actor' => [ 'LEFT JOIN', 'user_id = actor_user' ] ]
-                       );
-               } else {
-                       $res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
-               }
+               $res = $dbr->select(
+                       [ 'user', 'actor' ],
+                       [ 'user_id', 'user_name', 'user_touched', 'actor_id' ],
+                       '',
+                       __METHOD__,
+                       [],
+                       [ 'actor' => [ 'LEFT JOIN', 'user_id = actor_user' ] ]
+               );
                if ( $this->hasOption( 'ignore-groups' ) ) {
                        $excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
                } else {
@@ -94,30 +88,22 @@ class RemoveUnusedAccounts extends Maintenance {
                        $this->output( "\nDeleting unused accounts..." );
                        $dbw = $this->getDB( DB_MASTER );
                        $dbw->delete( 'user', [ 'user_id' => $delUser ], __METHOD__ );
-                       if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                               # Keep actor rows referenced from ipblocks
-                               $keep = $dbw->selectFieldValues(
-                                       'ipblocks', 'ipb_by_actor', [ 'ipb_by_actor' => $delActor ], __METHOD__
-                               );
-                               $del = array_diff( $delActor, $keep );
-                               if ( $del ) {
-                                       $dbw->delete( 'actor', [ 'actor_id' => $del ], __METHOD__ );
-                               }
-                               if ( $keep ) {
-                                       $dbw->update( 'actor', [ 'actor_user' => 0 ], [ 'actor_id' => $keep ], __METHOD__ );
-                               }
+                       # Keep actor rows referenced from ipblocks
+                       $keep = $dbw->selectFieldValues(
+                               'ipblocks', 'ipb_by_actor', [ 'ipb_by_actor' => $delActor ], __METHOD__
+                       );
+                       $del = array_diff( $delActor, $keep );
+                       if ( $del ) {
+                               $dbw->delete( 'actor', [ 'actor_id' => $del ], __METHOD__ );
+                       }
+                       if ( $keep ) {
+                               $dbw->update( 'actor', [ 'actor_user' => 0 ], [ 'actor_id' => $keep ], __METHOD__ );
                        }
                        $dbw->delete( 'user_groups', [ 'ug_user' => $delUser ], __METHOD__ );
                        $dbw->delete( 'user_former_groups', [ 'ufg_user' => $delUser ], __METHOD__ );
                        $dbw->delete( 'user_properties', [ 'up_user' => $delUser ], __METHOD__ );
-                       if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                               $dbw->delete( 'logging', [ 'log_actor' => $delActor ], __METHOD__ );
-                               $dbw->delete( 'recentchanges', [ 'rc_actor' => $delActor ], __METHOD__ );
-                       }
-                       if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
-                               $dbw->delete( 'logging', [ 'log_user' => $delUser ], __METHOD__ );
-                               $dbw->delete( 'recentchanges', [ 'rc_user' => $delUser ], __METHOD__ );
-                       }
+                       $dbw->delete( 'logging', [ 'log_actor' => $delActor ], __METHOD__ );
+                       $dbw->delete( 'recentchanges', [ 'rc_actor' => $delActor ], __METHOD__ );
                        $this->output( "done.\n" );
                        # Update the site_stats.ss_users field
                        $users = $dbw->selectField( 'user', 'COUNT(*)', [], __METHOD__ );
index cc5ae59..85795ca 100644 (file)
@@ -40,6 +40,8 @@ class DumpRenderer extends Maintenance {
 
        private $count = 0;
        private $outputDirectory, $startTime;
+       /** @var string */
+       private $prefix;
 
        public function __construct() {
                parent::__construct();
index 284db2c..154482c 100644 (file)
@@ -50,10 +50,10 @@ class ResetUserTokens extends Maintenance {
        }
 
        public function execute() {
-               $this->nullsOnly = $this->getOption( 'nulls' );
+               $nullsOnly = $this->getOption( 'nulls' );
 
                if ( !$this->getOption( 'nowarn' ) ) {
-                       if ( $this->nullsOnly ) {
+                       if ( $nullsOnly ) {
                                $this->output( "The script is about to reset the user_token "
                                        . "for USERS WITH NULL TOKENS in the database.\n" );
                        } else {
@@ -71,7 +71,7 @@ class ResetUserTokens extends Maintenance {
                $dbr = $this->getDB( DB_REPLICA );
 
                $where = [];
-               if ( $this->nullsOnly ) {
+               if ( $nullsOnly ) {
                        // Have to build this by hand, because \ is escaped in helper functions
                        $where = [ 'user_token = \'' . str_repeat( '\0', 32 ) . '\'' ];
                }
diff --git a/maintenance/sqlite/archives/patch-archive-drop-ar_user.sql b/maintenance/sqlite/archives/patch-archive-drop-ar_user.sql
new file mode 100644 (file)
index 0000000..ceb7d7d
--- /dev/null
@@ -0,0 +1,44 @@
+--
+-- patch-archive-drop-ar_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+CREATE TABLE /*_*/archive_tmp (
+  ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ar_namespace int NOT NULL default 0,
+  ar_title varchar(255) binary NOT NULL default '',
+  ar_comment_id bigint unsigned NOT NULL,
+  ar_actor bigint unsigned NOT NULL,
+  ar_timestamp binary(14) NOT NULL default '',
+  ar_minor_edit tinyint NOT NULL default 0,
+  ar_rev_id int unsigned NOT NULL,
+  ar_text_id int unsigned NOT NULL DEFAULT 0,
+  ar_deleted tinyint unsigned NOT NULL default 0,
+  ar_len int unsigned,
+  ar_page_id int unsigned,
+  ar_parent_id int unsigned default NULL,
+  ar_sha1 varbinary(32) NOT NULL default '',
+  ar_content_model varbinary(32) DEFAULT NULL,
+  ar_content_format varbinary(64) DEFAULT NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/archive_tmp (
+       ar_id, ar_namespace, ar_title, ar_comment_id, ar_actor,
+       ar_timestamp, ar_minor_edit, ar_rev_id, ar_text_id, ar_deleted,
+       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model, ar_content_format
+  ) SELECT
+       ar_id, ar_namespace, ar_title, ar_comment_id, ar_actor,
+       ar_timestamp, ar_minor_edit, ar_rev_id, ar_text_id, ar_deleted,
+       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model, ar_content_format
+  FROM /*_*/archive;
+
+DROP TABLE /*_*/archive;
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
+CREATE UNIQUE INDEX /*i*/ar_revid_uniq ON /*_*/archive (ar_rev_id);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-filearchive-drop-fa_user.sql b/maintenance/sqlite/archives/patch-filearchive-drop-fa_user.sql
new file mode 100644 (file)
index 0000000..0a20a56
--- /dev/null
@@ -0,0 +1,55 @@
+--
+-- patch-filearchive-drop-fa_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/filearchive_tmp;
+CREATE TABLE /*_*/filearchive_tmp (
+  fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  fa_name varchar(255) binary NOT NULL default '',
+  fa_archive_name varchar(255) binary default '',
+  fa_storage_group varbinary(16),
+  fa_storage_key varbinary(64) default '',
+  fa_deleted_user int,
+  fa_deleted_timestamp binary(14) default '',
+  fa_deleted_reason_id bigint unsigned NOT NULL,
+  fa_size int unsigned default 0,
+  fa_width int default 0,
+  fa_height int default 0,
+  fa_metadata mediumblob,
+  fa_bits int default 0,
+  fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+  fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
+  fa_minor_mime varbinary(100) default "unknown",
+  fa_description_id bigint unsigned NOT NULL,
+  fa_actor bigint unsigned NOT NULL DEFAULT 0,
+  fa_timestamp binary(14) default '',
+  fa_deleted tinyint unsigned NOT NULL default 0,
+  fa_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/filearchive_tmp (
+       fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key,
+       fa_deleted_user, fa_deleted_timestamp, fa_deleted_reason_id,
+       fa_size, fa_width, fa_height, fa_metadata, fa_bits,
+       fa_media_type, fa_major_mime, fa_minor_mime, fa_description_id,
+       fa_actor, fa_timestamp, fa_deleted, fa_sha1
+  ) SELECT
+       fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key,
+       fa_deleted_user, fa_deleted_timestamp, fa_deleted_reason_id,
+       fa_size, fa_width, fa_height, fa_metadata, fa_bits,
+       fa_media_type, fa_major_mime, fa_minor_mime, fa_description_id,
+       fa_actor, fa_timestamp, fa_deleted, fa_sha1
+  FROM /*_*/filearchive;
+
+DROP TABLE /*_*/filearchive;
+ALTER TABLE /*_*/filearchive_tmp RENAME TO /*_*/filearchive;
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
+CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-image-drop-img_user.sql b/maintenance/sqlite/archives/patch-image-drop-img_user.sql
new file mode 100644 (file)
index 0000000..d93e122
--- /dev/null
@@ -0,0 +1,43 @@
+--
+-- patch-image-drop-img_description.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/image_tmp;
+CREATE TABLE /*_*/image_tmp (
+  img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+  img_size int unsigned NOT NULL default 0,
+  img_width int NOT NULL default 0,
+  img_height int NOT NULL default 0,
+  img_metadata mediumblob NOT NULL,
+  img_bits int NOT NULL default 0,
+  img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+  img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+  img_minor_mime varbinary(100) NOT NULL default "unknown",
+  img_description_id bigint unsigned NOT NULL,
+  img_actor bigint unsigned NOT NULL,
+  img_timestamp varbinary(14) NOT NULL default '',
+  img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/image_tmp (
+       img_name, img_size, img_width, img_height, img_metadata, img_bits,
+       img_media_type, img_major_mime, img_minor_mime, img_description_id,
+       img_actor, img_timestamp, img_sha1
+  ) SELECT
+       img_name, img_size, img_width, img_height, img_metadata, img_bits,
+       img_media_type, img_major_mime, img_minor_mime, img_description_id,
+       img_actor, img_timestamp, img_sha1
+  FROM /*_*/image;
+
+DROP TABLE /*_*/image;
+ALTER TABLE /*_*/image_tmp RENAME TO /*_*/image;
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1(10));
+CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,img_minor_mime);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-ipblocks-drop-ipb_by.sql b/maintenance/sqlite/archives/patch-ipblocks-drop-ipb_by.sql
new file mode 100644 (file)
index 0000000..2de5e09
--- /dev/null
@@ -0,0 +1,51 @@
+--
+-- patch-ipblocks-drop-ipb_by.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS ipblocks_tmp;
+CREATE TABLE /*_*/ipblocks_tmp (
+  ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ipb_address tinyblob NOT NULL,
+  ipb_user int unsigned NOT NULL default 0,
+  ipb_by_actor bigint unsigned NOT NULL,
+  ipb_reason_id bigint unsigned NOT NULL,
+  ipb_timestamp binary(14) NOT NULL default '',
+  ipb_auto bool NOT NULL default 0,
+  ipb_anon_only bool NOT NULL default 0,
+  ipb_create_account bool NOT NULL default 1,
+  ipb_enable_autoblock bool NOT NULL default '1',
+  ipb_expiry varbinary(14) NOT NULL default '',
+  ipb_range_start tinyblob NOT NULL,
+  ipb_range_end tinyblob NOT NULL,
+  ipb_deleted bool NOT NULL default 0,
+  ipb_block_email bool NOT NULL default 0,
+  ipb_allow_usertalk bool NOT NULL default 0,
+  ipb_parent_block_id int default NULL,
+  ipb_sitewide bool NOT NULL default 1
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/ipblocks_tmp (
+       ipb_id, ipb_address, ipb_user, ipb_by_actor, ipb_reason_id,
+       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id, ipb_sitewide
+  ) SELECT
+       ipb_id, ipb_address, ipb_user, ipb_by_actor, ipb_reason_id,
+       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id, ipb_sitewide
+  FROM /*_*/ipblocks;
+
+DROP TABLE /*_*/ipblocks;
+ALTER TABLE /*_*/ipblocks_tmp RENAME TO /*_*/ipblocks;
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE INDEX /*i*/ipb_parent_block_id ON /*_*/ipblocks (ipb_parent_block_id);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-logging-drop-log_user.sql b/maintenance/sqlite/archives/patch-logging-drop-log_user.sql
new file mode 100644 (file)
index 0000000..0f5871a
--- /dev/null
@@ -0,0 +1,41 @@
+--
+-- patch-logging-drop-log_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/logging_tmp;
+CREATE TABLE /*_*/logging_tmp (
+  log_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  log_type varbinary(32) NOT NULL default '',
+  log_action varbinary(32) NOT NULL default '',
+  log_timestamp binary(14) NOT NULL default '19700101000000',
+  log_actor bigint unsigned NOT NULL DEFAULT 0,
+  log_namespace int NOT NULL default 0,
+  log_title varchar(255) binary NOT NULL default '',
+  log_page int unsigned NULL,
+  log_comment_id bigint unsigned NOT NULL,
+  log_params blob NOT NULL,
+  log_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/logging_tmp (
+       log_id, log_type, log_action, log_timestamp, log_actor,
+       log_namespace, log_title, log_page, log_comment_id, log_params, log_deleted
+  ) SELECT
+       log_id, log_type, log_action, log_timestamp, log_actor,
+       log_namespace, log_title, log_page, log_comment_id, log_params, log_deleted
+  FROM /*_*/logging;
+
+DROP TABLE /*_*/logging;
+ALTER TABLE /*_*/logging_tmp RENAME TO /*_*/logging;
+CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
+CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
+CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
+CREATE INDEX /*i*/log_type_action ON /*_*/logging (log_type, log_action, log_timestamp);
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-oldimage-drop-oi_user.sql b/maintenance/sqlite/archives/patch-oldimage-drop-oi_user.sql
new file mode 100644 (file)
index 0000000..8735238
--- /dev/null
@@ -0,0 +1,44 @@
+--
+-- patch-oldimage-drop-oi_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/oldimage_tmp;
+CREATE TABLE /*_*/oldimage_tmp (
+  oi_name varchar(255) binary NOT NULL default '',
+  oi_archive_name varchar(255) binary NOT NULL default '',
+  oi_size int unsigned NOT NULL default 0,
+  oi_width int NOT NULL default 0,
+  oi_height int NOT NULL default 0,
+  oi_bits int NOT NULL default 0,
+  oi_description_id bigint unsigned NOT NULL,
+  oi_actor bigint unsigned NOT NULL,
+  oi_timestamp binary(14) NOT NULL default '',
+  oi_metadata mediumblob NOT NULL,
+  oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+  oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+  oi_minor_mime varbinary(100) NOT NULL default "unknown",
+  oi_deleted tinyint unsigned NOT NULL default 0,
+  oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/oldimage_tmp (
+       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+       oi_description_id, oi_actor, oi_timestamp, oi_metadata,
+       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
+  ) SELECT
+       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+       oi_description_id, oi_actor, oi_timestamp, oi_metadata,
+       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
+  FROM /*_*/oldimage;
+
+DROP TABLE /*_*/oldimage;
+ALTER TABLE /*_*/oldimage_tmp RENAME TO /*_*/oldimage;
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1(10));
+
+COMMIT;
diff --git a/maintenance/sqlite/archives/patch-recentchanges-drop-rc_user.sql b/maintenance/sqlite/archives/patch-recentchanges-drop-rc_user.sql
new file mode 100644 (file)
index 0000000..ba1f434
--- /dev/null
@@ -0,0 +1,59 @@
+--
+-- patch-recentchanges-drop-rc_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/recentchanges_tmp;
+CREATE TABLE /*_*/recentchanges_tmp (
+  rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  rc_timestamp varbinary(14) NOT NULL default '',
+  rc_actor bigint unsigned NOT NULL DEFAULT 0,
+  rc_namespace int NOT NULL default 0,
+  rc_title varchar(255) binary NOT NULL default '',
+  rc_comment_id bigint unsigned NOT NULL,
+  rc_minor tinyint unsigned NOT NULL default 0,
+  rc_bot tinyint unsigned NOT NULL default 0,
+  rc_new tinyint unsigned NOT NULL default 0,
+  rc_cur_id int unsigned NOT NULL default 0,
+  rc_this_oldid int unsigned NOT NULL default 0,
+  rc_last_oldid int unsigned NOT NULL default 0,
+  rc_type tinyint unsigned NOT NULL default 0,
+  rc_source varchar(16) binary not null default '',
+  rc_patrolled tinyint unsigned NOT NULL default 0,
+  rc_ip varbinary(40) NOT NULL default '',
+  rc_old_len int,
+  rc_new_len int,
+  rc_deleted tinyint unsigned NOT NULL default 0,
+  rc_logid int unsigned NOT NULL default 0,
+  rc_log_type varbinary(255) NULL default NULL,
+  rc_log_action varbinary(255) NULL default NULL,
+  rc_params blob NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/recentchanges_tmp (
+       rc_id, rc_timestamp, rc_actor, rc_namespace, rc_title,
+       rc_comment_id, rc_minor, rc_bot, rc_new, rc_cur_id, rc_this_oldid, rc_last_oldid,
+       rc_type, rc_source, rc_patrolled, rc_ip, rc_old_len, rc_new_len, rc_deleted,
+       rc_logid, rc_log_type, rc_log_action, rc_params
+  ) SELECT
+       rc_id, rc_timestamp, rc_actor, rc_namespace, rc_title,
+       rc_comment_id, rc_minor, rc_bot, rc_new, rc_cur_id, rc_this_oldid, rc_last_oldid,
+       rc_type, rc_source, rc_patrolled, rc_ip, rc_old_len, rc_new_len, rc_deleted,
+       rc_logid, rc_log_type, rc_log_action, rc_params
+  FROM /*_*/recentchanges;
+
+DROP TABLE /*_*/recentchanges;
+ALTER TABLE /*_*/recentchanges_tmp RENAME TO /*_*/recentchanges;
+CREATE INDEX /*i*/rc_timestamp ON /*_*/recentchanges (rc_timestamp);
+CREATE INDEX /*i*/rc_namespace_title_timestamp ON /*_*/recentchanges (rc_namespace, rc_title, rc_timestamp);
+CREATE INDEX /*i*/rc_cur_id ON /*_*/recentchanges (rc_cur_id);
+CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,rc_timestamp);
+CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
+CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
+CREATE INDEX /*i*/rc_this_oldid ON /*_*/recentchanges (rc_this_oldid);
+
+COMMIT;
index 316d2d2..9f20e67 100644 (file)
@@ -275,7 +275,7 @@ class RecompressTracked {
        /**
         * Dispatch a command to the next available replica DB.
         * This may block until a replica DB finishes its work and becomes available.
-        * @param array ...$args
+        * @param array|string ...$args
         */
        function dispatch( ...$args ) {
                $pipes = $this->replicaPipes;
@@ -713,6 +713,8 @@ class CgzCopyTransaction {
        /** @var ConcatenatedGzipHistoryBlob|false */
        public $cgz;
        public $referrers;
+       /** @var array */
+       private $texts;
 
        /**
         * Create a transaction from a RecompressTracked object
index df15abf..55c6775 100644 (file)
@@ -594,9 +594,7 @@ CREATE TABLE /*_*/archive (
 
   -- Basic revision stuff...
   ar_comment_id bigint unsigned NOT NULL,
-  ar_user int unsigned NOT NULL default 0, -- Deprecated in favor of ar_actor
-  ar_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of ar_actor
-  ar_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ar_user/ar_user_text should be used)
+  ar_actor bigint unsigned NOT NULL,
   ar_timestamp binary(14) NOT NULL default '',
   ar_minor_edit tinyint NOT NULL default 0,
 
@@ -661,7 +659,6 @@ CREATE TABLE /*_*/archive (
 CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
 
 -- Index for Special:DeletedContributions
-CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
 CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
 
 -- Index for linking archive rows with tables that normally link with revision
@@ -1034,14 +1031,8 @@ CREATE TABLE /*_*/ipblocks (
   -- Blocked user ID or 0 for IP blocks.
   ipb_user int unsigned NOT NULL default 0,
 
-  -- User ID who made the block.
-  ipb_by int unsigned NOT NULL default 0, -- Deprecated in favor of ipb_by_actor
-
-  -- User name of blocker
-  ipb_by_text varchar(255) binary NOT NULL default '', -- Deprecated in favor of ipb_by_actor
-
   -- Actor who made the block.
-  ipb_by_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ipb_by/ipb_by_text should be used)
+  ipb_by_actor bigint unsigned NOT NULL,
 
   -- Key to comment_id. Text comment made by blocker.
   ipb_reason_id bigint unsigned NOT NULL,
@@ -1177,14 +1168,8 @@ CREATE TABLE /*_*/image (
   -- This is displayed in image upload history and logs.
   img_description_id bigint unsigned NOT NULL,
 
-  -- user_id and user_name of uploader.
-  -- Deprecated in favor of img_actor.
-  img_user int unsigned NOT NULL default 0,
-  img_user_text varchar(255) binary NOT NULL DEFAULT '',
-
   -- actor_id of the uploader.
-  -- ("DEFAULT 0" is temporary, signaling that img_user/img_user_text should be used)
-  img_actor bigint unsigned NOT NULL DEFAULT 0,
+  img_actor bigint unsigned NOT NULL,
 
   -- Time of the upload.
   img_timestamp varbinary(14) NOT NULL default '',
@@ -1194,8 +1179,6 @@ CREATE TABLE /*_*/image (
 ) /*$wgDBTableOptions*/;
 
 -- Used by Special:Newimages and ApiQueryAllImages
-CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
-CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
 CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor,img_timestamp);
 -- Used by Special:ListFiles for sort-by-size
 CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
@@ -1226,9 +1209,7 @@ CREATE TABLE /*_*/oldimage (
   oi_height int NOT NULL default 0,
   oi_bits int NOT NULL default 0,
   oi_description_id bigint unsigned NOT NULL,
-  oi_user int unsigned NOT NULL default 0, -- Deprecated in favor of oi_actor
-  oi_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of oi_actor
-  oi_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that oi_user/oi_user_text should be used)
+  oi_actor bigint unsigned NOT NULL,
   oi_timestamp binary(14) NOT NULL default '',
 
   oi_metadata mediumblob NOT NULL,
@@ -1239,7 +1220,6 @@ CREATE TABLE /*_*/oldimage (
   oi_sha1 varbinary(32) NOT NULL default ''
 ) /*$wgDBTableOptions*/;
 
-CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
 CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
 CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
 -- oi_archive_name truncated to 14 to avoid key length overflow
@@ -1287,9 +1267,7 @@ CREATE TABLE /*_*/filearchive (
   fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
   fa_minor_mime varbinary(100) default "unknown",
   fa_description_id bigint unsigned NOT NULL,
-  fa_user int unsigned default 0, -- Deprecated in favor of fa_actor
-  fa_user_text varchar(255) binary DEFAULT '', -- Deprecated in favor of fa_actor
-  fa_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that fa_user/fa_user_text should be used)
+  fa_actor bigint unsigned NOT NULL,
   fa_timestamp binary(14) default '',
 
   -- Visibility of deleted revisions, bitfield
@@ -1306,7 +1284,6 @@ CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_sto
 -- sort by deletion time
 CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
 -- sort by uploader
-CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
 CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
 -- find file by sha1, 10 bytes will be enough for hashes to be indexed
 CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
@@ -1378,9 +1355,7 @@ CREATE TABLE /*_*/recentchanges (
   rc_timestamp varbinary(14) NOT NULL default '',
 
   -- As in revision
-  rc_user int unsigned NOT NULL default 0, -- Deprecated in favor of rc_actor
-  rc_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of rc_actor
-  rc_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that rc_user/rc_user_text should be used)
+  rc_actor bigint unsigned NOT NULL,
 
   -- When pages are renamed, their RC entries do _not_ change.
   rc_namespace int NOT NULL default 0,
@@ -1461,11 +1436,9 @@ CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,
 CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
 
 -- Probably intended for Special:NewPages namespace filter
-CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
 
 -- SiteStats active user count, Special:ActiveUsers, Special:NewPages user filter
-CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
 CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
 
 -- ApiQueryRecentChanges (T140108)
@@ -1595,14 +1568,8 @@ CREATE TABLE /*_*/logging (
   -- Timestamp. Duh.
   log_timestamp binary(14) NOT NULL default '19700101000000',
 
-  -- The user who performed this action; key to user_id
-  log_user int unsigned NOT NULL default 0, -- Deprecated in favor of log_actor
-
-  -- Name of the user who performed this action
-  log_user_text varchar(255) binary NOT NULL default '', -- Deprecated in favor of log_actor
-
   -- The actor who performed this action
-  log_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that log_user/log_user_text should be used)
+  log_actor bigint unsigned NOT NULL,
 
   -- Key to the page affected. Where a user is the target,
   -- this will point to the user page.
@@ -1625,7 +1592,6 @@ CREATE TABLE /*_*/logging (
 CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
 
 -- Special:Log performer filter
-CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
 CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
 
 -- Special:Log title filter, log extract
@@ -1635,7 +1601,6 @@ CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_times
 CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
 
 -- Special:Log filter by performer and type
-CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
 CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
 
 -- Apparently just used for a few maintenance pages (findMissingFiles.php, Flow).
@@ -1645,12 +1610,6 @@ CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
 -- Special:Log action filter
 CREATE INDEX /*i*/log_type_action ON /*_*/logging (log_type, log_action, log_timestamp);
 
--- Special:Log filter by type and anonymous performer
-CREATE INDEX /*i*/log_user_text_type_time ON /*_*/logging (log_user_text, log_type, log_timestamp);
-
--- Special:Log filter by anonymous performer
-CREATE INDEX /*i*/log_user_text_time ON /*_*/logging (log_user_text, log_timestamp);
-
 
 CREATE TABLE /*_*/log_search (
   -- The type of ID (rev ID, log ID, rev timestamp, username)
index b546cbf..6425238 100644 (file)
@@ -24,6 +24,8 @@
 // details of the session. Enforce this constraint with respect to session use.
 define( 'MW_NO_SESSION', 1 );
 
+define( 'MW_ENTRY_POINT', 'opensearch_desc' );
+
 require_once __DIR__ . '/includes/WebStart.php';
 
 if ( $wgRequest->getVal( 'ctype' ) == 'application/xml' ) {
index dccdd38..7b652b7 100644 (file)
@@ -40,6 +40,8 @@
 // details of the session. Enforce this constraint with respect to session use.
 define( 'MW_NO_SESSION', 1 );
 
+define( 'MW_ENTRY_POINT', 'profileinfo' );
+
 ini_set( 'zlib.output_compression', 'off' );
 
 require __DIR__ . '/includes/WebStart.php';
index cfcfc19..1388128 100644 (file)
@@ -212,7 +212,7 @@ return [
        'jquery.highlightText' => [
                'scripts' => 'resources/src/jquery/jquery.highlightText.js',
                'dependencies' => [
-                       'mediawiki.RegExp',
+                       'mediawiki.util',
                ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
@@ -288,7 +288,7 @@ return [
                'messages' => [ 'sort-descending', 'sort-ascending' ],
                'dependencies' => [
                        'jquery.tablesorter.styles',
-                       'mediawiki.RegExp',
+                       'mediawiki.util',
                        'mediawiki.language.months',
                ],
        ],
@@ -755,7 +755,7 @@ return [
                ],
                'dependencies' => [
                        'mediawiki.language',
-                       'mediawiki.RegExp',
+                       'mediawiki.util',
                ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
@@ -926,7 +926,7 @@ return [
                        'resources/src/mediawiki.htmlform/selectorother.js',
                ],
                'dependencies' => [
-                       'mediawiki.RegExp',
+                       'mediawiki.util',
                        'jquery.lengthLimit',
                ],
                'messages' => [
@@ -967,7 +967,7 @@ return [
                'scripts' => 'resources/src/mediawiki.inspect.js',
                'dependencies' => [
                        'mediawiki.String',
-                       'mediawiki.RegExp',
+                       'mediawiki.util',
                ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
@@ -1027,8 +1027,11 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.RegExp' => [
-               'scripts' => 'resources/src/mediawiki.RegExp.js',
+               'deprecated' => 'Please use mw.util.escapeRegExp() instead.',
                'targets' => [ 'desktop', 'mobile' ],
+               'dependencies' => [
+                       'mediawiki.util',
+               ],
        ],
        'mediawiki.String' => [
                'scripts' => 'resources/src/mediawiki.String.js',
@@ -1261,7 +1264,6 @@ return [
                ],
                'dependencies' => [
                        'jquery.client',
-                       'mediawiki.RegExp',
                ],
                'messages' => [ 'brackets', 'word-separator' ],
                'targets' => [ 'desktop', 'mobile' ],
@@ -1699,7 +1701,6 @@ return [
                        'mediawiki.util',
                        'mediawiki.Title',
                        'mediawiki.jqueryMsg',
-                       'mediawiki.RegExp',
                ],
                'messages' => [
                        'watch',
@@ -2638,7 +2639,7 @@ return [
                        'period-pm',
                ],
                'dependencies' => [
-                       'mediawiki.RegExp',
+                       'mediawiki.util',
                        'oojs-ui-core',
                        'oojs-ui.styles.icons-moderation',
                        'oojs-ui.styles.icons-movement',
index 7a131da..a6c8cd7 100644 (file)
                        // Construct regexes for number identification
                        for ( i = 0; i < ascii.length; i++ ) {
                                ts.transformTable[ localised[ i ] ] = ascii[ i ];
-                               digits.push( mw.RegExp.escape( localised[ i ] ) );
+                               digits.push( mw.util.escapeRegExp( localised[ i ] ) );
                        }
                }
                digitClass = '[' + digits.join( '', digits ) + ']';
                for ( i = 0; i < 12; i++ ) {
                        name = mw.language.months.names[ i ].toLowerCase();
                        ts.monthNames[ name ] = i + 1;
-                       regex.push( mw.RegExp.escape( name ) );
+                       regex.push( mw.util.escapeRegExp( name ) );
                        name = mw.language.months.genitive[ i ].toLowerCase();
                        ts.monthNames[ name ] = i + 1;
-                       regex.push( mw.RegExp.escape( name ) );
+                       regex.push( mw.util.escapeRegExp( name ) );
                        name = mw.language.months.abbrev[ i ].toLowerCase().replace( '.', '' );
                        ts.monthNames[ name ] = i + 1;
-                       regex.push( mw.RegExp.escape( name ) );
+                       regex.push( mw.util.escapeRegExp( name ) );
                }
 
                // Build piped string
                if ( ts.collationTable ) {
                        // Build array of key names
                        for ( key in ts.collationTable ) {
-                               keys.push( mw.RegExp.escape( key ) );
+                               keys.push( mw.util.escapeRegExp( key ) );
                        }
                        if ( keys.length ) {
                                ts.collationRegex = new RegExp( keys.join( '|' ), 'ig' );
index de08607..1fabb43 100644 (file)
@@ -17,7 +17,7 @@
                                }
                                $.highlightText.innerHighlight(
                                        node,
-                                       new RegExp( '(^|\\s)' + mw.RegExp.escape( words[ i ] ), 'i' )
+                                       new RegExp( '(^|\\s)' + mw.util.escapeRegExp( words[ i ] ), 'i' )
                                );
                        }
                        return node;
@@ -26,7 +26,7 @@
                prefixHighlight: function ( node, prefix ) {
                        $.highlightText.innerHighlight(
                                node,
-                               new RegExp( '(^)' + mw.RegExp.escape( prefix ), 'i' )
+                               new RegExp( '(^)' + mw.util.escapeRegExp( prefix ), 'i' )
                        );
                },
 
@@ -38,7 +38,7 @@
 
                        $.highlightText.innerHighlight(
                                node,
-                               new RegExp( '(^)' + mw.RegExp.escape( prefix ) + comboMarks + '*', 'i' )
+                               new RegExp( '(^)' + mw.util.escapeRegExp( prefix ) + comboMarks + '*', 'i' )
                        );
                },
 
diff --git a/resources/src/mediawiki.RegExp.js b/resources/src/mediawiki.RegExp.js
deleted file mode 100644 (file)
index 5323d4f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-( function () {
-       /**
-        * @class mw.RegExp
-        */
-       mw.RegExp = {
-               /**
-                * Escape string for safe inclusion in regular expression
-                *
-                * The following characters are escaped:
-                *
-                *     \ { } ( ) | . ? * + - ^ $ [ ]
-                *
-                * @since 1.26
-                * @static
-                * @param {string} str String to escape
-                * @return {string} Escaped string
-                */
-               escape: function ( str ) {
-                       return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ); // eslint-disable-line no-useless-escape
-               }
-       };
-}() );
index 3084e12..3f39fd1 100644 (file)
@@ -137,11 +137,11 @@ var
                '[^' + mw.config.get( 'wgLegalTitleChars' ) + ']' +
                // URL percent encoding sequences interfere with the ability
                // to round-trip titles -- you can't link to them consistently.
-               '|%[0-9A-Fa-f]{2}' +
+               '|%[\\dA-Fa-f]{2}' +
                // XML/HTML character references produce similar issues.
-               '|&[A-Za-z0-9\u0080-\uFFFF]+;' +
-               '|&#[0-9]+;' +
-               '|&#x[0-9A-Fa-f]+;'
+               '|&[\\dA-Za-z\u0080-\uFFFF]+;' +
+               '|&#\\d+;' +
+               '|&#x[\\dA-Fa-f]+;'
        ),
 
        // From MediaWikiTitleCodec::splitTitleString() in PHP
@@ -149,7 +149,7 @@ var
        rWhitespace = /[ _\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+/g,
 
        // From MediaWikiTitleCodec::splitTitleString() in PHP
-       rUnicodeBidi = /[\u200E\u200F\u202A-\u202E]/g,
+       rUnicodeBidi = /[\u200E\u200F\u202A-\u202E]+/g,
 
        /**
         * Slightly modified from Flinfo. Credit goes to Lupo and Flominator.
@@ -173,13 +173,13 @@ var
                },
                // URL encoding (possibly)
                {
-                       pattern: /%([0-9A-Fa-f]{2})/g,
+                       pattern: /%([\dA-Fa-f]{2})/g,
                        replace: '% $1',
                        generalRule: true
                },
                // HTML-character-entities
                {
-                       pattern: /&(([A-Za-z0-9\x80-\xff]+|#[0-9]+|#x[0-9A-Fa-f]+);)/g,
+                       pattern: /&(([\dA-Za-z\x80-\xff]+|#\d+|#x[\dA-Fa-f]+);)/g,
                        replace: '& $1',
                        generalRule: true
                },
@@ -228,7 +228,7 @@ var
         * @return {Object|boolean}
         */
        parse = function ( title, defaultNamespace ) {
-               var namespace, m, id, i, fragment, ext;
+               var namespace, m, id, i, fragment;
 
                namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
 
@@ -294,7 +294,7 @@ var
                }
 
                // Reject illegal characters
-               if ( title.match( rInvalid ) ) {
+               if ( rInvalid.test( title ) ) {
                        return false;
                }
 
@@ -336,21 +336,9 @@ var
                        return false;
                }
 
-               // For backwards-compatibility with old mw.Title, we separate the extension from the
-               // rest of the title.
-               i = title.lastIndexOf( '.' );
-               if ( i === -1 || title.length <= i + 1 ) {
-                       // Extensions are the non-empty segment after the last dot
-                       ext = null;
-               } else {
-                       ext = title.slice( i + 1 );
-                       title = title.slice( 0, i );
-               }
-
                return {
                        namespace: namespace,
                        title: title,
-                       ext: ext,
                        fragment: fragment
                };
        },
@@ -438,7 +426,6 @@ function Title( title, namespace ) {
 
        this.namespace = parsed.namespace;
        this.title = parsed.title;
-       this.ext = parsed.ext;
        this.fragment = parsed.fragment;
 }
 
@@ -465,7 +452,6 @@ Title.newFromText = function ( title, namespace ) {
        t = Object.create( Title.prototype );
        t.namespace = parsed.namespace;
        t.title = parsed.title;
-       t.ext = parsed.ext;
        t.fragment = parsed.fragment;
 
        return t;
@@ -600,7 +586,6 @@ Title.newFromUserInput = function ( title, defaultNamespaceOrOptions, options )
  * @return {mw.Title|null} A valid Title object or null if the title is invalid
  */
 Title.newFromFileName = function ( uncleanName ) {
-
        return Title.newFromUserInput( 'File:' + uncleanName, {
                forUploading: true
        } );
@@ -622,10 +607,10 @@ Title.newFromImg = function ( img ) {
                thumbPhpRegex = /thumb\.php/,
                regexes = [
                        // Thumbnails
-                       /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)\/[^\s/]+-[^\s/]*$/,
+                       /\/[\da-f]\/[\da-f]{2}\/([^\s/]+)\/[^\s/]+-[^\s/]*$/,
 
                        // Full size images
-                       /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)$/,
+                       /\/[\da-f]\/[\da-f]{2}\/([^\s/]+)$/,
 
                        // Thumbnails in non-hashed upload directories
                        /\/([^\s/]+)\/[^\s/]+-(?:\1|thumbnail)[^\s/]*$/,
@@ -638,9 +623,7 @@ Title.newFromImg = function ( img ) {
 
        src = img.jquery ? img[ 0 ].src : img.src;
 
-       matches = src.match( thumbPhpRegex );
-
-       if ( matches ) {
+       if ( thumbPhpRegex.test( src ) ) {
                return mw.Title.newFromText( 'File:' + mw.util.getParamValue( 'f', src ) );
        }
 
@@ -759,16 +742,16 @@ Title.exist = {
 Title.normalizeExtension = function ( extension ) {
        var
                lower = extension.toLowerCase(),
-               squish = {
+               normalizations = {
                        htm: 'html',
                        jpeg: 'jpg',
                        mpeg: 'mpg',
                        tiff: 'tif',
                        ogv: 'ogg'
                };
-       if ( Object.prototype.hasOwnProperty.call( squish, lower ) ) {
-               return squish[ lower ];
-       } else if ( /^[0-9a-z]+$/.test( lower ) ) {
+       if ( Object.hasOwnProperty.call( normalizations, lower ) ) {
+               return normalizations[ lower ];
+       } else if ( /^[\da-z]+$/.test( lower ) ) {
                return lower;
        } else {
                return '';
@@ -824,13 +807,11 @@ Title.prototype = {
         * @return {string}
         */
        getName: function () {
-               if (
-                       mw.config.get( 'wgCaseSensitiveNamespaces' ).indexOf( this.namespace ) !== -1 ||
-                       !this.title.length
-               ) {
-                       return this.title;
+               var ext = this.getExtension();
+               if ( ext === null ) {
+                       return this.getMain();
                }
-               return mw.Title.phpCharToUpper( this.title[ 0 ] ) + this.title.slice( 1 );
+               return this.getMain().slice( 0, -ext.length - 1 );
        },
 
        /**
@@ -852,7 +833,11 @@ Title.prototype = {
         * @return {string|null} Name extension or null if there is none
         */
        getExtension: function () {
-               return this.ext;
+               var lastDot = this.title.lastIndexOf( '.' );
+               if ( lastDot === -1 ) {
+                       return null;
+               }
+               return this.title.slice( lastDot + 1 ) || null;
        },
 
        /**
@@ -863,7 +848,8 @@ Title.prototype = {
         * @return {string}
         */
        getDotExtension: function () {
-               return this.ext === null ? '' : '.' + this.ext;
+               var ext = this.getExtension();
+               return ext === null ? '' : '.' + ext;
        },
 
        /**
@@ -874,7 +860,13 @@ Title.prototype = {
         * @return {string}
         */
        getMain: function () {
-               return this.getName() + this.getDotExtension();
+               if (
+                       mw.config.get( 'wgCaseSensitiveNamespaces' ).indexOf( this.namespace ) !== -1 ||
+                       !this.title.length
+               ) {
+                       return this.title;
+               }
+               return mw.Title.phpCharToUpper( this.title[ 0 ] ) + this.title.slice( 1 );
        },
 
        /**
index 99eebae..9cafcd4 100644 (file)
@@ -16,7 +16,7 @@
                var $li,
                        $ul = $createButton.prev( 'ul.mw-htmlform-cloner-ul' ),
                        html = $ul.data( 'template' ).replace(
-                               new RegExp( mw.RegExp.escape( $ul.data( 'uniqueId' ) ), 'g' ),
+                               new RegExp( mw.util.escapeRegExp( $ul.data( 'uniqueId' ) ), 'g' ),
                                'clone' + ( ++cloneCounter )
                        );
 
index 7f4af5b..c7b99b2 100644 (file)
         */
        inspect.grep = function ( pattern ) {
                if ( typeof pattern.test !== 'function' ) {
-                       pattern = new RegExp( mw.RegExp.escape( pattern ), 'g' );
+                       pattern = new RegExp( mw.util.escapeRegExp( pattern ), 'g' );
                }
 
                return inspect.getLoadedModules().filter( function ( moduleName ) {
index f550a91..6b04b3c 100644 (file)
@@ -91,7 +91,7 @@
                actionPaths = mw.config.get( 'wgActionPaths' );
                for ( key in actionPaths ) {
                        parts = actionPaths[ key ].split( '$1' );
-                       parts = parts.map( mw.RegExp.escape );
+                       parts = parts.map( mw.util.escapeRegExp );
                        m = new RegExp( parts.join( '(.+)' ) ).exec( url );
                        if ( m && m[ 1 ] ) {
                                return key;
diff --git a/resources/src/mediawiki.util/.eslintrc.json b/resources/src/mediawiki.util/.eslintrc.json
new file mode 100644 (file)
index 0000000..ad8dbb3
--- /dev/null
@@ -0,0 +1,5 @@
+{
+       "parserOptions": {
+               "sourceType": "module"
+       }
+}
index cdc5808..07a06bf 100644 (file)
  *
  * @class jQuery.plugin.accessKeyLabel
  */
-( function () {
-
-       // Cached access key modifiers for used browser
-       var cachedAccessKeyModifiers,
-
-               // Whether to use 'test-' instead of correct prefix (used for testing)
-               useTestPrefix = false,
-
-               // tag names which can have a label tag
-               // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content
-               labelable = 'button, input, textarea, keygen, meter, output, progress, select';
-
-       /**
-        * Find the modifier keys that need to be pressed together with the accesskey to trigger the input.
-        *
-        * The result is dependant on the ua paramater or the current platform.
-        * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here.
-        * Valid key values that are returned can be: ctrl, alt, option, shift, esc
-        *
-        * @private
-        * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
-        * @return {Array} Array with 1 or more of the string values, in this order: ctrl, option, alt, shift, esc
-        */
-       function getAccessKeyModifiers( ua ) {
-               var profile, accessKeyModifiers;
-
-               // use cached prefix if possible
-               if ( !ua && cachedAccessKeyModifiers ) {
-                       return cachedAccessKeyModifiers;
-               }
 
-               profile = $.client.profile( ua );
+// Cached access key modifiers for used browser
+var cachedAccessKeyModifiers,
 
-               switch ( profile.name ) {
-                       case 'chrome':
-                       case 'opera':
-                               if ( profile.name === 'opera' && profile.versionNumber < 15 ) {
-                                       accessKeyModifiers = [ 'shift', 'esc' ];
-                               } else if ( profile.platform === 'mac' ) {
-                                       accessKeyModifiers = [ 'ctrl', 'option' ];
-                               } else {
-                                       // Chrome/Opera on Windows or Linux
-                                       // (both alt- and alt-shift work, but alt with E, D, F etc does not
-                                       // work since they are browser shortcuts)
-                                       accessKeyModifiers = [ 'alt', 'shift' ];
-                               }
-                               break;
-                       case 'firefox':
-                       case 'iceweasel':
-                               if ( profile.versionBase < 2 ) {
-                                       // Before v2, Firefox used alt, though it was rebindable in about:config
-                                       accessKeyModifiers = [ 'alt' ];
-                               } else {
-                                       if ( profile.platform === 'mac' ) {
-                                               if ( profile.versionNumber < 14 ) {
-                                                       accessKeyModifiers = [ 'ctrl' ];
-                                               } else {
-                                                       accessKeyModifiers = [ 'ctrl', 'option' ];
-                                               }
+       // Whether to use 'test-' instead of correct prefix (used for testing)
+       useTestPrefix = false,
+
+       // tag names which can have a label tag
+       // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content
+       labelable = 'button, input, textarea, keygen, meter, output, progress, select';
+
+/**
+ * Find the modifier keys that need to be pressed together with the accesskey to trigger the input.
+ *
+ * The result is dependant on the ua paramater or the current platform.
+ * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here.
+ * Valid key values that are returned can be: ctrl, alt, option, shift, esc
+ *
+ * @private
+ * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
+ * @return {Array} Array with 1 or more of the string values, in this order: ctrl, option, alt, shift, esc
+ */
+function getAccessKeyModifiers( ua ) {
+       var profile, accessKeyModifiers;
+
+       // use cached prefix if possible
+       if ( !ua && cachedAccessKeyModifiers ) {
+               return cachedAccessKeyModifiers;
+       }
+
+       profile = $.client.profile( ua );
+
+       switch ( profile.name ) {
+               case 'chrome':
+               case 'opera':
+                       if ( profile.name === 'opera' && profile.versionNumber < 15 ) {
+                               accessKeyModifiers = [ 'shift', 'esc' ];
+                       } else if ( profile.platform === 'mac' ) {
+                               accessKeyModifiers = [ 'ctrl', 'option' ];
+                       } else {
+                               // Chrome/Opera on Windows or Linux
+                               // (both alt- and alt-shift work, but alt with E, D, F etc does not
+                               // work since they are browser shortcuts)
+                               accessKeyModifiers = [ 'alt', 'shift' ];
+                       }
+                       break;
+               case 'firefox':
+               case 'iceweasel':
+                       if ( profile.versionBase < 2 ) {
+                               // Before v2, Firefox used alt, though it was rebindable in about:config
+                               accessKeyModifiers = [ 'alt' ];
+                       } else {
+                               if ( profile.platform === 'mac' ) {
+                                       if ( profile.versionNumber < 14 ) {
+                                               accessKeyModifiers = [ 'ctrl' ];
                                        } else {
-                                               accessKeyModifiers = [ 'alt', 'shift' ];
+                                               accessKeyModifiers = [ 'ctrl', 'option' ];
                                        }
-                               }
-                               break;
-                       case 'safari':
-                       case 'konqueror':
-                               if ( profile.platform === 'win' ) {
-                                       accessKeyModifiers = [ 'alt' ];
                                } else {
-                                       if ( profile.layoutVersion > 526 ) {
-                                               // Non-Windows Safari with webkit_version > 526
-                                               accessKeyModifiers = [ 'ctrl', profile.platform === 'mac' ? 'option' : 'alt' ];
-                                       } else {
-                                               accessKeyModifiers = [ 'ctrl' ];
-                                       }
+                                       accessKeyModifiers = [ 'alt', 'shift' ];
                                }
-                               break;
-                       case 'msie':
-                       case 'edge':
+                       }
+                       break;
+               case 'safari':
+               case 'konqueror':
+                       if ( profile.platform === 'win' ) {
                                accessKeyModifiers = [ 'alt' ];
-                               break;
-                       default:
-                               accessKeyModifiers = profile.platform === 'mac' ? [ 'ctrl' ] : [ 'alt' ];
-                               break;
-               }
+                       } else {
+                               if ( profile.layoutVersion > 526 ) {
+                                       // Non-Windows Safari with webkit_version > 526
+                                       accessKeyModifiers = [ 'ctrl', profile.platform === 'mac' ? 'option' : 'alt' ];
+                               } else {
+                                       accessKeyModifiers = [ 'ctrl' ];
+                               }
+                       }
+                       break;
+               case 'msie':
+               case 'edge':
+                       accessKeyModifiers = [ 'alt' ];
+                       break;
+               default:
+                       accessKeyModifiers = profile.platform === 'mac' ? [ 'ctrl' ] : [ 'alt' ];
+                       break;
+       }
 
-               // cache modifiers
-               if ( !ua ) {
-                       cachedAccessKeyModifiers = accessKeyModifiers;
-               }
-               return accessKeyModifiers;
+       // cache modifiers
+       if ( !ua ) {
+               cachedAccessKeyModifiers = accessKeyModifiers;
        }
+       return accessKeyModifiers;
+}
 
-       /**
-        * Get the access key label for an element.
-        *
-        * Will use native accessKeyLabel if available (currently only in Firefox 8+),
-        * falls back to #getAccessKeyModifiers.
-        *
-        * @private
-        * @param {HTMLElement} element Element to get the label for
-        * @return {string} Access key label
-        */
-       function getAccessKeyLabel( element ) {
-               // abort early if no access key
-               if ( !element.accessKey ) {
-                       return '';
-               }
-               // use accessKeyLabel if possible
-               // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel
-               if ( !useTestPrefix && element.accessKeyLabel ) {
-                       return element.accessKeyLabel;
-               }
-               return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey;
+/**
+ * Get the access key label for an element.
+ *
+ * Will use native accessKeyLabel if available (currently only in Firefox 8+),
+ * falls back to #getAccessKeyModifiers.
+ *
+ * @private
+ * @param {HTMLElement} element Element to get the label for
+ * @return {string} Access key label
+ */
+function getAccessKeyLabel( element ) {
+       // abort early if no access key
+       if ( !element.accessKey ) {
+               return '';
+       }
+       // use accessKeyLabel if possible
+       // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel
+       if ( !useTestPrefix && element.accessKeyLabel ) {
+               return element.accessKeyLabel;
        }
+       return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey;
+}
 
-       /**
       * Update the title for an element (on the element with the access key or it's label) to show
       * the correct access key label.
       *
       * @private
       * @param {HTMLElement} element Element with the accesskey
       * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`)
       */
-       function updateTooltipOnElement( element, titleElement ) {
-               var oldTitle, parts, regexp, newTitle, accessKeyLabel,
-                       separatorMsg = mw.message( 'word-separator' ).plain();
-
-               oldTitle = titleElement.title;
-               if ( !oldTitle ) {
-                       // don't add a title if the element didn't have one before
-                       return;
-               }
+/**
+ * Update the title for an element (on the element with the access key or it's label) to show
+ * the correct access key label.
+ *
+ * @private
+ * @param {HTMLElement} element Element with the accesskey
+ * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`)
+ */
+function updateTooltipOnElement( element, titleElement ) {
+       var oldTitle, parts, regexp, newTitle, accessKeyLabel,
+               separatorMsg = mw.message( 'word-separator' ).plain();
+
+       oldTitle = titleElement.title;
+       if ( !oldTitle ) {
+               // don't add a title if the element didn't have one before
+               return;
+       }
 
-               parts = ( separatorMsg + mw.message( 'brackets' ).plain() ).split( '$1' );
-               regexp = new RegExp( parts.map( mw.RegExp.escape ).join( '.*?' ) + '$' );
-               newTitle = oldTitle.replace( regexp, '' );
-               accessKeyLabel = getAccessKeyLabel( element );
+       parts = ( separatorMsg + mw.message( 'brackets' ).plain() ).split( '$1' );
+       regexp = new RegExp( parts.map( mw.util.escapeRegExp ).join( '.*?' ) + '$' );
+       newTitle = oldTitle.replace( regexp, '' );
+       accessKeyLabel = getAccessKeyLabel( element );
 
-               if ( accessKeyLabel ) {
-                       // Should be build the same as in Linker::titleAttrib
-                       newTitle += separatorMsg + mw.message( 'brackets', accessKeyLabel ).plain();
-               }
-               if ( oldTitle !== newTitle ) {
-                       titleElement.title = newTitle;
-               }
+       if ( accessKeyLabel ) {
+               // Should be build the same as in Linker::titleAttrib
+               newTitle += separatorMsg + mw.message( 'brackets', accessKeyLabel ).plain();
        }
+       if ( oldTitle !== newTitle ) {
+               titleElement.title = newTitle;
+       }
+}
 
-       /**
-        * Update the title for an element to show the correct access key label.
-        *
-        * @private
-        * @param {HTMLElement} element Element with the accesskey
-        */
-       function updateTooltip( element ) {
-               var id, $element, $label, $labelParent;
-               updateTooltipOnElement( element, element );
-
-               // update associated label if there is one
-               $element = $( element );
-               if ( $element.is( labelable ) ) {
-                       // Search it using 'for' attribute
-                       id = element.id.replace( /"/g, '\\"' );
-                       if ( id ) {
-                               $label = $( 'label[for="' + id + '"]' );
-                               if ( $label.length === 1 ) {
-                                       updateTooltipOnElement( element, $label[ 0 ] );
-                               }
+/**
+ * Update the title for an element to show the correct access key label.
+ *
+ * @private
+ * @param {HTMLElement} element Element with the accesskey
+ */
+function updateTooltip( element ) {
+       var id, $element, $label, $labelParent;
+       updateTooltipOnElement( element, element );
+
+       // update associated label if there is one
+       $element = $( element );
+       if ( $element.is( labelable ) ) {
+               // Search it using 'for' attribute
+               id = element.id.replace( /"/g, '\\"' );
+               if ( id ) {
+                       $label = $( 'label[for="' + id + '"]' );
+                       if ( $label.length === 1 ) {
+                               updateTooltipOnElement( element, $label[ 0 ] );
                        }
+               }
 
-                       // Search it as parent, because the form control can also be inside the label element itself
-                       $labelParent = $element.parents( 'label' );
-                       if ( $labelParent.length === 1 ) {
-                               updateTooltipOnElement( element, $labelParent[ 0 ] );
-                       }
+               // Search it as parent, because the form control can also be inside the label element itself
+               $labelParent = $element.parents( 'label' );
+               if ( $labelParent.length === 1 ) {
+                       updateTooltipOnElement( element, $labelParent[ 0 ] );
                }
        }
+}
+
+/**
+ * Update the titles for all elements in a jQuery selection.
+ *
+ * @return {jQuery}
+ * @chainable
+ */
+$.fn.updateTooltipAccessKeys = function () {
+       return this.each( function () {
+               updateTooltip( this );
+       } );
+};
 
-       /**
-        * Update the titles for all elements in a jQuery selection.
-        *
-        * @return {jQuery}
-        * @chainable
-        */
-       $.fn.updateTooltipAccessKeys = function () {
-               return this.each( function () {
-                       updateTooltip( this );
-               } );
-       };
-
-       /**
-        * getAccessKeyModifiers
-        *
-        * @method updateTooltipAccessKeys_getAccessKeyModifiers
-        * @inheritdoc #getAccessKeyModifiers
-        */
-       $.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers;
-
-       /**
-        * getAccessKeyLabel
-        *
-        * @method updateTooltipAccessKeys_getAccessKeyLabel
-        * @inheritdoc #getAccessKeyLabel
-        */
-       $.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel;
-
-       /**
-        * getAccessKeyPrefix
-        *
-        * @method updateTooltipAccessKeys_getAccessKeyPrefix
-        * @deprecated since 1.27 Use #getAccessKeyModifiers
-        * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
-        * @return {string}
-        */
-       $.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) {
-               return getAccessKeyModifiers( ua ).join( '-' ) + '-';
-       };
-
-       /**
-        * Switch test mode on and off.
-        *
-        * @method updateTooltipAccessKeys_setTestMode
-        * @param {boolean} mode New mode
-        */
-       $.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) {
-               useTestPrefix = mode;
-       };
-
-       /**
-        * @class jQuery
-        * @mixins jQuery.plugin.accessKeyLabel
-        */
-
-}() );
+/**
+ * getAccessKeyModifiers
+ *
+ * @method updateTooltipAccessKeys_getAccessKeyModifiers
+ * @inheritdoc #getAccessKeyModifiers
+ */
+$.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers;
+
+/**
+ * getAccessKeyLabel
+ *
+ * @method updateTooltipAccessKeys_getAccessKeyLabel
+ * @inheritdoc #getAccessKeyLabel
+ */
+$.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel;
+
+/**
+ * getAccessKeyPrefix
+ *
+ * @method updateTooltipAccessKeys_getAccessKeyPrefix
+ * @deprecated since 1.27 Use #getAccessKeyModifiers
+ * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
+ * @return {string}
+ */
+$.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) {
+       return getAccessKeyModifiers( ua ).join( '-' ) + '-';
+};
+
+/**
+ * Switch test mode on and off.
+ *
+ * @method updateTooltipAccessKeys_setTestMode
+ * @param {boolean} mode New mode
+ */
+$.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) {
+       useTestPrefix = mode;
+};
+
+/**
+ * @class jQuery
+ * @mixins jQuery.plugin.accessKeyLabel
+ */
index 7e0722f..e8823e1 100644 (file)
-( function () {
-       'use strict';
-
-       var util,
-               config = require( './config.json' );
+'use strict';
+
+var util,
+       config = require( './config.json' );
+
+require( './jquery.accessKeyLabel.js' );
+
+/**
+ * Encode the string like PHP's rawurlencode
+ * @ignore
+ *
+ * @param {string} str String to be encoded.
+ * @return {string} Encoded string
+ */
+function rawurlencode( str ) {
+       str = String( str );
+       return encodeURIComponent( str )
+               .replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' )
+               .replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' );
+}
+
+/**
+ * Private helper function used by util.escapeId*()
+ * @ignore
+ *
+ * @param {string} str String to be encoded
+ * @param {string} mode Encoding mode, see documentation for $wgFragmentMode
+ *     in DefaultSettings.php
+ * @return {string} Encoded string
+ */
+function escapeIdInternal( str, mode ) {
+       str = String( str );
+
+       switch ( mode ) {
+               case 'html5':
+                       return str.replace( / /g, '_' );
+               case 'legacy':
+                       return rawurlencode( str.replace( / /g, '_' ) )
+                               .replace( /%3A/g, ':' )
+                               .replace( /%/g, '.' );
+               default:
+                       throw new Error( 'Unrecognized ID escaping mode ' + mode );
+       }
+}
 
-       require( './jquery.accessKeyLabel.js' );
+/**
+ * Utility library
+ * @class mw.util
+ * @singleton
+ */
+util = {
 
        /**
         * Encode the string like PHP's rawurlencode
-        * @ignore
         *
         * @param {string} str String to be encoded.
         * @return {string} Encoded string
         */
-       function rawurlencode( str ) {
-               str = String( str );
-               return encodeURIComponent( str )
-                       .replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' )
-                       .replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' );
-       }
+       rawurlencode: rawurlencode,
+
+       /**
+        * Encode string into HTML id compatible form suitable for use in HTML
+        * Analog to PHP Sanitizer::escapeIdForAttribute()
+        *
+        * @since 1.30
+        *
+        * @param {string} str String to encode
+        * @return {string} Encoded string
+        */
+       escapeIdForAttribute: function ( str ) {
+               var mode = config.FragmentMode[ 0 ];
+
+               return escapeIdInternal( str, mode );
+       },
+
+       /**
+        * Encode string into HTML id compatible form suitable for use in links
+        * Analog to PHP Sanitizer::escapeIdForLink()
+        *
+        * @since 1.30
+        *
+        * @param {string} str String to encode
+        * @return {string} Encoded string
+        */
+       escapeIdForLink: function ( str ) {
+               var mode = config.FragmentMode[ 0 ];
+
+               return escapeIdInternal( str, mode );
+       },
 
        /**
-        * Private helper function used by util.escapeId*()
-        * @ignore
+        * Encode page titles for use in a URL
+        *
+        * We want / and : to be included as literal characters in our title URLs
+        * as they otherwise fatally break the title.
         *
-        * @param {string} str String to be encoded
-        * @param {string} mode Encoding mode, see documentation for $wgFragmentMode
-        *     in DefaultSettings.php
+        * The others are decoded because we can, it's prettier and matches behaviour
+        * of `wfUrlencode` in PHP.
+        *
+        * @param {string} str String to be encoded.
         * @return {string} Encoded string
         */
-       function escapeIdInternal( str, mode ) {
-               str = String( str );
-
-               switch ( mode ) {
-                       case 'html5':
-                               return str.replace( / /g, '_' );
-                       case 'legacy':
-                               return rawurlencode( str.replace( / /g, '_' ) )
-                                       .replace( /%3A/g, ':' )
-                                       .replace( /%/g, '.' );
-                       default:
-                               throw new Error( 'Unrecognized ID escaping mode ' + mode );
+       wikiUrlencode: function ( str ) {
+               return util.rawurlencode( str )
+                       .replace( /%20/g, '_' )
+                       // wfUrlencode replacements
+                       .replace( /%3B/g, ';' )
+                       .replace( /%40/g, '@' )
+                       .replace( /%24/g, '$' )
+                       .replace( /%21/g, '!' )
+                       .replace( /%2A/g, '*' )
+                       .replace( /%28/g, '(' )
+                       .replace( /%29/g, ')' )
+                       .replace( /%2C/g, ',' )
+                       .replace( /%2F/g, '/' )
+                       .replace( /%7E/g, '~' )
+                       .replace( /%3A/g, ':' );
+       },
+
+       /**
+        * Get the link to a page name (relative to `wgServer`),
+        *
+        * @param {string|null} [pageName=wgPageName] Page name
+        * @param {Object} [params] A mapping of query parameter names to values,
+        *  e.g. `{ action: 'edit' }`
+        * @return {string} Url of the page with name of `pageName`
+        */
+       getUrl: function ( pageName, params ) {
+               var titleFragmentStart, url, query,
+                       fragment = '',
+                       title = typeof pageName === 'string' ? pageName : mw.config.get( 'wgPageName' );
+
+               // Find any fragment
+               titleFragmentStart = title.indexOf( '#' );
+               if ( titleFragmentStart !== -1 ) {
+                       fragment = title.slice( titleFragmentStart + 1 );
+                       // Exclude the fragment from the page name
+                       title = title.slice( 0, titleFragmentStart );
                }
-       }
+
+               // Produce query string
+               if ( params ) {
+                       query = $.param( params );
+               }
+               if ( query ) {
+                       url = title ?
+                               util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query :
+                               util.wikiScript() + '?' + query;
+               } else {
+                       url = mw.config.get( 'wgArticlePath' )
+                               .replace( '$1', util.wikiUrlencode( title ).replace( /\$/g, '$$$$' ) );
+               }
+
+               // Append the encoded fragment
+               if ( fragment.length ) {
+                       url += '#' + util.escapeIdForLink( fragment );
+               }
+
+               return url;
+       },
+
+       /**
+        * Get address to a script in the wiki root.
+        * For index.php use `mw.config.get( 'wgScript' )`.
+        *
+        * @since 1.18
+        * @param {string} str Name of script (e.g. 'api'), defaults to 'index'
+        * @return {string} Address to script (e.g. '/w/api.php' )
+        */
+       wikiScript: function ( str ) {
+               str = str || 'index';
+               if ( str === 'index' ) {
+                       return mw.config.get( 'wgScript' );
+               } else if ( str === 'load' ) {
+                       return config.LoadScript;
+               } else {
+                       return mw.config.get( 'wgScriptPath' ) + '/' + str + '.php';
+               }
+       },
 
        /**
-        * Utility library
-        * @class mw.util
-        * @singleton
+        * Append a new style block to the head and return the CSSStyleSheet object.
+        * Use .ownerNode to access the `<style>` element, or use mw.loader#addStyleTag.
+        * This function returns the styleSheet object for convience (due to cross-browsers
+        * difference as to where it is located).
+        *
+        *     var sheet = util.addCSS( '.foobar { display: none; }' );
+        *     $( foo ).click( function () {
+        *         // Toggle the sheet on and off
+        *         sheet.disabled = !sheet.disabled;
+        *     } );
+        *
+        * @param {string} text CSS to be appended
+        * @return {CSSStyleSheet} Use .ownerNode to get to the `<style>` element.
         */
-       util = {
-
-               /**
-                * Encode the string like PHP's rawurlencode
-                *
-                * @param {string} str String to be encoded.
-                * @return {string} Encoded string
-                */
-               rawurlencode: rawurlencode,
-
-               /**
-                * Encode string into HTML id compatible form suitable for use in HTML
-                * Analog to PHP Sanitizer::escapeIdForAttribute()
-                *
-                * @since 1.30
-                *
-                * @param {string} str String to encode
-                * @return {string} Encoded string
-                */
-               escapeIdForAttribute: function ( str ) {
-                       var mode = config.FragmentMode[ 0 ];
-
-                       return escapeIdInternal( str, mode );
-               },
-
-               /**
-                * Encode string into HTML id compatible form suitable for use in links
-                * Analog to PHP Sanitizer::escapeIdForLink()
-                *
-                * @since 1.30
-                *
-                * @param {string} str String to encode
-                * @return {string} Encoded string
-                */
-               escapeIdForLink: function ( str ) {
-                       var mode = config.FragmentMode[ 0 ];
-
-                       return escapeIdInternal( str, mode );
-               },
-
-               /**
-                * Encode page titles for use in a URL
-                *
-                * We want / and : to be included as literal characters in our title URLs
-                * as they otherwise fatally break the title.
-                *
-                * The others are decoded because we can, it's prettier and matches behaviour
-                * of `wfUrlencode` in PHP.
-                *
-                * @param {string} str String to be encoded.
-                * @return {string} Encoded string
-                */
-               wikiUrlencode: function ( str ) {
-                       return util.rawurlencode( str )
-                               .replace( /%20/g, '_' )
-                               // wfUrlencode replacements
-                               .replace( /%3B/g, ';' )
-                               .replace( /%40/g, '@' )
-                               .replace( /%24/g, '$' )
-                               .replace( /%21/g, '!' )
-                               .replace( /%2A/g, '*' )
-                               .replace( /%28/g, '(' )
-                               .replace( /%29/g, ')' )
-                               .replace( /%2C/g, ',' )
-                               .replace( /%2F/g, '/' )
-                               .replace( /%7E/g, '~' )
-                               .replace( /%3A/g, ':' );
-               },
-
-               /**
-                * Get the link to a page name (relative to `wgServer`),
-                *
-                * @param {string|null} [pageName=wgPageName] Page name
-                * @param {Object} [params] A mapping of query parameter names to values,
-                *  e.g. `{ action: 'edit' }`
-                * @return {string} Url of the page with name of `pageName`
-                */
-               getUrl: function ( pageName, params ) {
-                       var titleFragmentStart, url, query,
-                               fragment = '',
-                               title = typeof pageName === 'string' ? pageName : mw.config.get( 'wgPageName' );
-
-                       // Find any fragment
-                       titleFragmentStart = title.indexOf( '#' );
-                       if ( titleFragmentStart !== -1 ) {
-                               fragment = title.slice( titleFragmentStart + 1 );
-                               // Exclude the fragment from the page name
-                               title = title.slice( 0, titleFragmentStart );
-                       }
+       addCSS: function ( text ) {
+               var s = mw.loader.addStyleTag( text );
+               return s.sheet || s.styleSheet || s;
+       },
 
-                       // Produce query string
-                       if ( params ) {
-                               query = $.param( params );
-                       }
-                       if ( query ) {
-                               url = title ?
-                                       util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query :
-                                       util.wikiScript() + '?' + query;
-                       } else {
-                               url = mw.config.get( 'wgArticlePath' )
-                                       .replace( '$1', util.wikiUrlencode( title ).replace( /\$/g, '$$$$' ) );
-                       }
+       /**
+        * Grab the URL parameter value for the given parameter.
+        * Returns null if not found.
+        *
+        * @param {string} param The parameter name.
+        * @param {string} [url=location.href] URL to search through, defaulting to the current browsing location.
+        * @return {Mixed} Parameter value or null.
+        */
+       getParamValue: function ( param, url ) {
+               // Get last match, stop at hash
+               var re = new RegExp( '^[^#]*[&?]' + util.escapeRegExp( param ) + '=([^&#]*)' ),
+                       m = re.exec( url !== undefined ? url : location.href );
+
+               if ( m ) {
+                       // Beware that decodeURIComponent is not required to understand '+'
+                       // by spec, as encodeURIComponent does not produce it.
+                       return decodeURIComponent( m[ 1 ].replace( /\+/g, '%20' ) );
+               }
+               return null;
+       },
 
-                       // Append the encoded fragment
-                       if ( fragment.length ) {
-                               url += '#' + util.escapeIdForLink( fragment );
-                       }
+       /**
+        * The content wrapper of the skin (e.g. `.mw-body`).
+        *
+        * Populated on document ready. To use this property,
+        * wait for `$.ready` and be sure to have a module dependency on
+        * `mediawiki.util` which will ensure
+        * your document ready handler fires after initialization.
+        *
+        * Because of the lazy-initialised nature of this property,
+        * you're discouraged from using it.
+        *
+        * If you need just the wikipage content (not any of the
+        * extra elements output by the skin), use `$( '#mw-content-text' )`
+        * instead. Or listen to mw.hook#wikipage_content which will
+        * allow your code to re-run when the page changes (e.g. live preview
+        * or re-render after ajax save).
+        *
+        * @property {jQuery}
+        */
+       $content: null,
 
-                       return url;
-               },
-
-               /**
-                * Get address to a script in the wiki root.
-                * For index.php use `mw.config.get( 'wgScript' )`.
-                *
-                * @since 1.18
-                * @param {string} str Name of script (e.g. 'api'), defaults to 'index'
-                * @return {string} Address to script (e.g. '/w/api.php' )
-                */
-               wikiScript: function ( str ) {
-                       str = str || 'index';
-                       if ( str === 'index' ) {
-                               return mw.config.get( 'wgScript' );
-                       } else if ( str === 'load' ) {
-                               return config.LoadScript;
-                       } else {
-                               return mw.config.get( 'wgScriptPath' ) + '/' + str + '.php';
-                       }
-               },
-
-               /**
-                * Append a new style block to the head and return the CSSStyleSheet object.
-                * Use .ownerNode to access the `<style>` element, or use mw.loader#addStyleTag.
-                * This function returns the styleSheet object for convience (due to cross-browsers
-                * difference as to where it is located).
-                *
-                *     var sheet = util.addCSS( '.foobar { display: none; }' );
-                *     $( foo ).click( function () {
-                *         // Toggle the sheet on and off
-                *         sheet.disabled = !sheet.disabled;
-                *     } );
-                *
-                * @param {string} text CSS to be appended
-                * @return {CSSStyleSheet} Use .ownerNode to get to the `<style>` element.
-                */
-               addCSS: function ( text ) {
-                       var s = mw.loader.addStyleTag( text );
-                       return s.sheet || s.styleSheet || s;
-               },
-
-               /**
-                * Grab the URL parameter value for the given parameter.
-                * Returns null if not found.
-                *
-                * @param {string} param The parameter name.
-                * @param {string} [url=location.href] URL to search through, defaulting to the current browsing location.
-                * @return {Mixed} Parameter value or null.
-                */
-               getParamValue: function ( param, url ) {
-                       // Get last match, stop at hash
-                       var re = new RegExp( '^[^#]*[&?]' + mw.RegExp.escape( param ) + '=([^&#]*)' ),
-                               m = re.exec( url !== undefined ? url : location.href );
-
-                       if ( m ) {
-                               // Beware that decodeURIComponent is not required to understand '+'
-                               // by spec, as encodeURIComponent does not produce it.
-                               return decodeURIComponent( m[ 1 ].replace( /\+/g, '%20' ) );
-                       }
+       /**
+        * Add a link to a portlet menu on the page, such as:
+        *
+        * p-cactions (Content actions), p-personal (Personal tools),
+        * p-navigation (Navigation), p-tb (Toolbox)
+        *
+        * The first three parameters are required, the others are optional and
+        * may be null. Though providing an id and tooltip is recommended.
+        *
+        * By default the new link will be added to the end of the list. To
+        * add the link before a given existing item, pass the DOM node
+        * (e.g. `document.getElementById( 'foobar' )`) or a jQuery-selector
+        * (e.g. `'#foobar'`) for that item.
+        *
+        *     util.addPortletLink(
+        *         'p-tb', 'https://www.mediawiki.org/',
+        *         'mediawiki.org', 't-mworg', 'Go to mediawiki.org', 'm', '#t-print'
+        *     );
+        *
+        *     var node = util.addPortletLink(
+        *         'p-tb',
+        *         new mw.Title( 'Special:Example' ).getUrl(),
+        *         'Example'
+        *     );
+        *     $( node ).on( 'click', function ( e ) {
+        *         console.log( 'Example' );
+        *         e.preventDefault();
+        *     } );
+        *
+        * @param {string} portletId ID of the target portlet (e.g. 'p-cactions' or 'p-personal')
+        * @param {string} href Link URL
+        * @param {string} text Link text
+        * @param {string} [id] ID of the list item, should be unique and preferably have
+        *  the appropriate prefix ('ca-', 'pt-', 'n-' or 't-')
+        * @param {string} [tooltip] Text to show when hovering over the link, without accesskey suffix
+        * @param {string} [accesskey] Access key to activate this link. One character only,
+        *  avoid conflicts with other links. Use `$( '[accesskey=x]' )` in the console to
+        *  see if 'x' is already used.
+        * @param {HTMLElement|jQuery|string} [nextnode] Element that the new item should be added before.
+        *  Must be another item in the same list, it will be ignored otherwise.
+        *  Can be specified as DOM reference, as jQuery object, or as CSS selector string.
+        * @return {HTMLElement|null} The added list item, or null if no element was added.
+        */
+       addPortletLink: function ( portletId, href, text, id, tooltip, accesskey, nextnode ) {
+               var item, link, $portlet, portlet, portletDiv, ul, next;
+
+               if ( !portletId ) {
+                       // Avoid confusing id="undefined" lookup
                        return null;
-               },
-
-               /**
-                * The content wrapper of the skin (e.g. `.mw-body`).
-                *
-                * Populated on document ready. To use this property,
-                * wait for `$.ready` and be sure to have a module dependency on
-                * `mediawiki.util` which will ensure
-                * your document ready handler fires after initialization.
-                *
-                * Because of the lazy-initialised nature of this property,
-                * you're discouraged from using it.
-                *
-                * If you need just the wikipage content (not any of the
-                * extra elements output by the skin), use `$( '#mw-content-text' )`
-                * instead. Or listen to mw.hook#wikipage_content which will
-                * allow your code to re-run when the page changes (e.g. live preview
-                * or re-render after ajax save).
-                *
-                * @property {jQuery}
-                */
-               $content: null,
-
-               /**
-                * Add a link to a portlet menu on the page, such as:
-                *
-                * p-cactions (Content actions), p-personal (Personal tools),
-                * p-navigation (Navigation), p-tb (Toolbox)
-                *
-                * The first three parameters are required, the others are optional and
-                * may be null. Though providing an id and tooltip is recommended.
-                *
-                * By default the new link will be added to the end of the list. To
-                * add the link before a given existing item, pass the DOM node
-                * (e.g. `document.getElementById( 'foobar' )`) or a jQuery-selector
-                * (e.g. `'#foobar'`) for that item.
-                *
-                *     util.addPortletLink(
-                *         'p-tb', 'https://www.mediawiki.org/',
-                *         'mediawiki.org', 't-mworg', 'Go to mediawiki.org', 'm', '#t-print'
-                *     );
-                *
-                *     var node = util.addPortletLink(
-                *         'p-tb',
-                *         new mw.Title( 'Special:Example' ).getUrl(),
-                *         'Example'
-                *     );
-                *     $( node ).on( 'click', function ( e ) {
-                *         console.log( 'Example' );
-                *         e.preventDefault();
-                *     } );
-                *
-                * @param {string} portletId ID of the target portlet (e.g. 'p-cactions' or 'p-personal')
-                * @param {string} href Link URL
-                * @param {string} text Link text
-                * @param {string} [id] ID of the list item, should be unique and preferably have
-                *  the appropriate prefix ('ca-', 'pt-', 'n-' or 't-')
-                * @param {string} [tooltip] Text to show when hovering over the link, without accesskey suffix
-                * @param {string} [accesskey] Access key to activate this link. One character only,
-                *  avoid conflicts with other links. Use `$( '[accesskey=x]' )` in the console to
-                *  see if 'x' is already used.
-                * @param {HTMLElement|jQuery|string} [nextnode] Element that the new item should be added before.
-                *  Must be another item in the same list, it will be ignored otherwise.
-                *  Can be specified as DOM reference, as jQuery object, or as CSS selector string.
-                * @return {HTMLElement|null} The added list item, or null if no element was added.
-                */
-               addPortletLink: function ( portletId, href, text, id, tooltip, accesskey, nextnode ) {
-                       var item, link, $portlet, portlet, portletDiv, ul, next;
-
-                       if ( !portletId ) {
-                               // Avoid confusing id="undefined" lookup
-                               return null;
-                       }
+               }
 
-                       portlet = document.getElementById( portletId );
-                       if ( !portlet ) {
-                               // Invalid portlet ID
-                               return null;
-                       }
+               portlet = document.getElementById( portletId );
+               if ( !portlet ) {
+                       // Invalid portlet ID
+                       return null;
+               }
 
-                       // Setup the anchor tag and set any the properties
-                       link = document.createElement( 'a' );
-                       link.href = href;
-                       link.textContent = text;
-                       if ( tooltip ) {
-                               link.title = tooltip;
-                       }
-                       if ( accesskey ) {
-                               link.accessKey = accesskey;
-                       }
+               // Setup the anchor tag and set any the properties
+               link = document.createElement( 'a' );
+               link.href = href;
+               link.textContent = text;
+               if ( tooltip ) {
+                       link.title = tooltip;
+               }
+               if ( accesskey ) {
+                       link.accessKey = accesskey;
+               }
 
-                       // Unhide portlet if it was hidden before
-                       $portlet = $( portlet );
-                       $portlet.removeClass( 'emptyPortlet' );
+               // Unhide portlet if it was hidden before
+               $portlet = $( portlet );
+               $portlet.removeClass( 'emptyPortlet' );
+
+               // Setup the list item (and a span if $portlet is a Vector tab)
+               // eslint-disable-next-line no-jquery/no-class-state
+               if ( $portlet.hasClass( 'vectorTabs' ) ) {
+                       item = $( '<li>' ).append( $( '<span>' ).append( link )[ 0 ] )[ 0 ];
+               } else {
+                       item = $( '<li>' ).append( link )[ 0 ];
+               }
+               if ( id ) {
+                       item.id = id;
+               }
 
-                       // Setup the list item (and a span if $portlet is a Vector tab)
-                       // eslint-disable-next-line no-jquery/no-class-state
-                       if ( $portlet.hasClass( 'vectorTabs' ) ) {
-                               item = $( '<li>' ).append( $( '<span>' ).append( link )[ 0 ] )[ 0 ];
+               // Select the first (most likely only) unordered list inside the portlet
+               ul = portlet.querySelector( 'ul' );
+               if ( !ul ) {
+                       // If it didn't have an unordered list yet, create one
+                       ul = document.createElement( 'ul' );
+                       portletDiv = portlet.querySelector( 'div' );
+                       if ( portletDiv ) {
+                               // Support: Legacy skins have a div (such as div.body or div.pBody).
+                               // Append the <ul> to that.
+                               portletDiv.appendChild( ul );
                        } else {
-                               item = $( '<li>' ).append( link )[ 0 ];
-                       }
-                       if ( id ) {
-                               item.id = id;
+                               // Append it to the portlet directly
+                               portlet.appendChild( ul );
                        }
+               }
 
-                       // Select the first (most likely only) unordered list inside the portlet
-                       ul = portlet.querySelector( 'ul' );
-                       if ( !ul ) {
-                               // If it didn't have an unordered list yet, create one
-                               ul = document.createElement( 'ul' );
-                               portletDiv = portlet.querySelector( 'div' );
-                               if ( portletDiv ) {
-                                       // Support: Legacy skins have a div (such as div.body or div.pBody).
-                                       // Append the <ul> to that.
-                                       portletDiv.appendChild( ul );
-                               } else {
-                                       // Append it to the portlet directly
-                                       portlet.appendChild( ul );
-                               }
+               if ( nextnode && ( typeof nextnode === 'string' || nextnode.nodeType || nextnode.jquery ) ) {
+                       nextnode = $( ul ).find( nextnode );
+                       if ( nextnode.length === 1 && nextnode[ 0 ].parentNode === ul ) {
+                               // Insertion point: Before nextnode
+                               nextnode.before( item );
+                               next = true;
                        }
+                       // Else: Invalid nextnode value (no match, more than one match, or not a direct child)
+                       // Else: Invalid nextnode type
+               }
 
-                       if ( nextnode && ( typeof nextnode === 'string' || nextnode.nodeType || nextnode.jquery ) ) {
-                               nextnode = $( ul ).find( nextnode );
-                               if ( nextnode.length === 1 && nextnode[ 0 ].parentNode === ul ) {
-                                       // Insertion point: Before nextnode
-                                       nextnode.before( item );
-                                       next = true;
-                               }
-                               // Else: Invalid nextnode value (no match, more than one match, or not a direct child)
-                               // Else: Invalid nextnode type
-                       }
+               if ( !next ) {
+                       // Insertion point: End of list (default)
+                       ul.appendChild( item );
+               }
 
-                       if ( !next ) {
-                               // Insertion point: End of list (default)
-                               ul.appendChild( item );
-                       }
+               // Update tooltip for the access key after inserting into DOM
+               // to get a localized access key label (T69946).
+               if ( accesskey ) {
+                       $( link ).updateTooltipAccessKeys();
+               }
 
-                       // Update tooltip for the access key after inserting into DOM
-                       // to get a localized access key label (T69946).
-                       if ( accesskey ) {
-                               $( link ).updateTooltipAccessKeys();
-                       }
+               return item;
+       },
 
-                       return item;
-               },
-
-               /**
-                * Validate a string as representing a valid e-mail address
-                * according to HTML5 specification. Please note the specification
-                * does not validate a domain with one character.
-                *
-                * FIXME: should be moved to or replaced by a validation module.
-                *
-                * @param {string} mailtxt E-mail address to be validated.
-                * @return {boolean|null} Null if `mailtxt` was an empty string, otherwise true/false
-                * as determined by validation.
-                */
-               validateEmail: function ( mailtxt ) {
-                       var rfc5322Atext, rfc1034LdhStr, html5EmailRegexp;
-
-                       if ( mailtxt === '' ) {
-                               return null;
-                       }
+       /**
+        * Validate a string as representing a valid e-mail address
+        * according to HTML5 specification. Please note the specification
+        * does not validate a domain with one character.
+        *
+        * FIXME: should be moved to or replaced by a validation module.
+        *
+        * @param {string} mailtxt E-mail address to be validated.
+        * @return {boolean|null} Null if `mailtxt` was an empty string, otherwise true/false
+        * as determined by validation.
+        */
+       validateEmail: function ( mailtxt ) {
+               var rfc5322Atext, rfc1034LdhStr, html5EmailRegexp;
 
-                       // HTML5 defines a string as valid e-mail address if it matches
-                       // the ABNF:
-                       //     1 * ( atext / "." ) "@" ldh-str 1*( "." ldh-str )
-                       // With:
-                       // - atext   : defined in RFC 5322 section 3.2.3
-                       // - ldh-str : defined in RFC 1034 section 3.5
-                       //
-                       // (see STD 68 / RFC 5234 https://tools.ietf.org/html/std68)
-                       // First, define the RFC 5322 'atext' which is pretty easy:
-                       // atext = ALPHA / DIGIT / ; Printable US-ASCII
-                       //     "!" / "#" /    ; characters not including
-                       //     "$" / "%" /    ; specials. Used for atoms.
-                       //     "&" / "'" /
-                       //     "*" / "+" /
-                       //     "-" / "/" /
-                       //     "=" / "?" /
-                       //     "^" / "_" /
-                       //     "`" / "{" /
-                       //     "|" / "}" /
-                       //     "~"
-                       rfc5322Atext = 'a-z0-9!#$%&\'*+\\-/=?^_`{|}~';
-
-                       // Next define the RFC 1034 'ldh-str'
-                       //     <domain> ::= <subdomain> | " "
-                       //     <subdomain> ::= <label> | <subdomain> "." <label>
-                       //     <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-                       //     <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-                       //     <let-dig-hyp> ::= <let-dig> | "-"
-                       //     <let-dig> ::= <letter> | <digit>
-                       rfc1034LdhStr = 'a-z0-9\\-';
-
-                       html5EmailRegexp = new RegExp(
-                               // start of string
-                               '^' +
-                               // User part which is liberal :p
-                               '[' + rfc5322Atext + '\\.]+' +
-                               // 'at'
-                               '@' +
-                               // Domain first part
-                               '[' + rfc1034LdhStr + ']+' +
-                               // Optional second part and following are separated by a dot
-                               '(?:\\.[' + rfc1034LdhStr + ']+)*' +
-                               // End of string
-                               '$',
-                               // RegExp is case insensitive
-                               'i'
-                       );
-                       return ( mailtxt.match( html5EmailRegexp ) !== null );
-               },
-
-               /**
-                * Note: borrows from IP::isIPv4
-                *
-                * @param {string} address
-                * @param {boolean} [allowBlock=false]
-                * @return {boolean}
-                */
-               isIPv4Address: function ( address, allowBlock ) {
-                       var block, RE_IP_BYTE, RE_IP_ADD;
-
-                       if ( typeof address !== 'string' ) {
-                               return false;
-                       }
+               if ( mailtxt === '' ) {
+                       return null;
+               }
 
-                       block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '';
-                       RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])';
-                       RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
-
-                       return ( new RegExp( '^' + RE_IP_ADD + block + '$' ).test( address ) );
-               },
-
-               /**
-                * Note: borrows from IP::isIPv6
-                *
-                * @param {string} address
-                * @param {boolean} [allowBlock=false]
-                * @return {boolean}
-                */
-               isIPv6Address: function ( address, allowBlock ) {
-                       var block, RE_IPV6_ADD;
-
-                       if ( typeof address !== 'string' ) {
-                               return false;
-                       }
+               // HTML5 defines a string as valid e-mail address if it matches
+               // the ABNF:
+               //     1 * ( atext / "." ) "@" ldh-str 1*( "." ldh-str )
+               // With:
+               // - atext   : defined in RFC 5322 section 3.2.3
+               // - ldh-str : defined in RFC 1034 section 3.5
+               //
+               // (see STD 68 / RFC 5234 https://tools.ietf.org/html/std68)
+               // First, define the RFC 5322 'atext' which is pretty easy:
+               // atext = ALPHA / DIGIT / ; Printable US-ASCII
+               //     "!" / "#" /    ; characters not including
+               //     "$" / "%" /    ; specials. Used for atoms.
+               //     "&" / "'" /
+               //     "*" / "+" /
+               //     "-" / "/" /
+               //     "=" / "?" /
+               //     "^" / "_" /
+               //     "`" / "{" /
+               //     "|" / "}" /
+               //     "~"
+               rfc5322Atext = 'a-z0-9!#$%&\'*+\\-/=?^_`{|}~';
+
+               // Next define the RFC 1034 'ldh-str'
+               //     <domain> ::= <subdomain> | " "
+               //     <subdomain> ::= <label> | <subdomain> "." <label>
+               //     <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+               //     <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+               //     <let-dig-hyp> ::= <let-dig> | "-"
+               //     <let-dig> ::= <letter> | <digit>
+               rfc1034LdhStr = 'a-z0-9\\-';
+
+               html5EmailRegexp = new RegExp(
+                       // start of string
+                       '^' +
+                       // User part which is liberal :p
+                       '[' + rfc5322Atext + '\\.]+' +
+                       // 'at'
+                       '@' +
+                       // Domain first part
+                       '[' + rfc1034LdhStr + ']+' +
+                       // Optional second part and following are separated by a dot
+                       '(?:\\.[' + rfc1034LdhStr + ']+)*' +
+                       // End of string
+                       '$',
+                       // RegExp is case insensitive
+                       'i'
+               );
+               return ( mailtxt.match( html5EmailRegexp ) !== null );
+       },
 
-                       block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '';
-                       RE_IPV6_ADD =
-                               '(?:' + // starts with "::" (including "::")
-                                       ':(?::|(?::' +
-                                               '[0-9A-Fa-f]{1,4}' +
-                                       '){1,7})' +
-                                       '|' + // ends with "::" (except "::")
-                                       '[0-9A-Fa-f]{1,4}' +
-                                       '(?::' +
-                                               '[0-9A-Fa-f]{1,4}' +
-                                       '){0,6}::' +
-                                       '|' + // contains no "::"
-                                       '[0-9A-Fa-f]{1,4}' +
-                                       '(?::' +
-                                               '[0-9A-Fa-f]{1,4}' +
-                                       '){7}' +
-                               ')';
+       /**
+        * Note: borrows from IP::isIPv4
+        *
+        * @param {string} address
+        * @param {boolean} [allowBlock=false]
+        * @return {boolean}
+        */
+       isIPv4Address: function ( address, allowBlock ) {
+               var block, RE_IP_BYTE, RE_IP_ADD;
 
-                       if ( new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) ) {
-                               return true;
-                       }
+               if ( typeof address !== 'string' ) {
+                       return false;
+               }
+
+               block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '';
+               RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])';
+               RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
+
+               return ( new RegExp( '^' + RE_IP_ADD + block + '$' ).test( address ) );
+       },
+
+       /**
+        * Note: borrows from IP::isIPv6
+        *
+        * @param {string} address
+        * @param {boolean} [allowBlock=false]
+        * @return {boolean}
+        */
+       isIPv6Address: function ( address, allowBlock ) {
+               var block, RE_IPV6_ADD;
+
+               if ( typeof address !== 'string' ) {
+                       return false;
+               }
 
-                       // contains one "::" in the middle (single '::' check below)
-                       RE_IPV6_ADD =
+               block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '';
+               RE_IPV6_ADD =
+                       '(?:' + // starts with "::" (including "::")
+                               ':(?::|(?::' +
+                                       '[0-9A-Fa-f]{1,4}' +
+                               '){1,7})' +
+                               '|' + // ends with "::" (except "::")
+                               '[0-9A-Fa-f]{1,4}' +
+                               '(?::' +
+                                       '[0-9A-Fa-f]{1,4}' +
+                               '){0,6}::' +
+                               '|' + // contains no "::"
                                '[0-9A-Fa-f]{1,4}' +
-                               '(?:::?' +
+                               '(?::' +
                                        '[0-9A-Fa-f]{1,4}' +
-                               '){1,6}';
-
-                       return (
-                               new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) &&
-                               /::/.test( address ) &&
-                               !/::.*::/.test( address )
-                       );
-               },
-
-               /**
-                * Check whether a string is an IP address
-                *
-                * @since 1.25
-                * @param {string} address String to check
-                * @param {boolean} [allowBlock=false] If a block of IPs should be allowed
-                * @return {boolean}
-                */
-               isIPAddress: function ( address, allowBlock ) {
-                       return util.isIPv4Address( address, allowBlock ) ||
-                               util.isIPv6Address( address, allowBlock );
+                               '){7}' +
+                       ')';
+
+               if ( new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) ) {
+                       return true;
                }
-       };
 
-       // Not allowed outside unit tests
-       if ( window.QUnit ) {
-               util.setOptionsForTest = function ( opts ) {
-                       var oldConfig = config;
-                       config = $.extend( {}, config, opts );
-                       return oldConfig;
-               };
-       }
+               // contains one "::" in the middle (single '::' check below)
+               RE_IPV6_ADD =
+                       '[0-9A-Fa-f]{1,4}' +
+                       '(?:::?' +
+                               '[0-9A-Fa-f]{1,4}' +
+                       '){1,6}';
+
+               return (
+                       new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) &&
+                       /::/.test( address ) &&
+                       !/::.*::/.test( address )
+               );
+       },
 
        /**
-        * Initialisation of mw.util.$content
+        * Check whether a string is an IP address
+        *
+        * @since 1.25
+        * @param {string} address String to check
+        * @param {boolean} [allowBlock=false] If a block of IPs should be allowed
+        * @return {boolean}
         */
-       function init() {
-               util.$content = ( function () {
-                       var i, l, $node, selectors;
-
-                       selectors = [
-                               // The preferred standard is class "mw-body".
-                               // You may also use class "mw-body mw-body-primary" if you use
-                               // mw-body in multiple locations. Or class "mw-body-primary" if
-                               // you use mw-body deeper in the DOM.
-                               '.mw-body-primary',
-                               '.mw-body',
-
-                               // If the skin has no such class, fall back to the parser output
-                               '#mw-content-text'
-                       ];
-
-                       for ( i = 0, l = selectors.length; i < l; i++ ) {
-                               $node = $( selectors[ i ] );
-                               if ( $node.length ) {
-                                       return $node.first();
-                               }
-                       }
+       isIPAddress: function ( address, allowBlock ) {
+               return util.isIPv4Address( address, allowBlock ) ||
+                       util.isIPv6Address( address, allowBlock );
+       },
 
-                       // Should never happen... well, it could if someone is not finished writing a
-                       // skin and has not yet inserted bodytext yet.
-                       return $( 'body' );
-               }() );
+       /**
+        * Escape string for safe inclusion in regular expression
+        *
+        * The following characters are escaped:
+        *
+        *     \ { } ( ) | . ? * + - ^ $ [ ]
+        *
+        * @since 1.26; moved to mw.util in 1.34
+        * @param {string} str String to escape
+        * @return {string} Escaped string
+        */
+       escapeRegExp: function ( str ) {
+               // eslint-disable-next-line no-useless-escape
+               return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' );
        }
+};
+
+// Backwards-compatible alias for mediawiki.RegExp module.
+// @deprecated since 1.34
+mw.RegExp = {};
+mw.log.deprecate( mw.RegExp, 'escape', util.escapeRegExp, 'Use mw.util.escapeRegExp() instead.', 'mw.RegExp.escape' );
+
+// Not allowed outside unit tests
+if ( window.QUnit ) {
+       util.setOptionsForTest = function ( opts ) {
+               var oldConfig = config;
+               config = $.extend( {}, config, opts );
+               return oldConfig;
+       };
+}
+
+/**
+ * Initialisation of mw.util.$content
+ */
+function init() {
+       util.$content = ( function () {
+               var i, l, $node, selectors;
+
+               selectors = [
+                       // The preferred standard is class "mw-body".
+                       // You may also use class "mw-body mw-body-primary" if you use
+                       // mw-body in multiple locations. Or class "mw-body-primary" if
+                       // you use mw-body deeper in the DOM.
+                       '.mw-body-primary',
+                       '.mw-body',
+
+                       // If the skin has no such class, fall back to the parser output
+                       '#mw-content-text'
+               ];
+
+               for ( i = 0, l = selectors.length; i < l; i++ ) {
+                       $node = $( selectors[ i ] );
+                       if ( $node.length ) {
+                               return $node.first();
+                       }
+               }
 
-       $( init );
+               // Should never happen... well, it could if someone is not finished writing a
+               // skin and has not yet inserted bodytext yet.
+               return $( 'body' );
+       }() );
+}
 
-       mw.util = util;
-       module.exports = util;
+$( init );
 
-}() );
+mw.util = util;
+module.exports = util;
index 81cf433..7102b67 100644 (file)
                        // eslint-disable-next-line no-restricted-properties
                        v = v.normalize();
                }
-               re = new RegExp( '^\\s*' + mw.RegExp.escape( v ), 'i' );
+               re = new RegExp( '^\\s*' + mw.util.escapeRegExp( v ), 'i' );
                for ( k in this.values ) {
                        k = +k;
                        if ( !isNaN( k ) && re.test( this.values[ k ] ) ) {
index 04658b9..d00ed82 100644 (file)
@@ -10,7 +10,7 @@
                        if ( mw.config.get( 'wgTranslateNumerals' ) ) {
                                for ( i = 0; i < 10; i++ ) {
                                        if ( table[ i ] !== undefined ) {
-                                               s = s.replace( new RegExp( mw.RegExp.escape( table[ i ] ), 'g' ), i );
+                                               s = s.replace( new RegExp( mw.util.escapeRegExp( table[ i ] ), 'g' ), i );
                                        }
                                }
                        }
index 3ac532e..f647f91 100644 (file)
--- a/rest.php
+++ b/rest.php
@@ -23,6 +23,9 @@
 
 use MediaWiki\Rest\EntryPoint;
 
+define( 'MW_REST_API', true );
+define( 'MW_ENTRY_POINT', 'rest' );
+
 require __DIR__ . '/includes/WebStart.php';
 
 EntryPoint::main();
index c8b8ef9..91c3d40 100644 (file)
@@ -1246,8 +1246,6 @@ class ParserTestRunner {
         * @return array
         */
        private function listTables() {
-               global $wgActorTableSchemaMigrationStage;
-
                $tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
                        'protected_titles', 'revision', 'ip_changes', 'text', 'pagelinks', 'imagelinks',
                        'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
@@ -1256,15 +1254,9 @@ class ParserTestRunner {
                        'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
                        'archive', 'user_groups', 'page_props', 'category',
                        'slots', 'content', 'slot_roles', 'content_models',
-                       'comment', 'revision_comment_temp',
+                       'comment', 'revision_comment_temp', 'actor', 'revision_actor_temp',
                ];
 
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       // The new tables for actors are in use
-                       $tables[] = 'actor';
-                       $tables[] = 'revision_actor_temp';
-               }
-
                if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite' ] ) ) {
                        array_push( $tables, 'searchindex' );
                }
index 46c0c42..a82b697 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 use MediaWiki\User\UserIdentity;
-use MediaWiki\MediaWikiServices;
+use Wikimedia\ScopedCallback;
 use Wikimedia\TestingAccessWrapper;
 
 /**
@@ -10,14 +10,60 @@ use Wikimedia\TestingAccessWrapper;
  */
 class ActorMigrationTest extends MediaWikiLangTestCase {
 
+       protected $resetActorMigration = null;
+       protected static $amId = 0;
+
        protected $tablesUsed = [
-               'revision',
-               'revision_actor_temp',
-               'ipblocks',
-               'recentchanges',
                'actor',
        ];
 
+       protected function setUp() {
+               parent::setUp();
+
+               $w = TestingAccessWrapper::newFromClass( ActorMigration::class );
+               $data = [
+                       'tempTables' => $w->tempTables,
+                       'formerTempTables' => $w->formerTempTables,
+                       'deprecated' => $w->deprecated,
+                       'removed' => $w->removed,
+                       'specialFields' => $w->specialFields,
+               ];
+               $this->resetActorMigration = new ScopedCallback( function ( $w, $data ) {
+                       foreach ( $data as $k => $v ) {
+                               $w->$k = $v;
+                       }
+               }, [ $w, $data ] );
+
+               $w->tempTables = [
+                       'am2_user' => [
+                               'table' => 'actormigration2_temp',
+                               'pk' => 'am2t_id',
+                               'field' => 'am2t_actor',
+                               'joinPK' => 'am2_id',
+                               'extra' => [],
+                       ]
+               ];
+               $w->specialFields = [
+                       'am3_xxx' => [ 'am3_xxx_text', 'am3_xxx_actor' ],
+               ];
+       }
+
+       protected function tearDown() {
+               parent::tearDown();
+               ScopedCallback::consume( $this->resetActorMigration );
+       }
+
+       protected function getSchemaOverrides( IMaintainableDatabase $db ) {
+               return [
+                       'scripts' => [
+                               __DIR__ . '/ActorMigrationTest.sql',
+                       ],
+                       'drop' => [],
+                       'create' => [ 'actormigration1', 'actormigration2', 'actormigration2_temp', 'actormigration3' ],
+                       'alter' => [],
+               ];
+       }
+
        /**
         * @dataProvider provideConstructor
         * @param int $stage
@@ -80,156 +126,156 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
        public static function provideGetJoin() {
                return [
                        'Simple table, old' => [
-                               SCHEMA_COMPAT_OLD, 'rc_user', [
+                               SCHEMA_COMPAT_OLD, 'am1_user', [
                                        'tables' => [],
                                        'fields' => [
-                                               'rc_user' => 'rc_user',
-                                               'rc_user_text' => 'rc_user_text',
-                                               'rc_actor' => 'NULL',
+                                               'am1_user' => 'am1_user',
+                                               'am1_user_text' => 'am1_user_text',
+                                               'am1_actor' => 'NULL',
                                        ],
                                        'joins' => [],
                                ],
                        ],
                        'Simple table, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', [
                                        'tables' => [],
                                        'fields' => [
-                                               'rc_user' => 'rc_user',
-                                               'rc_user_text' => 'rc_user_text',
-                                               'rc_actor' => 'NULL',
+                                               'am1_user' => 'am1_user',
+                                               'am1_user_text' => 'am1_user_text',
+                                               'am1_actor' => 'NULL',
                                        ],
                                        'joins' => [],
                                ],
                        ],
                        'Simple table, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', [
-                                       'tables' => [ 'actor_rc_user' => 'actor' ],
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', [
+                                       'tables' => [ 'actor_am1_user' => 'actor' ],
                                        'fields' => [
-                                               'rc_user' => 'actor_rc_user.actor_user',
-                                               'rc_user_text' => 'actor_rc_user.actor_name',
-                                               'rc_actor' => 'rc_actor',
+                                               'am1_user' => 'actor_am1_user.actor_user',
+                                               'am1_user_text' => 'actor_am1_user.actor_name',
+                                               'am1_actor' => 'am1_actor',
                                        ],
                                        'joins' => [
-                                               'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+                                               'actor_am1_user' => [ 'JOIN', 'actor_am1_user.actor_id = am1_actor' ],
                                        ],
                                ],
                        ],
                        'Simple table, new' => [
-                               SCHEMA_COMPAT_NEW, 'rc_user', [
-                                       'tables' => [ 'actor_rc_user' => 'actor' ],
+                               SCHEMA_COMPAT_NEW, 'am1_user', [
+                                       'tables' => [ 'actor_am1_user' => 'actor' ],
                                        'fields' => [
-                                               'rc_user' => 'actor_rc_user.actor_user',
-                                               'rc_user_text' => 'actor_rc_user.actor_name',
-                                               'rc_actor' => 'rc_actor',
+                                               'am1_user' => 'actor_am1_user.actor_user',
+                                               'am1_user_text' => 'actor_am1_user.actor_name',
+                                               'am1_actor' => 'am1_actor',
                                        ],
                                        'joins' => [
-                                               'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+                                               'actor_am1_user' => [ 'JOIN', 'actor_am1_user.actor_id = am1_actor' ],
                                        ],
                                ],
                        ],
 
-                       'ipblocks, old' => [
-                               SCHEMA_COMPAT_OLD, 'ipb_by', [
+                       'Special name, old' => [
+                               SCHEMA_COMPAT_OLD, 'am3_xxx', [
                                        'tables' => [],
                                        'fields' => [
-                                               'ipb_by' => 'ipb_by',
-                                               'ipb_by_text' => 'ipb_by_text',
-                                               'ipb_by_actor' => 'NULL',
+                                               'am3_xxx' => 'am3_xxx',
+                                               'am3_xxx_text' => 'am3_xxx_text',
+                                               'am3_xxx_actor' => 'NULL',
                                        ],
                                        'joins' => [],
                                ],
                        ],
-                       'ipblocks, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_by', [
+                       'Special name, read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am3_xxx', [
                                        'tables' => [],
                                        'fields' => [
-                                               'ipb_by' => 'ipb_by',
-                                               'ipb_by_text' => 'ipb_by_text',
-                                               'ipb_by_actor' => 'NULL',
+                                               'am3_xxx' => 'am3_xxx',
+                                               'am3_xxx_text' => 'am3_xxx_text',
+                                               'am3_xxx_actor' => 'NULL',
                                        ],
                                        'joins' => [],
                                ],
                        ],
-                       'ipblocks, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_by', [
-                                       'tables' => [ 'actor_ipb_by' => 'actor' ],
+                       'Special name, read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am3_xxx', [
+                                       'tables' => [ 'actor_am3_xxx' => 'actor' ],
                                        'fields' => [
-                                               'ipb_by' => 'actor_ipb_by.actor_user',
-                                               'ipb_by_text' => 'actor_ipb_by.actor_name',
-                                               'ipb_by_actor' => 'ipb_by_actor',
+                                               'am3_xxx' => 'actor_am3_xxx.actor_user',
+                                               'am3_xxx_text' => 'actor_am3_xxx.actor_name',
+                                               'am3_xxx_actor' => 'am3_xxx_actor',
                                        ],
                                        'joins' => [
-                                               'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+                                               'actor_am3_xxx' => [ 'JOIN', 'actor_am3_xxx.actor_id = am3_xxx_actor' ],
                                        ],
                                ],
                        ],
-                       'ipblocks, new' => [
-                               SCHEMA_COMPAT_NEW, 'ipb_by', [
-                                       'tables' => [ 'actor_ipb_by' => 'actor' ],
+                       'Special name, new' => [
+                               SCHEMA_COMPAT_NEW, 'am3_xxx', [
+                                       'tables' => [ 'actor_am3_xxx' => 'actor' ],
                                        'fields' => [
-                                               'ipb_by' => 'actor_ipb_by.actor_user',
-                                               'ipb_by_text' => 'actor_ipb_by.actor_name',
-                                               'ipb_by_actor' => 'ipb_by_actor',
+                                               'am3_xxx' => 'actor_am3_xxx.actor_user',
+                                               'am3_xxx_text' => 'actor_am3_xxx.actor_name',
+                                               'am3_xxx_actor' => 'am3_xxx_actor',
                                        ],
                                        'joins' => [
-                                               'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+                                               'actor_am3_xxx' => [ 'JOIN', 'actor_am3_xxx.actor_id = am3_xxx_actor' ],
                                        ],
                                ],
                        ],
 
-                       'Revision, old' => [
-                               SCHEMA_COMPAT_OLD, 'rev_user', [
+                       'Temp table, old' => [
+                               SCHEMA_COMPAT_OLD, 'am2_user', [
                                        'tables' => [],
                                        'fields' => [
-                                               'rev_user' => 'rev_user',
-                                               'rev_user_text' => 'rev_user_text',
-                                               'rev_actor' => 'NULL',
+                                               'am2_user' => 'am2_user',
+                                               'am2_user_text' => 'am2_user_text',
+                                               'am2_actor' => 'NULL',
                                        ],
                                        'joins' => [],
                                ],
                        ],
-                       'Revision, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_user', [
+                       'Temp table, read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am2_user', [
                                        'tables' => [],
                                        'fields' => [
-                                               'rev_user' => 'rev_user',
-                                               'rev_user_text' => 'rev_user_text',
-                                               'rev_actor' => 'NULL',
+                                               'am2_user' => 'am2_user',
+                                               'am2_user_text' => 'am2_user_text',
+                                               'am2_actor' => 'NULL',
                                        ],
                                        'joins' => [],
                                ],
                        ],
-                       'Revision, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_user', [
+                       'Temp table, read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am2_user', [
                                        'tables' => [
-                                               'temp_rev_user' => 'revision_actor_temp',
-                                               'actor_rev_user' => 'actor',
+                                               'temp_am2_user' => 'actormigration2_temp',
+                                               'actor_am2_user' => 'actor',
                                        ],
                                        'fields' => [
-                                               'rev_user' => 'actor_rev_user.actor_user',
-                                               'rev_user_text' => 'actor_rev_user.actor_name',
-                                               'rev_actor' => 'temp_rev_user.revactor_actor',
+                                               'am2_user' => 'actor_am2_user.actor_user',
+                                               'am2_user_text' => 'actor_am2_user.actor_name',
+                                               'am2_actor' => 'temp_am2_user.am2t_actor',
                                        ],
                                        'joins' => [
-                                               'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                               'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                                               'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
+                                               'actor_am2_user' => [ 'JOIN', 'actor_am2_user.actor_id = temp_am2_user.am2t_actor' ],
                                        ],
                                ],
                        ],
-                       'Revision, new' => [
-                               SCHEMA_COMPAT_NEW, 'rev_user', [
+                       'Temp table, new' => [
+                               SCHEMA_COMPAT_NEW, 'am2_user', [
                                        'tables' => [
-                                               'temp_rev_user' => 'revision_actor_temp',
-                                               'actor_rev_user' => 'actor',
+                                               'temp_am2_user' => 'actormigration2_temp',
+                                               'actor_am2_user' => 'actor',
                                        ],
                                        'fields' => [
-                                               'rev_user' => 'actor_rev_user.actor_user',
-                                               'rev_user_text' => 'actor_rev_user.actor_name',
-                                               'rev_actor' => 'temp_rev_user.revactor_actor',
+                                               'am2_user' => 'actor_am2_user.actor_user',
+                                               'am2_user_text' => 'actor_am2_user.actor_name',
+                                               'am2_actor' => 'temp_am2_user.am2t_actor',
                                        ],
                                        'joins' => [
-                                               'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
-                                               'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                                               'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
+                                               'actor_am2_user' => [ 'JOIN', 'actor_am2_user.actor_id = temp_am2_user.am2t_actor' ],
                                        ],
                                ],
                        ],
@@ -276,164 +322,164 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
 
                return [
                        'Simple table, old' => [
-                               SCHEMA_COMPAT_OLD, 'rc_user', $genericUser, true, [
+                               SCHEMA_COMPAT_OLD, 'am1_user', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'userid' => "rc_user = '1'" ],
+                                       'orconds' => [ 'userid' => "am1_user = '1'" ],
                                        'joins' => [],
                                ],
                        ],
                        'Simple table, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $genericUser, true, [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'userid' => "rc_user = '1'" ],
+                                       'orconds' => [ 'userid' => "am1_user = '1'" ],
                                        'joins' => [],
                                ],
                        ],
                        'Simple table, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $genericUser, true, [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "rc_actor = '11'" ],
+                                       'orconds' => [ 'actor' => "am1_actor = '11'" ],
                                        'joins' => [],
                                ],
                        ],
                        'Simple table, new' => [
-                               SCHEMA_COMPAT_NEW, 'rc_user', $genericUser, true, [
+                               SCHEMA_COMPAT_NEW, 'am1_user', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "rc_actor = '11'" ],
+                                       'orconds' => [ 'actor' => "am1_actor = '11'" ],
                                        'joins' => [],
                                ],
                        ],
 
-                       'ipblocks, old' => [
-                               SCHEMA_COMPAT_OLD, 'ipb_by', $genericUser, true, [
+                       'Special name, old' => [
+                               SCHEMA_COMPAT_OLD, 'am3_xxx', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'userid' => "ipb_by = '1'" ],
+                                       'orconds' => [ 'userid' => "am3_xxx = '1'" ],
                                        'joins' => [],
                                ],
                        ],
-                       'ipblocks, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_by', $genericUser, true, [
+                       'Special name, read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am3_xxx', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'userid' => "ipb_by = '1'" ],
+                                       'orconds' => [ 'userid' => "am3_xxx = '1'" ],
                                        'joins' => [],
                                ],
                        ],
-                       'ipblocks, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_by', $genericUser, true, [
+                       'Special name, read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am3_xxx', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
+                                       'orconds' => [ 'actor' => "am3_xxx_actor = '11'" ],
                                        'joins' => [],
                                ],
                        ],
-                       'ipblocks, new' => [
-                               SCHEMA_COMPAT_NEW, 'ipb_by', $genericUser, true, [
+                       'Special name, new' => [
+                               SCHEMA_COMPAT_NEW, 'am3_xxx', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
+                                       'orconds' => [ 'actor' => "am3_xxx_actor = '11'" ],
                                        'joins' => [],
                                ],
                        ],
 
-                       'Revision, old' => [
-                               SCHEMA_COMPAT_OLD, 'rev_user', $genericUser, true, [
+                       'Temp table, old' => [
+                               SCHEMA_COMPAT_OLD, 'am2_user', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'userid' => "rev_user = '1'" ],
+                                       'orconds' => [ 'userid' => "am2_user = '1'" ],
                                        'joins' => [],
                                ],
                        ],
-                       'Revision, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_user', $genericUser, true, [
+                       'Temp table, read-old' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am2_user', $genericUser, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'userid' => "rev_user = '1'" ],
+                                       'orconds' => [ 'userid' => "am2_user = '1'" ],
                                        'joins' => [],
                                ],
                        ],
-                       'Revision, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_user', $genericUser, true, [
+                       'Temp table, read-new' => [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am2_user', $genericUser, true, [
                                        'tables' => [
-                                               'temp_rev_user' => 'revision_actor_temp',
+                                               'temp_am2_user' => 'actormigration2_temp',
                                        ],
-                                       'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
+                                       'orconds' => [ 'actor' => "temp_am2_user.am2t_actor = '11'" ],
                                        'joins' => [
-                                               'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                               'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
                                        ],
                                ],
                        ],
-                       'Revision, new' => [
-                               SCHEMA_COMPAT_NEW, 'rev_user', $genericUser, true, [
+                       'Temp table, new' => [
+                               SCHEMA_COMPAT_NEW, 'am2_user', $genericUser, true, [
                                        'tables' => [
-                                               'temp_rev_user' => 'revision_actor_temp',
+                                               'temp_am2_user' => 'actormigration2_temp',
                                        ],
-                                       'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
+                                       'orconds' => [ 'actor' => "temp_am2_user.am2t_actor = '11'" ],
                                        'joins' => [
-                                               'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                               'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
                                        ],
                                ],
                        ],
 
                        'Multiple users, old' => [
-                               SCHEMA_COMPAT_OLD, 'rc_user', $complicatedUsers, true, [
+                               SCHEMA_COMPAT_OLD, 'am1_user', $complicatedUsers, true, [
                                        'tables' => [],
                                        'orconds' => [
-                                               'userid' => "rc_user IN ('1','2','3') ",
-                                               'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
+                                               'userid' => "am1_user IN ('1','2','3') ",
+                                               'username' => "am1_user_text IN ('192.168.12.34','192.168.12.35') "
                                        ],
                                        'joins' => [],
                                ],
                        ],
                        'Multiple users, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $complicatedUsers, true, [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $complicatedUsers, true, [
                                        'tables' => [],
                                        'orconds' => [
-                                               'userid' => "rc_user IN ('1','2','3') ",
-                                               'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
+                                               'userid' => "am1_user IN ('1','2','3') ",
+                                               'username' => "am1_user_text IN ('192.168.12.34','192.168.12.35') "
                                        ],
                                        'joins' => [],
                                ],
                        ],
                        'Multiple users, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $complicatedUsers, true, [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $complicatedUsers, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+                                       'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
                                        'joins' => [],
                                ],
                        ],
                        'Multiple users, new' => [
-                               SCHEMA_COMPAT_NEW, 'rc_user', $complicatedUsers, true, [
+                               SCHEMA_COMPAT_NEW, 'am1_user', $complicatedUsers, true, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+                                       'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
                                        'joins' => [],
                                ],
                        ],
 
                        'Multiple users, no use ID, old' => [
-                               SCHEMA_COMPAT_OLD, 'rc_user', $complicatedUsers, false, [
+                               SCHEMA_COMPAT_OLD, 'am1_user', $complicatedUsers, false, [
                                        'tables' => [],
                                        'orconds' => [
-                                               'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+                                               'username' => "am1_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
                                        ],
                                        'joins' => [],
                                ],
                        ],
                        'Multiple users, read-old' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $complicatedUsers, false, [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $complicatedUsers, false, [
                                        'tables' => [],
                                        'orconds' => [
-                                               'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+                                               'username' => "am1_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
                                        ],
                                        'joins' => [],
                                ],
                        ],
                        'Multiple users, read-new' => [
-                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $complicatedUsers, false, [
+                               SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $complicatedUsers, false, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+                                       'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
                                        'joins' => [],
                                ],
                        ],
                        'Multiple users, new' => [
-                               SCHEMA_COMPAT_NEW, 'rc_user', $complicatedUsers, false, [
+                               SCHEMA_COMPAT_NEW, 'am1_user', $complicatedUsers, false, [
                                        'tables' => [],
-                                       'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+                                       'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
                                        'joins' => [],
                                ],
                        ],
@@ -445,23 +491,14 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
         * @param string $table
         * @param string $key
         * @param string $pk
-        * @param array $extraFields
+        * @param bool $usesTemp
         */
-       public function testInsertRoundTrip( $table, $key, $pk, $extraFields ) {
+       public function testInsertRoundTrip( $table, $key, $pk, $usesTemp ) {
                $u = $this->getTestUser()->getUser();
                $user = $this->getMock( UserIdentity::class );
                $user->method( 'getId' )->willReturn( $u->getId() );
                $user->method( 'getName' )->willReturn( $u->getName() );
-               if ( $u->getActorId( $this->db ) ) {
-                       $user->method( 'getActorId' )->willReturn( $u->getActorId() );
-               } else {
-                       $this->db->insert(
-                               'actor',
-                               [ 'actor_user' => $u->getId(), 'actor_name' => $u->getName() ],
-                               __METHOD__
-                       );
-                       $user->method( 'getActorId' )->willReturn( $this->db->insertId() );
-               }
+               $user->method( 'getActorId' )->willReturn( $u->getActorId( $this->db ) );
 
                $stageNames = [
                        SCHEMA_COMPAT_OLD => 'old',
@@ -494,15 +531,10 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
                ];
 
                $nameKey = $key . '_text';
-               $actorKey = $key === 'ipb_by' ? 'ipb_by_actor' : substr( $key, 0, -5 ) . '_actor';
+               $actorKey = ( $key === 'am3_xxx' ? $key : substr( $key, 0, -5 ) ) . '_actor';
 
                foreach ( $stages as $writeStage => $possibleReadStages ) {
-                       if ( $key === 'ipb_by' ) {
-                               $extraFields['ipb_address'] = __CLASS__ . "#{$stageNames[$writeStage]}";
-                       }
-
                        $w = new ActorMigration( $writeStage );
-                       $usesTemp = $key === 'rev_user';
 
                        if ( $usesTemp ) {
                                list( $fields, $callback ) = $w->getInsertValuesWithTempTable( $this->db, $key, $user );
@@ -527,10 +559,10 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
                                        "new field, stage={$stageNames[$writeStage]}" );
                        }
 
-                       $this->db->insert( $table, $extraFields + $fields, __METHOD__ );
-                       $id = $this->db->insertId();
+                       $id = ++self::$amId;
+                       $this->db->insert( $table, [ $pk => $id ] + $fields, __METHOD__ );
                        if ( $usesTemp ) {
-                               $callback( $id, $extraFields );
+                               $callback( $id, [] );
                        }
 
                        foreach ( $possibleReadStages as $readStage ) {
@@ -560,33 +592,10 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
        }
 
        public static function provideInsertRoundTrip() {
-               $db = wfGetDB( DB_REPLICA ); // for timestamps
-
-               $comment = MediaWikiServices::getInstance()->getCommentStore()
-                       ->createComment( wfGetDB( DB_MASTER ), '' );
-
                return [
-                       'recentchanges' => [ 'recentchanges', 'rc_user', 'rc_id', [
-                               'rc_timestamp' => $db->timestamp(),
-                               'rc_namespace' => 0,
-                               'rc_title' => 'Test',
-                               'rc_this_oldid' => 42,
-                               'rc_last_oldid' => 41,
-                               'rc_source' => 'test',
-                               'rc_comment_id' => $comment->id,
-                       ] ],
-                       'ipblocks' => [ 'ipblocks', 'ipb_by', 'ipb_id', [
-                               'ipb_range_start' => '',
-                               'ipb_range_end' => '',
-                               'ipb_timestamp' => $db->timestamp(),
-                               'ipb_expiry' => $db->getInfinity(),
-                               'ipb_reason_id' => $comment->id,
-                       ] ],
-                       'revision' => [ 'revision', 'rev_user', 'rev_id', [
-                               'rev_page' => 42,
-                               'rev_len' => 0,
-                               'rev_timestamp' => $db->timestamp(),
-                       ] ],
+                       'normal' => [ 'actormigration1', 'am1_user', 'am1_id', false ],
+                       'temp table' => [ 'actormigration2', 'am2_user', 'am2_id', true ],
+                       'special name' => [ 'actormigration3', 'am3_xxx', 'am3_id', false ],
                ];
        }
 
@@ -603,22 +612,22 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
         * @dataProvider provideStages
         * @param int $stage
         * @expectedException InvalidArgumentException
-        * @expectedExceptionMessage Must use getInsertValuesWithTempTable() for rev_user
+        * @expectedExceptionMessage Must use getInsertValuesWithTempTable() for am2_user
         */
        public function testInsertWrong( $stage ) {
                $m = new ActorMigration( $stage );
-               $m->getInsertValues( $this->db, 'rev_user', $this->getTestUser()->getUser() );
+               $m->getInsertValues( $this->db, 'am2_user', $this->getTestUser()->getUser() );
        }
 
        /**
         * @dataProvider provideStages
         * @param int $stage
         * @expectedException InvalidArgumentException
-        * @expectedExceptionMessage Must use getInsertValues() for rc_user
+        * @expectedExceptionMessage Must use getInsertValues() for am1_user
         */
        public function testInsertWithTempTableWrong( $stage ) {
                $m = new ActorMigration( $stage );
-               $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
+               $m->getInsertValuesWithTempTable( $this->db, 'am1_user', $this->getTestUser()->getUser() );
        }
 
        /**
@@ -627,12 +636,12 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
         */
        public function testInsertWithTempTableDeprecated( $stage ) {
                $wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
-               $wrap->formerTempTables += [ 'rc_user' => '1.30' ];
+               $wrap->formerTempTables += [ 'am1_user' => '1.30' ];
 
-               $this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for rc_user' );
+               $this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for am1_user' );
                $m = new ActorMigration( $stage );
                list( $fields, $callback )
-                       = $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
+                       = $m->getInsertValuesWithTempTable( $this->db, 'am1_user', $this->getTestUser()->getUser() );
                $this->assertTrue( is_callable( $callback ) );
        }
 
@@ -640,12 +649,23 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
         * @dataProvider provideStages
         * @param int $stage
         * @expectedException InvalidArgumentException
-        * @expectedExceptionMessage $extra[rev_timestamp] is not provided
+        * @expectedExceptionMessage $extra[foo_timestamp] is not provided
         */
        public function testInsertWithTempTableCallbackMissingFields( $stage ) {
+               $w = TestingAccessWrapper::newFromClass( ActorMigration::class );
+               $w->tempTables = [
+                       'foo_user' => [
+                               'table' => 'foo_temp',
+                               'pk' => 'footmp_id',
+                               'field' => 'footmp_actor',
+                               'joinPK' => 'foo_id',
+                               'extra' => [ 'footmp_timestamp' => 'foo_timestamp' ],
+                       ],
+               ];
+
                $m = new ActorMigration( $stage );
                list( $fields, $callback )
-                       = $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $this->getTestUser()->getUser() );
+                       = $m->getInsertValuesWithTempTable( $this->db, 'foo_user', $this->getTestUser()->getUser() );
                $callback( 1, [] );
        }
 
@@ -654,41 +674,33 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
         * @param int $stage
         */
        public function testInsertUserIdentity( $stage ) {
-               $this->setMwGlobals( [
-                       // for User::getActorId()
-                       'wgActorTableSchemaMigrationStage' => $stage
-               ] );
-
                $user = $this->getMutableTestUser()->getUser();
                $userIdentity = $this->getMock( UserIdentity::class );
                $userIdentity->method( 'getId' )->willReturn( $user->getId() );
                $userIdentity->method( 'getName' )->willReturn( $user->getName() );
                $userIdentity->method( 'getActorId' )->willReturn( 0 );
 
-               list( $cFields, $cCallback ) = MediaWikiServices::getInstance()->getCommentStore()
-                       ->insertWithTempTable( $this->db, 'rev_comment', '' );
                $m = new ActorMigration( $stage );
                list( $fields, $callback ) =
-                       $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $userIdentity );
-               $extraFields = [
-                       'rev_page' => 42,
-                       'rev_len' => 0,
-                       'rev_timestamp' => $this->db->timestamp(),
-               ] + $cFields;
-               $this->db->insert( 'revision', $extraFields + $fields, __METHOD__ );
-               $id = $this->db->insertId();
-               $callback( $id, $extraFields );
-               $cCallback( $id );
-
-               $qi = $m->getJoin( 'rev_user' );
+                       $m->getInsertValuesWithTempTable( $this->db, 'am2_user', $userIdentity );
+               $id = ++self::$amId;
+               $this->db->insert( 'actormigration2', [ 'am2_id' => $id ] + $fields, __METHOD__ );
+               $callback( $id, [] );
+
+               $qi = $m->getJoin( 'am2_user' );
                $row = $this->db->selectRow(
-                       [ 'revision' ] + $qi['tables'], $qi['fields'], [ 'rev_id' => $id ], __METHOD__, [], $qi['joins']
+                       [ 'actormigration2' ] + $qi['tables'],
+                       $qi['fields'],
+                       [ 'am2_id' => $id ],
+                       __METHOD__,
+                       [],
+                       $qi['joins']
                );
-               $this->assertSame( $user->getId(), (int)$row->rev_user );
-               $this->assertSame( $user->getName(), $row->rev_user_text );
+               $this->assertSame( $user->getId(), (int)$row->am2_user );
+               $this->assertSame( $user->getName(), $row->am2_user_text );
                $this->assertSame(
                        ( $stage & SCHEMA_COMPAT_READ_NEW ) ? $user->getActorId() : 0,
-                       (int)$row->rev_actor
+                       (int)$row->am2_actor
                );
 
                $m = new ActorMigration( $stage );
@@ -736,4 +748,24 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
                ];
        }
 
+       public function testCheckDeprecation() {
+               $wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
+               $wrap->deprecated += [ 'soft' => null, 'hard' => '1.34' ];
+               $wrap->removed += [ 'gone' => '1.34' ];
+
+               $this->hideDeprecated( 'ActorMigration for \'hard\'' );
+
+               $wrap->checkDeprecation( 'valid' );
+               $wrap->checkDeprecation( 'soft' );
+               $wrap->checkDeprecation( 'hard' );
+               try {
+                       $wrap->checkDeprecation( 'gone' );
+               } catch ( InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'Use of ActorMigration for \'gone\' was removed in MediaWiki 1.34',
+                               $ex->getMessage()
+                       );
+               }
+       }
+
 }
diff --git a/tests/phpunit/includes/ActorMigrationTest.sql b/tests/phpunit/includes/ActorMigrationTest.sql
new file mode 100644 (file)
index 0000000..f387dac
--- /dev/null
@@ -0,0 +1,26 @@
+-- These are carefully crafted to work in all five supported databases
+
+CREATE TABLE /*_*/actormigration1 (
+  am1_id integer not null,
+  am1_user integer,
+  am1_user_text varchar(200),
+  am1_actor integer
+);
+
+CREATE TABLE /*_*/actormigration2 (
+  am2_id integer not null,
+  am2_user integer,
+  am2_user_text varchar(200)
+);
+
+CREATE TABLE /*_*/actormigration2_temp (
+  am2t_id integer not null,
+  am2t_actor integer
+);
+
+CREATE TABLE /*_*/actormigration3 (
+  am3_id integer not null,
+  am3_xxx integer,
+  am3_xxx_text varchar(200),
+  am3_xxx_actor integer
+);
index 5cc3646..949b664 100644 (file)
@@ -61,32 +61,11 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                ];
        }
 
-       protected function getOldActorQueryFields( $prefix ) {
-               return [
-                       "{$prefix}_user" => "{$prefix}_user",
-                       "{$prefix}_user_text" => "{$prefix}_user_text",
-                       "{$prefix}_actor" => 'NULL',
-               ];
-       }
-
        protected function getNewActorQueryFields( $prefix, $tmp = false ) {
                return [
                        "{$prefix}_user" => "actor_{$prefix}_user.actor_user",
                        "{$prefix}_user_text" => "actor_{$prefix}_user.actor_name",
-                       "{$prefix}_actor" => $tmp ?: "{$prefix}_actor",
-               ];
-       }
-
-       protected function getNewActorJoins( $prefix ) {
-               return [
-                       "temp_{$prefix}_user" => [
-                               "JOIN",
-                               "temp_{$prefix}_user.revactor_{$prefix} = {$prefix}_id",
-                       ],
-                       "actor_{$prefix}_user" => [
-                               "JOIN",
-                               "actor_{$prefix}_user.actor_id = temp_{$prefix}_user.revactor_actor",
-                       ],
+                       "{$prefix}_actor" => $tmp ? "temp_{$prefix}_user.{$prefix}actor_actor" : "{$prefix}_actor",
                ];
        }
 
@@ -125,7 +104,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                yield 'MCR, comment, actor' => [
                        [
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                        ],
                        [
                                'tables' => [
@@ -150,7 +128,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage'
                                        => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                        ],
                        [
                                'tables' => [
@@ -175,22 +152,23 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage'
                                        => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
                        ],
                        [
                                'tables' => [
                                        'archive',
+                                       'actor_ar_user' => 'actor',
                                        'comment_ar_comment' => 'comment',
                                ],
                                'fields' => array_merge(
                                        $this->getArchiveQueryFields( true ),
                                        $this->getContentHandlerQueryFields( 'ar' ),
-                                       $this->getOldActorQueryFields( 'ar' ),
+                                       $this->getNewActorQueryFields( 'ar' ),
                                        $this->getNewCommentQueryFields( 'ar' )
                                ),
                                'joins' => [
                                        'comment_ar_comment'
                                                => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
+                                       'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
                                ],
                        ]
                ];
@@ -198,21 +176,22 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => false,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [
                                'tables' => [
                                        'archive',
+                                       'actor_ar_user' => 'actor',
                                        'comment_ar_comment' => 'comment',
                                ],
                                'fields' => array_merge(
                                        $this->getArchiveQueryFields( true ),
-                                       $this->getOldActorQueryFields( 'ar' ),
+                                       $this->getNewActorQueryFields( 'ar' ),
                                        $this->getNewCommentQueryFields( 'ar' )
                                ),
                                'joins' => [
                                        'comment_ar_comment'
                                                => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
+                                       'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
                                ],
                        ]
                ];
@@ -224,7 +203,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                        ],
                        [ 'page', 'user' ],
                        [
@@ -260,6 +238,8 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        ],
                                        'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ]
                ];
@@ -268,7 +248,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage'
                                        => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
                        ],
                        [ 'page', 'user' ],
                        [
@@ -288,22 +267,21 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
-                               'joins' => array_merge(
-                                       [
-                                               'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
-                                               'user' => [
-                                                       'LEFT JOIN',
-                                                       [
-                                                               'actor_rev_user.actor_user != 0',
-                                                               'user_id = actor_rev_user.actor_user',
-                                                       ]
-                                               ],
-                                               'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                               'comment_rev_comment'
-                                                       => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                               'joins' => [
+                                       'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
+                                       'user' => [
+                                               'LEFT JOIN',
+                                               [
+                                                       'actor_rev_user.actor_user != 0',
+                                                       'user_id = actor_rev_user.actor_user',
+                                               ]
                                        ],
-                                       $this->getNewActorJoins( 'rev' )
-                               ),
+                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+                                       'comment_rev_comment'
+                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                               ],
                        ]
                ];
                yield 'MCR read-new' => [
@@ -311,7 +289,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage'
                                        => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
                        ],
                        [ 'page', 'user' ],
                        [
@@ -331,22 +308,21 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
-                               'joins' => array_merge(
-                                       [
-                                               'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
-                                               'user' => [
-                                                       'LEFT JOIN',
-                                                       [
-                                                               'actor_rev_user.actor_user != 0',
-                                                               'user_id = actor_rev_user.actor_user'
-                                                       ]
-                                               ],
-                                               'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                               'comment_rev_comment'
-                                                       => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                               'joins' => [
+                                       'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
+                                       'user' => [
+                                               'LEFT JOIN',
+                                               [
+                                                       'actor_rev_user.actor_user != 0',
+                                                       'user_id = actor_rev_user.actor_user'
+                                               ]
                                        ],
-                                       $this->getNewActorJoins( 'rev' )
-                               ),
+                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+                                       'comment_rev_comment'
+                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                               ],
                        ]
                ];
                yield 'MCR write-both/read-old' => [
@@ -354,7 +330,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage'
                                        => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
                        ],
                        [],
                        [
@@ -362,17 +337,21 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getContentHandlerQueryFields( 'rev' ),
-                                       $this->getOldActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
+                                       $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ]
                ];
@@ -381,7 +360,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage'
                                        => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
                        ],
                        [ 'page', 'user' ],
                        [
@@ -391,37 +369,38 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'user',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getContentHandlerQueryFields( 'rev' ),
                                        $this->getUserQueryFields(),
                                        $this->getPageQueryFields(),
-                                       $this->getOldActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
+                                       $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
-                               'joins' => array_merge(
-                                       [
-                                               'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
-                                               'user' => [
-                                                       'LEFT JOIN',
-                                                       [
-                                                               'rev_user != 0',
-                                                               'user_id = rev_user'
-                                                       ]
-                                               ],
-                                               'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
-                                               'comment_rev_comment'
-                                                       => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
-                                       ]
-                               ),
+                               'joins' => [
+                                       'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
+                                       'user' => [
+                                               'LEFT JOIN',
+                                               [
+                                                       'actor_rev_user.actor_user != 0',
+                                                       'user_id = actor_rev_user.actor_user',
+                                               ]
+                                       ],
+                                       'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+                                       'comment_rev_comment'
+                                               => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                               ],
                        ]
                ];
                yield 'pre-MCR' => [
                        [
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [],
                        [
@@ -429,17 +408,21 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getContentHandlerQueryFields( 'rev' ),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ]
                ];
@@ -447,7 +430,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => true,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [ 'page', 'user' ],
                        [
@@ -455,21 +437,28 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision', 'page', 'user',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getContentHandlerQueryFields( 'rev' ),
                                        $this->getPageQueryFields(),
                                        $this->getUserQueryFields(),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
                                        'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
-                                       'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
+                                       'user' => [ 'LEFT JOIN', [
+                                               'actor_rev_user.actor_user != 0',
+                                               'user_id = actor_rev_user.actor_user',
+                                       ] ],
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ]
                ];
@@ -477,7 +466,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => false,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [],
                        [
@@ -485,16 +473,20 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ],
                ];
@@ -502,7 +494,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => false,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [ 'page' ],
                        [
@@ -510,11 +501,13 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision', 'page',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getPageQueryFields(),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
@@ -522,6 +515,8 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ],
                ];
@@ -529,7 +524,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => false,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [ 'user' ],
                        [
@@ -537,18 +531,25 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision', 'user',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getUserQueryFields(),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
-                                       'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
+                                       'user' => [ 'LEFT JOIN', [
+                                               'actor_rev_user.actor_user != 0',
+                                               'user_id = actor_rev_user.actor_user',
+                                       ] ],
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ],
                ];
@@ -556,7 +557,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => false,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [ 'text' ],
                        [
@@ -564,11 +564,13 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision', 'text',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getTextQueryFields(),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
@@ -576,6 +578,8 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ],
                ];
@@ -583,7 +587,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                        [
                                'wgContentHandlerUseDB' => false,
                                'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
                        ],
                        [ 'text', 'page', 'user' ],
                        [
@@ -591,13 +594,15 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'revision', 'page', 'user', 'text',
                                        'temp_rev_comment' => 'revision_comment_temp',
                                        'comment_rev_comment' => 'comment',
+                                       'temp_rev_user' => 'revision_actor_temp',
+                                       'actor_rev_user' => 'actor',
                                ],
                                'fields' => array_merge(
                                        $this->getRevisionQueryFields( true ),
                                        $this->getPageQueryFields(),
                                        $this->getUserQueryFields(),
                                        $this->getTextQueryFields(),
-                                       $this->getOldActorQueryFields( 'rev' ),
+                                       $this->getNewActorQueryFields( 'rev', true ),
                                        $this->getNewCommentQueryFields( 'rev' )
                                ),
                                'joins' => [
@@ -608,8 +613,8 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'user' => [
                                                'LEFT JOIN',
                                                [
-                                                       'rev_user != 0',
-                                                       'user_id = rev_user',
+                                                       'actor_rev_user.actor_user != 0',
+                                                       'user_id = actor_rev_user.actor_user',
                                                ],
                                        ],
                                        'text' => [
@@ -619,6 +624,8 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                                        'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
                                        'comment_rev_comment'
                                                => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+                                       'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
                                ],
                        ],
                ];
@@ -848,190 +855,6 @@ class RevisionQueryInfoTest extends MediaWikiTestCase {
                ];
        }
 
-       public function provideSelectFields() {
-               yield 'with model, comment, and actor' => [
-                       [
-                               'wgContentHandlerUseDB' => true,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-                       ],
-                       'fields' => array_merge(
-                               [
-                                       'rev_id',
-                                       'rev_page',
-                                       'rev_text_id',
-                                       'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
-                                       'rev_actor' => 'NULL',
-                                       'rev_minor_edit',
-                                       'rev_deleted',
-                                       'rev_len',
-                                       'rev_parent_id',
-                                       'rev_sha1',
-                               ],
-                               $this->getContentHandlerQueryFields( 'rev' ),
-                               [
-                                       'rev_comment_pk' => 'rev_id',
-                               ]
-                       ),
-               ];
-               yield 'no mode, no comment, no actor' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       'fields' => array_merge(
-                               [
-                                       'rev_id',
-                                       'rev_page',
-                                       'rev_text_id',
-                                       'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
-                                       'rev_actor' => 'NULL',
-                                       'rev_minor_edit',
-                                       'rev_deleted',
-                                       'rev_len',
-                                       'rev_parent_id',
-                                       'rev_sha1',
-                                       'rev_comment_pk' => 'rev_id',
-                               ]
-                       ),
-               ];
-       }
-
-       public function provideSelectArchiveFields() {
-               yield 'with model, comment, and actor' => [
-                       [
-                               'wgContentHandlerUseDB' => true,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-                       ],
-                       'fields' => array_merge(
-                               [
-                                       'ar_id',
-                                       'ar_page_id',
-                                       'ar_rev_id',
-                                       'ar_text_id',
-                                       'ar_timestamp',
-                                       'ar_user_text',
-                                       'ar_user',
-                                       'ar_actor' => 'NULL',
-                                       'ar_minor_edit',
-                                       'ar_deleted',
-                                       'ar_len',
-                                       'ar_parent_id',
-                                       'ar_sha1',
-                               ],
-                               $this->getContentHandlerQueryFields( 'ar' ),
-                               [
-                                       'ar_comment_id' => 'ar_comment_id',
-                               ]
-                       ),
-               ];
-               yield 'no mode, no comment, no actor' => [
-                       [
-                               'wgContentHandlerUseDB' => false,
-                               'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
-                       ],
-                       'fields' => array_merge(
-                               [
-                                       'ar_id',
-                                       'ar_page_id',
-                                       'ar_rev_id',
-                                       'ar_text_id',
-                                       'ar_timestamp',
-                                       'ar_user_text',
-                                       'ar_user',
-                                       'ar_actor' => 'NULL',
-                                       'ar_minor_edit',
-                                       'ar_deleted',
-                                       'ar_len',
-                                       'ar_parent_id',
-                                       'ar_sha1',
-                                       'ar_comment_id' => 'ar_comment_id',
-                               ]
-                       ),
-               ];
-       }
-
-       /**
-        * @dataProvider provideSelectFields
-        * @covers Revision::selectFields
-        */
-       public function testRevisionSelectFields( $migrationStageSettings, $expected ) {
-               $this->setMwGlobals( $migrationStageSettings );
-
-               $this->hideDeprecated( 'Revision::selectFields' );
-               $this->assertArrayEqualsIgnoringIntKeyOrder( $expected, Revision::selectFields() );
-       }
-
-       /**
-        * @dataProvider provideSelectArchiveFields
-        * @covers Revision::selectArchiveFields
-        */
-       public function testRevisionSelectArchiveFields( $migrationStageSettings, $expected ) {
-               $this->setMwGlobals( $migrationStageSettings );
-
-               $this->hideDeprecated( 'Revision::selectArchiveFields' );
-               $this->assertArrayEqualsIgnoringIntKeyOrder( $expected, Revision::selectArchiveFields() );
-       }
-
-       /**
-        * @covers Revision::userJoinCond
-        */
-       public function testRevisionUserJoinCond() {
-               $this->hideDeprecated( 'Revision::userJoinCond' );
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_OLD );
-               $this->assertEquals(
-                       [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
-                       Revision::userJoinCond()
-               );
-       }
-
-       /**
-        * @covers Revision::pageJoinCond
-        */
-       public function testRevisionPageJoinCond() {
-               $this->hideDeprecated( 'Revision::pageJoinCond' );
-               $this->assertEquals(
-                       [ 'JOIN', [ 'page_id = rev_page' ] ],
-                       Revision::pageJoinCond()
-               );
-       }
-
-       /**
-        * @covers Revision::selectTextFields
-        */
-       public function testRevisionSelectTextFields() {
-               $this->hideDeprecated( 'Revision::selectTextFields' );
-               $this->assertEquals(
-                       $this->getTextQueryFields(),
-                       Revision::selectTextFields()
-               );
-       }
-
-       /**
-        * @covers Revision::selectPageFields
-        */
-       public function testRevisionSelectPageFields() {
-               $this->hideDeprecated( 'Revision::selectPageFields' );
-               $this->assertEquals(
-                       $this->getPageQueryFields(),
-                       Revision::selectPageFields()
-               );
-       }
-
-       /**
-        * @covers Revision::selectUserFields
-        */
-       public function testRevisionSelectUserFields() {
-               $this->hideDeprecated( 'Revision::selectUserFields' );
-               $this->assertEquals(
-                       $this->getUserQueryFields(),
-                       Revision::selectUserFields()
-               );
-       }
-
        /**
         * @covers Revision::getArchiveQueryInfo
         * @dataProvider provideArchiveQueryInfo
index b0b9ddf..76190e0 100644 (file)
@@ -81,7 +81,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->setMwGlobals( [
                        'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
                        'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
-                       'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                ] );
        }
 
@@ -1791,8 +1790,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         * @covers \MediaWiki\Revision\RevisionStore::getKnownCurrentRevision
         */
        public function testGetKnownCurrentRevision_userNameChange() {
-               global $wgActorTableSchemaMigrationStage;
-
                $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
                $this->setService( 'MainWANObjectCache', $cache );
 
@@ -1811,11 +1808,9 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->db->update( 'user',
                        [ 'user_name' => $newUserName ],
                        [ 'user_id' => $rev->getUser()->getId() ] );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $this->db->update( 'actor',
-                               [ 'actor_name' => $newUserName ],
-                               [ 'actor_user' => $rev->getUser()->getId() ] );
-               }
+               $this->db->update( 'actor',
+                       [ 'actor_name' => $newUserName ],
+                       [ 'actor_user' => $rev->getUser()->getId() ] );
 
                // Reload the revision and regrab the user name.
                $revAfter = $store->getKnownCurrentRevision( $page->getTitle(), $rev->getId() );
@@ -1864,8 +1859,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
         */
        public function testNewRevisionFromRow_userNameChange() {
-               global $wgActorTableSchemaMigrationStage;
-
                $page = $this->getTestPage();
                $text = __METHOD__;
                /** @var Revision $rev */
@@ -1895,11 +1888,9 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->db->update( 'user',
                        [ 'user_name' => $newUserName ],
                        [ 'user_id' => $record->getUser()->getId() ] );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $this->db->update( 'actor',
-                               [ 'actor_name' => $newUserName ],
-                               [ 'actor_user' => $record->getUser()->getId() ] );
-               }
+               $this->db->update( 'actor',
+                       [ 'actor_name' => $newUserName ],
+                       [ 'actor_user' => $record->getUser()->getId() ] );
 
                // Reload the record, passing $fromCache as true to force fresh info from the db,
                // and regrab the user name
index 7b334ee..d41c88f 100644 (file)
@@ -92,7 +92,6 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                $this->setMwGlobals( [
                        'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
                        'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
-                       'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                ] );
 
                if ( !$this->testPage ) {
index 865bd31..ed4a27f 100644 (file)
@@ -601,7 +601,6 @@ class RevisionTest extends MediaWikiTestCase {
         * @covers Revision::loadFromTitle
         */
        public function testLoadFromTitle() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
                $title = $this->getMockTitle();
 
                $conditions = [
index b01b90e..7747c70 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+use MediaWiki\MediaWikiServices;
+use Wikimedia\ObjectFactory;
+
 /**
  * @covers ApiModuleManager
  *
@@ -12,7 +15,8 @@ class ApiModuleManagerTest extends MediaWikiTestCase {
        private function getModuleManager() {
                $request = new FauxRequest();
                $main = new ApiMain( $request );
-               return new ApiModuleManager( $main );
+
+               return new ApiModuleManager( $main, MediaWikiServices::getInstance()->getObjectFactory() );
        }
 
        public function newApiLogin( $main, $action ) {
@@ -28,30 +32,55 @@ class ApiModuleManagerTest extends MediaWikiTestCase {
                                null,
                        ],
 
-                       'with factory' => [
+                       'with class and factory' => [
                                'login',
                                'action',
                                ApiLogin::class,
                                [ $this, 'newApiLogin' ],
                        ],
 
-                       'with closure' => [
-                               'logout',
+                       'with spec (class only)' => [
+                               'login',
                                'action',
-                               ApiLogout::class,
-                               function ( ApiMain $main, $action ) {
-                                       return new ApiLogout( $main, $action );
-                               },
+                               [
+                                       'class' => ApiLogin::class
+                               ],
+                               null,
+                       ],
+
+                       'with spec' => [
+                               'login',
+                               'action',
+                               [
+                                       'class' => ApiLogin::class,
+                                       'factory' => [ $this, 'newApiLogin' ],
+                               ],
+                               null,
                        ],
+
+                       'with spec (using services)' => [
+                               'logout',
+                               'action',
+                               [
+                                       'class' => ApiLogout::class,
+                                       'factory' => function ( ApiMain $main, $action, ObjectFactory $objectFactory ) {
+                                               return new ApiLogout( $main, $action );
+                                       },
+                                       'services' => [
+                                               'ObjectFactory'
+                                       ],
+                               ],
+                               null,
+                       ]
                ];
        }
 
        /**
         * @dataProvider addModuleProvider
         */
-       public function testAddModule( $name, $group, $class, $factory = null ) {
+       public function testAddModule( $name, $group, $spec, $factory ) {
                $moduleManager = $this->getModuleManager();
-               $moduleManager->addModule( $name, $group, $class, $factory );
+               $moduleManager->addModule( $name, $group, $spec, $factory );
 
                $this->assertTrue( $moduleManager->isDefined( $name, $group ), 'isDefined' );
                $this->assertNotNull( $moduleManager->getModule( $name, $group, true ), 'getModule' );
@@ -327,4 +356,22 @@ class ApiModuleManagerTest extends MediaWikiTestCase {
                        $moduleManager->getClassName( 'nonexistentmodule' )
                );
        }
+
+       /**
+        * @expectedException \InvalidArgumentException
+        * @expectedExceptionMessage $spec must define a class name
+        */
+       public function testAddModuleWithIncompleteSpec() {
+               $moduleManager = $this->getModuleManager();
+
+               $moduleManager->addModule(
+                       'logout',
+                       'action',
+                       [
+                               'factory' => function ( ApiMain $main, $action ) {
+                                       return new ApiLogout( $main, $action );
+                               },
+                       ]
+               );
+       }
 }
index 6bbdd3b..771e039 100644 (file)
@@ -44,14 +44,16 @@ class ApiQueryLanguageinfoTest extends ApiTestCase {
                                        $moduleManager->addModule(
                                                'languageinfo',
                                                'meta',
-                                               ApiQueryLanguageinfo::class,
-                                               function ( $parent, $name ) use ( $microtimeFunction ) {
-                                                       return new ApiQueryLanguageinfo(
-                                                               $parent,
-                                                               $name,
-                                                               $microtimeFunction
-                                                       );
-                                               }
+                                               [
+                                                       'class' => ApiQueryLanguageinfo::class,
+                                                       'factory' => function ( $parent, $name ) use ( $microtimeFunction ) {
+                                                               return new ApiQueryLanguageinfo(
+                                                                       $parent,
+                                                                       $name,
+                                                                       $microtimeFunction
+                                                               );
+                                                       }
+                                               ]
                                        );
                                }
                        );
index 27ec73a..789908c 100644 (file)
@@ -27,7 +27,6 @@ abstract class ApiFormatTestBase extends MediaWikiTestCase {
         *  - class: If set, register 'name' with this class (and 'factory', if that's set)
         *  - factory: Used with 'class' to register at runtime
         *  - returnPrinter: Return the printer object
-        * @param callable|null $factory Factory to use instead of the normal one
         * @return string|array The string if $options['returnPrinter'] isn't set, or an array if it is:
         *  - text: Output text string
         *  - printer: ApiFormatBase
@@ -44,8 +43,15 @@ abstract class ApiFormatTestBase extends MediaWikiTestCase {
                $context->setRequest( new FauxRequest( $params, true ) );
                $main = new ApiMain( $context );
                if ( isset( $options['class'] ) ) {
-                       $factory = $options['factory'] ?? null;
-                       $main->getModuleManager()->addModule( $printerName, 'format', $options['class'], $factory );
+                       $spec = [
+                               'class' => $options['class']
+                       ];
+
+                       if ( isset( $options['factory'] ) ) {
+                               $spec['factory'] = $options['factory'];
+                       }
+
+                       $main->getModuleManager()->addModule( $printerName, 'format', $spec );
                }
                $result = $main->getResult();
                $result->addArrayType( null, 'default' );
index 301ed10..df643d2 100644 (file)
@@ -8,15 +8,6 @@
  */
 class ApiQueryUserContribsTest extends ApiTestCase {
        public function addDBDataOnce() {
-               global $wgActorTableSchemaMigrationStage;
-
-               $reset = new \Wikimedia\ScopedCallback( function ( $v ) {
-                       $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $v );
-               }, [ $wgActorTableSchemaMigrationStage ] );
-               // Needs to WRITE_BOTH so READ_OLD tests below work. READ mode here doesn't really matter.
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage',
-                       SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW );
-
                $users = [
                        User::newFromName( '192.168.2.2', false ),
                        User::newFromName( '192.168.2.1', false ),
@@ -43,17 +34,14 @@ class ApiQueryUserContribsTest extends ApiTestCase {
 
        /**
         * @dataProvider provideSorting
-        * @param int $stage SCHEMA_COMPAT contants for $wgActorTableSchemaMigrationStage
         * @param array $params Extra parameters for the query
         * @param bool $reverse Reverse order?
         * @param int $revs Number of revisions to expect
         */
-       public function testSorting( $stage, $params, $reverse, $revs ) {
+       public function testSorting( $params, $reverse, $revs ) {
                // FIXME: fails under sqlite
                $this->markTestSkippedIfDbType( 'sqlite' );
 
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
-
                if ( isset( $params['ucuserids'] ) ) {
                        $params['ucuserids'] = implode( '|', array_map( 'User::idFromName', $params['ucuserids'] ) );
                }
@@ -116,38 +104,23 @@ class ApiQueryUserContribsTest extends ApiTestCase {
                $users2 = [ __CLASS__ . ' A', __CLASS__ . ' B', __CLASS__ . ' D' ];
                $ips = [ '192.168.2.1', '192.168.2.2', '192.168.2.3', '192.168.2.4' ];
 
-               foreach (
-                       [
-                               'old' => SCHEMA_COMPAT_OLD,
-                               'read old' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-                               'read new' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
-                               'new' => SCHEMA_COMPAT_NEW,
-                       ] as $stageName => $stage
-               ) {
-                       foreach ( [ false, true ] as $reverse ) {
-                               $name = $stageName . ( $reverse ? ', reverse' : '' );
-                               yield "Named users, $name" => [ $stage, [ 'ucuser' => $users ], $reverse, 9 ];
-                               yield "Named users including a no-edit user, $name" => [
-                                       $stage, [ 'ucuser' => $users2 ], $reverse, 6
-                               ];
-                               yield "IP users, $name" => [ $stage, [ 'ucuser' => $ips ], $reverse, 9 ];
-                               yield "All users, $name" => [
-                                       $stage, [ 'ucuser' => array_merge( $users, $ips ) ], $reverse, 18
-                               ];
-                               yield "User IDs, $name" => [ $stage, [ 'ucuserids' => $users ], $reverse, 9 ];
-                               yield "Users by prefix, $name" => [ $stage, [ 'ucuserprefix' => __CLASS__ ], $reverse, 9 ];
-                               yield "IPs by prefix, $name" => [ $stage, [ 'ucuserprefix' => '192.168.2.' ], $reverse, 9 ];
-                       }
+               foreach ( [ false, true ] as $reverse ) {
+                       $name = ( $reverse ? ', reverse' : '' );
+                       yield "Named users, $name" => [ [ 'ucuser' => $users ], $reverse, 9 ];
+                       yield "Named users including a no-edit user, $name" => [
+                               [ 'ucuser' => $users2 ], $reverse, 6
+                       ];
+                       yield "IP users, $name" => [ [ 'ucuser' => $ips ], $reverse, 9 ];
+                       yield "All users, $name" => [
+                               [ 'ucuser' => array_merge( $users, $ips ) ], $reverse, 18
+                       ];
+                       yield "User IDs, $name" => [ [ 'ucuserids' => $users ], $reverse, 9 ];
+                       yield "Users by prefix, $name" => [ [ 'ucuserprefix' => __CLASS__ ], $reverse, 9 ];
+                       yield "IPs by prefix, $name" => [ [ 'ucuserprefix' => '192.168.2.' ], $reverse, 9 ];
                }
        }
 
-       /**
-        * @dataProvider provideInterwikiUser
-        * @param int $stage SCHEMA_COMPAT constants for $wgActorTableSchemaMigrationStage
-        */
-       public function testInterwikiUser( $stage ) {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
-
+       public function testInterwikiUser() {
                $params = [
                        'action' => 'query',
                        'list' => 'usercontribs',
@@ -174,13 +147,4 @@ class ApiQueryUserContribsTest extends ApiTestCase {
                $this->assertSame( $sorted, $ids, "IDs are sorted" );
        }
 
-       public static function provideInterwikiUser() {
-               return [
-                       'old' => [ SCHEMA_COMPAT_OLD ],
-                       'read old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD ],
-                       'read new' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW ],
-                       'new' => [ SCHEMA_COMPAT_NEW ],
-               ];
-       }
-
 }
index 062087d..3b30d7e 100644 (file)
@@ -942,8 +942,7 @@ class FileBackendTest extends MediaWikiTestCase {
                        "$base/unittest-cont1/e/fileB.a",
                        "$base/unittest-cont1/e/fileC.a"
                ];
-               $createOps = [];
-               $purgeOps = [];
+               $createOps = $copyOps = $moveOps = $deleteOps = [];
                foreach ( $files as $path ) {
                        $status = $this->prepare( [ 'dir' => dirname( $path ) ] );
                        $this->assertGoodStatus( $status,
@@ -951,10 +950,21 @@ class FileBackendTest extends MediaWikiTestCase {
                        $createOps[] = [ 'op' => 'create', 'dst' => $path, 'content' => mt_rand( 0, 50000 ) ];
                        $copyOps[] = [ 'op' => 'copy', 'src' => $path, 'dst' => "$path-2" ];
                        $moveOps[] = [ 'op' => 'move', 'src' => "$path-2", 'dst' => "$path-3" ];
-                       $purgeOps[] = [ 'op' => 'delete', 'src' => $path ];
-                       $purgeOps[] = [ 'op' => 'delete', 'src' => "$path-3" ];
+                       $moveOps[] = [
+                               'op' => 'move',
+                               'src' => "$path-nothing",
+                               'dst' => "$path-nowhere",
+                               'ignoreMissingSource' => true
+                       ];
+                       $deleteOps[] = [ 'op' => 'delete', 'src' => $path ];
+                       $deleteOps[] = [ 'op' => 'delete', 'src' => "$path-3" ];
+                       $deleteOps[] = [
+                               'op' => 'delete',
+                               'src' => "$path-gone",
+                               'ignoreMissingSource' => true
+                       ];
                }
-               $purgeOps[] = [ 'op' => 'null' ];
+               $deleteOps[] = [ 'op' => 'null' ];
 
                $this->assertGoodStatus(
                        $this->backend->doQuickOperations( $createOps ),
@@ -995,7 +1005,7 @@ class FileBackendTest extends MediaWikiTestCase {
                        "File {$files[0]} still exists." );
 
                $this->assertGoodStatus(
-                       $this->backend->doQuickOperations( $purgeOps ),
+                       $this->backend->doQuickOperations( $deleteOps ),
                        "Quick deletion of source files succeeded ($backendName)." );
                foreach ( $files as $file ) {
                        $this->assertFalse( $this->backend->fileExists( [ 'src' => $file ] ),
index b183cde..a7040e0 100644 (file)
@@ -29,18 +29,12 @@ class DatabaseLogEntryTest extends MediaWikiTestCase {
         * @param array $selectFields
         * @param string[]|null $row
         * @param string[]|null $expectedFields
-        * @param int $actorMigration
         */
        public function testNewFromId( $id,
                array $selectFields,
                array $row = null,
-               array $expectedFields = null,
-               $actorMigration
+               array $expectedFields = null
        ) {
-               $this->setMwGlobals( [
-                       'wgActorTableSchemaMigrationStage' => $actorMigration,
-               ] );
-
                $row = $row ? (object)$row : null;
                $db = $this->getMock( IDatabase::class );
                $db->expects( self::once() )
@@ -67,36 +61,6 @@ class DatabaseLogEntryTest extends MediaWikiTestCase {
        }
 
        public function provideNewFromId() {
-               $oldTables = [
-                       'tables' => [
-                               'logging', 'user',
-                               'comment_log_comment' => 'comment',
-                       ],
-                       'fields' => [
-                               'log_id',
-                               'log_type',
-                               'log_action',
-                               'log_timestamp',
-                               'log_namespace',
-                               'log_title',
-                               'log_params',
-                               'log_deleted',
-                               'user_id',
-                               'user_name',
-                               'user_editcount',
-                               'log_comment_text' => 'comment_log_comment.comment_text',
-                               'log_comment_data' => 'comment_log_comment.comment_data',
-                               'log_comment_cid' => 'comment_log_comment.comment_id',
-                               'log_user' => 'log_user',
-                               'log_user_text' => 'log_user_text',
-                               'log_actor' => 'NULL',
-                       ],
-                       'options' => [],
-                       'join_conds' => [
-                               'user' => [ 'LEFT JOIN', 'user_id=log_user' ],
-                               'comment_log_comment' => [ 'JOIN', 'comment_log_comment.comment_id = log_comment_id' ],
-                       ],
-               ];
                $newTables = [
                        'tables' => [
                                'logging',
@@ -133,22 +97,20 @@ class DatabaseLogEntryTest extends MediaWikiTestCase {
                return [
                        [
                                0,
-                               $oldTables + [ 'conds' => [ 'log_id' => 0 ] ],
-                               null,
+                               $newTables + [ 'conds' => [ 'log_id' => 0 ] ],
                                null,
-                               SCHEMA_COMPAT_OLD,
+                               null
                        ],
                        [
                                123,
-                               $oldTables + [ 'conds' => [ 'log_id' => 123 ] ],
+                               $newTables + [ 'conds' => [ 'log_id' => 123 ] ],
                                [
                                        'log_id' => 123,
                                        'log_type' => 'foobarize',
                                        'log_comment_text' => 'test!',
                                        'log_comment_data' => null,
                                ],
-                               [ 'type' => 'foobarize', 'comment' => 'test!' ],
-                               SCHEMA_COMPAT_OLD,
+                               [ 'type' => 'foobarize', 'comment' => 'test!' ]
                        ],
                        [
                                567,
@@ -159,8 +121,7 @@ class DatabaseLogEntryTest extends MediaWikiTestCase {
                                        'log_comment_text' => 'test!',
                                        'log_comment_data' => null,
                                ],
-                               [ 'type' => 'foobarize', 'comment' => 'test!' ],
-                               SCHEMA_COMPAT_NEW,
+                               [ 'type' => 'foobarize', 'comment' => 'test!' ]
                        ],
                ];
        }
index bcbc1ed..90b118c 100644 (file)
@@ -84,7 +84,6 @@ abstract class PageArchiveTestBase extends MediaWikiTestCase {
                $this->tablesUsed += $this->getMcrTablesToReset();
 
                $this->setMwGlobals( [
-                       'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                        'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
                        'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
                ] );
index 68433c6..ff3e714 100644 (file)
@@ -224,8 +224,6 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testRcHidemyselfFilter() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
                $user = $this->getTestUser()->getUser();
                $user->getActorId( wfGetDB( DB_MASTER ) );
                $this->assertConditions(
@@ -253,41 +251,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testRcHidemyselfFilter_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $user = $this->getTestUser()->getUser();
-               $user->getActorId( wfGetDB( DB_MASTER ) );
-               $this->assertConditions(
-                       [ # expected
-                               "NOT((rc_user = '{$user->getId()}'))",
-                       ],
-                       [
-                               'hidemyself' => 1,
-                       ],
-                       "rc conditions: hidemyself=1 (logged in)",
-                       $user
-               );
-
-               $user = User::newFromName( '10.11.12.13', false );
-               $id = $user->getActorId( wfGetDB( DB_MASTER ) );
-               $this->assertConditions(
-                       [ # expected
-                               "NOT((rc_user_text = '10.11.12.13'))",
-                       ],
-                       [
-                               'hidemyself' => 1,
-                       ],
-                       "rc conditions: hidemyself=1 (anon)",
-                       $user
-               );
-       }
-
        public function testRcHidebyothersFilter() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
                $user = $this->getTestUser()->getUser();
                $user->getActorId( wfGetDB( DB_MASTER ) );
                $this->assertConditions(
@@ -315,38 +279,6 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testRcHidebyothersFilter_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $user = $this->getTestUser()->getUser();
-               $user->getActorId( wfGetDB( DB_MASTER ) );
-               $this->assertConditions(
-                       [ # expected
-                               "(rc_user_text = '{$user->getName()}')",
-                       ],
-                       [
-                               'hidebyothers' => 1,
-                       ],
-                       "rc conditions: hidebyothers=1 (logged in)",
-                       $user
-               );
-
-               $user = User::newFromName( '10.11.12.13', false );
-               $id = $user->getActorId( wfGetDB( DB_MASTER ) );
-               $this->assertConditions(
-                       [ # expected
-                               "(rc_user_text = '10.11.12.13')",
-                       ],
-                       [
-                               'hidebyothers' => 1,
-                       ],
-                       "rc conditions: hidebyothers=1 (anon)",
-                       $user
-               );
-       }
-
        public function testRcHidepageedits() {
                $this->assertConditions(
                        [ # expected
@@ -550,8 +482,6 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testFilterUserExpLevelAllExperienceLevels() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
                $this->assertConditions(
                        [
                                # expected
@@ -564,26 +494,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testFilterUserExpLevelAllExperienceLevels_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $this->assertConditions(
-                       [
-                               # expected
-                               'rc_user != 0',
-                       ],
-                       [
-                               'userExpLevel' => 'newcomer;learner;experienced',
-                       ],
-                       "rc conditions: userExpLevel=newcomer;learner;experienced"
-               );
-       }
-
-       public function testFilterUserExpLevelRegistrered() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+       public function testFilterUserExpLevelRegistered() {
                $this->assertConditions(
                        [
                                # expected
@@ -596,26 +507,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testFilterUserExpLevelRegistrered_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $this->assertConditions(
-                       [
-                               # expected
-                               'rc_user != 0',
-                       ],
-                       [
-                               'userExpLevel' => 'registered',
-                       ],
-                       "rc conditions: userExpLevel=registered"
-               );
-       }
-
-       public function testFilterUserExpLevelUnregistrered() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+       public function testFilterUserExpLevelUnregistered() {
                $this->assertConditions(
                        [
                                # expected
@@ -628,26 +520,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testFilterUserExpLevelUnregistrered_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $this->assertConditions(
-                       [
-                               # expected
-                               'rc_user = 0',
-                       ],
-                       [
-                               'userExpLevel' => 'unregistered',
-                       ],
-                       "rc conditions: userExpLevel=unregistered"
-               );
-       }
-
-       public function testFilterUserExpLevelRegistreredOrLearner() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+       public function testFilterUserExpLevelRegisteredOrLearner() {
                $this->assertConditions(
                        [
                                # expected
@@ -660,26 +533,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testFilterUserExpLevelRegistreredOrLearner_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $this->assertConditions(
-                       [
-                               # expected
-                               'rc_user != 0',
-                       ],
-                       [
-                               'userExpLevel' => 'registered;learner',
-                       ],
-                       "rc conditions: userExpLevel=registered;learner"
-               );
-       }
-
-       public function testFilterUserExpLevelUnregistreredOrExperienced() {
-               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+       public function testFilterUserExpLevelUnregisteredOrExperienced() {
                $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
 
                $this->assertRegExp(
@@ -690,21 +544,6 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
-       public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
-               $this->setMwGlobals(
-                       'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
-               );
-
-               $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
-
-               $this->assertRegExp(
-                       '/\(rc_user = 0\) OR '
-                               . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
-                       reset( $conds ),
-                       "rc conditions: userExpLevel=unregistered;experienced"
-               );
-       }
-
        public function testFilterUserExpLevel() {
                $now = time();
                $this->setMwGlobals( [
index c0376ad..a5d5946 100644 (file)
@@ -60,6 +60,10 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
                        'callback array' => [
                                [ 'SpecialPageTestHelper', 'newSpecialAllPages' ],
                                false
+                       ],
+                       'object factory spec' => [
+                               [ 'class' => SpecialAllPages::class ],
+                               false
                        ]
                ];
        }
@@ -264,4 +268,29 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
                $this->assertTrue( $called, 'Recursive call succeeded' );
        }
 
+       /**
+        * @covers \MediaWiki\Special\SpecialPageFactory::getPage
+        */
+       public function testSpecialPageCreationThatRequiresService() {
+               $type = null;
+
+               $this->setMwGlobals( 'wgSpecialPages',
+                       [ 'TestPage' => [
+                               'factory' => function ( $spf ) use ( &$type ) {
+                                       $type = get_class( $spf );
+
+                                       return new class() extends SpecialPage {
+
+                                       };
+                               },
+                               'services' => [
+                                       'SpecialPageFactory'
+                               ]
+                       ] ]
+               );
+
+               SpecialPageFactory::getPage( 'TestPage' );
+
+               $this->assertEquals( \MediaWiki\Special\SpecialPageFactory::class, $type );
+       }
 }
index 2d87c78..3005ae9 100644 (file)
@@ -31,7 +31,6 @@ class UserTest extends MediaWikiTestCase {
                $this->setMwGlobals( [
                        'wgGroupPermissions' => [],
                        'wgRevokePermissions' => [],
-                       'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                ] );
 
                $this->setUpPermissionGlobals();
@@ -1075,83 +1074,6 @@ class UserTest extends MediaWikiTestCase {
                        'User::newFromActorId works for an anonymous user' );
        }
 
-       /**
-        * Actor tests with SCHEMA_COMPAT_READ_OLD
-        *
-        * The only thing different from testActorId() is the behavior if the actor
-        * row doesn't exist in the DB, since with SCHEMA_COMPAT_READ_NEW that
-        * situation can't happen. But we copy all the other tests too just for good measure.
-        *
-        * @covers User::newFromActorId
-        */
-       public function testActorId_old() {
-               $this->setMwGlobals( [
-                       'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
-               ] );
-
-               $domain = MediaWikiServices::getInstance()->getDBLoadBalancer()->getLocalDomainID();
-               $this->hideDeprecated( 'User::selectFields' );
-
-               // Newly-created user has an actor ID
-               $user = User::createNew( 'UserTestActorIdOld1' );
-               $id = $user->getId();
-               $this->assertTrue( $user->getActorId() > 0, 'User::createNew sets an actor ID' );
-
-               $user = User::newFromName( 'UserTestActorIdOld2' );
-               $user->addToDatabase();
-               $this->assertTrue( $user->getActorId() > 0, 'User::addToDatabase sets an actor ID' );
-
-               $user = User::newFromName( 'UserTestActorIdOld1' );
-               $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by name' );
-
-               $user = User::newFromId( $id );
-               $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
-
-               $user2 = User::newFromActorId( $user->getActorId() );
-               $this->assertEquals( $user->getId(), $user2->getId(),
-                       'User::newFromActorId works for an existing user' );
-
-               $row = $this->db->selectRow( 'user', User::selectFields(), [ 'user_id' => $id ], __METHOD__ );
-               $user = User::newFromRow( $row );
-               $this->assertTrue( $user->getActorId() > 0,
-                       'Actor ID can be retrieved for user loaded with User::selectFields()' );
-
-               $this->db->delete( 'actor', [ 'actor_user' => $id ], __METHOD__ );
-               User::purge( $domain, $id );
-               // Because WANObjectCache->delete() stupidly doesn't delete from the process cache.
-
-               MediaWikiServices::getInstance()->getMainWANObjectCache()->clearProcessCache();
-
-               $user = User::newFromId( $id );
-               $this->assertFalse( $user->getActorId() > 0, 'No Actor ID by default if none in database' );
-               $this->assertTrue( $user->getActorId( $this->db ) > 0, 'Actor ID can be created if none in db' );
-
-               $user->setName( 'UserTestActorIdOld4-renamed' );
-               $user->saveSettings();
-               $this->assertEquals(
-                       $user->getName(),
-                       $this->db->selectField(
-                               'actor', 'actor_name', [ 'actor_id' => $user->getActorId() ], __METHOD__
-                       ),
-                       'User::saveSettings updates actor table for name change'
-               );
-
-               // For sanity
-               $ip = '192.168.12.34';
-               $this->db->delete( 'actor', [ 'actor_name' => $ip ], __METHOD__ );
-
-               $user = User::newFromName( $ip, false );
-               $this->assertFalse( $user->getActorId() > 0, 'Anonymous user has no actor ID by default' );
-               $this->assertTrue( $user->getActorId( $this->db ) > 0,
-                       'Actor ID can be created for an anonymous user' );
-
-               $user = User::newFromName( $ip, false );
-               $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be loaded for an anonymous user' );
-               $user2 = User::newFromActorId( $user->getActorId() );
-               $this->assertEquals( $user->getName(), $user2->getName(),
-                       'User::newFromActorId works for an anonymous user' );
-       }
-
        /**
         * @covers User::newFromAnyId
         */
index b8d1383..cd0d7a5 100644 (file)
@@ -37,7 +37,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'patrol',
-                       'log_user' => 7251,
+                       'log_actor' => 7251,
                        'log_params' => '',
                        'log_timestamp' => $dbw->timestamp( '20041223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -49,7 +49,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'autopatrol',
-                       'log_user' => 7252,
+                       'log_actor' => 7252,
                        'log_params' => '',
                        'log_timestamp' => $dbw->timestamp( '20051223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -61,7 +61,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'block',
                        'log_action' => 'block',
-                       'log_user' => 7253,
+                       'log_actor' => 7253,
                        'log_params' => '',
                        'log_timestamp' => $dbw->timestamp( '20061223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -73,7 +73,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'patrol',
-                       'log_user' => 7253,
+                       'log_actor' => 7253,
                        'log_params' => 'nanana',
                        'log_timestamp' => $dbw->timestamp( '20061223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -85,7 +85,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'autopatrol',
-                       'log_user' => 7254,
+                       'log_actor' => 7254,
                        'log_params' => '',
                        'log_timestamp' => $dbw->timestamp( '20071223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -97,7 +97,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'patrol',
-                       'log_user' => 7255,
+                       'log_actor' => 7255,
                        'log_params' => serialize( [ '6::auto' => true ] ),
                        'log_timestamp' => $dbw->timestamp( '20081223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -109,7 +109,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'patrol',
-                       'log_user' => 7256,
+                       'log_actor' => 7256,
                        'log_params' => serialize( [ '6::auto' => false ] ),
                        'log_timestamp' => $dbw->timestamp( '20091223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -121,7 +121,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'patrol',
-                       'log_user' => 7257,
+                       'log_actor' => 7257,
                        'log_params' => "9227851\n0\n1",
                        'log_timestamp' => $dbw->timestamp( '20081223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -133,7 +133,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $logs[] = [
                        'log_type' => 'patrol',
                        'log_action' => 'patrol',
-                       'log_user' => 7258,
+                       'log_actor' => 7258,
                        'log_params' => "9227851\n0\n0",
                        'log_timestamp' => $dbw->timestamp( '20091223210426' ),
                        'log_namespace' => NS_MAIN,
@@ -149,47 +149,47 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'patrol',
-                               'log_user' => '7251',
+                               'log_actor' => '7251',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'autopatrol',
-                               'log_user' => '7252',
+                               'log_actor' => '7252',
                        ],
                        (object)[
                                'log_type' => 'block',
                                'log_action' => 'block',
-                               'log_user' => '7253',
+                               'log_actor' => '7253',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'patrol',
-                               'log_user' => '7253',
+                               'log_actor' => '7253',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'autopatrol',
-                               'log_user' => '7254',
+                               'log_actor' => '7254',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'patrol',
-                               'log_user' => '7255',
+                               'log_actor' => '7255',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'patrol',
-                               'log_user' => '7256',
+                               'log_actor' => '7256',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'patrol',
-                               'log_user' => '7257',
+                               'log_actor' => '7257',
                        ],
                        (object)[
                                'log_type' => 'patrol',
                                'log_action' => 'patrol',
-                               'log_user' => '7258',
+                               'log_actor' => '7258',
                        ],
                ];
 
@@ -266,7 +266,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
 
                $remainingLogs = wfGetDB( DB_REPLICA )->select(
                        [ 'logging' ],
-                       [ 'log_type', 'log_action', 'log_user' ],
+                       [ 'log_type', 'log_action', 'log_actor' ],
                        [],
                        __METHOD__,
                        [ 'ORDER BY' => 'log_id' ]
@@ -288,7 +288,7 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
 
                $remainingLogs = wfGetDB( DB_REPLICA )->select(
                        [ 'logging' ],
-                       [ 'log_type', 'log_action', 'log_user' ],
+                       [ 'log_type', 'log_action', 'log_actor' ],
                        [],
                        __METHOD__,
                        [ 'ORDER BY' => 'log_id' ]
@@ -297,12 +297,12 @@ class DeleteAutoPatrolLogsTest extends MaintenanceBaseTestCase {
                $deleted = [
                        'log_type' => 'patrol',
                        'log_action' => 'autopatrol',
-                       'log_user' => '7254',
+                       'log_actor' => '7254',
                ];
                $notDeleted = [
                        'log_type' => 'patrol',
                        'log_action' => 'autopatrol',
-                       'log_user' => '7252',
+                       'log_actor' => '7252',
                ];
 
                $remainingLogs = array_map(
index 76f4f82..cab6c3b 100644 (file)
@@ -60,7 +60,6 @@ return [
                        'tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js',
-                       'tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.String.byteLength.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.String.trimByteLength.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js',
@@ -115,7 +114,6 @@ return [
                        'mediawiki.ForeignApi.core',
                        'mediawiki.jqueryMsg',
                        'mediawiki.messagePoster',
-                       'mediawiki.RegExp',
                        'mediawiki.String',
                        'mediawiki.storage',
                        'mediawiki.Title',
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js
deleted file mode 100644 (file)
index cde77e7..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-( function () {
-       QUnit.module( 'mediawiki.RegExp' );
-
-       QUnit.test( 'escape', function ( assert ) {
-               var specials, normal;
-
-               specials = [
-                       '\\',
-                       '{',
-                       '}',
-                       '(',
-                       ')',
-                       '[',
-                       ']',
-                       '|',
-                       '.',
-                       '?',
-                       '*',
-                       '+',
-                       '-',
-                       '^',
-                       '$'
-               ];
-
-               normal = [
-                       'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
-                       'abcdefghijklmnopqrstuvwxyz',
-                       '0123456789'
-               ].join( '' );
-
-               specials.forEach( function ( str ) {
-                       assert.propEqual( str.match( new RegExp( mw.RegExp.escape( str ) ) ), [ str ], 'Match ' + str );
-               } );
-
-               assert.strictEqual( mw.RegExp.escape( normal ), normal, 'Alphanumerals are left alone' );
-       } );
-
-}() );
index 17672db..4f61abd 100644 (file)
                        assert.strictEqual( util.isIPv6Address( ipCase[ 1 ] ), ipCase[ 0 ], ipCase[ 2 ] );
                } );
        } );
+
+       QUnit.test( 'escapeRegExp', function ( assert ) {
+               var specials, normal;
+
+               specials = [
+                       '\\',
+                       '{',
+                       '}',
+                       '(',
+                       ')',
+                       '[',
+                       ']',
+                       '|',
+                       '.',
+                       '?',
+                       '*',
+                       '+',
+                       '-',
+                       '^',
+                       '$'
+               ];
+
+               normal = [
+                       'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+                       'abcdefghijklmnopqrstuvwxyz',
+                       '0123456789'
+               ].join( '' );
+
+               specials.forEach( function ( str ) {
+                       assert.propEqual( str.match( new RegExp( mw.util.escapeRegExp( str ) ) ), [ str ], 'Match ' + str );
+               } );
+
+               assert.strictEqual( mw.util.escapeRegExp( normal ), normal, 'Alphanumerals are left alone' );
+       } );
 }() );
index f425d87..5be1ed0 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -25,6 +25,7 @@ use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
 
 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
+define( 'MW_ENTRY_POINT', 'thumb' );
 require __DIR__ . '/includes/WebStart.php';
 
 // Don't use fancy MIME detection, just check the file extension for jpg/gif/png
index e2b165b..4e6bbd9 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 define( 'THUMB_HANDLER', true );
+define( 'MW_ENTRY_POINT', 'thumb_handler' );
 
 // Execute thumb.php, having set THUMB_HANDLER so that
 // it knows to extract params from a thumbnail file URL.