Merge "Make phpunit.php less hackish, and install the listener unconditionally"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 5 Sep 2018 04:32:49 +0000 (04:32 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 5 Sep 2018 04:32:49 +0000 (04:32 +0000)
57 files changed:
.eslintrc.json
RELEASE-NOTES-1.32
composer.json
includes/MediaWikiServices.php
includes/ServiceWiring.php
includes/Storage/NameTableStoreFactory.php [new file with mode: 0644]
includes/Storage/RevisionStoreFactory.php
includes/TemplateParser.php
includes/api/i18n/zh-hant.json
includes/changetags/ChangeTags.php
includes/htmlform/fields/HTMLCheckMatrix.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/specials/SpecialJavaScriptTest.php
includes/widget/CheckMatrixWidget.php
languages/i18n/af.json
languages/i18n/be-tarask.json
languages/i18n/bs.json
languages/i18n/cdo.json
languages/i18n/el.json
languages/i18n/gl.json
languages/i18n/hr.json
languages/i18n/id.json
languages/i18n/mk.json
languages/i18n/mr.json
languages/i18n/my.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/ro.json
languages/i18n/sd.json
languages/i18n/sh.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/vi.json
languages/i18n/wa.json
languages/i18n/wuu.json
languages/i18n/zh-hans.json
maintenance/resources/foreign-resources.yaml
resources/Resources.php
resources/lib/jquery/jquery.js
resources/src/mediawiki.base/mediawiki.base.js
resources/src/mediawiki.base/mediawiki.errorLogger.js
resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js
resources/src/startup/mediawiki.js
resources/src/startup/mediawiki.log.js
resources/src/startup/mediawiki.requestIdleCallback.js
resources/src/startup/profiler.js
resources/src/startup/startup.js
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/data/templates/parentvars.mustache [new file with mode: 0644]
tests/phpunit/includes/Storage/NameTableStoreFactoryTest.php [new file with mode: 0644]
tests/phpunit/includes/Storage/RevisionStoreFactoryTest.php
tests/phpunit/includes/Storage/RevisionStoreTest.php
tests/phpunit/includes/TemplateParserTest.php
tests/phpunit/includes/changetags/ChangeTagsTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js

index fbf2a5a..f7a79ac 100644 (file)
@@ -1,13 +1,15 @@
 {
        "extends": "wikimedia",
        "env": {
-               "browser": true,
-               "jquery": true
+               "browser": true
        },
        "globals": {
                "require": false,
                "module": false,
+               "mw": false,
+               "$": false,
                "mediaWiki": false,
+               "jQuery": false,
                "OO": false
        },
        "rules": {
index 34dbfd9..0dca8f1 100644 (file)
@@ -106,6 +106,7 @@ production.
 * Updated jquery.i18n from 1.0.4 to 1.0.5.
 * Updated wikimedia/timestamp from 1.0.0 to 2.0.0.
 * Updated wikimedia/remex-html from 1.0.3 to 2.0.0.
+* Updated jquery from v3.2.1 to v3.3.1.
 
 ==== Removed external libraries ====
 * …
index 4284260..0994259 100644 (file)
@@ -48,7 +48,7 @@
                "wikimedia/running-stat": "1.2.1",
                "wikimedia/scoped-callback": "2.0.0",
                "wikimedia/utfnormal": "2.0.0",
-               "wikimedia/timestamp": "2.0.0",
+               "wikimedia/timestamp": "2.1.0",
                "wikimedia/wait-condition-loop": "1.0.1",
                "wikimedia/wrappedstring": "3.0.1",
                "wikimedia/xmp-reader": "0.6.0",
index 5b53ad1..b236ca1 100644 (file)
@@ -21,6 +21,7 @@ use MediaWiki\Special\SpecialPageFactory;
 use MediaWiki\Storage\BlobStore;
 use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\NameTableStore;
+use MediaWiki\Storage\NameTableStoreFactory;
 use MediaWiki\Storage\RevisionFactory;
 use MediaWiki\Storage\RevisionLookup;
 use MediaWiki\Storage\RevisionStore;
@@ -452,7 +453,7 @@ class MediaWikiServices extends ServiceContainer {
         * @return NameTableStore
         */
        public function getChangeTagDefStore() {
-               return $this->getService( 'ChangeTagDefStore' );
+               return $this->getService( 'NameTableStoreFactory' )->getChangeTagDef();
        }
 
        /**
@@ -500,7 +501,7 @@ class MediaWikiServices extends ServiceContainer {
         * @return NameTableStore
         */
        public function getContentModelStore() {
-               return $this->getService( 'ContentModelStore' );
+               return $this->getService( 'NameTableStoreFactory' )->getContentModels();
        }
 
        /**
@@ -664,6 +665,13 @@ class MediaWikiServices extends ServiceContainer {
 
        /**
         * @since 1.32
+        * @return NameTableStoreFactory
+        */
+       public function getNameTableStoreFactory() {
+               return $this->getService( 'NameTableStoreFactory' );
+       }
+
+       /**
         * @return OldRevisionImporter
         */
        public function getOldRevisionImporter() {
@@ -836,7 +844,7 @@ class MediaWikiServices extends ServiceContainer {
         * @return NameTableStore
         */
        public function getSlotRoleStore() {
-               return $this->getService( 'SlotRoleStore' );
+               return $this->getService( 'NameTableStoreFactory' )->getSlotRoles();
        }
 
        /**
index 59cdec9..b8bd5d2 100644 (file)
@@ -53,7 +53,7 @@ use MediaWiki\Special\SpecialPageFactory;
 use MediaWiki\Storage\BlobStore;
 use MediaWiki\Revision\RevisionRenderer;
 use MediaWiki\Storage\BlobStoreFactory;
-use MediaWiki\Storage\NameTableStore;
+use MediaWiki\Storage\NameTableStoreFactory;
 use MediaWiki\Storage\RevisionFactory;
 use MediaWiki\Storage\RevisionLookup;
 use MediaWiki\Storage\RevisionStore;
@@ -80,24 +80,6 @@ return [
                );
        },
 
-       'ChangeTagDefStore' => function ( MediaWikiServices $services ) : NameTableStore {
-               return new NameTableStore(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache(),
-                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
-                       'change_tag_def',
-                       'ctd_id',
-                       'ctd_name',
-                       null,
-                       false,
-                       function ( $insertFields ) {
-                               $insertFields['ctd_user_defined'] = 0;
-                               $insertFields['ctd_count'] = 0;
-                               return $insertFields;
-                       }
-               );
-       },
-
        'CommentStore' => function ( MediaWikiServices $services ) : CommentStore {
                return new CommentStore(
                        $services->getContentLanguage(),
@@ -128,23 +110,6 @@ return [
                return Language::factory( $services->getMainConfig()->get( 'LanguageCode' ) );
        },
 
-       'ContentModelStore' => function ( MediaWikiServices $services ) : NameTableStore {
-               return new NameTableStore(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache(),
-                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
-                       'content_models',
-                       'model_id',
-                       'model_name'
-                       /**
-                        * No strtolower normalization is added to the service as there are examples of
-                        * extensions that do not stick to this assumption.
-                        * - extensions/examples/DataPages define( 'CONTENT_MODEL_XML_DATA','XML_DATA' );
-                        * - extensions/Scribunto define( 'CONTENT_MODEL_SCRIBUNTO', 'Scribunto' );
-                        */
-               );
-       },
-
        'CryptHKDF' => function ( MediaWikiServices $services ) : CryptHKDF {
                $config = $services->getMainConfig();
 
@@ -360,6 +325,14 @@ return [
                return new MimeMagic( $params );
        },
 
+       'NameTableStoreFactory' => function ( MediaWikiServices $services ) : NameTableStoreFactory {
+               return new NameTableStoreFactory(
+                       $services->getDBLoadBalancerFactory(),
+                       $services->getMainWANObjectCache(),
+                       LoggerFactory::getInstance( 'NameTableSqlStore' )
+               );
+       },
+
        'OldRevisionImporter' => function ( MediaWikiServices $services ) : OldRevisionImporter {
                return new ImportableOldRevisionImporter(
                        true,
@@ -459,6 +432,7 @@ return [
                $store = new RevisionStoreFactory(
                        $services->getDBLoadBalancerFactory(),
                        $services->getBlobStoreFactory(),
+                       $services->getNameTableStoreFactory(),
                        $services->getMainWANObjectCache(),
                        $services->getCommentStore(),
                        $services->getActorMigration(),
@@ -542,18 +516,6 @@ return [
                return $factory;
        },
 
-       'SlotRoleStore' => function ( MediaWikiServices $services ) : NameTableStore {
-               return new NameTableStore(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache(),
-                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
-                       'slot_roles',
-                       'role_id',
-                       'role_name',
-                       'strtolower'
-               );
-       },
-
        'SpecialPageFactory' => function ( MediaWikiServices $services ) : SpecialPageFactory {
                return new SpecialPageFactory(
                        $services->getMainConfig(),
diff --git a/includes/Storage/NameTableStoreFactory.php b/includes/Storage/NameTableStoreFactory.php
new file mode 100644 (file)
index 0000000..02ea9a7
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ * @file
+ */
+
+namespace MediaWiki\Storage;
+
+use Wikimedia\Rdbms\ILBFactory;
+use WANObjectCache;
+use Psr\Log\LoggerInterface;
+
+class NameTableStoreFactory {
+       private static $info;
+       private $stores = [];
+
+       /** @var ILBFactory */
+       private $lbFactory;
+
+       /** @var WANObjectCache */
+       private $cache;
+
+       /** @var LoggerInterface */
+       private $logger;
+
+       private static function getTableInfo() {
+               if ( self::$info ) {
+                       return self::$info;
+               }
+               self::$info = [
+                       'change_tag_def' => [
+                               'idField' => 'ctd_id',
+                               'nameField' => 'ctd_name',
+                               'normalizationCallback' => null,
+                               'insertCallback' => function ( $insertFields ) {
+                                       $insertFields['ctd_user_defined'] = 0;
+                                       $insertFields['ctd_count'] = 0;
+                                       return $insertFields;
+                               }
+                       ],
+
+                       'content_models' => [
+                               'idField' => 'model_id',
+                               'nameField' => 'model_name',
+                               /**
+                                * No strtolower normalization is added to the service as there are examples of
+                                * extensions that do not stick to this assumption.
+                                * - extensions/examples/DataPages define( 'CONTENT_MODEL_XML_DATA','XML_DATA' );
+                                * - extensions/Scribunto define( 'CONTENT_MODEL_SCRIBUNTO', 'Scribunto' );
+                                */
+                       ],
+
+                       'slot_roles' => [
+                               'idField' => 'role_id',
+                               'nameField' => 'role_name',
+                               'normalizationCallback' => 'strtolower',
+                       ],
+               ];
+               return self::$info;
+       }
+
+       public function __construct(
+               ILBFactory $lbFactory,
+               WANObjectCache $cache,
+               LoggerInterface $logger
+       ) {
+               $this->lbFactory = $lbFactory;
+               $this->cache = $cache;
+               $this->logger = $logger;
+       }
+
+       /**
+        * Get a NameTableStore for a specific table
+        *
+        * @param string $tableName The table name
+        * @param string|false $wiki The target wiki ID, or false for the current wiki
+        * @return NameTableStore
+        */
+       public function get( $tableName, $wiki = false ) : NameTableStore {
+               $infos = self::getTableInfo();
+               if ( !isset( $infos[$tableName] ) ) {
+                       throw new \InvalidArgumentException( "Invalid table name \$tableName" );
+               }
+               if ( $wiki === wfWikiID() ) {
+                       $wiki = false;
+               }
+               if ( isset( $this->stores[$tableName][$wiki] ) ) {
+                       return $this->stores[$tableName][$wiki];
+               }
+
+               $info = $infos[$tableName];
+               $store = new NameTableStore(
+                       $this->lbFactory->getMainLB( $wiki ),
+                       $this->cache,
+                       $this->logger,
+                       $tableName,
+                       $info['idField'],
+                       $info['nameField'],
+                       $info['normalizationCallback'] ?? null,
+                       $wiki,
+                       $info['insertCallback'] ?? null
+               );
+               $this->stores[$tableName][$wiki] = $store;
+               return $store;
+       }
+
+       /**
+        * Get a NameTableStore for the change_tag_def table
+        *
+        * @param string|bool $wiki
+        * @return NameTableStore
+        */
+       public function getChangeTagDef( $wiki = false ) : NameTableStore {
+               return $this->get( 'change_tag_def', $wiki );
+       }
+
+       /**
+        * Get a NameTableStore for the content_models table
+        *
+        * @param string|bool $wiki
+        * @return NameTableStore
+        */
+       public function getContentModels( $wiki = false ) : NameTableStore {
+               return $this->get( 'content_models', $wiki );
+       }
+
+       /**
+        * Get a NameTableStore for the slot_roles table
+        *
+        * @param string|bool $wiki
+        * @return NameTableStore
+        */
+       public function getSlotRoles( $wiki = false ) : NameTableStore {
+               return $this->get( 'slot_roles', $wiki );
+       }
+}
index 9419b40..aaaafc1 100644 (file)
@@ -67,9 +67,13 @@ class RevisionStoreFactory {
         */
        private $contentHandlerUseDB;
 
+       /** @var NameTableStoreFactory */
+       private $nameTables;
+
        /**
         * @param ILBFactory $dbLoadBalancerFactory
         * @param BlobStoreFactory $blobStoreFactory
+        * @param NameTableStoreFactory $nameTables
         * @param WANObjectCache $cache
         * @param CommentStore $commentStore
         * @param ActorMigration $actorMigration
@@ -81,6 +85,7 @@ class RevisionStoreFactory {
        public function __construct(
                ILBFactory $dbLoadBalancerFactory,
                BlobStoreFactory $blobStoreFactory,
+               NameTableStoreFactory $nameTables,
                WANObjectCache $cache,
                CommentStore $commentStore,
                ActorMigration $actorMigration,
@@ -91,6 +96,7 @@ class RevisionStoreFactory {
                Assert::parameterType( 'integer', $migrationStage, '$migrationStage' );
                $this->dbLoadBalancerFactory = $dbLoadBalancerFactory;
                $this->blobStoreFactory = $blobStoreFactory;
+               $this->nameTables = $nameTables;
                $this->cache = $cache;
                $this->commentStore = $commentStore;
                $this->actorMigration = $actorMigration;
@@ -98,7 +104,6 @@ class RevisionStoreFactory {
                $this->loggerProvider = $loggerProvider;
                $this->contentHandlerUseDB = $contentHandlerUseDB;
        }
-       /**
 
        /**
         * @since 1.32
@@ -115,8 +120,8 @@ class RevisionStoreFactory {
                        $this->blobStoreFactory->newSqlBlobStore( $wikiId ),
                        $this->cache, // Pass local cache instance; Leave cache sharing to RevisionStore.
                        $this->commentStore,
-                       $this->getContentModelStore( $wikiId ),
-                       $this->getSlotRoleStore( $wikiId ),
+                       $this->nameTables->getContentModels( $wikiId ),
+                       $this->nameTables->getSlotRoles( $wikiId ),
                        $this->mcrMigrationStage,
                        $this->actorMigration,
                        $wikiId
@@ -127,43 +132,4 @@ class RevisionStoreFactory {
 
                return $store;
        }
-
-       /**
-        * @param string $wikiId
-        * @return NameTableStore
-        */
-       private function getContentModelStore( $wikiId ) {
-               // XXX: a dedicated ContentModelStore subclass would avoid hard-coding
-               // knowledge about the schema here.
-               return new NameTableStore(
-                       $this->dbLoadBalancerFactory->getMainLB( $wikiId ),
-                       $this->cache, // Pass local cache instance; Leave cache sharing to NameTableStore.
-                       $this->loggerProvider->getLogger( 'NameTableSqlStore' ),
-                       'content_models',
-                       'model_id',
-                       'model_name',
-                       null,
-                       $wikiId
-               );
-       }
-
-       /**
-        * @param string $wikiId
-        * @return NameTableStore
-        */
-       private function getSlotRoleStore( $wikiId ) {
-               // XXX: a dedicated ContentModelStore subclass would avoid hard-coding
-               // knowledge about the schema here.
-               return new NameTableStore(
-                       $this->dbLoadBalancerFactory->getMainLB( $wikiId ),
-                       $this->cache, // Pass local cache instance; Leave cache sharing to NameTableStore.
-                       $this->loggerProvider->getLogger( 'NameTableSqlStore' ),
-                       'slot_roles',
-                       'role_id',
-                       'role_name',
-                       'strtolower',
-                       $wikiId
-               );
-       }
-
 }
index 4210a96..75494b1 100644 (file)
@@ -41,9 +41,7 @@ class TemplateParser {
        /**
         * @var int Compilation flags passed to LightnCandy
         */
-       // Do not add more flags here without discussion.
-       // If you do add more flags, be sure to update unit tests as well.
-       protected $compileFlags = LightnCandy::FLAG_ERROR_EXCEPTION;
+       protected $compileFlags;
 
        /**
         * @param string|null $templateDir
@@ -52,6 +50,10 @@ class TemplateParser {
        public function __construct( $templateDir = null, $forceRecompile = false ) {
                $this->templateDir = $templateDir ?: __DIR__ . '/templates';
                $this->forceRecompile = $forceRecompile;
+
+               // Do not add more flags here without discussion.
+               // If you do add more flags, be sure to update unit tests as well.
+               $this->compileFlags = LightnCandy::FLAG_ERROR_EXCEPTION | LightnCandy::FLAG_MUSTACHELOOKUP;
        }
 
        /**
index f989ce4..4aba84c 100644 (file)
        "apihelp-logout-summary": "登出並清除 session 資料。",
        "apihelp-logout-example-logout": "登出當前使用者",
        "apihelp-managetags-summary": "執行相關到更改標籤的管理任務。",
+       "apihelp-managetags-param-ignorewarnings": "是否在處理期間發生問題時忽略任何警告。",
        "apihelp-managetags-param-tags": "在標籤管理日誌裡更改套用到項目的標籤。",
        "apihelp-mergehistory-summary": "合併頁面歷史",
        "apihelp-mergehistory-param-reason": "合併歷史的原因。",
        "apihelp-query+alldeletedrevisions-param-user": "此列出由該使用者作出的修訂。",
        "apihelp-query+alldeletedrevisions-param-excludeuser": "不要列出由該使用者作出的修訂。",
        "apihelp-query+alldeletedrevisions-param-namespace": "僅列出此命名空間的頁面。",
+       "apihelp-query+alldeletedrevisions-example-user": "列出由使用者 <kbd>Example</kbd> 做出的最近 50 個貢獻。",
+       "apihelp-query+alldeletedrevisions-example-ns-main": "列出在主命名空間的前 50 個已刪除修訂。",
        "apihelp-query+allfileusages-summary": "列出所有檔案用途,包含不存在的。",
        "apihelp-query+allfileusages-param-from": "要起始列舉的檔案標題。",
        "apihelp-query+allfileusages-param-to": "要終止列舉的檔案標題。",
        "apihelp-query+allrevisions-param-user": "此列出由該使用者作出的修訂。",
        "apihelp-query+allrevisions-param-excludeuser": "不要列出由該使用者作出的修訂。",
        "apihelp-query+allrevisions-param-namespace": "僅列出此命名空間的頁面。",
+       "apihelp-query+allrevisions-example-user": "列出由使用者 <kbd>Example</kbd> 做出的最近 50 個貢獻。",
        "apihelp-query+allrevisions-example-ns-main": "列出在主命名空間的前 50 個修訂。",
        "apihelp-query+mystashedfiles-param-prop": "要索取的檔案屬性。",
        "apihelp-query+mystashedfiles-paramvalue-prop-size": "索取檔案大小與圖片尺寸。",
        "apihelp-query+filearchive-param-limit": "要回傳的圖片總數。",
        "apihelp-query+filearchive-param-dir": "列出時所採用的方向。",
        "apihelp-query+filearchive-param-sha1": "圖片的 SHA1 雜湊值。覆蓋 $1sha1base36。",
+       "apihelp-query+filearchive-param-sha1base36": "以 base 36 的圖片 SHA1 雜湊值(使用在 MediaWiki)。",
        "apihelp-query+filearchive-param-prop": "要取得的圖片資訊:",
        "apihelp-query+filearchive-paramvalue-prop-sha1": "替圖片添加 SHA-1 雜湊值。",
        "apihelp-query+filearchive-paramvalue-prop-timestamp": "添加上傳版本的時間戳記。",
        "apihelp-query+imageinfo-paramvalue-prop-timestamp": "添加上傳版本的時間戳記。",
        "apihelp-query+imageinfo-paramvalue-prop-comment": "版本的註釋。",
        "apihelp-query+imageinfo-paramvalue-prop-parsedcomment": "解析版本上的註釋。",
+       "apihelp-query+imageinfo-paramvalue-prop-url": "提供檔案與描述頁面的 URL。",
        "apihelp-query+imageinfo-paramvalue-prop-sha1": "替檔案添加 SHA-1 雜湊值。",
        "apihelp-query+imageinfo-paramvalue-prop-mime": "替檔案添加 MIME 類型。",
        "apihelp-query+imageinfo-paramvalue-prop-mediatype": "添加檔案的媒體類型。",
+       "apihelp-query+imageinfo-paramvalue-prop-badfile": "無論檔案是否在 [[MediaWiki:Bad image list]] 都添加",
        "apihelp-query+imageinfo-param-limit": "每個檔案要回傳的檔案修訂數量。",
        "apihelp-query+imageinfo-param-start": "列出的起始時間戳記。",
        "apihelp-query+imageinfo-param-end": "列出的終止時間戳記。",
        "apihelp-query+imageinfo-param-urlheight": "與 $1urlwidth 相似。",
+       "apihelp-query+imageinfo-example-dated": "索取 [[:File:Test.jpg]] 自 2008 年以來的版本資訊。",
        "apihelp-query+images-summary": "回傳指定頁面中包含的所有檔案。",
        "apihelp-query+images-param-limit": "要回傳的檔案數量。",
        "apihelp-query+images-param-dir": "列出時所採用的方向。",
        "apihelp-query+images-example-simple": "取得使用在 [[Main Page]] 的檔案清單。",
+       "apihelp-query+images-example-generator": "取得在 [[Main Page]] 所有使用到檔案的相關資訊。",
        "apihelp-query+imageusage-summary": "尋找使用到指定圖片標題的所有頁面。",
        "apihelp-query+imageusage-param-title": "要搜尋的標題。不能與 $1pageid 一起使用。",
        "apihelp-query+imageusage-param-pageid": "要搜尋的頁面 ID。不能與 $1title 一起使用。",
        "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "添加跨 wiki 標題。",
        "apihelp-query+iwbacklinks-param-dir": "列出時所採用的方向。",
        "apihelp-query+iwbacklinks-example-simple": "取得連結至 [[wikibooks:Test]] 的頁面。",
+       "apihelp-query+iwbacklinks-example-generator": "取得連結至 [[wikibooks:Test]] 的頁面相關資訊。",
        "apihelp-query+iwlinks-summary": "回傳指定頁面的所有 interwiki 連結。",
        "apihelp-query+iwlinks-param-url": "是否取得完整的 URL(不能與 $1prop 一同使用)。",
        "apihelp-query+iwlinks-paramvalue-prop-url": "添加完整的 URL。",
        "apihelp-query+logevents-paramvalue-prop-ids": "添加日誌事件的 ID。",
        "apihelp-query+logevents-paramvalue-prop-title": "添加日誌事件的頁面標題。",
        "apihelp-query+logevents-paramvalue-prop-type": "添加日誌事件的類型。",
+       "apihelp-query+logevents-paramvalue-prop-timestamp": "添加日誌事件的時間戳記。",
+       "apihelp-query+logevents-paramvalue-prop-comment": "添加日誌事件的註釋。",
+       "apihelp-query+logevents-paramvalue-prop-details": "列出日誌事件的額外詳細資訊。",
+       "apihelp-query+logevents-paramvalue-prop-tags": "列出日誌事件的標籤。",
        "apihelp-query+logevents-param-type": "篩選僅為此類型的日誌項目。",
        "apihelp-query+logevents-param-start": "起始列舉的時間戳記。",
        "apihelp-query+logevents-param-end": "結束列舉的時間戳記。",
index 0bb8484..008a2f6 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\NameTableAccessException;
 use Wikimedia\Rdbms\Database;
 
 class ChangeTags {
@@ -802,16 +803,18 @@ class ChangeTags {
                        throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
                }
 
+               $tagTables[] = 'change_tag';
                if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
-                       $tables[] = 'change_tag_def';
-                       $join_cond = [ $join_cond, 'ct_tag_id=ctd_id' ];
+                       $tagTables[] = 'change_tag_def';
+                       $join_cond_ts_tags = [ $join_cond, 'ct_tag_id=ctd_id' ];
                        $field = 'ctd_name';
                } else {
                        $field = 'ct_tag';
+                       $join_cond_ts_tags = $join_cond;
                }
 
                $fields['ts_tags'] = wfGetDB( DB_REPLICA )->buildGroupConcatField(
-                       ',', 'change_tag', $field, $join_cond
+                       ',', $tagTables, $field, $join_cond_ts_tags
                );
 
                if ( $wgUseTagFilter && $filter_tag ) {
@@ -821,9 +824,18 @@ class ChangeTags {
                        $tables[] = 'change_tag';
                        $join_conds['change_tag'] = [ 'INNER JOIN', $join_cond ];
                        if ( $wgChangeTagsSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
-                               $tables[] = 'change_tag_def';
-                               $join_conds['change_tag_def'] = [ 'INNER JOIN', 'ct_tag_id=ctd_id' ];
-                               $conds['ctd_name'] = $filter_tag;
+                               $filterTagIds = [];
+                               $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
+                               foreach ( (array)$filter_tag as $filterTagName ) {
+                                       try {
+                                               $filterTagIds[] = $changeTagDefStore->getId( $filterTagName );
+                                       } catch ( NameTableAccessException $exception ) {
+                                               // Return nothing.
+                                               $conds[] = '0';
+                                               break;
+                                       };
+                               }
+                               $conds['ct_tag_id'] = $filterTagIds;
                        } else {
                                $conds['ct_tag'] = $filter_tag;
                        }
index e5e5cdd..281d348 100644 (file)
@@ -9,17 +9,18 @@
  *
  * Options:
  *   - columns
- *     - Required list of columns in the matrix.
+ *     - Required associative array mapping column labels (as HTML) to their tags.
  *   - rows
- *     - Required list of rows in the matrix.
+ *     - Required associative array mapping row labels (as HTML) to their tags.
  *   - force-options-on
- *     - Accepts array of column-row tags to be displayed as enabled but unavailable to change
+ *     - Array of column-row tags to be displayed as enabled but unavailable to change.
  *   - force-options-off
- *     - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
+ *     - Array of column-row tags to be displayed as disabled but unavailable to change.
  *   - tooltips
- *     - Optional array mapping row label to tooltip content
+ *     - Optional associative array mapping row labels to tooltips (as text, will be escaped).
  *   - tooltip-class
  *     - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
+ *       Not used by OOUI form fields.
  */
 class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
        static private $requiredParams = [
@@ -158,10 +159,10 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
                                'id' => $this->mID,
                                'rows' => $this->mParams['rows'],
                                'columns' => $this->mParams['columns'],
-                               'tooltips' => $this->mParams['tooltips'],
+                               'tooltips' => $this->mParams['tooltips'] ?? [],
                                'forcedOff' => $this->mParams['force-options-off'] ?? [],
                                'forcedOn' => $this->mParams['force-options-on'] ?? [],
-                               'values' => $value
+                               'values' => $value,
                        ] + OOUI\Element::configFromHtmlAttributes( $attribs )
                );
        }
index fc0ca1d..8f5d083 100644 (file)
@@ -627,14 +627,13 @@ class ResourceLoader implements LoggerAwareInterface {
        /**
         * Add an error to the 'errors' array and log it.
         *
-        * Should only be called from within respond().
-        *
+        * @private For internal use by ResourceLoader and ResourceLoaderStartUpModule.
         * @since 1.29
         * @param Exception $e
         * @param string $msg
         * @param array $context
         */
-       protected function outputErrorAndLog( Exception $e, $msg, array $context = [] ) {
+       public function outputErrorAndLog( Exception $e, $msg, array $context = [] ) {
                MWExceptionHandler::logException( $e );
                $this->logger->warning(
                        $msg,
@@ -659,9 +658,8 @@ class ResourceLoader implements LoggerAwareInterface {
                        try {
                                return $this->getModule( $module )->getVersionHash( $context );
                        } catch ( Exception $e ) {
-                               // If modules fail to compute a version, do still consider the versions
-                               // of other modules - don't set an empty string E-Tag for the whole request.
-                               // See also T152266 and StartupModule::getModuleRegistrations().
+                               // If modules fail to compute a version, don't fail the request (T152266)
+                               // and still compute versions of other modules.
                                $this->outputErrorAndLog( $e,
                                        'Calculating version for "{module}" failed: {exception}',
                                        [
index 18cc4d5..8140c2c 100644 (file)
@@ -40,21 +40,13 @@ use MediaWiki\MediaWikiServices;
  *   See also: OutputPage::disallowUserJs()
  */
 class ResourceLoaderStartUpModule extends ResourceLoaderModule {
-
-       // Cache for getConfigSettings() as it's called by multiple methods
-       protected $configVars = [];
        protected $targets = [ 'desktop', 'mobile' ];
 
        /**
         * @param ResourceLoaderContext $context
         * @return array
         */
-       protected function getConfigSettings( $context ) {
-               $hash = $context->getHash();
-               if ( isset( $this->configVars[$hash] ) ) {
-                       return $this->configVars[$hash];
-               }
-
+       private function getConfigSettings( $context ) {
                $conf = $this->getConfig();
 
                // We can't use Title::newMainPage() if 'mainpage' is in
@@ -136,8 +128,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
 
                Hooks::run( 'ResourceLoaderGetConfigVars', [ &$vars ] );
 
-               $this->configVars[$hash] = $vars;
-               return $this->configVars[$hash];
+               return $vars;
        }
 
        /**
@@ -222,9 +213,23 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                $out = '';
                $states = [];
                $registryData = [];
+               $moduleNames = $resourceLoader->getModuleNames();
+
+               // Preload with a batch so that the below calls to getVersionHash() for each module
+               // don't require on-demand loading of more information.
+               try {
+                       $resourceLoader->preloadModuleInfo( $moduleNames, $context );
+               } catch ( Exception $e ) {
+                       // Don't fail the request (T152266)
+                       // Also print the error in the main output
+                       $resourceLoader->outputErrorAndLog( $e,
+                               'Preloading module info from startup failed: {exception}',
+                               [ 'exception' => $e ]
+                       );
+               }
 
                // Get registry data
-               foreach ( $resourceLoader->getModuleNames() as $name ) {
+               foreach ( $moduleNames as $name ) {
                        $module = $resourceLoader->getModule( $name );
                        $moduleTargets = $module->getTargets();
                        if (
@@ -235,18 +240,25 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        }
 
                        if ( $module->isRaw() ) {
-                               // Don't register "raw" modules (like 'jquery' and 'mediawiki') client-side because
-                               // depending on them is illegal anyway and would only lead to them being reloaded
-                               // causing any state to be lost (like jQuery plugins, mw.config etc.)
+                               // Don't register "raw" modules (like 'startup') client-side because depending on them
+                               // is illegal anyway and would only lead to them being loaded a second time,
+                               // causing any state to be lost.
+
+                               // ATTENTION: Because of the line below, this is not going to cause infinite recursion.
+                               // Think carefully before making changes to this code!
+                               // The below code is going to call ResourceLoaderModule::getVersionHash() for every module.
+                               // For StartUpModule (this module) the hash is computed based on the manifest content,
+                               // which is the very thing we are computing right here. As such, this must skip iterating
+                               // over 'startup' itself.
                                continue;
                        }
 
                        try {
                                $versionHash = $module->getVersionHash( $context );
                        } catch ( Exception $e ) {
-                               // See also T152266 and ResourceLoader::getCombinedVersion()
-                               MWExceptionHandler::logException( $e );
-                               $context->getLogger()->warning(
+                               // Don't fail the request (T152266)
+                               // Also print the error in the main output
+                               $resourceLoader->outputErrorAndLog( $e,
                                        'Calculating version for "{module}" failed: {exception}',
                                        [
                                                'module' => $name,
@@ -445,59 +457,12 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
        }
 
        /**
-        * Get the definition summary for this module.
-        *
-        * @param ResourceLoaderContext $context
-        * @return array
-        */
-       public function getDefinitionSummary( ResourceLoaderContext $context ) {
-               global $IP;
-               $summary = parent::getDefinitionSummary( $context );
-               $startup = [
-                       // getScript() exposes these variables to mw.config (T30899).
-                       'vars' => $this->getConfigSettings( $context ),
-                       // getScript() uses this to decide how configure mw.Map for mw.config.
-                       'wgLegacyJavaScriptGlobals' => $this->getConfig()->get( 'LegacyJavaScriptGlobals' ),
-                       // Detect changes to the module registrations output by getScript().
-                       'moduleHashes' => $this->getAllModuleHashes( $context ),
-                       // Detect changes to base modules listed by getScript().
-                       'baseModules' => $this->getBaseModules(),
-
-                       'fileHashes' => [
-                               $this->safeFileHash( "$IP/resources/src/startup/startup.js" ),
-                               $this->safeFileHash( "$IP/resources/src/startup/mediawiki.js" ),
-                               $this->safeFileHash( "$IP/resources/src/startup/mediawiki.requestIdleCallback.js" ),
-                       ],
-               ];
-               if ( $context->getDebug() ) {
-                       $startup['fileHashes'][] = $this->safeFileHash( "$IP/resources/src/startup/mediawiki.log.js" );
-               }
-               if ( $this->getConfig()->get( 'ResourceLoaderEnableJSProfiler' ) ) {
-                       $startup['fileHashes'][] = $this->safeFileHash( "$IP/resources/src/startup/profiling.js" );
-               }
-               $summary[] = $startup;
-               return $summary;
-       }
-
-       /**
-        * Helper method for getDefinitionSummary().
-        *
-        * @param ResourceLoaderContext $context
-        * @return string SHA-1
+        * @return bool
         */
-       protected function getAllModuleHashes( ResourceLoaderContext $context ) {
-               $rl = $context->getResourceLoader();
-               // Preload for getCombinedVersion()
-               $rl->preloadModuleInfo( $rl->getModuleNames(), $context );
-
-               // ATTENTION: Because of the line below, this is not going to cause infinite recursion.
-               // Think carefully before making changes to this code!
-               // Pre-populate versionHash with something because the loop over all modules below includes
-               // the startup module (this module).
-               // See ResourceLoaderModule::getVersionHash() for usage of this cache.
-               $this->versionHash[$context->getHash()] = null;
-
-               return $rl->getCombinedVersion( $context, $rl->getModuleNames() );
+       public function enableModuleContentVersion() {
+               // Enabling this means that ResourceLoader::getVersionHash will simply call getScript()
+               // and hash it to determine the version (as used by E-Tag HTTP response header).
+               return true;
        }
 
        /**
index 87bd2ed..c984af8 100644 (file)
@@ -120,6 +120,12 @@ class SpecialJavaScriptTest extends SpecialPage {
                $code = $rl->makeModuleResponse( $startupContext, [
                        'startup' => $rl->getModule( 'startup' ),
                ] );
+               $code .= <<<JAVASCRIPT
+       // Disable module storage.
+       // The unit test for mw.loader.store will enable it
+       // explicitly with a mock timer.
+       mw.loader.store.enabled = false;
+JAVASCRIPT;
                // The following has to be deferred via RLQ because the startup module is asynchronous.
                $code .= ResourceLoader::makeLoaderConditionalScript(
                        // Embed page-specific mw.config variables.
index 510b352..8038c54 100644 (file)
@@ -28,17 +28,15 @@ class CheckMatrixWidget extends \OOUI\Widget {
         *
         * @param array $config Configuration array with the following options:
         *   - columns
-        *     - Required list of columns in the matrix.
+        *     - Required associative array mapping column labels (as HTML) to their tags.
         *   - rows
-        *     - Required list of rows in the matrix.
+        *     - Required associative array mapping row labels (as HTML) to their tags.
         *   - force-options-on
-        *     - Accepts array of column-row tags to be displayed as enabled but unavailable to change
+        *     - Array of column-row tags to be displayed as enabled but unavailable to change.
         *   - force-options-off
-        *     - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
+        *     - Array of column-row tags to be displayed as disabled but unavailable to change.
         *   - tooltips
-        *     - Optional array mapping row label to tooltip content
-        *   - tooltip-class
-        *     - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
+        *     - Optional associative array mapping row labels to tooltips (as text, will be escaped).
         */
        public function __construct( array $config = [] ) {
                // Configuration initialization
@@ -65,7 +63,7 @@ class CheckMatrixWidget extends \OOUI\Widget {
                $tr->appendContent( $this->getCellTag( "\u{00A0}" ) );
                foreach ( $this->columns as $columnLabel => $columnTag ) {
                        $tr->appendContent(
-                               $this->getCellTag( $columnLabel )
+                               $this->getCellTag( new \OOUI\HtmlSnippet( $columnLabel ) )
                        );
                }
                $table->appendContent( $tr );
@@ -86,7 +84,7 @@ class CheckMatrixWidget extends \OOUI\Widget {
         * Get a formatted table row for the option, with
         * a checkbox widget.
         *
-        * @param  string $label Row label
+        * @param  string $label Row label (as HTML)
         * @param  string $tag   Row tag name
         * @return \OOUI\Tag The resulting table row
         */
@@ -98,7 +96,7 @@ class CheckMatrixWidget extends \OOUI\Widget {
                $labelField = new \OOUI\FieldLayout(
                        new \OOUI\Widget(), // Empty widget, since we don't have the checkboxes here
                        [
-                               'label' => $label,
+                               'label' => new \OOUI\HtmlSnippet( $label ),
                                'align' => 'inline',
                        ] + $labelFieldConfig
                );
@@ -125,7 +123,7 @@ class CheckMatrixWidget extends \OOUI\Widget {
        /**
         * Get an individual cell tag with requested content
         *
-        * @param  string $content Content for the <td> cell
+        * @param  mixed $content Content for the <td> cell
         * @return \OOUI\Tag Resulting cell
         */
        private function getCellTag( $content ) {
index dc6ed48..7c28e5e 100644 (file)
        "jumptonavigation": "navigasie",
        "jumptosearch": "soek",
        "view-pool-error": "Jammer, die bedieners is tans oorbelas.\nTe veel gebruikers probeer om na hierdie bladsy te kyk.\nWag asseblief 'n rukkie voordat u weer probeer om die bladsy op te roep.\n\n$1",
-       "generic-pool-error": "Jammer, die bedieners is tans oorbelas.\nTe veel gebruikers probeer om na hierdie bladsy te kyk.\nWag asseblief 'n rukkie voordat u weer toegang tot hierdie bron verkry.",
+       "generic-pool-error": "Jammer, die bedieners is tans oorlaai.\nTe veel gebruikers probeer om na hierdie hulpbron te kyk.\nWag asseblief 'n rukkie voordat u weer die hulpbron probeer besoek.",
        "pool-timeout": "Die maksimum wagtyd vir 'n databasisversperring is oorskry.",
        "pool-queuefull": "Die poel se wagtou is vol",
        "pool-errorunknown": "Onbekende fout",
        "ns-specialprotected": "Spesiale bladsye kan nie geredigeer word nie.",
        "titleprotected": "Hierdie titel is beskerm teen skepping deur [[User:$1|$1]].\nDie rede gegee is <em>$2</em>.",
        "filereadonlyerror": "Dit was nie moontlik om die lêer \"$1\" te wysig nie omdat die lêerstoor \"$2\" tans lees-alleen is.\n\nDie rede hiervoor is \"''$3''\".",
+       "invalidtitle": "Ongeldige titel",
        "invalidtitle-knownnamespace": "Ongeldige titel met naamruimte \"$2\" en teks \"$3\"",
        "invalidtitle-unknownnamespace": "Ongeldige titel met onbekende naamruimtenummer $1 en teks \"$2\"",
        "exception-nologin": "Nie aangeteken nie",
-       "exception-nologin-text": "[[Special:Userlogin|Meld aan]] om hierdie bladsy te wys of om die handeling uit te voer.",
+       "exception-nologin-text": "Meld aan om hierdie bladsy te sien of om die handeling uit te voer.",
        "exception-nologin-text-manual": "U moet $1 om hierdie bladsy te wys of die handeling uit te voer.",
        "virus-badscanner": "Slegte konfigurasie: onbekende virusskandeerder: ''$1''",
        "virus-scanfailed": "skandering het misluk (kode $1)",
        "virus-unknownscanner": "onbekende antivirus:",
        "logouttext": "'''U is nou afgemeld'''\n\nSommige bladsye kan moontlik nog aandui dat u steeds aangemeld is, totdat u u webblaaier se kas skoonmaak.",
+       "cannotlogoutnow-title": "Kan nie tans afmeld nie",
+       "cannotlogoutnow-text": "Afmeld is nie moontlik terwyl $1 gebruik word nie.",
        "welcomeuser": "Welkom, $1!",
        "welcomecreation-msg": "U gebruiker is geskep.\nMoenie vergeet om u [[Special:Preferences|voorkeure vir {{SITENAME}}]] te stel nie.",
        "yourname": "Gebruikersnaam:",
        "createacct-yourpasswordagain-ph": "Sleutel weer u wagwoord in",
        "userlogin-remembermypassword": "Hou my aangemeld",
        "userlogin-signwithsecure": "Gebruik veilige verbinding",
+       "cannotlogin-title": "Kan nie aanmeld nie",
+       "cannotlogin-text": "Aanmelding is nie moontlik nie.",
+       "cannotloginnow-title": "Kan nie tans aanmeld nie",
+       "cannotloginnow-text": "Aanmelding is nie moontlik terwyl $1 gebruik word nie.",
+       "cannotcreateaccount-title": "Kan nie rekeninge skep nie",
+       "cannotcreateaccount-text": "Direkte skep van rekeninge is nie geaktiveer op hierdie wiki nie.",
        "yourdomainname": "U domein:",
        "password-change-forbidden": "U kan nie wagwoorde op hierdie wiki verander nie.",
        "externaldberror": "'n Databasisfout het tydens aanmelding voorgekom of u het nie toestemming om u eksterne rekening op te dateer nie.",
        "login": "Meld aan",
+       "login-security": "Verifieer u identiteit",
        "nav-login-createaccount": "Meld aan / registreer",
        "logout": "Teken uit",
        "userlogout": "Teken uit",
        "userlogin-resetpassword-link": "Wagwoord vergeet?",
        "userlogin-helplink2": "Hulp met aanmelding",
        "userlogin-loggedin": "U is reeds aangemeld as {{GENDER:$1|$1}}.\nGebruik die onderstaande vorm om as 'n ander gebruiker aan te meld.",
+       "userlogin-reauth": "U moet weer aanmeld om te verifieer dat u $1 is.",
        "userlogin-createanother": "Skep nog 'n rekening",
        "createacct-emailrequired": "E-posadres",
        "createacct-emailoptional": "E-posadres (opsioneel)",
        "createacct-email-ph": "Sleutel u e-posadres in",
        "createacct-another-email-ph": "Verskaf e-posadres",
-       "createaccountmail": "Gebruik 'n tydelike lukrake wagwoord en stuur dit na die e-posadres hier onder",
+       "createaccountmail": "Gebruik 'n tydelike lukrake wagwoord en stuur dit na die gespesifiseerde e-posadres",
+       "createaccountmail-help": "Kan gebruik word om 'n rekening vir 'n ander persoon te skep sonder om die wagwoord te weet.",
        "createacct-realname": "Regte naam (opsioneel)",
        "createacct-reason": "Rede",
        "createacct-reason-ph": "Hoekom u nog 'n rekening skep",
        "nosuchusershort": "Daar is geen gebruikersnaam \"$1\" nie. Maak seker dit is reg gespel.",
        "nouserspecified": "U moet 'n gebruikersnaam spesifiseer.",
        "login-userblocked": "Hierdie gebruiker is geblokkeer.\nIntekening word verbied.",
-       "wrongpassword": "Ongeldige wagwoord, probeer weer.",
+       "wrongpassword": "Verkeerde gebruikernaam of wagwoord is getik. Probeer gerus weer.",
        "wrongpasswordempty": "Die wagwoord was leeg. Probeer asseblief weer.",
        "passwordtooshort": "Wagwoorde moet ten minste {{PLURAL:$1|1 karakter|$1 karakters}} lank wees.",
+       "passwordtoolong": "Wagwoorde kan nie langer as {{PLURAL:$1|1 karakter|$1 karakters}} wees nie.",
+       "passwordtoopopular": "Algemene wagwoorde kan nie gebruik word nie. Kies asb. 'n wagwoord wat moeiliker is om te raai.",
        "password-name-match": "U wagwoord mag nie dieselfde as u gebruikersnaam wees nie.",
        "password-login-forbidden": "Die gebruik van hierdie gebruikersnaam en wagwoord is geweier.",
        "mailmypassword": "E-pos nuwe wagwoord",
        "passwordremindertitle": "Wagwoordwenk van {{SITENAME}}",
-       "passwordremindertext": "Iemand (waarskynlik u vanaf IP-adres $1) het 'n nuwe wagwoord vir {{SITENAME}} ($4) aangevra. 'n Tydelike wagwoord is vir gebruiker \"$2\" geskep. Die nuwe wagwoord is \"$3\". U kan met die tydelike wagwoord aanmeld en 'n nuwe wagwoord stel. Die tydelike wagwoord sal na {{PLURAL:$5|een dag|$5 dae}} verval.\n\nIndien iemand anders hierdie navraag gerig het, of u het die wagwoord intussen onthou en wil nie meer die wagwoord wysig nie, kan u die boodskap ignoreer en voortgaan om die ou wagwoord te gebruik.",
+       "passwordremindertext": "Iemand (vanaf IP-adres $1) het 'n nuwe wagwoord vir {{SITENAME}} ($4) aangevra. 'n Tydelike wagwoord is vir gebruiker \"$2\" geskep. Die nuwe wagwoord is \"$3\". As dit u bedoeling was, moet u nou aanmeld en 'n nuwe wagwoord kies. Die tydelike wagwoord sal na {{PLURAL:$5|een dag|$5 dae}} verval.\n\nIndien iemand anders hierdie navraag gerig het, of u die wagwoord intussen onthou het en nie meer die wagwoord wil wysig nie, kan u die boodskap ignoreer en voortgaan om die ou wagwoord te gebruik.",
        "noemail": "Daar is geen e-posadres vir gebruiker \"$1\" nie.",
        "noemailcreate": "U moet 'n geldige e-posadres verskaf",
        "passwordsent": "'n Nuwe wagwoord is na die e-posadres vir \"$1\" gestuur.\nMeld asseblief aan sodra u dit ontvang het.",
-       "blocked-mailpassword": "U IP-adres is tans teen wysigings geblokkeer. Om verdere misbruik te voorkom is dit dus nie moontlik om die wagwoordherwinningfunksie te gebruik nie.",
-       "eauthentsent": "'n Bevestigingpos is gestuur na die gekose e-posadres.\nVoordat ander pos na die adres gestuur word,\nmoet die instruksies in bogenoemde pos gevolg word om te bevestig dat die adres werklik u adres is.",
+       "blocked-mailpassword": "U IP-adres is tans teen wysigings geblokkeer. Om verdere misbruik te voorkom is dit nie moontlik om wagwoorde te herwin vanaf hierdie IP-adres te nie.",
+       "eauthentsent": "'n Bevestigingpos is gestuur na die gegewe e-posadres.\nVoordat ander e-pos na die adres gestuur word, moet die instruksies in die e-pos gevolg word om te bevestig dat die adres werklik u adres is.",
        "throttled-mailpassword": "Daar is reeds 'n wagwoordwenk in die laaste {{PLURAL:$1|uur|$1 ure}} gestuur.\nOm misbruik te voorkom, word slegs een E-pos per {{PLURAL:$1|uur|$1 ure}} gestuur.",
        "mailerror": "Fout tydens e-pos versending: $1",
        "acct_creation_throttle_hit": "Besoekers aan hierdie wiki wat u IP-adres gebruik het reeds {{PLURAL:$1|'n rekening|$1 rekeninge}} in die laaste dag geskep, wat die maksimum toelaatbaar is vir die periode. Dus kan besoekers wat hierdie IP-adres gebruik tans nie meer nuwe gebruikers registreer nie.",
        "passwordreset-emailelement": "Gebruikersnaam: \n$1\n\nTydelike wagwoord: \n$2",
        "passwordreset-emailsentemail": "'n E-pos is gestuur om u wagwoord te herstel.",
        "passwordreset-invalidemail": "Ongeldige e-posadres",
-       "changeemail": "Wysig E-posadres",
+       "passwordreset-nodata": "Daar is geen gebruikernaam of e-posadres gegee nie",
+       "changeemail": "Verander of verwyder e-posadres",
        "changeemail-header": "Wysig rekening se e-posadres",
        "changeemail-no-info": "U moet aangemeld wees om regstreeks toegang tot die bladsy te kry.",
        "changeemail-oldemail": "Huidige e-posadres:",
        "changeemail-none": "(geen)",
        "changeemail-password": "U wagwoord vir {{SITENAME}}:",
        "changeemail-submit": "Wysig E-posadres",
+       "changeemail-nochange": "Gee gerus 'n ander nuwe e-posadres",
        "resettokens-token-label": "$1 (huidige waarde: $2)",
        "bold_sample": "Vetdruk",
        "bold_tip": "Vetdruk",
        "savechanges": "Stoor wysigings",
        "publishpage": "Publiseer bladsy",
        "publishchanges": "Publiseer wysigings",
+       "savearticle-start": "Stoor bladsy…",
+       "savechanges-start": "Stoor wysigings…",
+       "publishpage-start": "Publiseer bladsy…",
+       "publishchanges-start": "Publiseer wysigings…",
        "preview": "Voorskou",
        "showpreview": "Wys voorskou",
        "showdiff": "Wys veranderings",
+       "blankarticle": "<strong>Waarskuwing:</strong> Die bladsy wat u skep is leg.\nDeur weer \"$1\" te klik, sal die bladsy sonder enige inhoud geskep word.",
        "anoneditwarning": "'''Waarskuwing:''' u is nie aangemeld nie. U IP-adres sal in die bladsy se wysigingsgeskiedenis gestoor word. As u <strong>[$1 aanmeld]</strong> of <strong>[$2 'n rekening skep]</strong> verskyn u wysigings onder u gebruikersnaam, naas andere voordele.",
        "anonpreviewwarning": "''U is nie aangeteken nie.''\n''As u die bladsy stoor sal u IP-adres in die bladsy se geskeidenis aangeteken word.''",
        "missingsummary": "'''Neem kennis''': Geen opsomming van die wysiging is verskaf nie. As \"Stoor\" weer gekliek word, word die wysiging sonder 'n opsomming gestoor.",
-       "missingcommenttext": "Tik die opsomming onder.",
+       "selfredirect": "<strong>Waarskuwing:</strong> U stuur die bladsy na homself aan.\nDalk is die verkeerde teiken vir die aanstuur gegee, of dalk wysig u die verkeerde bladsy. Deur weer \"$1\" te klik, sal die aanstuur in elk geval gemaak word.",
+       "missingcommenttext": "Tik asb. die opmerking.",
        "missingcommentheader": "'''Let op:''' U het geen onderwerp/opskrif vir die opmerking verskaf nie. As u weer op \"$1\" klik, sal u wysiging sonder die onderwerp/opskrif gestoor word.",
-       "summary-preview": "Opsommingsvoorskou:",
-       "subject-preview": "Onderwerp/opskrif voorskou:",
+       "summary-preview": "Voorskou van wysigingopsomming:",
+       "subject-preview": "Voorskou van onderwerp:",
        "blockedtitle": "Gebruiker is geblokkeer",
-       "blockedtext": "'''U gebruiker of IP-adres is geblokkeer.'''\n\nDie blokkade is deur $1 uitgevoer.\nDie rede verskaf is ''$2''.\n\n* Begin van blokkade: $8\n* Blokkade eindig: $6\n* Blokkade gemik teen: $7\n\nU mag $1 of een van die ander [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] kontak om dit te bespreek.\nU kan nie die 'e-pos hierdie gebruiker'-opsie gebruik nie, tensy 'n geldige e-posadres in u [[Special:Preferences|voorkeure]] gespesifiseer is en u nie geblokkeer is om dit te gebruik nie.\nU huidige IP-adres is $3 en die blokkadenommer is #$5.\nSluit asseblief een of albei hierdie verwysings by enige navrae in.",
+       "blockedtext": "<strong>U gebruikernaam of IP-adres is geblokkeer.</strong>\n\nDie blokkade is deur $1 uitgevoer.\nDie rede verskaf is <em>$2</em>.\n\n* Begin van blokkade: $8\n* Blokkade eindig: $6\n* Blokkade gemik teen: $7\n\nU mag $1 of een van die ander [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] kontak om dit te bespreek.\nU kan nie die \"{{int:emailuser}}\"-opsie gebruik nie, tensy 'n geldige e-posadres in u [[Special:Preferences|rekeningvoorkeure]] gespesifiseer is en u nie geblokkeer is om dit te gebruik nie.\nU huidige IP-adres is $3 en die blokkadenommer is #$5.\nSluit asseblief een of albei hierdie verwysings by enige navrae in.",
        "autoblockedtext": "U IP-adres is outomaties geblok omdat dit deur 'n gebruiker gebruik was, wat deur $1 geblokkeer is.\nDie rede verskaf is:\n\n:''$2''\n\n* Aanvang van blok: $8\n* Einde van blok: $6\n* Bedoelde blokkeerder: $7\n\nU kan die blok met $1 of enige van die [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] bespreek.\n\nNeem kennis dat u slegs die 'e-pos die gebruiker' funksionaliteit kan gebruik as u 'n geldige e-posadres het in u [[Special:Preferences|voorkeure]] het, en die gebruik daarvan is nie ook geblokkeer is nie.\n\nU huidige IP-adres is $3 en die blokkadenommer is #$5.\nVermeld asseblief die bovermelde bloknommer as u die saak rapporteer,",
        "blockednoreason": "geen rede verskaf nie",
        "whitelistedittext": "U moet $1 om bladsye te wysig.",
        "userpage-userdoesnotexist": "U is besig om 'n gebruikersblad wat nie bestaan nie te wysig (gebruiker \"<nowiki>$1</nowiki>\"). Maak asseblief seker of u die bladsy wil skep/ wysig.",
        "userpage-userdoesnotexist-view": "Die gebruiker \"$1\" is nie geregistreer nie.",
        "blocked-notice-logextract": "Hierdie gebruiker is tans geblokkeer.\nDie laaste inskrywing in die blokkeerlogboek word hieronder vertoon:",
-       "clearyourcache": "'''Neem kennis''': Na die wysiging is dit dalk nodig om u blaaier se kasgeheue verfris voordat u die veranderinge sal sien:\n* '''Firefox / Safari:''' hou ''Shift'' en kliek ''Reload'', of druk ''Ctrl-F5'' of ''Ctrl-R'' (''⌘-R'' op 'n Mac)\n* '''Google Chrome:''' druk ''Ctrl-Shift-R'' (''⌘-Shift-R'' op 'n Mac)\n* '''Internet Explorer:''' hou ''Ctrl'' en kliek ''Refresh'', of druk ''Ctrl-F5''\n* '''Opera:''' maak die kas skoon by ''Tools → Preferences''",
+       "clearyourcache": "<strong>Let wel</strong>: Na die wysiging is dit dalk nodig om u blaaier se kasgeheue te verfris voordat u die veranderinge sal sien:\n* <strong>Firefox / Safari:</strong> hou <em>Shift</em> en kliek <em>Herlaai</em>, of druk <em>Ctrl-F5</em> of <em>Ctrl-R</em> (<em>⌘-R</em> op 'n Mac)\n* <strong>Google Chrome:</strong> Druk <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> op 'n Mac)\n* <strong>Internet Explorer:</strong> Hou <em>Ctrl</em> en kliek <em>Refresh</em>, of druk <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Gaan na <em>Kieslys → Settings</em (<em>Opera → Preferences</em> op 'n Mac) en dan na <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''Wenk:''' Gebruik die \"{{int:showpreview}}\"-knoppie om u nuwe CSS te toets voor u dit stoor.",
        "userjsyoucanpreview": "'''Wenk:''' Gebruik die \"{{int:showpreview}}\"-knoppie om u nuwe JS te toets voor u dit stoor.",
        "usercsspreview": "'''Onthou hierdie is slegs 'n voorskou van u persoonlike CSS.'''\n'''Dit is nog nie gestoor nie!'''",
        "postedit-confirmation-created": "Die bladsy is geskep.",
        "postedit-confirmation-restored": "Die bladsy is teruggeplaas.",
        "postedit-confirmation-saved": "U wysigings is gestoor.",
+       "postedit-confirmation-published": "Die wysiging is gepubliseer.",
        "edit-already-exists": "Die bladsy is nie geskep nie.\nDit bestaan alreeds.",
        "defaultmessagetext": "Verstekteks",
        "content-failed-to-parse": "Dit was nie moontlik om die inhoud van die MIME-tipe $2 vir die model $1 te verwerk nie: $3.",
        "prefs-rc": "Onlangse wysigings",
        "prefs-watchlist": "Dophoulys",
        "prefs-editwatchlist": "Wysig dophoulys",
+       "prefs-editwatchlist-label": "Wysig inskrywings op u dophoulys:",
+       "prefs-editwatchlist-edit": "Sien en verwyder titels op u dophoulys",
+       "prefs-editwatchlist-raw": "Wysig rou dophoulys",
+       "prefs-editwatchlist-clear": "Vee u dophoulys uit",
        "prefs-watchlist-days": "Aantal dae om in dophoulys te wys:",
        "prefs-watchlist-days-max": "Maksimum $1 {{PLURAL:$1|dag|dae}}",
        "prefs-watchlist-edits": "Aantal wysigings om in uitgebreide dophoulys te wys:",
        "recentchangeslinked-feed": "Verwante veranderings",
        "recentchangeslinked-toolbox": "Verwante veranderings",
        "recentchangeslinked-title": "Wysigings verwant aan \"$1\"",
-       "recentchangeslinked-summary": "Hier volg 'n lys van wysigings wat onlangs gemaak is aan bladsye wat van die gespesifiseerde bladsy geskakel word (of van bladsye van die gespesifiseerde kategorie).\nBladsye op [[Special:Watchlist|u dophoulys]] word in '''vetdruk''' uitgewys.",
+       "recentchangeslinked-summary": "Tik 'n bladsynaam om veranderinge aan bladsye te sien wat daarvan of daarheen skakel. (Om inskrywings van 'n kategorie te sien, tik {{ns:category}}:Naam van kategorie). Veranderinge aan bladsye op [[Special:Watchlist|u dophoulys]] word in <strong>vetdruk</strong> aangedui.",
        "recentchangeslinked-page": "Bladsynaam:",
        "recentchangeslinked-to": "Besigtig wysigings aan bladsye met skakels na die bladsy",
        "recentchanges-page-added-to-category": "[[:$1]] by kategorie gevoeg",
        "filehist-filesize": "Lêergrootte",
        "filehist-comment": "Opmerking",
        "imagelinks": "Lêergebruik",
-       "linkstoimage": "Die volgende {{PLURAL:$1|bladsy|$1 bladsye}} gebruik hierdie prent:",
-       "linkstoimage-more": "Daar is meer as $1 {{PLURAL:$1|skakel|skakels}} na hierdie lêer.\nDie volgende lys vertoon slegs die eerste {{PLURAL:$1|skakel|$1 skakels}} wat na die lêer verwys.\n'n [[Special:WhatLinksHere/$2|Volledige lys]] is ook beskikbaar.",
-       "nolinkstoimage": "Daar is geen bladsye wat skakel na hierdie lêer nie.",
+       "linkstoimage": "Die volgende {{PLURAL:$1|bladsy|$1 bladsye}} gebruik dié lêer:",
+       "linkstoimage-more": "Meer as $1 {{PLURAL:$1|bladsy|bladsye}} gebruik dié lêer.\nDie volgende lys vertoon die eerste {{PLURAL:$1|bladsy|$1 bladsye}} wat slegs dié lêer gebruik.\n'n [[Special:WhatLinksHere/$2|Volledige lys]] is beskikbaar.",
+       "nolinkstoimage": "Daar is geen bladsye wat dié lêer gebruik nie.",
        "morelinkstoimage": "Wys [[Special:WhatLinksHere/$1|meer skakels]] na die lêer.",
        "linkstoimage-redirect": "$1 (lêeraanstuur) $2",
        "duplicatesoffile": "Die volgende {{PLURAL:$1|lêer is 'n duplikaat|$1 lêers is duplikate}} van die lêer ([[Special:FileDuplicateSearch/$2|meer details]]):",
        "watchlist-details": "Daar is {{PLURAL:$1|$1 bladsy|$1 bladsye}} in u dophoulys (besprekingsblaaie ingesluit).",
        "wlheader-enotif": "E-pos kennisgewings is aangeskakel.",
        "wlheader-showupdated": "Bladsye wat verander is sedert u hulle laas besoek het word in '''vetdruk''' uitgewys.",
-       "wlnote": "Hier volg die laaste {{PLURAL:$1|verandering<strong>$1</strong> veranderings}} binne die laaste {{PLURAL:$2|uur|<strong>$2</strong> ure}}, soos op $3 om $4.",
+       "wlnote": "Hier volg die laaste {{PLURAL:$1|verandering|<strong>$1</strong> veranderings}} binne die laaste {{PLURAL:$2|uur|<strong>$2</strong> ure}}, soos op $3 om $4.",
        "wlshowlast": "Wys afgelope $1 ure, $2 dae",
        "watchlist-hide": "Versteek",
        "watchlist-submit": "Wys",
        "version-libraries-license": "Lisensie",
        "version-libraries-description": "Beskrywing",
        "version-libraries-authors": "Outeurs",
-       "redirect": "Aanstuur volgens lêer, gebruiker, bladsy of weergawenommer",
-       "redirect-summary": "Hierdie spesiale bladsy stuur aan na 'n lêer (as 'n lêernaam verskaf word), 'n bladsy (as 'n weergawe-nommer verskaf word) of 'n gebruikersblad (as 'n gebruiker-ID verskaf word).",
+       "redirect": "Stuur aan volgens lêernaam, gebruiker-, bladsy-, weergawe-, of log-ID",
+       "redirect-summary": "Hierdie spesiale bladsy stuur aan na 'n lêer (as 'n lêernaam verskaf word), 'n bladsy (as 'n weergawe-ID of bladsy-ID verskaf word), 'n gebruikersblad (as 'n numeriese gebruiker-ID verskaf word) of 'n loginskrywing (as 'n log-ID verskaf word). Gebruik: [[{{#Special:Redirect}}/file/Voorbeeld.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] of [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "OK",
        "redirect-lookup": "Soek volgens:",
        "redirect-value": "Waarde:",
        "specialpage-securitylevel-not-allowed-title": "Nie toegestaan",
        "cannotauth-not-allowed-title": "Geen toegang",
        "cannotauth-not-allowed": "U word nie toegelaat om die bladsy te gebruik nie",
+       "changecredentials": "Wysig rekeninginligting",
+       "changecredentials-submit": "Wysig rekeninginligting",
+       "changecredentials-success": "U rekeninginligting is gewysig.",
+       "removecredentials": "Verwyder rekeninginligting",
+       "removecredentials-submit": "Verwyder rekeninginligting",
        "credentialsform-account": "Gebruikersnaam:",
        "edit-error-short": "Fout: $1",
-       "edit-error-long": "Foute:\n\n$1"
+       "edit-error-long": "Foute:\n\n$1",
+       "pagedata-bad-title": "Ongeldige titel: $1.",
+       "passwordpolicies-policy-minimalpasswordlength": "Wagwoord moet ten minste $1 {{PLURAL:$1|karakter|karakters}} lank wees",
+       "passwordpolicies-policy-minimumpasswordlengthtologin": "Wagwoord moet ten minste $1 {{PLURAL:$1|karakter|karakters}} lank wees om te kan aanmeld",
+       "passwordpolicies-policy-passwordcannotmatchusername": "Wagwoord mag nie dieselfde wees as gebruikernaam nie"
 }
index f860934..8285704 100644 (file)
        "customjsprotected": "Вы ня маеце правоў на рэдагаваньне гэтай старонкі JavaScript, таму што яна ўтрымлівае пэрсанальныя налады іншага ўдзельніка.",
        "sitecssprotected": "Вы ня маеце дазволу на рэдагаваньне гэтай CSS-старонкі, бо гэта можа паўплываць на ўсіх удзельнікаў.",
        "sitejsonprotected": "Вы ня маеце дазволу на рэдагаваньне гэтай JSON-старонкі, бо гэта можа паўплываць на ўсіх удзельнікаў.",
-       "sitejsprotected": "Вы ня маеце дазволу на рэдагаваньне гэтай JavaScript-старонкі, бо гэта можа паўплываць на ўсіх наведнікаў",
+       "sitejsprotected": "Вы ня маеце дазволу на рэдагаваньне гэтай JavaScript-старонкі, бо гэта можа паўплываць на ўсіх наведнікаў.",
        "mycustomcssprotected": "Вы ня маеце дазволу рэдагаваць гэтую CSS-старонку.",
        "mycustomjsonprotected": "Вы ня маеце дазволу на рэдагаваньне гэтай JSON-старонкі.",
        "mycustomjsprotected": "Вы ня маеце дазволу рэдагаваць гэтую JavaScript-старонку.",
        "backend-fail-describe": "Не атрымалася зьмяніць мэтазьвесткі для файлу «$1».",
        "backend-fail-alreadyexists": "Файл «$1» ужо існуе.",
        "backend-fail-store": "Немагчыма захаваць файл «$1» у «$2».",
-       "backend-fail-copy": "Немагчыма скапіяваць файл $1 у $2.",
+       "backend-fail-copy": "Немагчыма скапіяваць файл «$1» у «$2».",
        "backend-fail-move": "Немагчыма перанесьці файл $1 у $2.",
        "backend-fail-opentemp": "Немагчыма адкрыць часовы файл.",
        "backend-fail-writetemp": "Немагчыма запісаць часовы файл.",
index fe0bbef..08da373 100644 (file)
        "autoblockedtext": "Vaša IP-adresa automatski je blokirana jer ju je koristio drugi korisnik, a blokirao ju je $1.\nNaveden je sljedeći razlog:\n\n:''$2''\n\n* Početak blokade: $8\n* Kraj blokade: $6\n* Blokirani korisnik: $7\n\nMožete kontaktirati sa $1 ili nekim drugim iz grupe [[{{MediaWiki:Grouppage-sysop}}|administratora]] i zahtijevati da Vas deblokira.\n\nZapamtite da ne možete koristiti opciju \"pošalji e-mail ovom korisniku\" sve dok ne unesete validnu e-mail adresu pri registraciji u Vašim [[Special:Preferences|korisničkim postavkama]] i dok niste spriječeni (blokadom) da je koristite.\n\nVaša trenutna IP-adresa je $3, a ID blokade je $5.\nMolimo da navedete sve gore navedene detalje u zahtjevu za deblokadu.",
        "systemblockedtext": "MediaWiki je automatski blokirao Vaše korisničko ime ili IP-adresu.\nDat je sljedeći razlog:\n\n:<em>$2</em>\n\n* Početak blokade: $8\n* Istek blokade: $6\n* Blokada je namijenjena za: $7\n\nVaša trenutna IP-adresa je $3.\nAko imate pitanja u vezi s blokadom, priložite sve gorenavedene pojedinosti.",
        "blockednoreason": "razlog nije naveden",
-       "whitelistedittext": "Morate biti $1 da biste uređivali stranice.",
+       "whitelistedittext": "$1 da biste uređivali stranice.",
        "confirmedittext": "Morate potvrditi svoju adresu e-pošte prije nego počnete mijenjati stranice.\nPostavite i potvrdite svoju adresu e-pošte u [[Special:Preferences|korisničkim postavkama]].",
        "nosuchsectiontitle": "Ne mogu pronaći sekciju",
        "nosuchsectiontext": "Pokušali ste uređivati sekciju koja ne postoji.\nMožda je premještena ili obrisana dok ste pregledavali stranicu.",
        "loginreqtitle": "Potrebna je prijava",
-       "loginreqlink": "prijavljeni",
-       "loginreqpagetext": "Morate biti $1 da biste vidjeli druge stranice.",
+       "loginreqlink": "Prijavite se",
+       "loginreqpagetext": "$1 da biste vidjeli druge stranice.",
        "accmailtitle": "Šifra poslana.",
        "accmailtext": "Nasumično odabrana lozinka za [[User talk:$1|$1]] poslana je na $2. Lozinka se <em>[[Special:ChangePassword|može promijeniti]]</em> nakon prijave.",
        "newarticle": "(Novi)",
        "prefs-watchlist": "Spisak praćenja",
        "prefs-editwatchlist": "Uređivanje spiska praćenja",
        "prefs-editwatchlist-label": "Uređivanje spiska:",
-       "prefs-editwatchlist-edit": "Uredi spisak",
-       "prefs-editwatchlist-raw": "Napredno uredi spisak",
-       "prefs-editwatchlist-clear": "Isprazni spisak",
+       "prefs-editwatchlist-edit": "vidi i ukloni naslove sa spiska praćenja",
+       "prefs-editwatchlist-raw": "uredi sirov spisak praćenja",
+       "prefs-editwatchlist-clear": "očisti spisak praćenja",
        "prefs-watchlist-days": "Broj dana za prikaz u spisku praćenja:",
        "prefs-watchlist-days-max": "Najviše $1 {{PLURAL:$1|dan|dana}}",
        "prefs-watchlist-edits": "Najviše prikazanih izmjena na spisku praćenja:",
        "reuploaddesc": "Vrati me na formular za postavljanje datoteka.",
        "upload-tryagain": "Pošaljite izmijenjeni opis datoteke",
        "uploadnologin": "Niste prijavljeni",
-       "uploadnologintext": "Morate biti $1 kako biste postavljali datoteke.",
+       "uploadnologintext": "$1 da biste postavljali datoteke.",
        "upload_directory_missing": "Folder za postavljanje ($1) nedostaje i webserver ga ne može napraviti.",
        "upload_directory_read_only": "Folder za postavljanje ($1) na webserveru je postavljen samo za čitanje.",
        "uploaderror": "Greška pri slanju",
        "namespace_association": "Povezan imenski prostor",
        "tooltip-namespace_association": "Označite ovu kutiju da također uključite razgovor ili imenski prostor teme koja je povezana sa odabranim imenskim prostorom",
        "blanknamespace": "(glavni)",
-       "contributions": "{{GENDER:$1|Korisnički}} doprinosi",
-       "contributions-title": "Doprinosi korisnika $1",
+       "contributions": "Doprinosi {{GENDER:$1|korisnika|korisnice}}",
+       "contributions-title": "Doprinosi {{GENDER:$1|korisnika|korisnice}} $1",
        "mycontris": "Doprinosi",
        "anoncontribs": "Doprinosi",
        "contribsub2": "Za {{GENDER:$3|$1}} ($2)",
        "sp-contributions-newbies-sub": "Za nove korisnike",
        "sp-contributions-newbies-title": "Doprinosi novih korisnika",
        "sp-contributions-blocklog": "zapisnik blokiranja",
-       "sp-contributions-suppresslog": "obrisani {{GENDER:$1|korisnički}} doprinosi",
-       "sp-contributions-deleted": "obrisani {{GENDER:$1|korisnički}} doprinosi",
+       "sp-contributions-suppresslog": "izbrisani doprinosi {{GENDER:$1|korisnika|korisnice}}",
+       "sp-contributions-deleted": "izbrisani doprinosi {{GENDER:$1|korisnika|korisnice}}",
        "sp-contributions-uploads": "postavljanja",
        "sp-contributions-logs": "zapisnici",
        "sp-contributions-talk": "razgovor",
        "tooltip-pt-userpage": "{{GENDER:|Vaša}} korisnička stranica",
        "tooltip-pt-anonuserpage": "Korisnička stranica za ip koju Vi uređujete kao",
        "tooltip-pt-mytalk": "{{GENDER:|Vaša}} stranica za razgovor",
-       "tooltip-pt-anontalk": "Razgovor o doprinosu sa ove IP adrese",
+       "tooltip-pt-anontalk": "Rasprava o izmjenama s ove IP-adrese",
        "tooltip-pt-preferences": "{{GENDER:|Vaše}} postavke",
        "tooltip-pt-watchlist": "Spisak stranica koje pratite",
        "tooltip-pt-mycontris": "Spisak {{GENDER:|Vaših}} doprinosa",
        "tooltip-pt-anoncontribs": "Spisak izmjena napravljenih s ove IP-adrese",
-       "tooltip-pt-login": "Predlažemo da se prijavite, ali nije obvezno.",
+       "tooltip-pt-login": "Predlažemo vam da se prijavite, iako to nije obavezno",
        "tooltip-pt-login-private": "Morate se prijaviti da biste koristili ovaj wiki",
        "tooltip-pt-logout": "Odjavi me",
-       "tooltip-pt-createaccount": "Ohrabrujemo vas da otvorite nalog i prijavite se, međutim to nije obavezno",
+       "tooltip-pt-createaccount": "Predlažemo vam da napravite račun i prijavite se; međutim, to nije obavezno",
        "tooltip-ca-talk": "Razgovor o sadržaju",
        "tooltip-ca-edit": "Uredi ovu stranicu",
        "tooltip-ca-addsection": "Započni novu sekciju.",
index 3e55964..62a0ca9 100644 (file)
@@ -51,6 +51,7 @@
        "tog-watchlisthideminor": "囥起監視單其過幼修改",
        "tog-watchlisthideliu": "共已經登錄其用戶其編輯趁監視單𡅏囥起咯",
        "tog-watchlistreloadautomatically": "Sìng-tō̤ dèu-giông gāi-biéng sèng-hâiu cê̤ṳ-dông gĕng-sĭng gáng-sê-dăng (JavaScript diŏh kŭi lā̤)",
+       "tog-watchlistunwatchlinks": "Ké̤ṳk ô gĕng-gāi gì găng-sê hiĕk-miêng tiĕng-gă dĭk-ciék (chṳ̄-siĕu) găng-sê biĕu-gé ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}, diŏh ô JavaScript câ â̤ sāi)",
        "tog-watchlisthideanons": "共匿名其用戶其編輯趁監視單𡅏囥起咯",
        "tog-watchlisthidepatrolled": "共巡查其編輯趁監視單𡅏囥起咯",
        "tog-watchlisthidecategorization": "Káung kī hiĕk gì lôi-biék",
        "editfont-monospace": "蜀様寬其字體",
        "editfont-sansserif": "無襯線其字體",
        "editfont-serif": "有襯線其字體",
-       "sunday": "禮拜",
-       "monday": "拜一",
+       "sunday": "Lā̤-bái",
+       "monday": "Bái-ék",
        "tuesday": "Bái-nê",
        "wednesday": "拜三",
        "thursday": "拜四",
-       "friday": "拜五",
-       "saturday": "拜六",
-       "sun": "Lā̤ buái",
+       "friday": "Bái-ngô",
+       "saturday": "Bái-lĕ̤k",
+       "sun": "Lā̤-buái",
        "mon": "Buái ék",
-       "tue": "Bái-nê",
+       "tue": "B2",
        "wed": "Buái săng",
        "thu": "Buái sé",
        "fri": "Buái ngô",
        "jan": "1 ng.",
        "feb": "2 ng.",
        "mar": "3 ng.",
-       "apr": "四月",
-       "may": "五月",
+       "apr": "4 ng.",
+       "may": "5 ng.",
        "jun": "6 ng.",
-       "jul": "七月",
+       "jul": "7 ng.",
        "aug": "8 ng.",
        "sep": "9 ng.",
        "oct": "10 ng.",
        "mytalk": "我其討論",
        "anontalk": "攀講",
        "navigation": "Dô̤-hòng",
-       "and": "&#32;",
+       "and": "&#32;gê̤ṳng",
        "faq": "真稠碰著其問題",
        "actions": "動作",
        "namespaces": "Miàng-kŭng-găng",
        "navigation-heading": "Dô̤-hòng chái-dăng",
        "errorpagetitle": "綻咯",
        "returnto": "轉去$1。",
-       "tagline": "Lài-nguòng: {{SITENAME}}",
+       "tagline": "Chók-cê̤ṳ {{SITENAME}}",
        "help": "Bŏng-cô",
        "search": "Sìng-tō̤",
-       "searchbutton": "尋討",
+       "searchbutton": "Sìng-tō̤",
        "go": "去",
        "searcharticle": "Kó̤",
        "history": "頁面歷史",
-       "history_short": "歷史",
+       "history_short": "Lĭk-sṳ̄",
        "history_small": "歷史",
        "updatedmarker": "趁我最後蜀回訪問開始更新",
        "printableversion": "Kō̤ páh-éng bēng-buōng",
        "permalink": "Īng-giū lièng-giék",
        "print": "拍印",
-       "view": "覷蜀覷",
+       "view": "Ché̤ṳ siŏh ché̤ṳ",
        "view-foreign": "敆$1𡅏看",
        "edit": "Siŭ-gāi",
        "edit-local": "編輯當地描述",
        "talk": "Tō̤-lâung",
        "views": "Ché̤ṳ siŏh ché̤ṳ",
        "toolbox": "Gă-sĭ-huă",
+       "tool-link-userrights": "Gāi {{GENDER:$1|ê̤ṳng-hô}} hŭng-cū",
+       "tool-link-userrights-readonly": "Káng {{GENDER:$1|ê̤ṳng-hô}} hŭng-cū",
+       "tool-link-emailuser": "Siā-piĕ ké̤ṳk cī-ciáh {{GENDER:$1|ê̤ṳng-hô}}",
        "imagepage": "覷蜀覷文件頁面",
        "mediawikipage": "看消息頁",
        "templatepage": "看模板頁",
        "redirectedfrom": "(téng $1 tṳ̀ng-déng-hióng guó-lì)",
        "redirectpagesub": "重定向頁",
        "redirectto": "重定向遘",
-       "lastmodifiedat": "茲蜀頁是著$1地方,$2辰候之後修改其。",
+       "lastmodifiedat": "Cī-hiĕk muōi-muōi siŏh-huôi biĕng-cĭk găk $1 $2.",
        "viewcount": "茲蜀頁已經乞訪問$1回了。{{PLURAL:$1}}",
        "protectedpage": "保護頁",
        "jumpto": "Tiéu gáu:",
        "pool-timeout": "等待鎖定其時間遘了",
        "pool-queuefull": "隊列池已經滿了",
        "pool-errorunknown": "𣍐曉什乇綻咯",
+       "pool-servererror": "Mò̤ bêng-huák sāi tiàng-sê̤ṳ gié-só hŭk-ô ($1).",
        "poolcounter-usage-error": "Ê̤ṳng-huák chó̤-nguô: $1",
        "aboutsite": "Guăng-ṳ̀ {{SITENAME}}",
        "aboutpage": "Project:Guăng-ṳ̀",
        "versionrequired": "需要版本$1其MediaWiki",
        "versionrequiredtext": "需要MediaWiki其版本$1來使茲蜀頁。\n覷[[Special:Version|版本頁面]]。",
        "ok": "好",
-       "retrievedfrom": "Lài-nguòng: \"$1\"",
+       "retrievedfrom": "Lài-nguòng „$1“",
        "youhavenewmessages": "{{PLURAL:$3|汝有}}$1($2)。",
        "youhavenewmessagesfromusers": "汝有趁$3用戶($2)來其$1萆信息{{PLURAL:$3}}",
        "youhavenewmessagesmanyusers": "汝有趁雅価用戶($2)其$1信息",
        "viewsourceold": "看源代碼",
        "editlink": "siŭ-gāi",
        "viewsourcelink": "Káng nguòng-dâi-mā",
-       "editsectionhint": "修改段落:$1",
+       "editsectionhint": "Siŭ-gāi dâung-lŏk: $1",
        "toc": "Mŭk-liŏh",
        "showtoc": "顯示",
        "hidetoc": "囥起",
        "readonly_lag": "從數據庫跟上主數據庫其辰候,數據庫已經自動鎖定",
        "internalerror": "內部錯誤",
        "internalerror_info": "內部錯誤:$1",
+       "internalerror-fatal-exception": "\"$1\" lôi gì ngièng-dê̤ṳng liê-nguôi",
        "filecopyerror": "𣍐使趁「$1」𡅏複製文件遘「$2」。",
        "filerenameerror": "𣍐使共「$1」其名字改去「$2」。",
        "filedeleteerror": "𣍐使刪掉文件「$1」。",
        "delete-hook-aborted": "刪除乞鉤子拍斷咯。\n無給出解釋。",
        "no-null-revision": "𣍐使敆頁面$1𡅏新建空操作。",
        "badtitle": "呆其標題",
+       "title-invalid-empty": "Sū chīng-giù gì hiĕk-miêng biĕu-dà̤ siŏh-cê dŭ mò̤, hĕ̤k-chiā nâ ô miàng-kŭng-găng.",
+       "title-invalid-utf8": "Sū chīng-giù gì hiĕk-miêng biĕu-dà̤ hàng ô ù-hâu gì UTF-8 hù-hô̤.",
+       "title-invalid-interwiki": "Sū chīng-giù gì hiĕk-miêng biĕu-dà̤ hàng ô mò̤ bêng-huák găk biĕu-dà̤ lā̤ sāi gì kuá Wiki liêng-ciék.",
+       "title-invalid-talk-namespace": "Chīng-giù gì hiĕk-miêng biĕu-dà̤ īng-ê̤ṳng gì tō̤-lâung-hiĕk kō̤-nèng mò̤ còng-câi.",
        "perfcached": "下底其數據乞緩存固加可能伓是最新其。{{PLURAL:$1|$1條結果}}會敆緩存臺中討著。",
        "perfcachedts": "下底其數據已經緩存過了,最後更新遘$1。{{PLURAL:$4|$4條結果}}會敆緩存臺中討著。",
        "querypage-no-updates": "茲蜀頁其更新乞禁止了。\n數據嚽塊現刻時𣍐更新了。",
        "nosuchusershort": "無總款其用戶名「$1」。\n檢查汝其拼寫。",
        "nouserspecified": "汝著指定蜀萆用戶名。",
        "login-userblocked": "茲隻用戶已經乞封鎖去了。登錄是𣍐允許其。",
-       "wrongpassword": "密碼綻咯。\n起動再查蜀下。",
+       "wrongpassword": "Ê̤ṳng-hô-miàng hĕ̤k-chiā mĭk-mā dâng kó̤.\nKī-dâe̤ng gái că siŏh-hâ.",
        "wrongpasswordempty": "未拍入密碼。\n起動汝再試蜀下。",
        "passwordtooshort": "密碼著設最少{{PLURAL:$1|$1萆字符}}。",
        "password-name-match": "汝其密碼無能耐共汝其用戶名蜀模蜀樣。",
        "loginlanguagelabel": "語言:$1",
        "pt-login": "Láuk-diē",
        "pt-login-button": "躒入",
+       "pt-login-continue-button": "Gié-sṳ̆k láuk-diē",
        "pt-createaccount": "Kŭi sĭng dióng-hô̤",
        "pt-userlogout": "Láuk-chók",
        "php-mail-error-unknown": "PHP其mail()函數,𣍐曉什乇綻去。",
        "newpassword": "新密碼:",
        "retypenew": "確認密碼:",
        "resetpass_submit": "設置密碼再登錄",
+       "botpasswords": "Gĭ-ké-nè̤ng mĭk-mā",
+       "botpasswords-label-appid": "Gĭ-ké-nè̤ng miàng-cê:",
+       "botpasswords-label-create": "Cháung-gióng",
        "botpasswords-label-update": "Gĕng-sĭng",
+       "botpasswords-label-cancel": "Chṳ̄-siĕu",
+       "botpasswords-label-delete": "Săng-dṳ̀",
+       "botpasswords-label-resetpassword": "Tṳ̀ng-gāi mĭk-mā",
        "resetpass_forbidden": "密碼改𣍐來",
        "resetpass-no-info": "汝著登錄乍會使直接看茲蜀頁。",
        "resetpass-submit-loggedin": "修改密碼",
        "passwordreset-email": "電批地址:",
        "passwordreset-emailsentemail": "如果茲萆电子郵件地址共汝其賬號有關聯,噲密碼重設电子郵件會發過去。",
        "changeemail": "修改或者刪除電子郵件地址",
-       "changeemail-header": "修改賬戶電子郵件地址",
+       "changeemail-header": "Chiāng dèng lâ ciā dăng-dăng, siŭ-gāi nṳ̄ gì diêng-cṳ̄ iù-giông dê-cī, ṳ̀-guō nṳ̄ buóh siōng dṳ̀-lâi sū-iū ô liêng-ciék gáu nṳ̄ dióng-hô̤ gì diêng-cṳ̄ iù-giông dê-cī, cêu ng-sāi dèng sĭng diêng-cṳ̄ iù-giông dê-cī gì kĕ̤ng.",
        "changeemail-oldemail": "現刻時其電批地址:",
        "changeemail-newemail": "新其電批地址:",
        "changeemail-none": "(無)",
        "savearticle": "Bō̤-còng ciā hiĕk",
        "publishpage": "Huák-buó ùng-ciŏng",
        "publishchanges": "Huák-buó siŭ-gāi",
+       "savearticle-start": "Bō̤-còng ciā hiĕk…",
+       "savechanges-start": "Bō̤-còng siŭ-gāi…",
+       "publishpage-start": "Huák-buó hiĕk-miêng…",
+       "publishchanges-start": "Huák-buó siŭ-gāi…",
        "preview": "預覽",
        "showpreview": "顯示預覽",
        "showdiff": "看改變其部分",
        "anoneditwarning": "<strong>警告:</strong>汝固未登錄。\n儷是汝做出修改其話,汝其IP地址會敆編輯歷史裡勢公開。儷是汝<strong>[$1躒入]</strong>或者<strong>[$2註册新賬號]</strong>,汝其修改記錄會顯示汝其用戶名,固有其它其好處。",
        "anonpreviewwarning": "<em>汝固未躒入。儷是汝卜想保存茲蜀頁其修改,汝其IP地址會乞記著茲蜀頁其編輯歷史臺中。</em>",
-       "missingcommenttext": "起動敆下底拍蜀條評論。",
+       "missingcommenttext": "Kī-dâe̤ng páh siŏh-dèu bìng-lâung.",
        "summary-preview": "修改總結預覽:",
        "blockedtitle": "用戶乞封鎖了",
        "blockednoreason": "無掏出原因",
        "newarticletext": "汝已經跟鏈接跟遘無存在其頁面了。\n卜想創建頁面,敆下底其框框𡅏拍字(覷蜀覷[$1 幫助頁面]有無更更価其幫助)。\n如果汝是無注意來遘茲蜀萆頁面,篤囇汝其瀏覽器上其「返回」按鈕。",
        "anontalkpagetext": "<em>茲是未登錄其用戶討論頁面。</em>\n故此儂家著使數字IP來確定伊。\n總款其IP地址會乞雅価用戶共享。\n如果蜀隻未登錄其用戶見覺無關係其評論指向汝,起動[[Special:CreateAccount|開賬戶]]或者[[Special:UserLogin|登錄]]來避免以後共其它未登錄其用戶混蜀堆。",
        "noarticletext": "現在敆茲蜀頁𡅏無文字。汝會使敆其它其頁面𡅏[[Special:Search/{{PAGENAME}}|討蜀討茲蜀萆標題]],<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 討相關其記錄],或者[{{fullurl:{{FULLPAGENAME}}|action=edit}} 創建茲蜀頁]</span>。",
-       "clearyourcache": "'''注意:'''保存以後,汝可能固著刷新汝其瀏覽器緩存來看遘變化。\n* '''火狐/Safari:'''擪下''Shift''篤蜀篤''重新載入'',或者擪蜀擪''Ctrl+F5''或者''Ctrl+R'' (''⌘-R''敆Mac懸頂)\n* '''Google Chrome:'''擪''Ctrl+Shift+R''(敆Mac𡅏使''⌘-Shift-R'')\n* '''Internet Explorer:'''擪''Ctrl''其時候篤蜀篤''刷新'',或者擪''Ctrl+F5''\n* '''Opera:'''敆''工具→首選項''𡅏清除緩存",
+       "userpage-userdoesnotexist-view": "Ê̤ṳng-hô „$1“ gó muôi kŭi dióng-hô̤.",
+       "clearyourcache": "<strong>注意:</strong>保存以後,汝可能固著刷新汝其瀏覽器緩存來看遘變化。\n* <strong>火狐/Safari:</strong>擪下<em>Shift</em>篤蜀篤<em>重新載入</em>,或者擪蜀擪<em>Ctrl+F5</em>或者<em>Ctrl+R</em>(<em>⌘-R</em>敆Mac懸頂)\n* <strong>Google Chrome:</strong>擪<em>Ctrl+Shift+R</em>(敆Mac𡅏使<em>⌘-Shift-R</em>)\n* <strong>Internet Explorer:</strong>擪<em>Ctrl</em>其時候篤蜀篤<em>刷新</em>,或者擪<em>Ctrl+F5</em>\n* <strong>Opera:</strong>去<em>菜單→設定</em>(Mac是<em>Opera→偏好設定</em>)再去<em>隱私&安全性→清除瀏覽資料→已緩存其圖片共檔案</em>。",
        "note": "<strong>注意:</strong>",
        "previewnote": "'''記定茲若是蜀萆預覽。'''\n汝其改變固𡅏未保存!",
        "continue-editing": "繼續修改",
        "yourdiff": "差別",
        "readonlywarning": "<strong>警告:數據庫已經乞鎖定來保養去了,故此汝現刻時𣍐使保存汝其編輯。</strong>\n汝可能希望複製再粘貼汝其文字遘蜀萆文本文件𡅏,再共伊保存起咯。\n\n鎖定伊其系統管理員給出茲蜀萆解釋:$1",
        "protectedpagewarning": "''警告:茲蜀頁已經乞保護起去了,故此囇有管理員權力其用戶乍會使修改伊。'''\n最新其日誌已經敆下底提供來做參考:",
-       "semiprotectedpagewarning": "'''注意:''' 茲蜀頁已經乞保護起去了,故此囇有註冊其用戶乍會使修改伊。\n最新其日誌已經敆下底提供來做參考:",
+       "semiprotectedpagewarning": "<strong>Cé̤ṳ-é:</strong> cī-siŏh-hiĕk ī-gĭng ké̤ṳk bō̤-hô kī-kó̤ lāu, gó-chṳ̄ nâ ô cê̤ṳ-dông cê̤ṳ-nêng ê̤ṳng-hô câ â̤-sāi siŭ-gāi.\nCī-bŏng gì nĭk-cé dŭ găk â-dā̤, ké̤ṳk nṳ̄ chăng-kō̤:",
        "templatesused": "{{PLURAL:$1}}茲頁裏勢使其模板:",
        "templatesusedpreview": "茲萆預覽使其{{PLURAL:$1|模板}}:",
        "templatesusedsection": "茲蜀段使其{{PLURAL:$1|模板}}:",
        "histfirst": "最舊",
        "histlast": "最遲",
        "historysize": "({{PLURAL:$1|$1字節}})",
+       "historyempty": "(kĕ̤ng)",
        "history-feed-title": "修改歷史",
        "history-feed-description": "維基百科敆茲頁其修改歷史",
+       "history-feed-item-nocomment": "$1 găk $2",
        "rev-delundel": "㪗/藏",
+       "rev-showdeleted": "hiēng-sê",
+       "revdelete-show-file-submit": "Ciáng-sê",
+       "revdelete-hide-comment": "Biĕng-cĭk cáik-iéu",
        "revertmerge": "伓使合併",
        "history-title": "「$1」其修改歷史",
        "difference-title": "「$1」調整以後𣍐蜀樣其地方",
        "lineno": "Dâ̤ $1 hòng:",
        "compareselectedversions": "比並揀選版本",
        "showhideselectedversions": "顯/藏揀選其調整",
-       "editundo": "Chṳ̄-siĕu",
+       "editundo": "chṳ̄-siĕu",
+       "diff-empty": "(Mò̤ chă)",
        "searchresults": "Sìng-tō̤ giék-guō",
-       "searchresults-title": "Sìng-tō̤ \"$1\" gì giék-guō",
+       "searchresults-title": "Sìng-tō̤ „$1“ gì giék-guō",
        "prevn": "sèng $1 bĭk",
        "nextn": "âu $1 bĭk",
        "shown-title": "Mūi hiĕk hiēng-sê $1{{PLURAL:$1|bĭk giék-guō}}",
        "viewprevnext": "Káng ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "cī siŏh bĭh wiki ī-gĭng ô „$1“ mìng-chĭng gì dèu-mŭk",
+       "searchmenu-exists": "<strong>Cī-siŏh-bĭh wiki ī-gĭng ô dèu-mŭk hô̤ lā̤ „[[:$1]]“.</strong> {{PLURAL:$2|0=|Hĕ̤k-chiā gái káng-siŏh-káng tŏ̤-diŏh gì gì-tă nó̤h.}}",
        "searchprofile-articles": "Nô̤i-ṳ̀ng hiĕk",
        "searchprofile-images": "Dŏ̤-mùi-tā̤",
        "searchprofile-everything": "Sū-iū-nó̤h",
        "searchprofile-advanced": "Gŏ̤-ngék",
        "searchprofile-articles-tooltip": "Găk $1 lā̤ sìng-tō̤",
        "searchprofile-images-tooltip": "Sìng-tō̤ ùng-giông",
-       "search-result-size": "$1 ({{PLURAL:$2|$2 bĭk dăng-sṳ̀}})",
-       "search-redirect": "(趁$1重定向過來)",
+       "search-result-size": "$1 ({{PLURAL:$2|1 bĭh dăng-sṳ̀|$2 bĭh dăng-sṳ̀}})",
+       "search-result-category-size": "$1 giông nó̤h sṳ̆k-ṳ̀ cī-bĭh hŭng-lôi ($2 bĭh cṳ̄-hŭng-lôi, $3 bĭh ùng-giông)",
+       "search-redirect": "(téng $1 tṳ̀ng-déng-hióng guó-lì)",
+       "search-section": "(dâung-lŏk $1)",
+       "search-file-match": "(hù-hăk ùng-giông nô̤i-ṳ̀ng)",
        "search-suggest": "汝其意思是伓是:$1",
        "searchrelated": "相關其",
        "searchall": "全部",
        "saveprefs": "保存",
        "prefs-editing": "編輯",
        "searchresultshead": "尋討",
-       "recentchangescount": "這般改變其條目:",
+       "recentchangescount": "Cī-bŏng gì gāi-biéng, hiĕk-miêng lĭk-sṳ̄, nĭk-cé diē-sié, ê̤ṳ-siék hièng-sê nióh-uâi biĕng-cĭk:",
        "savedprefs": "汝其設定已經乞保存了。",
        "timezonelegend": "時區:",
        "localtime": "當地時間:",
        "timezoneregion-europe": "歐洲",
        "timezoneregion-indian": "印度洋",
        "timezoneregion-pacific": "太平洋",
-       "allowemail": "會肯別儂寄電批乞汝",
+       "allowemail": "Â̤ kīng biĕk-nè̤ng gié diêng-piĕ ké̤ṳk-nṳ̄",
        "prefs-searchoptions": "尋討",
        "prefs-namespaces": "命名空間",
        "prefs-files": "文件",
        "grouppage-suppress": "{{ns:project}}:監督員",
        "newuserlogpage": "Kŭi dióng-hô nĭk-cé",
        "action-edit": "修改茲蜀頁",
+       "enhancedrc-history": "lĭk-sṳ̄",
        "recentchanges": "Cī-bŏng gì gāi-biéng",
+       "recentchanges-legend": "Cī-bŏng gāi-biéng gì sōng-hâung",
        "recentchanges-summary": "敆維基茲頁跟蹤兹般其改變。",
        "recentchanges-label-newpage": "Cī siŏh bĭk siŭ-gāi cháung-gióng lāu sĭng hiĕk",
        "recentchanges-label-minor": "Cuòi sê siŏh bĭk guó-éu siŭ-gāi",
        "recentchanges-label-bot": "Cuòi sê gĭ-ké-nè̤ng siŭ-gāi gì",
+       "recentchanges-label-unpatrolled": "Cī-bĭh biĕng-cĭk gó muôi sùng-că",
        "recentchanges-label-plusminus": "Cī-bĭh hiĕk-miêng gāi-biéng gì ôi-nguòng-cū duâi-nâung",
+       "recentchanges-legend-heading": "<strong>Dù-liê:</strong>",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (chiāng chăng-kō̤ [[Special:NewPages|sĭng hiĕk-miêng chĭng-dăng]])",
        "rcfilters-legend-heading": "<strong>Gāng-siā liĕk-biēu:</strong>",
        "rcfilters-other-review-tools": "Gì-tă gì giēng-că gĕ̤ng-gê̤ṳ",
        "rcfilters-activefilters": "Gēng-chók-lì gì biĕng-cĭk",
        "rcfilters-liveupdates-button": "Sĭk-sì gĕng-sĭng",
        "rclistfrom": "Hiēng-sê téng $3 $2 gáu dāng gì sĭng gāi-biéng",
        "rcshowhideminor": "$1 guó-éu siŭ-gāi",
+       "rcshowhideminor-show": "Hiêng",
+       "rcshowhideminor-hide": "Iēng",
        "rcshowhidebots": "$1 gĭ-ké-nè̤ng",
+       "rcshowhidebots-show": "Hiêng",
+       "rcshowhidebots-hide": "Iēng",
        "rcshowhideliu": "$1 ī dĕng-gé gì ê̤ṳng-hô",
+       "rcshowhideliu-show": "Hiêng",
+       "rcshowhideliu-hide": "Iēng",
        "rcshowhideanons": "$1 ù-mìng-sê",
+       "rcshowhideanons-show": "Hiêng",
+       "rcshowhideanons-hide": "Iēng",
        "rcshowhidemine": "$1 nguāi gì siŭ-gāi",
+       "rcshowhidemine-show": "Hiêng",
+       "rcshowhidemine-hide": "Iēng",
        "rclinks": "Hiēng-sê có̤i-gê̤ṳng $2 gĕ̤ng ī-nô̤i gì $1 huòi gāi-biéng",
        "diff": "chă",
        "hist": "sṳ̄",
-       "hide": "",
-       "show": "",
+       "hide": "Iēng",
+       "show": "Hiêng",
        "minoreditletter": "~",
        "newpageletter": "!",
        "boteditletter": "^",
        "recentchangeslinked": "相關其改變",
        "recentchangeslinked-feed": "相關其改變",
        "recentchangeslinked-toolbox": "Sŏng-guăng gì gāi-biéng",
+       "recentchangeslinked-title": "Gê̤ṳng „$1“ ô guăng-hiê gì gāi-biéng",
        "recentchangeslinked-page": "頁面名:",
        "recentchangeslinked-to": "Hiēng-sê liêng-ciék gáu cī-dêng hiĕk-miêng gì gāi-biéng",
        "upload": "Siông-diòng ùng-giông",
        "listfiles_size": "尺寸",
        "file-anchor-link": "Ùng-giông",
        "filehist": "Ùng-giông lĭk-sṳ̄",
+       "filehist-revert": "Huàng-nguòng",
        "filehist-current": "hiêng-káik-sì",
        "filehist-datetime": "Nĭk-gĭ/Sì-găng",
+       "filehist-thumb": "Sáuk-liŏk-dù",
+       "filehist-thumbtext": "$1 bēng-buōng gì sáuk-liŏk-dù",
+       "filehist-nothumb": "Mò̤ sáuk-liŏk-dù",
        "filehist-user": "Ê̤ṳng-hô",
        "filehist-dimensions": "Chióh-cháung",
        "filehist-comment": "Suók-mìng",
        "imagelinks": "Ùng-giông sāi-ê̤ṳng cìng-huóng",
-       "linkstoimage": "Â-dā̤ {{PLURAL:$1|$1 hiĕk}} lièng gáu ciā ùng-giông:",
-       "nolinkstoimage": "無鏈接遘茲蜀萆文件其頁面。",
+       "linkstoimage": "Â-dā̤ {{PLURAL:$1|$1 hiĕk ô sāi}} ciā ùng-giông:",
+       "nolinkstoimage": "Mò̤ sāi cī-bĭh ùng-giông gì hiĕk-miêng.",
+       "sharedupload-desc-here": "Cī-siŏh-bĭh ùng-giông téng $1 lì, bĕng-chiā ô kō̤-nèng ké̤ṳk gì-tă cuŏng-áng sāi.\nÂ-dā̤ sê cī-bĭh ùng-giông găk [$2 ùng-giông mièu-sŭk hiĕk-miêng] gì mièu-sŭk nô̤i-ṳ̀ng.",
        "uploadnewversion-linktext": "上傳蜀萆新版本其茲萆文件。",
        "shared-repo-name-wikimediacommons": "Wikimedia Commons",
        "upload-disallowed-here": "汝無年呆覆蓋茲萆文件。",
        "withoutinterwiki": "Mò̤ ngṳ̄-nièng lièng-giék gì hiĕk",
        "withoutinterwiki-summary": "下底其頁面無鏈接遘其它語言其版本。",
        "fewestrevisions": "修改最少其頁面",
-       "nbytes": "$1{{PLURAL:$1}} bĭk dăng-sṳ̀",
+       "nbytes": "$1 {{PLURAL:$1|ôi-nguòng-cū}}",
        "nlinks": "$1 ciáh {{PLURAL:$1|lièng-giék}}",
        "nmembers": "$1隻成員{{PLURAL:$1}}",
        "wantedcategories": "卜挃其類別",
        "ancientpages": "最舊其頁面",
        "move": "移動",
        "movethispage": "移動茲頁",
-       "booksources": "書源",
+       "pager-newer-n": "Bĭ-gáu sĭng gì $1 huòi",
+       "booksources": "Dù-cṳ̆ cṳ̆-nguòng",
        "booksources-search-legend": "尋討書源",
+       "booksources-search": "Sìng-tō̤",
        "booksources-text": "下底是鏈接遘其它賣新書共舊書其站點其單單,固加可能有更多關於汝敆𡅏看其茲本書其信息:",
-       "specialloguserlabel": "表演者:",
+       "specialloguserlabel": "Có̤ gì nè̤ng:",
        "speciallogtitlelabel": "目標(頭銜或者{{ns:user}}:用戶其用戶名):",
        "log": "日誌",
+       "all-logs-page": "Sū-iū gŭng-kŭi nĭk-cé",
        "alllogstext": "所有會使趁{{SITENAME}}獲得其日誌其都合併顯示。\n汝會使使揀選日誌類型、用戶名(大小寫敏感),或者受影響其頁面(大小寫敏感)其方法來縮小視角。",
        "logempty": "日誌裏勢討要𣍐着項目",
        "allpages": "所有頁面",
        "deletedcontributions-title": "乞刪唻其用戶貢獻",
        "linksearch-ok": "尋討",
        "linksearch-line": "$1是趁$2𡅏鏈接過其",
+       "listgrouprights-members": "(sìng-uòng chĭng-dăng)",
        "emailuser": "寄電批乞茲隻用戶",
        "defemailsubject": "{{SITENAME}}其用戶「$1」寄來其批",
        "noemailtitle": "無電批地址",
        "mywatchlist": "我其監視單",
        "nowatchlist": "汝其監視單𡅏無項目。",
        "watchnologin": "未躒入",
-       "addedwatchtext": "頁面「[[:$1]]」已經加遘汝其[[Special:Watchlist|監視單]]。以後敆茲蜀頁其改變共伊關聯其討論頁都會列敆嚽塊。",
+       "addedwatchtext": "„[[:$1]]“ gê̤ṳng ĭ gì tō̤-lâung-hiĕk dŭ gă-diē nṳ̄ gì [[Special:Watchlist|găng-sê-dăng]] kó̤-lāu.",
        "removewatch": "趁汝其監視單臺中移去",
-       "removedwatchtext": "頁面「[[:$1]]」已經趁[[Special:Watchlist|汝其監視單]]移去了。",
+       "removedwatchtext": "„[[:$1]]“ gê̤ṳng ĭ gì tō̤-lâung-hiĕk dŭ téng nṳ̄ gì [[Special:Watchlist|găng-sê-dăng]] dṳ̀-kó̤ lāu.",
        "watch": "監視",
        "watchthispage": "監視茲頁",
        "unwatch": "伓使監視",
        "unwatchthispage": "停止監視",
-       "watchlist-details": "{{PLURAL:$1|$1頁|$1頁}}敆汝其監視單𡅏,無單獨算討論頁。",
+       "watchlist-details": "Nṳ̄ gì găng-sê-dăng {{PLURAL:$1|ô $1 gó|dŭ liāng ô $1 gó}} hiĕk-miêng (bău-guák tō̤-lâung-hiĕk).",
        "wlshowlast": "顯示最$1點鐘$2日",
        "watchlist-options": "監視單選項",
        "watching": "監視...",
        "editcomment": "修改評論是:<em>$1</em>。",
        "revertpage": "[[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]])所做其修改轉去[[User:$1|$1]]其前蜀萆版本",
        "protectlogpage": "保護日誌",
+       "protectedarticle": "ī bō̤-hô „[[$1]]“",
        "protect-title": "改變「$1」其保護等級",
        "prot_1movedto2": "[[$1]]移遘[[$2]]",
        "protect-legend": "確認保護",
        "protectcomment": "原因:",
+       "protect-default": "Ṳ̄ng-hṳ̄ sū-iū ê̤ṳng-hô",
        "protect-level-autoconfirmed": "囇允許自動確認用戶",
        "protect-level-sysop": "囇允許管理員",
        "protect-expiry-options": "1點鐘:1 hour,1 日:1 day,1禮拜:1 week,2禮拜:2 weeks,1間月日:1 month,3間月日:3 months,6間月日:6 months,1年:1 year,永遠:infinite",
        "undelete-search-submit": "尋討",
        "namespace": "Miàng-kŭng-găng:",
        "invert": "Huāng-sōng",
-       "blanknamespace": "(ciō-iéu)",
+       "blanknamespace": "(Ciō-iéu)",
        "contributions": "{{GENDER:$1|User}}用戶貢獻",
        "contributions-title": "$1其用戶貢獻",
        "mycontris": "我其貢獻",
+       "anoncontribs": "Góng-hióng",
+       "contribsub2": "{{GENDER:$3|$1}} gì góng-hióng ($2)",
        "uctop": "(當前)",
        "month": "趁月(共更早):",
        "year": "趁年(共更早):",
        "linkshere": "下底其頁面鏈接遘'''$2''':",
        "nolinkshere": "無頁鏈接遘'''$2'''。",
        "isredirect": "重定向頁面",
+       "istemplate": "káng-ĭk",
        "isimage": "文件鏈接",
        "whatlinkshere-prev": "{{PLURAL:$1|前|前$1}}",
        "whatlinkshere-next": "{{PLURAL:$1|下|下$1}}",
        "whatlinkshere-links": "← 鏈接",
        "whatlinkshere-hideredirs": "$1重定向",
+       "whatlinkshere-hidetrans": "$1 īng-ê̤ṳng",
        "whatlinkshere-hidelinks": "$1鏈接",
        "whatlinkshere-hideimages": "$1 文件鏈接",
        "whatlinkshere-filters": "過濾器",
        "blockip": "封鎖{{GENDER:$1|用戶}}",
-       "blockiptext": "使下底其表單來封鎖趁指定IP地址或者用戶名其寫入訪問。茲囇使廮𡅏防止破壞,固加著符合[[{{MediaWiki:Policy-url}}|政策]]。敆下底填入指定其原因(比如講:引用乞破壞其頁面)。",
+       "blockiptext": "Dèng lâ â-dā̤ gì dăng-dăng, kō̤-ī dṳ̀-kó̤ dĕk-dêng IP dê-cī hĕ̤k-chiā ê̤ṳng-hô-miàng biĕng-cĭk gì guòng-âing. \nCuòi nâ-sāi ĕng lā̤ huòng-cī pó-huâi, gó gă diŏh hù-hăk [[{{MediaWiki:Policy-url}}|huŏng-cĕng gê̤ṳng céng-cháik]]. \nChiāng găk â-dā̤ dèng-siā gê̤ṳ-tā̤ gì lī-iù, bī-ṳ̀ gōng, īng-sŭk siŏh-piĕng ké̤ṳk pó-huâi gì hiĕk-miêng.\nNṳ̄ kō̤-ī sāi [//cdo.wikipedia.org/wiki/ù lôi-biék mĭk-găng lô-iù CIDR] ngṳ̄-huák gáik-sék hŭng-sō̤ IP huâng-ùi, IPv4 dék duâi ṳ̄ng-hṳ̄ gì huâng-ùi sê /$1, IPv6 sê  /$2.",
        "ipaddressorusername": "IP地址或者用戶名:",
        "ipbexpiry": "過期:",
        "ipbreason": "原因:",
        "databasenotlocked": "茲數據庫無鎖。",
        "move-page-legend": "移動頁面",
        "movepagetext": "使下底其表單重新乞茲蜀頁起蜀萆名字,移動伊共伊所有其歷史遘伊其新名字。\n舊其標題會變成新其標題其重定向頁。\n汝會使自動更新重定向許蜀點遘原底其標題。\n如果伊結果伓是總款其話,汝著檢查蜀下[[Special:DoubleRedirects|雙重重定向]]或者[[Special:BrokenRedirects|獃其重定向]]。\n汝有責任讓頁面鏈接遘正確其地方。\n\n注意儷是許塊已經有蜀隻頁面,噲就'''無能耐'''移動過了,除開噲儷是蜀萆重定向並且無舊底其修改歷史。\n嚽其意思就是講儷是汝名字起綻了,汝會使將茲蜀萆頁面重新起伊原底其名字,但是𣍐使覆蓋已經有其頁面。\n\n<strong>注意:</strong>\n嚽可能會對一般頁面造成盡大其並且無能耐想遘其改變;\n起動汝著敆做之前會意總款做其後果。",
-       "movepagetalktext": "相關其討論頁會自動共伊移遘'''無挃''':\n* 汝其新其用戶名已經有蜀頁有內容其討論頁,或者\n* 汝取消下底其框框。\n\n若總款,汝會使自家移動或者是合併頁面。",
+       "movepagetalktext": "Nâ gău-sōng cī-bĭh huŏng-kuái, siŏng-guăng gì tō̤-lâung-hiĕk â̤ cê̤ṳ-dông gê̤ṳng cī-siŏh-hiĕk iè gáu sĭng gì sū-câi, dṳ̀-hĭ sĭng gì sū-câi ī-gĭng ô siŏh-bĭh tō̤-lâung-hiĕk còng-câi.\nNâ sê dŏng-cĭng ô hiĕk-miêng còng-câi, nṳ̄ diŏh cê-gă iè-dông ī-gĭng còng-câi gì hiĕk-miêng, hĕ̤k-chiā ciŏng ciā lâng-gì hăk siŏh-dŏi.",
        "movenologintext": "著[[Special:UserLogin|躒入]]才有能耐移動頁面。",
        "newtitle": "新題目:",
        "move-watch": "監視茲頁",
        "movelogpagetext": "下底是乞移動過其頁其單單。",
        "movereason": "原因:",
        "delete_and_move_confirm": "正式,刪掉茲蜀頁",
+       "export": "Dô̤-chók hiĕk-miêng",
        "allmessages": "系統消息",
        "allmessagesname": "名",
        "allmessagesdefault": "默認其消息文字",
        "tooltip-pt-preferences": "{{GENDER:|汝其}}設定",
        "tooltip-pt-watchlist": "汝監視其頁面有改過其單單",
        "tooltip-pt-mycontris": "{{GENDER:|汝其}}貢獻其單單",
-       "tooltip-pt-login": "Gióng-ngiê nṳ̄ sĕng láuk-diē; bók-guó nàng-gă mò̤ ăng nṳ̄ cūng-kuāng có̤.",
+       "tooltip-pt-login": "Gióng-ngiê nṳ̄ sĕng láuk-diē; bók-guó nàng-gă mò̤ ăng nṳ̄ cūng-kuāng có̤",
        "tooltip-pt-logout": "Láuk-chók",
-       "tooltip-pt-createaccount": "Gióng-ngiê nṳ̄ sĕng kŭi dióng-hô gái láuk-diē; bók-guó nàng-gă mò̤ ăng nṳ̄ cūng-kuāng có̤.",
+       "tooltip-pt-createaccount": "Gióng-ngiê nṳ̄ sĕng kŭi dióng-hô gái láuk-diē; bók-guó nāng-gă mò̤ ăng nṳ̄ cūng-kuāng có̤",
        "tooltip-ca-talk": "Nô̤i-ṳ̀ng gì tō̤-lâung",
        "tooltip-ca-edit": "Siŭ-gāi cī hiĕk",
        "tooltip-ca-addsection": "Gă sĭng dâung",
        "tooltip-p-logo": "Ché̤ṳ-siŏh-ché̤ṳ tàu-hiĕk",
        "tooltip-n-mainpage": "Ché̤ṳ-siŏh-ché̤ṳ tàu-hiĕk",
        "tooltip-n-mainpage-description": "Ché̤ṳ-siŏh-ché̤ṳ tàu-hiĕk",
-       "tooltip-n-portal": "Guăng-ṳ̀ ciā gĕ̤ng-tiàng, nṳ̄ â̤ có̤ gì, kó̤ diē-nē̤ tō̤ nó̤h",
+       "tooltip-n-portal": "Guăng-ṳ̀ ciā gŭng-tiàng, nṳ̄ â̤ có̤ gì, kó̤ diē-nē̤ tō̤ nó̤h",
        "tooltip-n-recentchanges": "Cī-bŏng diŏh wiki ô gāi-biéng gì dăng-dăng",
        "tooltip-n-randompage": "Sùi-biêng muōng ché̤ṳ",
        "tooltip-n-help": "Sìng-tō̤ bŏng-cô gì sū-câi",
        "tooltip-t-whatlinkshere": "Ciòng-buô lièng-gáu cŭ-uái gì wiki hiĕk-miêng dăng-dăng",
-       "tooltip-t-recentchangeslinked": "鏈遘茲頁其頁面其最近修改\nCī hiĕk lièng gáu bĕk hiĕk gì cī-bŏng gì gāi-biéng",
+       "tooltip-t-recentchangeslinked": "Cī hiĕk lièng gáu bĕk hiĕk gì cī-bŏng gì gāi-biéng",
+       "tooltip-feed-atom": "Cī-siŏh-hiĕk gì Atom lài-nguòng",
        "tooltip-t-contributions": "{{GENDER:$1|茲蜀隻用戶}}其貢獻單單",
        "tooltip-t-emailuser": "向{{GENDER:$1|茲蜀隻用戶}}寄電批",
        "tooltip-t-upload": "Siông-diòng ùng-giông",
        "tooltip-t-specialpages": "Cuòng-buô dĕk-sṳ̀-hiĕk dăng-dăng",
-       "tooltip-t-print": "Cī hiĕk gì â̤ páh-éng bēng-buōng",
-       "tooltip-t-permalink": "茲頁茲版本其永久鏈接\nCī hiĕk cī bēng-buōng gì īng-giū lièng-giék",
+       "tooltip-t-print": "Cī-hiĕk â̤ páh-éng gì bēng-buōng",
+       "tooltip-t-permalink": "Cī hiĕk cī bēng-buōng gì īng-giū lièng-giék",
        "tooltip-ca-nstab-main": "Káng iĕk gì nô̤i-ṳ̀ng",
        "tooltip-ca-nstab-user": "覷蜀覷用戶頁",
-       "tooltip-ca-nstab-special": "茲是蜀萆特殊頁,𣍐使修改。",
+       "tooltip-ca-nstab-special": "Cuòi sê dĕk-sṳ̀-hiĕk, mâ̤-sāi siŭ-gāi",
        "tooltip-ca-nstab-project": "看工程頁",
        "tooltip-ca-nstab-image": "Ché̤ṳ ùng-giông hiĕk",
+       "tooltip-ca-nstab-mediawiki": "Káng hiê-tūng séng-sék",
        "tooltip-ca-nstab-template": "覷蜀覷模板",
+       "tooltip-ca-nstab-category": "Káng hŭng-lôi-hiĕk",
        "tooltip-minoredit": "共茲標記成過幼修改",
        "tooltip-save": "保存汝其改變 [alt-s]",
        "tooltip-preview": "先覷蜀下汝其改變,起動汝敆保存之前先使蜀使嚽。",
        "tooltip-watch": "共茲蜀頁加遘汝其監視單[alt-w]",
+       "tooltip-rollback": "Dók „huàng-nguòng“ cêu kō̤-ī huàng-nguòng gáu sèng-siŏh-ciáh nè̤ng gì bēng-buōng",
+       "tooltip-summary": "Siā nék-giāng cáik-iéu",
        "anonymous": "{{SITENAME}}其無名{{PLURAL:$1|用戶}}",
        "lastmodifiedatby": "茲頁最後是$3著$1$2改變其。",
+       "pageinfo-title": "„$1“ gì séng-sék",
+       "pageinfo-header-basic": "Gĭ-buōng séng-sék",
+       "pageinfo-header-restrictions": "Bō̤-hô hiĕk-miêng",
+       "pageinfo-display-title": "Hiēng-sê biĕu-dà̤",
+       "pageinfo-article-id": "Hiĕk-miêng ID",
+       "pageinfo-robot-policy": "Niông gĭ-ké-nè̤ng có̤ sáuk-īng",
+       "pageinfo-robot-noindex": "Mâ̤-sāi",
+       "pageinfo-watchers": "Găng-sê ciā hiĕk gì nè̤ng-só",
        "pageinfo-toolboxlink": "Hiĕk-miêng séng-sék",
+       "pageinfo-contentpage-yes": "Ciáng-sê",
        "deletedrevision": "刪掉舊其版本$1",
        "previousdiff": "← 舊其修改",
        "nextdiff": "新其修改 →",
+       "file-info-size-pages": "$1 × $2 chuông-só, ùng-giông duâi-nâung: $3, MIME lôi-hìng: $4, $5 {{PLURAL:$5|hiĕk}}",
        "file-nohires": "無更高決斷",
-       "show-big-image": "原底其文件",
-       "show-big-image-preview": "茲萆預覽其尺寸:$1.",
-       "show-big-image-other": "其他{{PLURAL:$2|分辨率}}:$1。",
+       "show-big-image": "Nguòng-dā̤ gì ùng-giông",
+       "show-big-image-preview": "Ê̤ṳ-lāng chék-cháung:$1.",
+       "show-big-image-other": "Gì-tă {{PLURAL:$2|hŭng-biêng-lŭk}}: $1.",
        "show-big-image-size": "$1 × $2 chiông-só",
        "ilsubmit": "尋討",
        "bydate": "按日期",
        "metadata": "Nguòng-só-gé̤ṳ",
        "metadata-help": "茲萆文件臺中有多餘其信息,可能是數碼相機或者掃描儀敆創建或者數字化其過程臺中添加其。如果文件趁初始狀態開始就已經受遘修改,噲有其詳細說明可能無法反映修改以後其文件。",
        "metadata-expand": "顯示詳細資料",
+       "exif-orientation": "Huōng-ôi",
+       "exif-make": "Kák-sióng-gĭ cié-cô̤-siŏng",
+       "exif-model": "Kák-sióng-gĭ hìng-hô̤",
+       "exif-software": "Sāi gì nuōng-giông",
+       "exif-exifversion": "Exif bēng-buōng",
+       "exif-colorspace": "Sáik-chāi kŭng-găng",
+       "exif-orientation-1": "Biĕu-cūng",
        "exif-componentsconfiguration-0": "無存在",
        "exif-meteringmode-0": "𣍐八",
        "exif-lightsource-0": "𣍐八",
        "imgmultipageprev": "← 前蜀頁",
        "imgmultipagenext": "下蜀頁 →",
        "imgmultigo": "去!",
+       "imgmultigoto": "Kó̤ {{PLURAL:$1|tàu-siŏh-hiĕk|dâ̤ $1 hiĕk}}",
        "ascending_abbrev": "升",
        "descending_abbrev": "降",
        "table_pager_next": "下蜀頁",
        "watchlisttools-view": "看相關改變",
        "watchlisttools-edit": "看共修改監視單",
        "watchlisttools-raw": "修改原始監視單",
+       "redirect-submit": "Kó̤",
+       "redirect-user": "Ê̤ṳng-hô ID",
+       "redirect-page": "Hiĕk-miêng ID",
+       "redirect-file": "Ùng-gióng miàng",
        "specialpages": "Dĕk-sṳ̀-hiĕk",
-       "tag-filter": "[[Special:Tags|標籤]]過濾器:",
-       "tag-list-wrapper": "([[Special:Tags|$1萆標籤]]:$2)",
-       "searchsuggest-search": "Tō̤ {{SITENAME}}"
+       "tag-filter": "Áng [[Special:Tags|biĕu-chiĕng]] tō̤:",
+       "tag-list-wrapper": "([[Special:Tags|$1 bĭh biĕu-chiĕng]]: $2)",
+       "tags-active-yes": "Ciáng-sê",
+       "tags-active-no": "Ng-sê",
+       "logentry-move-move": "$1 ī-gĭng ciŏng hiĕk-miêng $3 {{GENDER:$2|iè-dông}} gáu $4",
+       "logentry-newusers-create": "Ê̤ṳng-hô dióng-hô $1 {{GENDER:$2|kŭi-hō̤}} lāu",
+       "logentry-upload-upload": "$1 {{GENDER:$2|ī siông-diòng}} $3",
+       "searchsuggest-search": "Tō̤ {{SITENAME}}",
+       "duration-days": "$1 gĕ̤ng"
 }
index c66aa07..ca46c54 100644 (file)
        "longpageerror": "'''Σφάλμα: Το κείμενο που καταχωρήσατε έχει μήκος {{PLURAL:$1|ένα kilobyte|$1 kilobytes}}, το οποίο είναι μεγαλύτερο από το μέγιστο {{PLURAL:$2|του ενός kilobyte|των $2 kilobytes}}.'''\nΔεν μπορεί να αποθηκευτεί.",
        "readonlywarning": "'''Προειδοποίηση: Η βάση δεδομένων έχει κλειδωθεί για συντήρηση, έτσι δεν θα μπορέσετε να αποθηκεύσετε τις επεξεργασίες σας αυτή τη στιγμή.'''\nΜπορείτε αν θέλετε να μεταφέρετε με αντιγραφή-επικόλληση το κείμενό σας σε αρχείο κειμένου και να το αποθηκεύσετε για αργότερα.\n\nΟ διαχειριστής που την κλείδωσε έδωσε την εξής εξήγηση: $1",
        "protectedpagewarning": "'''Προειδοποίηση: Αυτή η σελίδα έχει κλειδωθεί ώστε μόνο χρήστες με δικαιώματα διαχειριστή μπορούν να την επεξεργαστούν.'''\nΗ πιο πρόσφατη καταχώρηση στο αρχείο καταγραφής παρέχεται παρακάτω για αναφορά:",
-       "semiprotectedpagewarning": "<strong>Σημείωση:<strong> Αυτή η σελίδα έχει προστατευθεί ώστε μόνο αυτοεπιβεβαιωμένοι χρήστες μπορούν να την επεξεργαστούν.\nΗ πιο πρόσφατη καταχώρηση στο αρχείο καταγραφής παρέχεται παρακάτω για αναφορά:",
+       "semiprotectedpagewarning": "<strong>Σημείωση:</strong> Αυτή η σελίδα έχει προστατευτεί ώστε μόνο αυτοεπιβεβαιωμένοι χρήστες μπορούν να την επεξεργαστούν.\nΗ πιο πρόσφατη καταχώρηση στο αρχείο καταγραφής παρέχεται παρακάτω για αναφορά:",
        "cascadeprotectedwarning": "<strong>Προσοχή:</strong> Αυτή η σελίδα έχει κλειδωθεί ώστε μόνο χρήστες με [[Special:ListGroupRights|συγκεκριμένα δικαιώματα]] να μπορούν να την επεξεργαστούν, επειδή περιλαμβάνεται {{PLURAL:$1|στην ακόλουθη|στις ακόλουθες}} διαδοχικά (cascaded) {{PLURAL:$1|προστατευμένη σελίδα|προστατευμένες σελίδες}}:",
        "titleprotectedwarning": "'''Προειδοποίηση: Αυτή η σελίδα έχει κλειδωθεί ώστε χρειάζονται [[Special:ListGroupRights|ειδικά δικαιώματα]] για να δημιουργηθεί.'''\nΗ πιο πρόσφατη καταχώρηση στο αρχείο καταγραφής παρέχεται παρακάτω για αναφορά:",
        "templatesused": "{{PLURAL:$1|Πρότυπο που χρησιμοποιείται|Πρότυπα που χρησιμοποιούνται}} σε αυτή τη σελίδα:",
index 1ebf800..117a336 100644 (file)
        "confirm-unwatch-top": "Quere eliminar esta páxina da lista de vixilancia?",
        "confirm-rollback-button": "Aceptar",
        "confirm-rollback-top": "Quere reverter as edicións desta páxina?",
+       "confirm-mcrundo-title": "Desfacer un cambio",
+       "mcrundofailed": "Erro ao desfacer",
+       "mcrundo-missingparam": "Faltan parámetros obrigatorios na solicitude.",
+       "mcrundo-changed": "A páxina foi mudada dende que veu o diff. Por favor, revise o novo cambio.",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
        "edit-error-long": "Erros:\n\n$1",
        "revid": "revisión $1",
        "pageid": "identificador de páxina $1",
-       "interfaceadmin-info": "$1\n\nOs permisos para editar ficheiros globais de CSS/JS/JSON separáronse recentemente do dereito <code>editinterface</code>. Se non comprende porqué está vendo este erro, vexa [[mw:MediaWiki_1.32/interface-admin]].",
+       "interfaceadmin-info": "$1\n\nOs permisos para editar os ficheiros CSS/JS/JSON separáronse recentemente do dereito <code>editinterface</code>. Se non comprende porqué está vendo este erro, vexa [[mw:MediaWiki_1.32/interface-admin]].",
        "rawhtml-notallowed": "As marcas &lt;html&gt; non poden usarse fóra das páxinas normais.",
        "gotointerwiki": "Deixando {{SITENAME}}",
        "gotointerwiki-invalid": "O título especificado non é válido.",
index 5bdbad1..805beca 100644 (file)
        "yourdomainname": "Vaša domena",
        "password-change-forbidden": "Ne možete promjeniti zaporku na ovom projektu.",
        "externaldberror": "Došlo je do pogreške s vanjskom autorizacijom ili Vam nije dopušteno osvježavanje vanjskog suradničkog računa.",
-       "login": "Prijavi se",
+       "login": "Prijava",
        "login-security": "Potvrdite svoj identitet",
        "nav-login-createaccount": "Prijavi se",
        "logout": "Odjavi se",
        "pt-login": "Prijavi se",
        "pt-login-button": "Prijavi se",
        "pt-login-continue-button": "Nastavi prijavu",
-       "pt-createaccount": "Otvori novi suradnički račun",
+       "pt-createaccount": "Stvori račun",
        "pt-userlogout": "Odjavi se",
        "php-mail-error-unknown": "Nepoznata pogrješka u funkciji PHP-poruke()",
        "user-mail-no-addy": "Pokušaj slanja e-maila bez e-mail adrese.",
        "prefs-watchlist": "Praćene stranice",
        "prefs-editwatchlist": "Uredi popis praćenja",
        "prefs-editwatchlist-label": "Uredi stavke na popisu praćenja:",
-       "prefs-editwatchlist-edit": "vidi i ukloni stavke s popisa praćenja",
-       "prefs-editwatchlist-raw": "uredi popis praćenih stranica u okviru za uređivanje",
+       "prefs-editwatchlist-edit": "vidi i ukloni naslove s popisa praćenja",
+       "prefs-editwatchlist-raw": "uredi sirovi popis praćenja",
        "prefs-editwatchlist-clear": "očisti popis praćenja",
        "prefs-watchlist-days": "Broj dana koji će se prikazati na popisu praćenja:",
        "prefs-watchlist-days-max": "Najviše $1 {{PLURAL:$1|dan|dana}}",
        "tooltip-namespace_association": "Označite ovu kućicu da biste uključili i imenski prostor razgovora ili imenski prostor teme koja je povezana s odabranim imenskim prostorom",
        "blanknamespace": "(Glavni)",
        "contributions": "Doprinosi {{GENDER:$1|suradnika|suradnice}}",
-       "contributions-title": "Suradnički doprinosi za $1",
+       "contributions-title": "Doprinosi {{GENDER:$1|suradnika|suradnice}} $1",
        "mycontris": "Doprinosi",
        "anoncontribs": "Doprinosi",
        "contribsub2": "Za {{GENDER:$3|$1}} ($2)",
        "sp-contributions-newbies-sub": "Za nove suradnike",
        "sp-contributions-newbies-title": "Doprinosi novih suradnika",
        "sp-contributions-blocklog": "evidencija blokiranja",
-       "sp-contributions-suppresslog": "pobrisani {{GENDER:$1|suradnikovi|suradničini}} doprinosi",
-       "sp-contributions-deleted": "pobrisani {{GENDER:$1|suradnikovi|suradničini}} doprinosi",
+       "sp-contributions-suppresslog": "izbrisani doprinosi {{GENDER:$1|suradnika|suradnice}}",
+       "sp-contributions-deleted": "izbrisani doprinosi {{GENDER:$1|suradnika|suradnice}}",
        "sp-contributions-uploads": "postavljene datoteke",
        "sp-contributions-logs": "evidencije",
        "sp-contributions-talk": "razgovor",
        "tooltip-pt-userpage": "Moja suradnička stranica",
        "tooltip-pt-anonuserpage": "Suradnička stranica za IP adresu pod kojom uređujete",
        "tooltip-pt-mytalk": "Vaša stranica za razgovor",
-       "tooltip-pt-anontalk": "Razgovor o uređivanjima s ove IP adrese",
+       "tooltip-pt-anontalk": "Rasprava o uređivanjima s ove IP adrese",
        "tooltip-pt-preferences": "Vaše postavke",
        "tooltip-pt-watchlist": "Popis stranica koje pratite.",
        "tooltip-pt-mycontris": "Popis Vaših doprinosa",
        "tooltip-pt-anoncontribs": "Popis uređivanja učinjenih s ove IP adrese",
-       "tooltip-pt-login": "Predlažemo Vam da se prijavite, međutim nije obvezno.",
+       "tooltip-pt-login": "Predlažemo Vam da se prijavite, iako to nije obavezno",
        "tooltip-pt-logout": "Odjavi se",
-       "tooltip-pt-createaccount": "Predlažemo Vam mogućnost stvaranja računa i prijave, iako to nije nužno.",
+       "tooltip-pt-createaccount": "Predlažemo Vam da stvorite račun i prijavite se, iako to nije obavezno",
        "tooltip-ca-talk": "Razgovorna stranica",
        "tooltip-ca-edit": "Uredi ovu stranicu",
        "tooltip-ca-addsection": "Dodaj novi odlomak",
index fc966d7..68f49a0 100644 (file)
@@ -59,7 +59,8 @@
                        "Archd",
                        "Empu",
                        "Dodolzk",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Palladin911"
                ]
        },
        "tog-underline": "Garis bawahi pranala:",
        "watchlisttools-view": "Tampilkan perubahan terkait",
        "watchlisttools-edit": "Tampilkan dan sunting daftar pantauan",
        "watchlisttools-raw": "Sunting daftar pantauan mentah",
-       "hijri-calendar-m1": "Muharram",
+       "hijri-calendar-m1": "Muharam",
        "hijri-calendar-m2": "Safar",
-       "hijri-calendar-m3": "Rabiul awal",
-       "hijri-calendar-m4": "Rabiul akhir",
-       "hijri-calendar-m5": "Jumadil awal",
-       "hijri-calendar-m6": "Jumadil akhir",
+       "hijri-calendar-m3": "Rabiulawal",
+       "hijri-calendar-m4": "Rabiulakhir",
+       "hijri-calendar-m5": "Jumadilawal",
+       "hijri-calendar-m6": "Jumadilakhir",
        "hijri-calendar-m7": "Rajab",
-       "hijri-calendar-m8": "Sya'ban",
-       "hijri-calendar-m9": "Ramadhan",
+       "hijri-calendar-m8": "Syakban",
+       "hijri-calendar-m9": "Ramadan",
        "hijri-calendar-m10": "Syawal",
-       "hijri-calendar-m11": "Dzulkaidah",
-       "hijri-calendar-m12": "Dzulhijjah",
+       "hijri-calendar-m11": "Zulkaidah",
+       "hijri-calendar-m12": "Zulhijah",
        "hebrew-calendar-m1": "Tisyri",
        "hebrew-calendar-m2": "Markhesywan",
        "hebrew-calendar-m3": "Kislew",
index 4ba5e6d..866d399 100644 (file)
        "right-minoredit": "Означување на уредувањата како ситни",
        "right-move": "Преместување страници",
        "right-move-subpages": "Преместување на страници со нивните потстраници",
-       "right-move-rootuserpages": "Ð\9fÑ\80емеÑ\81Ñ\82Ñ\83ваÑ\9aе Ð½Ð° Ð¾Ñ\81новна ÐºÐ¾Ñ\80иÑ\81ниÑ\87ка Ñ\81Ñ\82Ñ\80аниÑ\86а",
+       "right-move-rootuserpages": "Ð\9fÑ\80емеÑ\81Ñ\82Ñ\83ваÑ\9aе Ð½Ð° Ð¾Ñ\81новни ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки Ñ\81Ñ\82Ñ\80аниÑ\86и",
        "right-move-categorypages": "Преместување на категориски страници",
        "right-movefile": "Преместување на податотеки",
        "right-suppressredirect": "Не прави пренасочување од старото име при преместување на страница",
index 7b5d04b..280a29d 100644 (file)
        "ntransclusions": "$1{{PLURAL:$1|पानावर|पानांवर}} वापर",
        "specialpage-empty": "या अहवालाकरिता(रिपोर्ट)कोणताही निकाल नाही.",
        "lonelypages": "पोरकी पाने",
-       "lonelypagestext": "खालील पानांना {{SITENAME}}च्या इतर पानांकडून दुवा जोड झालेली नाही.",
+       "lonelypagestext": "खालील पानांना या {{SITENAME}}च्या इतर कोणत्याही पानांवरुन दुवा जोडण्यात आला नाही किंवा ती आंतरविन्यासित झाली नाहीत.",
        "uncategorizedpages": "अवर्गीकृत पाने",
        "uncategorizedcategories": "अवर्गीकृत वर्ग",
        "uncategorizedimages": "अवर्गीकृत संचिका",
        "wantedpages-summary": "ही,ज्यांना अधिकांश दुवे आहेत अश्या अस्तित्वात नसलेल्या पानांची यादी आहे. यात ती पाने वगळली आहेत, ज्यांना फक्त पुनर्निर्देशनाचा दुवा आहे. अस्तित्वात नसलेली पण पुनर्निर्देशनाने जोडलेली जी पाने आहेत, अश्यांच्या यादीसाठी [[{{#special:BrokenRedirects}}|मोडकी पुनर्निर्देशने असलेल्या पानांची यादी]] बघा.",
        "wantedpages-badtitle": "परिणामाच्या यादीत अवैध शीर्षक: $1",
        "wantedfiles": "पाहिजे असलेल्या संचिका",
-       "wantedfiletext-cat": "पà¥\81ढà¥\80ल à¤«à¤¾à¤\87लà¥\8dस à¤µà¤¾à¤ªà¤°à¤²à¥\8dया à¤\85सतà¥\80ल à¤ªà¤£ à¤\86ता à¤\85सà¥\8dतितà¥\8dवात à¤¨à¤¾à¤¹à¥\80त. à¤¬à¤¾à¤¹à¥\87रà¥\80ल à¤ à¤¿à¤\95ाणाà¤\82à¤\9aà¥\8dया à¤«à¤¾à¤\87लà¥\8dस à¤¯à¥\87थà¥\87 à¤¦à¤¿à¤¸à¤¤à¤¾à¤¤ à¤ªà¤£ à¤\85सतà¥\80लà¤\9a à¤\85सà¥\87 à¤¨à¤¾à¤¹à¥\80. à¤\85शा à¤«à¤¾à¤\87लà¥\8dस à¤\86ढळलà¥\8dयास à¤µà¤\97ळलà¥\8dया à¤\9cातà¥\80ल. à¤\85तिरिà¤\95à¥\8dतपणà¥\87,à¤\85शà¥\80 à¤ªà¤¾à¤¨à¥\87, à¤\9cà¥\8dयात à¤\9fाà¤\95लà¥\87लà¥\8dया à¤¸à¤\82à¤\9aिà¤\95ा à¤\85सà¥\8dतितà¥\8dवात à¤¨à¤¾à¤¹à¥\80त,त्याची यादी [[:$1]] येथे दिसेल.",
+       "wantedfiletext-cat": "पà¥\81ढà¥\80ल à¤¸à¤\82à¤\9aिà¤\95ा (फाà¤\87लà¥\8dस) à¤µà¤¾à¤ªà¤°à¤²à¥\8dया à¤\85सतà¥\80ल à¤ªà¤£ à¤¤à¥\8dया à¤\86ता à¤\85सà¥\8dतितà¥\8dवात à¤¨à¤¾à¤¹à¥\80त. à¤¬à¤¾à¤¹à¥\87रà¥\80ल à¤ à¤¿à¤\95ाणाà¤\82à¤\9aà¥\8dया à¤¸à¤\82à¤\9aिà¤\95ा (फाà¤\87लà¥\8dस) à¤¯à¥\87थà¥\87 à¤¦à¤¿à¤¸à¤¤à¤¾à¤¤ à¤ªà¤£ à¤¤à¥\8dया à¤\85सतà¥\80लà¤\9a à¤\85सà¥\87 à¤¨à¤¾à¤¹à¥\80. à¤\85शा à¤¸à¤\82à¤\9aिà¤\95ा (फाà¤\87लà¥\8dस) à¤\86ढळलà¥\8dयास à¤µà¤\97ळलà¥\8dया à¤\9cातà¥\80ल. à¤\85तिरिà¤\95à¥\8dतपणà¥\87,à¤\85शà¥\80 à¤ªà¤¾à¤¨à¥\87, à¤\9cà¥\8dयात à¤\9fाà¤\95लà¥\87लà¥\8dया à¤¸à¤\82à¤\9aिà¤\95ा à¤\85सà¥\8dतितà¥\8dवात à¤¨à¤¾à¤¹à¥\80त, त्याची यादी [[:$1]] येथे दिसेल.",
        "wantedfiletext-nocat": "पुढील फाइल्स वापरल्या असतील पण आता अस्तित्वात नाहीत. बाहेरील ठिकाणांच्या फाइल्स येथे दिसतात पण असतीलच असे नाही. अशा फाइल्स आढळल्यास वगळल्या जातील.",
        "wantedtemplates": "पाहिजे असलेले साचे",
        "mostlinked": "सर्वाधिक जोडलेली पाने",
        "protectedpages": "सुरक्षित पाने",
        "protectedpages-indef": "फक्त अनंत काळासाठी सुरक्षित केलेले",
        "protectedpages-summary": "या पानात,अस्तित्वात असणाऱ्या संरक्षित अशा पानाची यादी आहे.नवनिर्माणापासून संरक्षित शीर्षकांच्या यादीसाठी [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]]  बघा.",
-       "protectedpages-cascade": "à¤\95à¥\87वळ à¤\8fà¤\95ामà¥\87à¤\95ाà¤\82वर à¤\85वलà¤\82बà¥\82न à¤\95ास्केडींग सुरक्षा (सुरक्षा शिडी)",
+       "protectedpages-cascade": "à¤\95à¥\87वळ à¤\8fà¤\95ामà¥\87à¤\95ाà¤\82वर à¤\85वलà¤\82बà¥\82न à¤\95à¥\85स्केडींग सुरक्षा (सुरक्षा शिडी)",
        "protectedpages-noredirect": "पुनर्निर्देशने लपवा",
        "protectedpagesempty": "सध्या या नियमावलीने कोणतीही पाने सुरक्षित केलेली नाहीत.",
        "protectedpages-timestamp": "वेळशिक्का",
index 7017d5d..0e61c33 100644 (file)
        "cannotcreateaccount-text": "တိုက်ရိုက် အကောင့်ဖန်တီးခြင်းအား ဤဝီကီပေါ်၌ ဖွင့်မထားပါ။",
        "yourdomainname": "သင့်ဒိုမိန်း -",
        "password-change-forbidden": "ဤဝီကီတွင် စကားဝှက်များကို ပြောင်းလဲ၍ မရပါ။",
-       "login": "Log in ဝင်ရန်",
+       "login": "အကောင့်ထဲဝင်ရန်",
        "login-security": "သင်၏ အထောက်အထားကို အတည်ပြုပါ",
-       "nav-login-createaccount": "Log in ဝင်ရန်/ အကောင့် ဖန်တီးရန်",
+       "nav-login-createaccount": "အကောင့်ထဲဝင်ရန် / အကောင့်ဖန်တီးရန်",
        "logout": "ထွက်ရန်",
        "userlogout": "ထွက်ရန်",
-       "notloggedin": "log in ဝင်မထားပါ",
+       "notloggedin": "အကောင့် မဝင်ထားပါ",
        "userlogin-noaccount": "အကောင့် မရှိဘူးလား။",
        "userlogin-joinproject": "{{SITENAME}} ကို ချိတ်ဆက်ရန်",
        "createaccount": "အကောင့် ဖန်တီးရန်",
        "oldpassword": "စကားဝှက် အဟောင်း -",
        "newpassword": "စကားဝှက် အသစ် -",
        "retypenew": "စကားဝှက် အသစ်ကို ထပ်ရိုက်ပါ -",
-       "resetpass_submit": "စကားဝှက်ကို သတ်မှတ်ပြီးနောက် Log in ဝင်ရန်",
+       "resetpass_submit": "စကားဝှက်ကို သတ်မှတ်ပြီးနောက် အကောင့်ထဲဝင်ရန်",
        "changepassword-success": "သင့်စကားဝှက်ကို ပြောင်းလဲပြီးပါပြီ!",
        "changepassword-throttled": "သင်သည် login ဝင်ရန် အကြိမ်ပေါင်းများစွာ အားထုတ်ခဲ့ပြီးဖြစ်သည်။ ကျေးဇူးပြု၍ ထပ်မဝင်ခင် $1 စောင့်ပေးပါ။",
        "botpasswords": "ဘော့ စကားဝှက်များ",
        "uploadbtn": "ဖိုင်​တင်​ရန်​",
        "reuploaddesc": "Upload တင်နေခြင်းကို ဖျက်သိမ်းပြီး upload တင်သည့် ပုံစံသို့ ပြန်သွားရန်",
        "upload-tryagain": "ပြုပြင်ထားသောဖိုင်၏ ဖော်ပြချက်ကို ထည့်သွင်းရန်",
-       "uploadnologin": "logged in ဝင်မထားပါ",
+       "uploadnologin": "အကောင့် မဝင်ထားပါ",
        "uploadnologintext": "ဖိုင်များကို တင်ရန် ကျေးဇူးပြု၍ $1 ပါ။",
        "uploaderror": "အပ်လုပ်တင်ခြင်း အမှား",
        "upload-recreate-warning": "<strong>သတိပေးချက်။ ဤအမည်ဖြင့်ဖိုင်သည် ဖျက်ထားခြင်း သို့မဟုတ် ရွေ့ပြောင်းထားခြင်း ခံထားရသည်။</strong>\n\nဖျက်ထားခြင်းနှင့် ရွေ့ပြောင်းထားခြင်း မှတ်တမ်းကို သိရှိနိုင်ရန် ဖော်ပြထားပါသည်။",
        "mywatchlist": "စောင့်ကြည့်စာရင်း",
        "watchlistfor2": "$1 အတွက် $2",
        "nowatchlist": "သင့်စောင့်ကြည့်စာရင်းမှာ ဘာမှ မရှိပါ။",
-       "watchnologin": "logged in ဝင်မထားပါ",
+       "watchnologin": "အကောင့် မဝင်ထားပါ",
        "addwatch": "စောင့်ကြည့်စာရင်းသို့ ပေါင်းထည့်ရန်",
        "addedwatchtext": "\"[[:$1]]\" နှင့် ၎င်း၏ ဆွေးနွေးချက် စာမျက်နှာကို သင်၏ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]]ထဲသို့ ပေါင်းထည့်ပြီးဖြစ်သည်။",
        "addedwatchtext-talk": "\"[[:$1]]\" နှင့် ယင်း၏ဆက်နွယ် စာမျက်နှာကို သင်၏ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]]ထဲသို့ ပေါင်းထည့်ပြီးဖြစ်သည်။",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] ကို ပိတ်ပင်ထားပါသည်။<br />\nပိတ်ပင်မှုများကို ပြန်လည်ဆန်းစစ်ရန် [[Special:BlockList|ပိတ်ပင်မှု စာရင်း]]ကို ကြည့်ပါ။",
        "ipb-blockingself": "သင့်ကိုယ်သင် ပိတ်ပင်တားဆီးတော့မည် ဖြစ်သည်။ ဤသည်ကို လုပ်ဆောင်လိုသည်မှာ သေချာပါသလား။",
        "ipb-confirmaction": "ဤသည်ကို လုပ်ဆောင်လိုသည်မှာ သေချာပါက အောက်ခြေရှိ \"{{int:ipb-confirm}}\" ကွင်းအား ကျေးဇူးပြု၌ စစ်ဆေးပါ။",
-       "ipb-edit-dropdown": "ပိတ်ပင်ရသောအကြောင်းရင်းများ",
+       "ipb-edit-dropdown": "ပိတ်ပင်ရသောအကြောင်းရင်းများကို ပြင်ဆင်ရန်",
        "ipb-unblock-addr": "$1 ကို ပိတ်ထားရာမှ ပြန်ဖွင့်ရန်",
        "ipb-unblock": "အသုံးပြုသူအမည် သို့ IP address ကို ပိတ်ထားရာမှ ပြန်ဖွင့်ပေးရန်",
        "ipb-blocklist": "ရှိနှင့်ပြီးသား ပိတ်ပင်မှုများကို ကြည့်ရန်",
index 2900e99..101c38a 100644 (file)
        "savechanges": "Publiser endringane",
        "publishpage": "Publiser sida",
        "publishchanges": "Publiser endringar",
+       "publishpage-start": "Publiser side …",
+       "publishchanges-start": "Publiser endringar …",
        "preview": "Førehandsvising",
        "showpreview": "Førehandsvis",
        "showdiff": "Sjå skilnader",
        "postedit-confirmation-created": "Sida vart oppretta.",
        "postedit-confirmation-restored": "Sida vart attoppretta.",
        "postedit-confirmation-saved": "Endringa di vart lagra.",
+       "postedit-confirmation-published": "Endringa di vart publisert.",
        "edit-already-exists": "Kunne ikkje opprette ny side fordi ho alt eksisterer.",
        "defaultmessagetext": "Standard meldingstekst",
        "content-failed-to-parse": "Klarte ikkje å tolke innhaldet «$2» for innhaldsmodellen «$1»: $3",
        "tooltip-ca-nstab-category": "Vis kategoriside",
        "tooltip-minoredit": "Merk endringa som småplukk",
        "tooltip-save": "Lagra endringane dine",
+       "tooltip-publish": "Publiser endringane dine",
        "tooltip-preview": "Førehandsvis endringane dine. Helst brukar du denne funksjonen før du lagrar.",
        "tooltip-diff": "Sjå kva endringar du gjorde i teksten",
        "tooltip-compareselectedversions": "Sjå endringane mellom dei valde versjonane av denne sida.",
index e4dfc68..b0b3f6f 100644 (file)
        "customcssprotected": "Nie jesteś uprawniony do edytowania tej strony CSS, ponieważ zawiera ona ustawienia osobiste innego użytkownika.",
        "customjsonprotected": "Nie jesteś uprawniony do edytowania tej strony JSON, ponieważ zawiera ona ustawienia osobiste innego użytkownika.",
        "customjsprotected": "Nie jesteś uprawniony do edytowania tej strony JavaScript, ponieważ zawiera ona ustawienia osobiste innego użytkownika.",
-       "sitecssprotected": "Nie masz uprawnień do edytowania tej strony CSS, ponieważ może to wpłynąć na wszystkich odwiedzających",
-       "sitejsonprotected": "Nie masz uprawnień do edytowania tej strony JSON, ponieważ może to wpłynąć na wszystkich odwiedzających",
-       "sitejsprotected": "Nie masz uprawnień do edytowania tej strony JavaScript, ponieważ może to wpłynąć na wszystkich odwiedzających",
+       "sitecssprotected": "Nie masz uprawnień do edytowania tej strony CSS, ponieważ może to wpłynąć na wszystkich odwiedzających.",
+       "sitejsonprotected": "Nie masz uprawnień do edytowania tej strony JSON, ponieważ może to wpłynąć na wszystkich odwiedzających.",
+       "sitejsprotected": "Nie masz uprawnień do edytowania tej strony JavaScript, ponieważ może to wpłynąć na wszystkich odwiedzających.",
        "mycustomcssprotected": "Nie masz uprawnień do edytowania tej strony CSS.",
        "mycustomjsonprotected": "Nie masz uprawnień do edytowania tej strony JSON.",
        "mycustomjsprotected": "Nie masz uprawnień do edytowania tej strony JavaScript.",
index c028e0a..a0fe773 100644 (file)
        "customcssprotected": "Nu aveți permisiunea de a modifica această pagină CSS, deoarece conține setările personale ale altui utilizator.",
        "customjsonprotected": "Nu aveți permisiunea de a modifica această pagină JSON, deoarece conține setările personale ale altui utilizator.",
        "customjsprotected": "Nu aveți permisiunea de a modifica această pagină JavaScript, deoarece conține setările personale ale altui utilizator.",
-       "sitecssprotected": "Nu aveți dreptul să editați această pagină CSS deoarece poate afecta toți vizitatorii",
-       "sitejsonprotected": "Nu aveți dreptul să editați această pagină JSON deoarece poate afecta toți vizitatorii",
-       "sitejsprotected": "Nu aveți dreptul să editați această pagină JavaScript deoarece poate afecta toți vizitatorii",
+       "sitecssprotected": "Nu aveți dreptul să editați această pagină CSS deoarece poate afecta toți vizitatorii.",
+       "sitejsonprotected": "Nu aveți dreptul să editați această pagină JSON deoarece poate afecta toți vizitatorii.",
+       "sitejsprotected": "Nu aveți dreptul să editați această pagină JavaScript deoarece poate afecta toți vizitatorii.",
        "mycustomcssprotected": "Nu aveți permisiunea să modificați această pagină CSS.",
        "mycustomjsonprotected": "Nu aveți permisiunea să modificați această pagină JSON.",
        "mycustomjsprotected": "Nu aveți permisiunea să modificați această pagină JavaScript.",
        "http-timed-out": "Cererea HTTP a expirat.",
        "http-curl-error": "Eroare la preluarea adresei URL: $1",
        "http-bad-status": "A apărut o problemă în timpul solicitării HTTP: $1 $2",
+       "http-internal-error": "Eroare internă HTTP.",
        "upload-curl-error6": "Nu pot găsi adresa URL",
        "upload-curl-error6-text": "Adresa URL introdusă nu a putut fi atinsă.\nVă rugăm, verificați că adresa URL este corectă și că situl este funcțional.",
        "upload-curl-error28": "Încărcarea a expirat",
        "protectedtitles-submit": "Afișează titlurile",
        "listusers": "Listă utilizatori",
        "listusers-editsonly": "Arată doar utilizatorii cu modificări",
+       "listusers-temporarygroupsonly": "Arată doar utilizatorii din grupuri temporare",
        "listusers-creationsort": "Sortează după data creării",
        "listusers-desc": "Sortează descrescător",
        "usereditcount": "$1 {{PLURAL:$1|editare|editări}}",
        "interlanguage-link-title-nonlang": "$1 – $2",
        "common.css": "/** CSS plasate aici vor fi aplicate tuturor aparițiilor */",
        "print.css": "/* CSS plasate aici vor afecta modul în care paginile vor fi imprimate */",
+       "common.json": "/* Orice JSON din această pagină va fi încărcat pentru toți utilizatorii la fiecare pagină încărcată. */",
        "anonymous": "{{PLURAL:$1|Utilizator anonim|Utilizatori anonimi}} ai {{SITENAME}}",
        "siteuser": "Utilizator {{SITENAME}} $1",
        "anonuser": "utlizator anonim $1 al {{SITENAME}}",
        "confirm-unwatch-top": "Eliminați această pagină din lista de pagini urmărite?",
        "confirm-rollback-button": "OK",
        "confirm-rollback-top": "Anulați editările asupra acestei pagini?",
+       "confirm-mcrundo-title": "Anulează o modificare",
+       "mcrundofailed": "Anularea nu a reușit",
+       "mcrundo-missingparam": "Lipsește un parametru necesar în cerere.",
+       "mcrundo-changed": "Această pagină a fost schimbată de când ați văzut diferența. Vă rugăm să revizuiți noua schimbare.",
        "quotation-marks": "„$1”",
        "imgmultipageprev": "← pagina anterioară",
        "imgmultipagenext": "pagina următoare →",
        "tags-delete-not-found": "Eticheta „$1” nu există.",
        "tags-delete-too-many-uses": "Eticheta „$1” este aplicată pentru mai mult de $2 {{PLURAL:$2|versiune|versiuni|de versiuni}}, ceea ce înseamnă că nu poate fi ștearsă.",
        "tags-delete-warnings-after-delete": "Eticheta „$1” a fost ștearsă, deși {{PLURAL:$2|s-a întâlnit următorul avertisment|s-au întâlnit următoarele avertismente}}:",
+       "tags-delete-no-permission": "Nu aveți permisiunea de a șterge etichetele.",
        "tags-activate-title": "Activare etichetă",
        "tags-activate-question": "Sunteți pe cale să activați eticheta „$1”.",
        "tags-activate-reason": "Motiv:",
index 9018359..eb07e94 100644 (file)
        "search-category": "(ذمرو $1)",
        "search-file-match": "(فائيل جي مواد سان ملي ٿو)",
        "search-suggest": "ڇا توهان جو مطلب ھيو: $1",
+       "search-rewritten": "نتيجا براءِ $1. يا $2 بابت نتيجا ڏسو.",
        "search-interwiki-caption": "برادر رٿائن مان نتيجا",
        "search-interwiki-default": "$1 مان نتيجا",
        "search-interwiki-more": "(وڌيڪ)",
index 1e11dbc..88163d8 100644 (file)
        "morenotlisted": "Ovaj spisak nije kompletan.",
        "mypage": "Moja stranica",
        "mytalk": "Razgovor",
-       "anontalk": "Razgovor za ovu IP adresu",
+       "anontalk": "Razgovor",
        "navigation": "Navigacija - Навигација",
        "and": "&#32;i",
        "faq": "ČPP",
        "yourdomainname": "Vaš domen:",
        "password-change-forbidden": "Ne možete da promenite lozinku na ovom vikiju.",
        "externaldberror": "Došlo je do greške pri vanjskoj autorizaciji baze podataka ili vam nije dopušteno osvježavanje Vašeg vanjskog korisničkog računa.",
-       "login": "Prijavi me - Пријави ме",
+       "login": "Prijava / Пријава",
        "nav-login-createaccount": "Prijavi se / Registruj se",
        "logout": "Odjava",
        "userlogout": "Odjavi se / Одјави се",
        "loginlanguagelabel": "Jezik: $1",
        "suspicious-userlogout": "Vaš zahtjev za odjavu je odbijen jer je poslan preko pokvarenog preglednika ili keširanog proksija.",
        "createacct-another-realname-tip": "Pravo ime nije obavezno.\nAko izaberete da date ime, biće korišteno za pripisivanje za vaš rad.",
-       "pt-login": "Prijava",
+       "pt-login": "Prijavi me / Пријави ме",
        "pt-login-button": "Prijavi me / Пријави ме",
        "pt-createaccount": "Izradi račun",
        "pt-userlogout": "Odjava",
        "prefs-watchlist": "Lista praćenja",
        "prefs-editwatchlist": "Uredi popis praćenja",
        "prefs-editwatchlist-label": "Uredi unose na popisu praćenja:",
-       "prefs-editwatchlist-edit": "Vidite i uklonite naslove na vašem popisu praćenja",
-       "prefs-editwatchlist-raw": "Uredi grubi popis praćenja",
-       "prefs-editwatchlist-clear": "očisti popis praćenja",
+       "prefs-editwatchlist-edit": "vidi i ukloni naslove s liste praćenja",
+       "prefs-editwatchlist-raw": "uredi sirovu listu praćenja",
+       "prefs-editwatchlist-clear": "očisti listu praćenja",
        "prefs-watchlist-days": "Broj dana za prikaz u spisku praćenja:",
        "prefs-watchlist-days-max": "(najviše $1 {{PLURAL:$1|dan|dana}})",
        "prefs-watchlist-edits": "Najveći broj izmjena za prikaz u proširenom spisku praćenja:",
        "namespace_association": "Povezan imenski prostor",
        "tooltip-namespace_association": "Označite ovu kutiju da također uključite razgovor ili imenski prostor teme koja je povezana sa odabranim imenskim prostorom",
        "blanknamespace": "(Glavno)",
-       "contributions": "Doprinosi {{GENDER:$1|korisnika|korisnice|korisnika}}",
-       "contributions-title": "Korisnički doprinosi od $1",
+       "contributions": "Doprinosi {{GENDER:$1|korisnika|korisnice}}",
+       "contributions-title": "Doprinosi {{GENDER:$1|korisnika|korisnice}} $1",
        "mycontris": "Doprinosi",
        "anoncontribs": "Doprinosi",
        "contribsub2": "Za {{GENDER:$3|$1}} ($2)",
        "sp-contributions-newbies-sub": "Prikaži samo doprinose novih korisnika",
        "sp-contributions-newbies-title": "Doprinosi novih korisnika",
        "sp-contributions-blocklog": "registar blokiranja",
-       "sp-contributions-suppresslog": "obrisani doprinosi korisnika",
-       "sp-contributions-deleted": "obrisani doprinosi korisnika",
+       "sp-contributions-suppresslog": "izbrisani doprinosi {{GENDER:$1|korisnika|korisnice}}",
+       "sp-contributions-deleted": "izbrisani doprinosi {{GENDER:$1|korisnika|korisnice}}",
        "sp-contributions-uploads": "postavljanja",
        "sp-contributions-logs": "registri",
        "sp-contributions-talk": "razgovor",
        "tooltip-pt-userpage": "{{GENDER:|Vaša korisnička}} stranica",
        "tooltip-pt-anonuserpage": "Korisnička stranica za ip koju Vi uređujete kao",
        "tooltip-pt-mytalk": "{{GENDER:|Vaša}} stranica za razgovor",
-       "tooltip-pt-anontalk": "Razgovor o doprinosu sa ove IP adrese",
+       "tooltip-pt-anontalk": "Rasprava o izmjenama s ove IP adrese",
        "tooltip-pt-preferences": "{{GENDER:|Vaše}} postavke",
        "tooltip-pt-watchlist": "Lista stranica čije izmjene pratite",
        "tooltip-pt-mycontris": "Lista {{GENDER:|vaših}} doprinosa",
-       "tooltip-pt-anoncontribs": "Lista uređenja napravljenih s ove IP adrese",
+       "tooltip-pt-anoncontribs": "Lista izmjena napravljenih s ove IP adrese",
        "tooltip-pt-login": "Predlažem da se prijavite; međutim, to nije obavezno",
        "tooltip-pt-logout": "Odjava sa projekta {{SITENAME}}",
-       "tooltip-pt-createaccount": "Ohrabrujemo vas da otvorite račun i prijavite se; to, međutim, nije obavezno",
+       "tooltip-pt-createaccount": "Predlažemo vam da izradite račun i prijavite se, iako to nije obavezno",
        "tooltip-ca-talk": "Diskusija o stranici sadržaja",
        "tooltip-ca-edit": "Uredi ovu stranicu",
        "tooltip-ca-addsection": "Započnite novu sekciju.",
index f132cd6..cd6aeb9 100644 (file)
        "yourdomainname": "Домен:",
        "password-change-forbidden": "Не можете да промените лозинку на овом викију.",
        "externaldberror": "Дошло је до грешке при потврди идентитета базе података или вам није дозвољено да ажурирате свој спољни налог.",
-       "login": "Ð\9fÑ\80иÑ\98ави Ð¼Ðµ",
+       "login": "Ð\9fÑ\80иÑ\98ава",
        "login-security": "Потврда вашег индентитета",
        "nav-login-createaccount": "Пријава/регистрација",
        "logout": "Одјава",
        "blockedtext": "<strong>Ваше корисничко име или IP адреса је блокирана.</strong>\n\nБлокирање је {{GENDER:$4|извршио|извршила}} $1.\nРазлог је <em>$2</em>.\n\n* Почетак блокирања: $8\n* Истек блокирања: $6\n* Блокирани: $7\n\nМожете да контактирате {{GENDER:$4|корисника|корисницу}} $1 или другог [[{{MediaWiki:Grouppage-sysop}}|администратора]] да бисте разговарали о блокирању.\nНе можете да користите могућност „{{int:emailuser}}” осим ако сте навели валидну имејл адресу у својим [[Special:Preferences|подешавањима налога]] и нисте блокирани од коришћења исте.\nВаша актуелна IP адреса је $3, а ID блокаде #$5.\nНаведите све горње детаље при прављењу било каквих упита.",
        "autoblockedtext": "Ваша IP адреса је аутоматски блокирана јер ју је користио други корисник, кога је {{GENDER:$4|блокирао|блокирала}} $1.\nРазлог:\n\n:<em>$2</em>\n\n* Почетак блокаде: $8\n* Крај блокаде: $6\n* Име корисника: $7\n\nМожете да контактирате {{GENDER:$4|корисника|корисницу}} $1 или другог [[{{MediaWiki:Grouppage-sysop}}|администратора]] да бисте расправљали о блокади.\n\nЗапамтите да не можете да користите могућност „{{int:emailuser}}“ осим ако сте навели ваљану имејл адресу у својим [[Special:Preferences|подешавањима]].\n\nВаша актуелна IP адреса је $3, а ID блокаде $5.\nУкључите све горње детаље при прављењу било каквих упита.",
        "blockednoreason": "разлог није наведен",
-       "whitelistedittext": "За уређивање странице је потребно да будете $1.",
+       "whitelistedittext": "$1 да бисте уређивали странице.",
        "confirmedittext": "Морате да потврдите своју имејл адресу пре уређивања страница.\nПоставите и потврдите имејл адресу преко [[Special:Preferences|подешавања]].",
        "nosuchsectiontitle": "Не могу да пронађем одељак.",
        "nosuchsectiontext": "Покушали сте да уредите одељак који не постоји.\nМожда је премештен или избрисан док сте прегледали страницу.",
        "loginreqtitle": "Потребна је пријава",
-       "loginreqlink": "пÑ\80иÑ\98авÑ\99ени",
-       "loginreqpagetext": "Морате бити $1 да бисте видели друге странице.",
+       "loginreqlink": "Ð\9fÑ\80иÑ\98авиÑ\82е Ñ\81е",
+       "loginreqpagetext": "$1 да бисте видели друге странице.",
        "accmailtitle": "Лозинка је послата.",
        "accmailtext": "Лозинка за {{GENDER:$1|корисника|корисницу}} [[User talk:$1|$1]] је послата на $2. Након пријаве, лозинка се може променити [[Special:ChangePassword|овде]].",
        "newarticle": "(нови)",
        "hiddencategories": "Ова страница је члан {{PLURAL:$1|једне скривене категорије|$1 скривене категорије|$1 скривених категорија}}:",
        "edittools": "<!-- Овај текст ће бити приказан испод обрасца за уређивање и отпремање. -->",
        "edittools-upload": "-",
-       "nocreatetext": "Ð\9dа Ð¾Ð²Ð¾Ð¼ Ð²Ð¸ÐºÐ¸Ñ\98Ñ\83 Ñ\98е Ð¾Ð³Ñ\80аниÑ\87ено Ð¿Ñ\80авÑ\99еÑ\9aе Ð½Ð¾Ð²Ð¸Ñ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а.\nÐ\9cожеÑ\82е Ñ\81е Ð²Ñ\80аÑ\82иÑ\82и Ð¸ Ñ\83Ñ\80едиÑ\82и Ð¿Ð¾Ñ\81Ñ\82оÑ\98еÑ\9bÑ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, или се [[Special:UserLogin|пријавите или отворите налог]].",
+       "nocreatetext": "Ð\9dа Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}} Ñ\98е Ð¾Ð³Ñ\80аниÑ\87ена Ð¼Ð¾Ð³Ñ\83Ñ\9bноÑ\81Ñ\82 Ð¿Ñ\80авÑ\99еÑ\9aа Ð½Ð¾Ð²Ð¸Ñ\85 Ñ\81Ñ\82Ñ\80аниÑ\86а.\nÐ\9cожеÑ\82е Ñ\81е Ð²Ñ\80аÑ\82иÑ\82и Ð¸ Ñ\83Ñ\80едиÑ\82и Ð¿Ð¾Ñ\81Ñ\82оÑ\98еÑ\9bÑ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 или се [[Special:UserLogin|пријавите или отворите налог]].",
        "nocreate-loggedin": "Немате дозволу да правите нове странице.",
        "sectioneditnotsupported-title": "Уређивање одељка није подржано",
        "sectioneditnotsupported-text": "Уређивање одељка није подржано на овој страници.",
        "prefs-watchlist": "Списак надгледања",
        "prefs-editwatchlist": "Уређивање списка надгледања",
        "prefs-editwatchlist-label": "Уреди уносе на списку надгледања:",
-       "prefs-editwatchlist-edit": "погледајте и уклоните наслове са списка надгледања",
+       "prefs-editwatchlist-edit": "погледај и уклони наслове са списка надгледања",
        "prefs-editwatchlist-raw": "уреди сиров списак надгледања",
        "prefs-editwatchlist-clear": "очисти списак надгледања",
        "prefs-watchlist-days": "Број дана у списку надгледања:",
        "upload-tryagain": "Пошаљи измењени опис датотеке",
        "upload-tryagain-nostash": "Пошаљите ре-отпремљену датотеку и измењен опис",
        "uploadnologin": "Нисте пријављени",
-       "uploadnologintext": "Морате бити $1 да бисте отпремали датотеке.",
+       "uploadnologintext": "$1 да бисте отпремали датотеке.",
        "upload_directory_missing": "Фасцикла за слање ($1) недостаје и сервер је не може направити.",
        "upload_directory_read_only": "Сервер не може да пише по фасцикли за слање ($1).",
        "uploaderror": "Грешка при отпремању",
        "tooltip-pt-userpage": "{{GENDER:|Ваша}} корисничка страница",
        "tooltip-pt-anonuserpage": "Корисничка страница за IP адресу с које уређујете",
        "tooltip-pt-mytalk": "{{GENDER:|Ваша}} страница за разговор",
-       "tooltip-pt-anontalk": "РазговоÑ\80 Ð¾ Ð¸Ð·Ð¼ÐµÐ½Ð°ма са ове IP адресе",
+       "tooltip-pt-anontalk": "Ð\94иÑ\81кÑ\83Ñ\81иÑ\98а Ð¾ Ñ\83Ñ\80еÑ\92иваÑ\9aима са ове IP адресе",
        "tooltip-pt-preferences": "{{GENDER:|Ваша}} подешавања",
        "tooltip-pt-watchlist": "Списак страница које надгледате",
        "tooltip-pt-mycontris": "Списак {{GENDER:|Ваших}} доприноса",
index 51f5c3c..d2147b4 100644 (file)
        "yourdomainname": "Domen:",
        "password-change-forbidden": "Ne možete da promenite lozinku na ovom vikiju.",
        "externaldberror": "Došlo je do greške pri potvrdi identiteta baze podataka ili vam nije dozvoljeno da ažurirate svoj spoljni nalog.",
-       "login": "Prijavi me",
+       "login": "Prijava",
        "login-security": "Potvrda vašeg indentiteta",
        "nav-login-createaccount": "Prijava/registracija",
        "logout": "Odjava",
        "prefs-watchlist": "Spisak nadgledanja",
        "prefs-editwatchlist": "Uređivanje spiska nadgledanja",
        "prefs-editwatchlist-label": "Uredi unose na spisku nadgledanja:",
-       "prefs-editwatchlist-edit": "pogledajte i uklonite naslove sa spiska nadgledanja",
+       "prefs-editwatchlist-edit": "pogledaj i ukloni naslove sa spiska nadgledanja",
        "prefs-editwatchlist-raw": "uredi sirov spisak nadgledanja",
        "prefs-editwatchlist-clear": "očisti spisak nadgledanja",
        "prefs-watchlist-days": "Broj dana u spisku nadgledanja:",
        "tooltip-pt-userpage": "{{GENDER:|Vaša}} korisnička stranica",
        "tooltip-pt-anonuserpage": "Korisnička stranica za IP adresu s koje uređujete",
        "tooltip-pt-mytalk": "{{GENDER:|Vaša}} stranica za razgovor",
-       "tooltip-pt-anontalk": "Razgovor o izmenama sa ove IP adrese",
+       "tooltip-pt-anontalk": "Diskusija o uređivanjima sa ove IP adrese",
        "tooltip-pt-preferences": "{{GENDER:|Vaša}} podešavanja",
        "tooltip-pt-watchlist": "Spisak stranica koje nadgledate",
        "tooltip-pt-mycontris": "Spisak {{GENDER:|Vaših}} doprinosa",
index 46a76e6..3cced69 100644 (file)
        "watchlistanontext": "Xin hãy đăng nhập để xem hay sửa đổi các trang trong danh sách theo dõi của bạn.",
        "watchnologin": "Chưa đăng nhập",
        "addwatch": "Thêm vào danh sách theo dõi",
-       "addedwatchtext": "“[[:$1]]” cùng trang thảo luận đã vào [[Special:Watchlist|danh sách theo dõi]] của bạn.",
+       "addedwatchtext": "Trang “[[:$1]]” cùng trang thảo luận đã được thêm vào [[Special:Watchlist|danh sách theo dõi]] của bạn.",
        "addedwatchtext-talk": "“[[:$1]]” cùng trang đi kèm đã vào [[Special:Watchlist|danh sách theo dõi]] của bạn.",
        "addedwatchtext-short": "Trang “$1” đã được thêm vào danh sách theo dõi của bạn.",
        "removewatch": "Gỡ khỏi danh sách theo dõi",
index 3ab8c74..f456b88 100644 (file)
        "category-file-count": "{{PLURAL:$2|Dins cisse categoreye ci, gn a k' ene pådje.|{{PLURAL:$1|Gn a cisse pådje ci|Gn a les $1 pådjes ki shuvèt}} dins cisse categoreye ci, po $2 pådjes å totå.}}",
        "category-file-count-limited": "{{PLURAL:$1|Gn a k' cisse pådje cial|Gn a les $1 pådjes ciddé padzo}} dins cisse categoreye ci.",
        "listingcontinuesabbrev": "(shûte)",
+       "broken-file-category": "Pådjes avou des hårdêyes esketêyes",
        "about": "Åd fwait",
        "article": "Årtike",
        "newwindow": "(drovant en on novea purnea)",
index c1642fa..2047dbf 100644 (file)
        "namespaceprotected": "侬无没编辑'''$1'''名字空间里向页面个权限。",
        "customcssprotected": "箇CSS页你呒处编,箇页有各许用户个私人设置。",
        "customjsprotected": "箇JavaScript页你呒处编,箇页有各许用户个私人设置。",
+       "sitecssprotected": "编箇CSS代码页面尔呒数呒箇权力,张页面会搭一切望个人有影响。",
+       "sitejsonprotected": "编箇JSON代码页面尔呒数呒箇权力,张页面会搭一切望个人有影响。",
+       "sitejsprotected": "编箇Javascript代码页面尔呒数呒箇权力,张页面会搭一切望个人有影响。",
        "mycustomcssprotected": "箇CSS页你呒处编。",
        "mycustomjsprotected": "箇JavaScript页你呒处编。",
        "myprivateinfoprotected": "你个私人信息你呒处编。",
        "search-external": "外部搜索",
        "searchdisabled": "{{SITENAME}}个搜索已禁用。侬可以暂时使用Google搜索,须注意渠拉索引个{{SITENAME}}内容作兴会过时。",
        "search-error": "搜寻辰光发生错误:$1",
+       "search-warning": "寻$1箇朞,有一个“警示”趒出。",
        "preferences": "偏好",
        "mypreferences": "偏好设定",
        "prefs-edits": "编辑数量:",
        "filehist-filesize": "文件大細",
        "filehist-comment": "备注",
        "imagelinks": "文件用法",
-       "linkstoimage": "ä¸\8b头$1个页é\9d¢é\93¾å\88°箇文件:",
-       "linkstoimage-more": "超过$1只{{PLURAL:$1|页面链接}}到该只文件。下底个单子只显示链接到该只文件个{{PLURAL:$1|头一只页面|头$1只页面}}。侬好望[[Special:WhatLinksHere/$2|完整个单子]]。",
-       "nolinkstoimage": "å\91\92ä¸\8d页é\9d¢é\93¾æ\8e¥å\88°è¯¥å\8fª文件。",
+       "linkstoimage": "ä¸\8bå\90\91许$1{{PLURAL:$1|å¼ ç\94¨ç\9d\80}}箇文件:",
+       "linkstoimage-more": "超过$1{{PLURAL:$1|张数页面用着}}箇文件。\n玱朞畀用着箇文件个{{PLURAL:$1|头一张|前$1张}}放下向。\n张[[Special:WhatLinksHere/$2|单]]囥㙮,尔好望。",
+       "nolinkstoimage": "å\91\92ä¸\80页ç\94¨ç\9d\80ç®\87文件。",
        "linkstoimage-redirect": "$1(文件轉戳到)$2",
        "sharedupload": "箇只文件来源于$1,渠作兴垃拉其他项目当中畀应用。",
        "sharedupload-desc-here": "箇文件$1里个,作兴会畀别个项目使用。渠个[$2 描述页]里个说明显示如下。",
index b09e609..fc0e95a 100644 (file)
        "customcssprotected": "您没有权限编辑此CSS页面,因为它包含另一位用户的个人设置。",
        "customjsonprotected": "您没有权限编辑此JSON页面,因为它包含另一位用户的个人设置。",
        "customjsprotected": "您没有权限编辑此JavaScript页面,因为它包含另一位用户的个人设置。",
-       "sitecssprotected": "您无权编辑此CSS页面,因为它可能会影响所有访问者",
-       "sitejsonprotected": "您无权编辑此JSON页面,因为它可能会影响所有访问者",
-       "sitejsprotected": "您无权编辑此JavaScript页面,因为它可能会影响所有访问者",
+       "sitecssprotected": "您无权编辑此CSS页面,因为它可能会影响所有访问者",
+       "sitejsonprotected": "您无权编辑此JSON页面,因为它可能会影响所有访问者",
+       "sitejsprotected": "您无权编辑此JavaScript页面,因为它可能会影响所有访问者",
        "mycustomcssprotected": "您没有权限编辑这个 CSS 页面。",
        "mycustomjsonprotected": "您没有权限编辑这个JSON页面。",
        "mycustomjsprotected": "您没有权限编辑这个 JavaScript 页面。",
index 6bb130d..35c37c1 100644 (file)
@@ -73,9 +73,9 @@ oojs-ui:
     package/dist/README.md:
 jquery:
   type: file
-  src: https://code.jquery.com/jquery-3.2.1.js
+  src: https://code.jquery.com/jquery-3.3.1.js
   # Integrity from link modals https://code.jquery.com/jquery/
-  integrity: sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE=
+  integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=
   dest: jquery.js
 qunitjs:
   type: multi-file
index 14dab33..52e8d49 100644 (file)
@@ -1450,6 +1450,7 @@ return [
                        'oojs-ui-core',
                        'mediawiki.widgets.visibleLengthLimit',
                        'mediawiki.api',
+                       'mediawiki.util',
                ],
        ],
        'mediawiki.action.edit.styles' => [
index d2d8ca4..9b5206b 100644 (file)
@@ -1,5 +1,5 @@
 /*!
- * jQuery JavaScript Library v3.2.1
+ * jQuery JavaScript Library v3.3.1
  * https://jquery.com/
  *
  * Includes Sizzle.js
@@ -9,7 +9,7 @@
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2017-03-20T18:59Z
+ * Date: 2018-01-20T17:24Z
  */
 ( function( global, factory ) {
 
@@ -71,16 +71,57 @@ var ObjectFunctionString = fnToString.call( Object );
 
 var support = {};
 
+var isFunction = function isFunction( obj ) {
 
+      // Support: Chrome <=57, Firefox <=52
+      // In some browsers, typeof returns "function" for HTML <object> elements
+      // (i.e., `typeof document.createElement( "object" ) === "function"`).
+      // We don't want to classify *any* DOM node as a function.
+      return typeof obj === "function" && typeof obj.nodeType !== "number";
+  };
 
-       function DOMEval( code, doc ) {
+
+var isWindow = function isWindow( obj ) {
+               return obj != null && obj === obj.window;
+       };
+
+
+
+
+       var preservedScriptAttributes = {
+               type: true,
+               src: true,
+               noModule: true
+       };
+
+       function DOMEval( code, doc, node ) {
                doc = doc || document;
 
-               var script = doc.createElement( "script" );
+               var i,
+                       script = doc.createElement( "script" );
 
                script.text = code;
+               if ( node ) {
+                       for ( i in preservedScriptAttributes ) {
+                               if ( node[ i ] ) {
+                                       script[ i ] = node[ i ];
+                               }
+                       }
+               }
                doc.head.appendChild( script ).parentNode.removeChild( script );
        }
+
+
+function toType( obj ) {
+       if ( obj == null ) {
+               return obj + "";
+       }
+
+       // Support: Android <=2.3 only (functionish RegExp)
+       return typeof obj === "object" || typeof obj === "function" ?
+               class2type[ toString.call( obj ) ] || "object" :
+               typeof obj;
+}
 /* global Symbol */
 // Defining this global in .eslintrc.json would create a danger of using the global
 // unguarded in another place, it seems safer to define global only for this module
@@ -88,7 +129,7 @@ var support = {};
 
 
 var
-       version = "3.2.1",
+       version = "3.3.1",
 
        // Define a local copy of jQuery
        jQuery = function( selector, context ) {
@@ -100,16 +141,7 @@ var
 
        // Support: Android <=4.0 only
        // Make sure we trim BOM and NBSP
-       rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-
-       // Matches dashed string for camelizing
-       rmsPrefix = /^-ms-/,
-       rdashAlpha = /-([a-z])/g,
-
-       // Used by jQuery.camelCase as callback to replace()
-       fcamelCase = function( all, letter ) {
-               return letter.toUpperCase();
-       };
+       rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
 
 jQuery.fn = jQuery.prototype = {
 
@@ -209,7 +241,7 @@ jQuery.extend = jQuery.fn.extend = function() {
        }
 
        // Handle case when target is a string or something (possible in deep copy)
-       if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
+       if ( typeof target !== "object" && !isFunction( target ) ) {
                target = {};
        }
 
@@ -275,28 +307,6 @@ jQuery.extend( {
 
        noop: function() {},
 
-       isFunction: function( obj ) {
-               return jQuery.type( obj ) === "function";
-       },
-
-       isWindow: function( obj ) {
-               return obj != null && obj === obj.window;
-       },
-
-       isNumeric: function( obj ) {
-
-               // As of jQuery 3.0, isNumeric is limited to
-               // strings and numbers (primitives or objects)
-               // that can be coerced to finite numbers (gh-2662)
-               var type = jQuery.type( obj );
-               return ( type === "number" || type === "string" ) &&
-
-                       // parseFloat NaNs numeric-cast false positives ("")
-                       // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
-                       // subtraction forces infinities to NaN
-                       !isNaN( obj - parseFloat( obj ) );
-       },
-
        isPlainObject: function( obj ) {
                var proto, Ctor;
 
@@ -330,29 +340,11 @@ jQuery.extend( {
                return true;
        },
 
-       type: function( obj ) {
-               if ( obj == null ) {
-                       return obj + "";
-               }
-
-               // Support: Android <=2.3 only (functionish RegExp)
-               return typeof obj === "object" || typeof obj === "function" ?
-                       class2type[ toString.call( obj ) ] || "object" :
-                       typeof obj;
-       },
-
        // Evaluates a script in a global context
        globalEval: function( code ) {
                DOMEval( code );
        },
 
-       // Convert dashed to camelCase; used by the css and data modules
-       // Support: IE <=9 - 11, Edge 12 - 13
-       // Microsoft forgot to hump their vendor prefix (#9572)
-       camelCase: function( string ) {
-               return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
-       },
-
        each: function( obj, callback ) {
                var length, i = 0;
 
@@ -473,37 +465,6 @@ jQuery.extend( {
        // A global GUID counter for objects
        guid: 1,
 
-       // Bind a function to a context, optionally partially applying any
-       // arguments.
-       proxy: function( fn, context ) {
-               var tmp, args, proxy;
-
-               if ( typeof context === "string" ) {
-                       tmp = fn[ context ];
-                       context = fn;
-                       fn = tmp;
-               }
-
-               // Quick check to determine if target is callable, in the spec
-               // this throws a TypeError, but we will just return undefined.
-               if ( !jQuery.isFunction( fn ) ) {
-                       return undefined;
-               }
-
-               // Simulated bind
-               args = slice.call( arguments, 2 );
-               proxy = function() {
-                       return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
-               };
-
-               // Set the guid of unique handler to the same of original handler, so it can be removed
-               proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
-               return proxy;
-       },
-
-       now: Date.now,
-
        // jQuery.support is not used in Core but other projects attach their
        // properties to it so it needs to exist.
        support: support
@@ -526,9 +487,9 @@ function isArrayLike( obj ) {
        // hasOwn isn't used here due to false negatives
        // regarding Nodelist length in IE
        var length = !!obj && "length" in obj && obj.length,
-               type = jQuery.type( obj );
+               type = toType( obj );
 
-       if ( type === "function" || jQuery.isWindow( obj ) ) {
+       if ( isFunction( obj ) || isWindow( obj ) ) {
                return false;
        }
 
@@ -2848,11 +2809,9 @@ var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|
 
 
 
-var risSimple = /^.[^:#\[\.,]*$/;
-
 // Implement the identical functionality for filter and not
 function winnow( elements, qualifier, not ) {
-       if ( jQuery.isFunction( qualifier ) ) {
+       if ( isFunction( qualifier ) ) {
                return jQuery.grep( elements, function( elem, i ) {
                        return !!qualifier.call( elem, i, elem ) !== not;
                } );
@@ -2872,16 +2831,8 @@ function winnow( elements, qualifier, not ) {
                } );
        }
 
-       // Simple selector that can be filtered directly, removing non-Elements
-       if ( risSimple.test( qualifier ) ) {
-               return jQuery.filter( qualifier, elements, not );
-       }
-
-       // Complex selector, compare the two sets, removing non-Elements
-       qualifier = jQuery.filter( qualifier, elements );
-       return jQuery.grep( elements, function( elem ) {
-               return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1;
-       } );
+       // Filtered directly for both simple and complex selectors
+       return jQuery.filter( qualifier, elements, not );
 }
 
 jQuery.filter = function( expr, elems, not ) {
@@ -3002,7 +2953,7 @@ var rootjQuery,
                                                for ( match in context ) {
 
                                                        // Properties of context are called as methods if possible
-                                                       if ( jQuery.isFunction( this[ match ] ) ) {
+                                                       if ( isFunction( this[ match ] ) ) {
                                                                this[ match ]( context[ match ] );
 
                                                        // ...and otherwise set as attributes
@@ -3045,7 +2996,7 @@ var rootjQuery,
 
                // HANDLE: $(function)
                // Shortcut for document ready
-               } else if ( jQuery.isFunction( selector ) ) {
+               } else if ( isFunction( selector ) ) {
                        return root.ready !== undefined ?
                                root.ready( selector ) :
 
@@ -3360,11 +3311,11 @@ jQuery.Callbacks = function( options ) {
 
                                        ( function add( args ) {
                                                jQuery.each( args, function( _, arg ) {
-                                                       if ( jQuery.isFunction( arg ) ) {
+                                                       if ( isFunction( arg ) ) {
                                                                if ( !options.unique || !self.has( arg ) ) {
                                                                        list.push( arg );
                                                                }
-                                                       } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
+                                                       } else if ( arg && arg.length && toType( arg ) !== "string" ) {
 
                                                                // Inspect recursively
                                                                add( arg );
@@ -3479,11 +3430,11 @@ function adoptValue( value, resolve, reject, noValue ) {
        try {
 
                // Check for promise aspect first to privilege synchronous behavior
-               if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
+               if ( value && isFunction( ( method = value.promise ) ) ) {
                        method.call( value ).done( resolve ).fail( reject );
 
                // Other thenables
-               } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
+               } else if ( value && isFunction( ( method = value.then ) ) ) {
                        method.call( value, resolve, reject );
 
                // Other non-thenables
@@ -3541,14 +3492,14 @@ jQuery.extend( {
                                                jQuery.each( tuples, function( i, tuple ) {
 
                                                        // Map tuples (progress, done, fail) to arguments (done, fail, progress)
-                                                       var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
+                                                       var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
 
                                                        // deferred.progress(function() { bind to newDefer or newDefer.notify })
                                                        // deferred.done(function() { bind to newDefer or newDefer.resolve })
                                                        // deferred.fail(function() { bind to newDefer or newDefer.reject })
                                                        deferred[ tuple[ 1 ] ]( function() {
                                                                var returned = fn && fn.apply( this, arguments );
-                                                               if ( returned && jQuery.isFunction( returned.promise ) ) {
+                                                               if ( returned && isFunction( returned.promise ) ) {
                                                                        returned.promise()
                                                                                .progress( newDefer.notify )
                                                                                .done( newDefer.resolve )
@@ -3602,7 +3553,7 @@ jQuery.extend( {
                                                                                returned.then;
 
                                                                        // Handle a returned thenable
-                                                                       if ( jQuery.isFunction( then ) ) {
+                                                                       if ( isFunction( then ) ) {
 
                                                                                // Special processors (notify) just wait for resolution
                                                                                if ( special ) {
@@ -3698,7 +3649,7 @@ jQuery.extend( {
                                                        resolve(
                                                                0,
                                                                newDefer,
-                                                               jQuery.isFunction( onProgress ) ?
+                                                               isFunction( onProgress ) ?
                                                                        onProgress :
                                                                        Identity,
                                                                newDefer.notifyWith
@@ -3710,7 +3661,7 @@ jQuery.extend( {
                                                        resolve(
                                                                0,
                                                                newDefer,
-                                                               jQuery.isFunction( onFulfilled ) ?
+                                                               isFunction( onFulfilled ) ?
                                                                        onFulfilled :
                                                                        Identity
                                                        )
@@ -3721,7 +3672,7 @@ jQuery.extend( {
                                                        resolve(
                                                                0,
                                                                newDefer,
-                                                               jQuery.isFunction( onRejected ) ?
+                                                               isFunction( onRejected ) ?
                                                                        onRejected :
                                                                        Thrower
                                                        )
@@ -3761,8 +3712,15 @@ jQuery.extend( {
                                        // fulfilled_callbacks.disable
                                        tuples[ 3 - i ][ 2 ].disable,
 
+                                       // rejected_handlers.disable
+                                       // fulfilled_handlers.disable
+                                       tuples[ 3 - i ][ 3 ].disable,
+
                                        // progress_callbacks.lock
-                                       tuples[ 0 ][ 2 ].lock
+                                       tuples[ 0 ][ 2 ].lock,
+
+                                       // progress_handlers.lock
+                                       tuples[ 0 ][ 3 ].lock
                                );
                        }
 
@@ -3832,7 +3790,7 @@ jQuery.extend( {
 
                        // Use .then() to unwrap secondary thenables (cf. gh-3000)
                        if ( master.state() === "pending" ||
-                               jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
+                               isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
 
                                return master.then();
                        }
@@ -3960,7 +3918,7 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
                bulk = key == null;
 
        // Sets many values
-       if ( jQuery.type( key ) === "object" ) {
+       if ( toType( key ) === "object" ) {
                chainable = true;
                for ( i in key ) {
                        access( elems, fn, i, key[ i ], true, emptyGet, raw );
@@ -3970,7 +3928,7 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
        } else if ( value !== undefined ) {
                chainable = true;
 
-               if ( !jQuery.isFunction( value ) ) {
+               if ( !isFunction( value ) ) {
                        raw = true;
                }
 
@@ -4012,6 +3970,23 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
 
        return len ? fn( elems[ 0 ], key ) : emptyGet;
 };
+
+
+// Matches dashed string for camelizing
+var rmsPrefix = /^-ms-/,
+       rdashAlpha = /-([a-z])/g;
+
+// Used by camelCase as callback to replace()
+function fcamelCase( all, letter ) {
+       return letter.toUpperCase();
+}
+
+// Convert dashed to camelCase; used by the css and data modules
+// Support: IE <=9 - 11, Edge 12 - 15
+// Microsoft forgot to hump their vendor prefix (#9572)
+function camelCase( string ) {
+       return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+}
 var acceptData = function( owner ) {
 
        // Accepts only:
@@ -4074,14 +4049,14 @@ Data.prototype = {
                // Handle: [ owner, key, value ] args
                // Always use camelCase key (gh-2257)
                if ( typeof data === "string" ) {
-                       cache[ jQuery.camelCase( data ) ] = value;
+                       cache[ camelCase( data ) ] = value;
 
                // Handle: [ owner, { properties } ] args
                } else {
 
                        // Copy the properties one-by-one to the cache object
                        for ( prop in data ) {
-                               cache[ jQuery.camelCase( prop ) ] = data[ prop ];
+                               cache[ camelCase( prop ) ] = data[ prop ];
                        }
                }
                return cache;
@@ -4091,7 +4066,7 @@ Data.prototype = {
                        this.cache( owner ) :
 
                        // Always use camelCase key (gh-2257)
-                       owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ];
+                       owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
        },
        access: function( owner, key, value ) {
 
@@ -4139,9 +4114,9 @@ Data.prototype = {
 
                                // If key is an array of keys...
                                // We always set camelCase keys, so remove that.
-                               key = key.map( jQuery.camelCase );
+                               key = key.map( camelCase );
                        } else {
-                               key = jQuery.camelCase( key );
+                               key = camelCase( key );
 
                                // If a key with the spaces exists, use it.
                                // Otherwise, create an array by matching non-whitespace
@@ -4287,7 +4262,7 @@ jQuery.fn.extend( {
                                                if ( attrs[ i ] ) {
                                                        name = attrs[ i ].name;
                                                        if ( name.indexOf( "data-" ) === 0 ) {
-                                                               name = jQuery.camelCase( name.slice( 5 ) );
+                                                               name = camelCase( name.slice( 5 ) );
                                                                dataAttr( elem, name, data[ name ] );
                                                        }
                                                }
@@ -4534,8 +4509,7 @@ var swap = function( elem, options, callback, args ) {
 
 
 function adjustCSS( elem, prop, valueParts, tween ) {
-       var adjusted,
-               scale = 1,
+       var adjusted, scale,
                maxIterations = 20,
                currentValue = tween ?
                        function() {
@@ -4553,30 +4527,33 @@ function adjustCSS( elem, prop, valueParts, tween ) {
 
        if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
 
+               // Support: Firefox <=54
+               // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
+               initial = initial / 2;
+
                // Trust units reported by jQuery.css
                unit = unit || initialInUnit[ 3 ];
 
-               // Make sure we update the tween properties later on
-               valueParts = valueParts || [];
-
                // Iteratively approximate from a nonzero starting point
                initialInUnit = +initial || 1;
 
-               do {
+               while ( maxIterations-- ) {
 
-                       // If previous iteration zeroed out, double until we get *something*.
-                       // Use string for doubling so we don't accidentally see scale as unchanged below
-                       scale = scale || ".5";
-
-                       // Adjust and apply
-                       initialInUnit = initialInUnit / scale;
+                       // Evaluate and update our best guess (doubling guesses that zero out).
+                       // Finish if the scale equals or crosses 1 (making the old*new product non-positive).
                        jQuery.style( elem, prop, initialInUnit + unit );
+                       if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
+                               maxIterations = 0;
+                       }
+                       initialInUnit = initialInUnit / scale;
 
-               // Update scale, tolerating zero or NaN from tween.cur()
-               // Break the loop if scale is unchanged or perfect, or if we've just had enough.
-               } while (
-                       scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
-               );
+               }
+
+               initialInUnit = initialInUnit * 2;
+               jQuery.style( elem, prop, initialInUnit + unit );
+
+               // Make sure we update the tween properties later on
+               valueParts = valueParts || [];
        }
 
        if ( valueParts ) {
@@ -4694,7 +4671,7 @@ var rcheckableType = ( /^(?:checkbox|radio)$/i );
 
 var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i );
 
-var rscriptType = ( /^$|\/(?:java|ecma)script/i );
+var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
 
 
 
@@ -4776,7 +4753,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
                if ( elem || elem === 0 ) {
 
                        // Add nodes directly
-                       if ( jQuery.type( elem ) === "object" ) {
+                       if ( toType( elem ) === "object" ) {
 
                                // Support: Android <=4.0 only, PhantomJS 1 only
                                // push.apply(_, arraylike) throws on ancient WebKit
@@ -5286,7 +5263,7 @@ jQuery.event = {
                        enumerable: true,
                        configurable: true,
 
-                       get: jQuery.isFunction( hook ) ?
+                       get: isFunction( hook ) ?
                                function() {
                                        if ( this.originalEvent ) {
                                                        return hook( this.originalEvent );
@@ -5421,7 +5398,7 @@ jQuery.Event = function( src, props ) {
        }
 
        // Create a timestamp if incoming event doesn't have one
-       this.timeStamp = src && src.timeStamp || jQuery.now();
+       this.timeStamp = src && src.timeStamp || Date.now();
 
        // Mark it as fixed
        this[ jQuery.expando ] = true;
@@ -5620,14 +5597,13 @@ var
 
        /* eslint-enable */
 
-       // Support: IE <=10 - 11, Edge 12 - 13
+       // Support: IE <=10 - 11, Edge 12 - 13 only
        // In IE/Edge using regex groups here causes severe slowdowns.
        // See https://connect.microsoft.com/IE/feedback/details/1736512/
        rnoInnerhtml = /<script|<style|<link/i,
 
        // checked="checked" or checked
        rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
-       rscriptTypeMasked = /^true\/(.*)/,
        rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
 
 // Prefer a tbody over its parent table for containing new rows
@@ -5635,7 +5611,7 @@ function manipulationTarget( elem, content ) {
        if ( nodeName( elem, "table" ) &&
                nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
 
-               return jQuery( ">tbody", elem )[ 0 ] || elem;
+               return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
        }
 
        return elem;
@@ -5647,10 +5623,8 @@ function disableScript( elem ) {
        return elem;
 }
 function restoreScript( elem ) {
-       var match = rscriptTypeMasked.exec( elem.type );
-
-       if ( match ) {
-               elem.type = match[ 1 ];
+       if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
+               elem.type = elem.type.slice( 5 );
        } else {
                elem.removeAttribute( "type" );
        }
@@ -5716,15 +5690,15 @@ function domManip( collection, args, callback, ignored ) {
                l = collection.length,
                iNoClone = l - 1,
                value = args[ 0 ],
-               isFunction = jQuery.isFunction( value );
+               valueIsFunction = isFunction( value );
 
        // We can't cloneNode fragments that contain checked, in WebKit
-       if ( isFunction ||
+       if ( valueIsFunction ||
                        ( l > 1 && typeof value === "string" &&
                                !support.checkClone && rchecked.test( value ) ) ) {
                return collection.each( function( index ) {
                        var self = collection.eq( index );
-                       if ( isFunction ) {
+                       if ( valueIsFunction ) {
                                args[ 0 ] = value.call( this, index, self.html() );
                        }
                        domManip( self, args, callback, ignored );
@@ -5778,14 +5752,14 @@ function domManip( collection, args, callback, ignored ) {
                                                !dataPriv.access( node, "globalEval" ) &&
                                                jQuery.contains( doc, node ) ) {
 
-                                               if ( node.src ) {
+                                               if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {
 
                                                        // Optional AJAX dependency, but won't run scripts if not present
                                                        if ( jQuery._evalUrl ) {
                                                                jQuery._evalUrl( node.src );
                                                        }
                                                } else {
-                                                       DOMEval( node.textContent.replace( rcleanScript, "" ), doc );
+                                                       DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node );
                                                }
                                        }
                                }
@@ -6065,8 +6039,6 @@ jQuery.each( {
                return this.pushStack( ret );
        };
 } );
-var rmargin = ( /^margin/ );
-
 var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
 
 var getStyles = function( elem ) {
@@ -6083,6 +6055,8 @@ var getStyles = function( elem ) {
                return view.getComputedStyle( elem );
        };
 
+var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
+
 
 
 ( function() {
@@ -6096,25 +6070,33 @@ var getStyles = function( elem ) {
                        return;
                }
 
+               container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
+                       "margin-top:1px;padding:0;border:0";
                div.style.cssText =
-                       "box-sizing:border-box;" +
-                       "position:relative;display:block;" +
+                       "position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
                        "margin:auto;border:1px;padding:1px;" +
-                       "top:1%;width:50%";
-               div.innerHTML = "";
-               documentElement.appendChild( container );
+                       "width:60%;top:1%";
+               documentElement.appendChild( container ).appendChild( div );
 
                var divStyle = window.getComputedStyle( div );
                pixelPositionVal = divStyle.top !== "1%";
 
                // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
-               reliableMarginLeftVal = divStyle.marginLeft === "2px";
-               boxSizingReliableVal = divStyle.width === "4px";
+               reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
 
-               // Support: Android 4.0 - 4.3 only
+               // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
                // Some styles come back with percentage values, even though they shouldn't
-               div.style.marginRight = "50%";
-               pixelMarginRightVal = divStyle.marginRight === "4px";
+               div.style.right = "60%";
+               pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;
+
+               // Support: IE 9 - 11 only
+               // Detect misreporting of content dimensions for box-sizing:border-box elements
+               boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
+
+               // Support: IE 9 only
+               // Detect overflow:scroll screwiness (gh-3699)
+               div.style.position = "absolute";
+               scrollboxSizeVal = div.offsetWidth === 36 || "absolute";
 
                documentElement.removeChild( container );
 
@@ -6123,7 +6105,12 @@ var getStyles = function( elem ) {
                div = null;
        }
 
-       var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
+       function roundPixelMeasures( measure ) {
+               return Math.round( parseFloat( measure ) );
+       }
+
+       var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
+               reliableMarginLeftVal,
                container = document.createElement( "div" ),
                div = document.createElement( "div" );
 
@@ -6138,26 +6125,26 @@ var getStyles = function( elem ) {
        div.cloneNode( true ).style.backgroundClip = "";
        support.clearCloneStyle = div.style.backgroundClip === "content-box";
 
-       container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
-               "padding:0;margin-top:1px;position:absolute";
-       container.appendChild( div );
-
        jQuery.extend( support, {
-               pixelPosition: function() {
-                       computeStyleTests();
-                       return pixelPositionVal;
-               },
                boxSizingReliable: function() {
                        computeStyleTests();
                        return boxSizingReliableVal;
                },
-               pixelMarginRight: function() {
+               pixelBoxStyles: function() {
+                       computeStyleTests();
+                       return pixelBoxStylesVal;
+               },
+               pixelPosition: function() {
                        computeStyleTests();
-                       return pixelMarginRightVal;
+                       return pixelPositionVal;
                },
                reliableMarginLeft: function() {
                        computeStyleTests();
                        return reliableMarginLeftVal;
+               },
+               scrollboxSize: function() {
+                       computeStyleTests();
+                       return scrollboxSizeVal;
                }
        } );
 } )();
@@ -6189,7 +6176,7 @@ function curCSS( elem, name, computed ) {
                // but width seems to be reliably pixels.
                // This is against the CSSOM draft spec:
                // https://drafts.csswg.org/cssom/#resolved-values
-               if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+               if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
 
                        // Remember the original values
                        width = style.width;
@@ -6294,87 +6281,120 @@ function setPositiveNumber( elem, value, subtract ) {
                value;
 }
 
-function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
-       var i,
-               val = 0;
-
-       // If we already have the right measurement, avoid augmentation
-       if ( extra === ( isBorderBox ? "border" : "content" ) ) {
-               i = 4;
+function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
+       var i = dimension === "width" ? 1 : 0,
+               extra = 0,
+               delta = 0;
 
-       // Otherwise initialize for horizontal or vertical properties
-       } else {
-               i = name === "width" ? 1 : 0;
+       // Adjustment may not be necessary
+       if ( box === ( isBorderBox ? "border" : "content" ) ) {
+               return 0;
        }
 
        for ( ; i < 4; i += 2 ) {
 
-               // Both box models exclude margin, so add it if we want it
-               if ( extra === "margin" ) {
-                       val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+               // Both box models exclude margin
+               if ( box === "margin" ) {
+                       delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
                }
 
-               if ( isBorderBox ) {
+               // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
+               if ( !isBorderBox ) {
 
-                       // border-box includes padding, so remove it if we want content
-                       if ( extra === "content" ) {
-                               val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
-                       }
+                       // Add padding
+                       delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
 
-                       // At this point, extra isn't border nor margin, so remove border
-                       if ( extra !== "margin" ) {
-                               val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+                       // For "border" or "margin", add border
+                       if ( box !== "padding" ) {
+                               delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+
+                       // But still keep track of it otherwise
+                       } else {
+                               extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
                        }
+
+               // If we get here with a border-box (content + padding + border), we're seeking "content" or
+               // "padding" or "margin"
                } else {
 
-                       // At this point, extra isn't content, so add padding
-                       val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+                       // For "content", subtract padding
+                       if ( box === "content" ) {
+                               delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+                       }
 
-                       // At this point, extra isn't content nor padding, so add border
-                       if ( extra !== "padding" ) {
-                               val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+                       // For "content" or "padding", subtract border
+                       if ( box !== "margin" ) {
+                               delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
                        }
                }
        }
 
-       return val;
+       // Account for positive content-box scroll gutter when requested by providing computedVal
+       if ( !isBorderBox && computedVal >= 0 ) {
+
+               // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
+               // Assuming integer scroll gutter, subtract the rest and round down
+               delta += Math.max( 0, Math.ceil(
+                       elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
+                       computedVal -
+                       delta -
+                       extra -
+                       0.5
+               ) );
+       }
+
+       return delta;
 }
 
-function getWidthOrHeight( elem, name, extra ) {
+function getWidthOrHeight( elem, dimension, extra ) {
 
        // Start with computed style
-       var valueIsBorderBox,
-               styles = getStyles( elem ),
-               val = curCSS( elem, name, styles ),
-               isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+       var styles = getStyles( elem ),
+               val = curCSS( elem, dimension, styles ),
+               isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+               valueIsBorderBox = isBorderBox;
 
-       // Computed unit is not pixels. Stop here and return.
+       // Support: Firefox <=54
+       // Return a confounding non-pixel value or feign ignorance, as appropriate.
        if ( rnumnonpx.test( val ) ) {
-               return val;
+               if ( !extra ) {
+                       return val;
+               }
+               val = "auto";
        }
 
        // Check for style in case a browser which returns unreliable values
        // for getComputedStyle silently falls back to the reliable elem.style
-       valueIsBorderBox = isBorderBox &&
-               ( support.boxSizingReliable() || val === elem.style[ name ] );
+       valueIsBorderBox = valueIsBorderBox &&
+               ( support.boxSizingReliable() || val === elem.style[ dimension ] );
 
-       // Fall back to offsetWidth/Height when value is "auto"
+       // Fall back to offsetWidth/offsetHeight when value is "auto"
        // This happens for inline elements with no explicit setting (gh-3571)
-       if ( val === "auto" ) {
-               val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ];
+       // Support: Android <=4.1 - 4.3 only
+       // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
+       if ( val === "auto" ||
+               !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) {
+
+               val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ];
+
+               // offsetWidth/offsetHeight provide border-box values
+               valueIsBorderBox = true;
        }
 
-       // Normalize "", auto, and prepare for extra
+       // Normalize "" and auto
        val = parseFloat( val ) || 0;
 
-       // Use the active box-sizing model to add/subtract irrelevant styles
+       // Adjust for the element's box model
        return ( val +
-               augmentWidthOrHeight(
+               boxModelAdjustment(
                        elem,
-                       name,
+                       dimension,
                        extra || ( isBorderBox ? "border" : "content" ),
                        valueIsBorderBox,
-                       styles
+                       styles,
+
+                       // Provide the current computed size to request scroll gutter calculation (gh-3589)
+                       val
                )
        ) + "px";
 }
@@ -6415,9 +6435,7 @@ jQuery.extend( {
 
        // Add in properties whose names you wish to fix before
        // setting or getting the value
-       cssProps: {
-               "float": "cssFloat"
-       },
+       cssProps: {},
 
        // Get and set the style property on a DOM Node
        style: function( elem, name, value, extra ) {
@@ -6429,7 +6447,7 @@ jQuery.extend( {
 
                // Make sure that we're working with the right name
                var ret, type, hooks,
-                       origName = jQuery.camelCase( name ),
+                       origName = camelCase( name ),
                        isCustomProp = rcustomProp.test( name ),
                        style = elem.style;
 
@@ -6497,7 +6515,7 @@ jQuery.extend( {
 
        css: function( elem, name, extra, styles ) {
                var val, num, hooks,
-                       origName = jQuery.camelCase( name ),
+                       origName = camelCase( name ),
                        isCustomProp = rcustomProp.test( name );
 
                // Make sure that we're working with the right name. We don't
@@ -6535,8 +6553,8 @@ jQuery.extend( {
        }
 } );
 
-jQuery.each( [ "height", "width" ], function( i, name ) {
-       jQuery.cssHooks[ name ] = {
+jQuery.each( [ "height", "width" ], function( i, dimension ) {
+       jQuery.cssHooks[ dimension ] = {
                get: function( elem, computed, extra ) {
                        if ( computed ) {
 
@@ -6552,29 +6570,41 @@ jQuery.each( [ "height", "width" ], function( i, name ) {
                                        // in IE throws an error.
                                        ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
                                                swap( elem, cssShow, function() {
-                                                       return getWidthOrHeight( elem, name, extra );
+                                                       return getWidthOrHeight( elem, dimension, extra );
                                                } ) :
-                                               getWidthOrHeight( elem, name, extra );
+                                               getWidthOrHeight( elem, dimension, extra );
                        }
                },
 
                set: function( elem, value, extra ) {
                        var matches,
-                               styles = extra && getStyles( elem ),
-                               subtract = extra && augmentWidthOrHeight(
+                               styles = getStyles( elem ),
+                               isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+                               subtract = extra && boxModelAdjustment(
                                        elem,
-                                       name,
+                                       dimension,
                                        extra,
-                                       jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+                                       isBorderBox,
                                        styles
                                );
 
+                       // Account for unreliable border-box dimensions by comparing offset* to computed and
+                       // faking a content-box to get border and padding (gh-3699)
+                       if ( isBorderBox && support.scrollboxSize() === styles.position ) {
+                               subtract -= Math.ceil(
+                                       elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
+                                       parseFloat( styles[ dimension ] ) -
+                                       boxModelAdjustment( elem, dimension, "border", false, styles ) -
+                                       0.5
+                               );
+                       }
+
                        // Convert to pixels if value adjustment is needed
                        if ( subtract && ( matches = rcssNum.exec( value ) ) &&
                                ( matches[ 3 ] || "px" ) !== "px" ) {
 
-                               elem.style[ name ] = value;
-                               value = jQuery.css( elem, name );
+                               elem.style[ dimension ] = value;
+                               value = jQuery.css( elem, dimension );
                        }
 
                        return setPositiveNumber( elem, value, subtract );
@@ -6618,7 +6648,7 @@ jQuery.each( {
                }
        };
 
-       if ( !rmargin.test( prefix ) ) {
+       if ( prefix !== "margin" ) {
                jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
        }
 } );
@@ -6789,7 +6819,7 @@ function createFxNow() {
        window.setTimeout( function() {
                fxNow = undefined;
        } );
-       return ( fxNow = jQuery.now() );
+       return ( fxNow = Date.now() );
 }
 
 // Generate parameters to create a standard animation
@@ -6893,9 +6923,10 @@ function defaultPrefilter( elem, props, opts ) {
        // Restrict "overflow" and "display" styles during box animations
        if ( isBox && elem.nodeType === 1 ) {
 
-               // Support: IE <=9 - 11, Edge 12 - 13
+               // Support: IE <=9 - 11, Edge 12 - 15
                // Record all 3 overflow attributes because IE does not infer the shorthand
-               // from identically-valued overflowX and overflowY
+               // from identically-valued overflowX and overflowY and Edge just mirrors
+               // the overflowX value there.
                opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
 
                // Identify a display type, preferring old show/hide data over the CSS cascade
@@ -7003,7 +7034,7 @@ function propFilter( props, specialEasing ) {
 
        // camelCase, specialEasing and expand cssHook pass
        for ( index in props ) {
-               name = jQuery.camelCase( index );
+               name = camelCase( index );
                easing = specialEasing[ name ];
                value = props[ index ];
                if ( Array.isArray( value ) ) {
@@ -7128,9 +7159,9 @@ function Animation( elem, properties, options ) {
        for ( ; index < length; index++ ) {
                result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
                if ( result ) {
-                       if ( jQuery.isFunction( result.stop ) ) {
+                       if ( isFunction( result.stop ) ) {
                                jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
-                                       jQuery.proxy( result.stop, result );
+                                       result.stop.bind( result );
                        }
                        return result;
                }
@@ -7138,7 +7169,7 @@ function Animation( elem, properties, options ) {
 
        jQuery.map( props, createTween, animation );
 
-       if ( jQuery.isFunction( animation.opts.start ) ) {
+       if ( isFunction( animation.opts.start ) ) {
                animation.opts.start.call( elem, animation );
        }
 
@@ -7171,7 +7202,7 @@ jQuery.Animation = jQuery.extend( Animation, {
        },
 
        tweener: function( props, callback ) {
-               if ( jQuery.isFunction( props ) ) {
+               if ( isFunction( props ) ) {
                        callback = props;
                        props = [ "*" ];
                } else {
@@ -7203,9 +7234,9 @@ jQuery.Animation = jQuery.extend( Animation, {
 jQuery.speed = function( speed, easing, fn ) {
        var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
                complete: fn || !fn && easing ||
-                       jQuery.isFunction( speed ) && speed,
+                       isFunction( speed ) && speed,
                duration: speed,
-               easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+               easing: fn && easing || easing && !isFunction( easing ) && easing
        };
 
        // Go to the end state if fx are off
@@ -7232,7 +7263,7 @@ jQuery.speed = function( speed, easing, fn ) {
        opt.old = opt.complete;
 
        opt.complete = function() {
-               if ( jQuery.isFunction( opt.old ) ) {
+               if ( isFunction( opt.old ) ) {
                        opt.old.call( this );
                }
 
@@ -7396,7 +7427,7 @@ jQuery.fx.tick = function() {
                i = 0,
                timers = jQuery.timers;
 
-       fxNow = jQuery.now();
+       fxNow = Date.now();
 
        for ( ; i < timers.length; i++ ) {
                timer = timers[ i ];
@@ -7749,7 +7780,7 @@ jQuery.each( [
 
 
        // Strip and collapse whitespace according to HTML spec
-       // https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace
+       // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
        function stripAndCollapse( value ) {
                var tokens = value.match( rnothtmlwhite ) || [];
                return tokens.join( " " );
@@ -7760,20 +7791,30 @@ function getClass( elem ) {
        return elem.getAttribute && elem.getAttribute( "class" ) || "";
 }
 
+function classesToArray( value ) {
+       if ( Array.isArray( value ) ) {
+               return value;
+       }
+       if ( typeof value === "string" ) {
+               return value.match( rnothtmlwhite ) || [];
+       }
+       return [];
+}
+
 jQuery.fn.extend( {
        addClass: function( value ) {
                var classes, elem, cur, curValue, clazz, j, finalValue,
                        i = 0;
 
-               if ( jQuery.isFunction( value ) ) {
+               if ( isFunction( value ) ) {
                        return this.each( function( j ) {
                                jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
                        } );
                }
 
-               if ( typeof value === "string" && value ) {
-                       classes = value.match( rnothtmlwhite ) || [];
+               classes = classesToArray( value );
 
+               if ( classes.length ) {
                        while ( ( elem = this[ i++ ] ) ) {
                                curValue = getClass( elem );
                                cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
@@ -7802,7 +7843,7 @@ jQuery.fn.extend( {
                var classes, elem, cur, curValue, clazz, j, finalValue,
                        i = 0;
 
-               if ( jQuery.isFunction( value ) ) {
+               if ( isFunction( value ) ) {
                        return this.each( function( j ) {
                                jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
                        } );
@@ -7812,9 +7853,9 @@ jQuery.fn.extend( {
                        return this.attr( "class", "" );
                }
 
-               if ( typeof value === "string" && value ) {
-                       classes = value.match( rnothtmlwhite ) || [];
+               classes = classesToArray( value );
 
+               if ( classes.length ) {
                        while ( ( elem = this[ i++ ] ) ) {
                                curValue = getClass( elem );
 
@@ -7844,13 +7885,14 @@ jQuery.fn.extend( {
        },
 
        toggleClass: function( value, stateVal ) {
-               var type = typeof value;
+               var type = typeof value,
+                       isValidValue = type === "string" || Array.isArray( value );
 
-               if ( typeof stateVal === "boolean" && type === "string" ) {
+               if ( typeof stateVal === "boolean" && isValidValue ) {
                        return stateVal ? this.addClass( value ) : this.removeClass( value );
                }
 
-               if ( jQuery.isFunction( value ) ) {
+               if ( isFunction( value ) ) {
                        return this.each( function( i ) {
                                jQuery( this ).toggleClass(
                                        value.call( this, i, getClass( this ), stateVal ),
@@ -7862,12 +7904,12 @@ jQuery.fn.extend( {
                return this.each( function() {
                        var className, i, self, classNames;
 
-                       if ( type === "string" ) {
+                       if ( isValidValue ) {
 
                                // Toggle individual class names
                                i = 0;
                                self = jQuery( this );
-                               classNames = value.match( rnothtmlwhite ) || [];
+                               classNames = classesToArray( value );
 
                                while ( ( className = classNames[ i++ ] ) ) {
 
@@ -7926,7 +7968,7 @@ var rreturn = /\r/g;
 
 jQuery.fn.extend( {
        val: function( value ) {
-               var hooks, ret, isFunction,
+               var hooks, ret, valueIsFunction,
                        elem = this[ 0 ];
 
                if ( !arguments.length ) {
@@ -7955,7 +7997,7 @@ jQuery.fn.extend( {
                        return;
                }
 
-               isFunction = jQuery.isFunction( value );
+               valueIsFunction = isFunction( value );
 
                return this.each( function( i ) {
                        var val;
@@ -7964,7 +8006,7 @@ jQuery.fn.extend( {
                                return;
                        }
 
-                       if ( isFunction ) {
+                       if ( valueIsFunction ) {
                                val = value.call( this, i, jQuery( this ).val() );
                        } else {
                                val = value;
@@ -8106,18 +8148,24 @@ jQuery.each( [ "radio", "checkbox" ], function() {
 // Return jQuery for attributes-only inclusion
 
 
-var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;
+support.focusin = "onfocusin" in window;
+
+
+var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+       stopPropagationCallback = function( e ) {
+               e.stopPropagation();
+       };
 
 jQuery.extend( jQuery.event, {
 
        trigger: function( event, data, elem, onlyHandlers ) {
 
-               var i, cur, tmp, bubbleType, ontype, handle, special,
+               var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
                        eventPath = [ elem || document ],
                        type = hasOwn.call( event, "type" ) ? event.type : event,
                        namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
 
-               cur = tmp = elem = elem || document;
+               cur = lastElement = tmp = elem = elem || document;
 
                // Don't do events on text and comment nodes
                if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
@@ -8169,7 +8217,7 @@ jQuery.extend( jQuery.event, {
 
                // Determine event propagation path in advance, per W3C events spec (#9951)
                // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
-               if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+               if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
 
                        bubbleType = special.delegateType || type;
                        if ( !rfocusMorph.test( bubbleType + type ) ) {
@@ -8189,7 +8237,7 @@ jQuery.extend( jQuery.event, {
                // Fire handlers on the event path
                i = 0;
                while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
-
+                       lastElement = cur;
                        event.type = i > 1 ?
                                bubbleType :
                                special.bindType || type;
@@ -8221,7 +8269,7 @@ jQuery.extend( jQuery.event, {
 
                                // Call a native DOM method on the target with the same name as the event.
                                // Don't do default actions on window, that's where global variables be (#6170)
-                               if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
+                               if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
 
                                        // Don't re-trigger an onFOO event when we call its FOO() method
                                        tmp = elem[ ontype ];
@@ -8232,7 +8280,17 @@ jQuery.extend( jQuery.event, {
 
                                        // Prevent re-triggering of the same event, since we already bubbled it above
                                        jQuery.event.triggered = type;
+
+                                       if ( event.isPropagationStopped() ) {
+                                               lastElement.addEventListener( type, stopPropagationCallback );
+                                       }
+
                                        elem[ type ]();
+
+                                       if ( event.isPropagationStopped() ) {
+                                               lastElement.removeEventListener( type, stopPropagationCallback );
+                                       }
+
                                        jQuery.event.triggered = undefined;
 
                                        if ( tmp ) {
@@ -8278,31 +8336,6 @@ jQuery.fn.extend( {
 } );
 
 
-jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
-       "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
-       "change select submit keydown keypress keyup contextmenu" ).split( " " ),
-       function( i, name ) {
-
-       // Handle event binding
-       jQuery.fn[ name ] = function( data, fn ) {
-               return arguments.length > 0 ?
-                       this.on( name, null, data, fn ) :
-                       this.trigger( name );
-       };
-} );
-
-jQuery.fn.extend( {
-       hover: function( fnOver, fnOut ) {
-               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
-       }
-} );
-
-
-
-
-support.focusin = "onfocusin" in window;
-
-
 // Support: Firefox <=44
 // Firefox doesn't have focus(in | out) events
 // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
@@ -8346,7 +8379,7 @@ if ( !support.focusin ) {
 }
 var location = window.location;
 
-var nonce = jQuery.now();
+var nonce = Date.now();
 
 var rquery = ( /\?/ );
 
@@ -8404,7 +8437,7 @@ function buildParams( prefix, obj, traditional, add ) {
                        }
                } );
 
-       } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+       } else if ( !traditional && toType( obj ) === "object" ) {
 
                // Serialize object item.
                for ( name in obj ) {
@@ -8426,7 +8459,7 @@ jQuery.param = function( a, traditional ) {
                add = function( key, valueOrFunction ) {
 
                        // If value is a function, invoke it and use its return value
-                       var value = jQuery.isFunction( valueOrFunction ) ?
+                       var value = isFunction( valueOrFunction ) ?
                                valueOrFunction() :
                                valueOrFunction;
 
@@ -8544,7 +8577,7 @@ function addToPrefiltersOrTransports( structure ) {
                        i = 0,
                        dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
 
-               if ( jQuery.isFunction( func ) ) {
+               if ( isFunction( func ) ) {
 
                        // For each dataType in the dataTypeExpression
                        while ( ( dataType = dataTypes[ i++ ] ) ) {
@@ -9016,7 +9049,7 @@ jQuery.extend( {
                if ( s.crossDomain == null ) {
                        urlAnchor = document.createElement( "a" );
 
-                       // Support: IE <=8 - 11, Edge 12 - 13
+                       // Support: IE <=8 - 11, Edge 12 - 15
                        // IE throws exception on accessing the href property if url is malformed,
                        // e.g. http://example.com:80x/
                        try {
@@ -9074,8 +9107,8 @@ jQuery.extend( {
                        // Remember the hash so we can put it back
                        uncached = s.url.slice( cacheURL.length );
 
-                       // If data is available, append data to url
-                       if ( s.data ) {
+                       // If data is available and should be processed, append data to url
+                       if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
                                cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
 
                                // #9682: remove data so that it's not used in an eventual retry
@@ -9312,7 +9345,7 @@ jQuery.each( [ "get", "post" ], function( i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {
 
                // Shift arguments if data argument was omitted
-               if ( jQuery.isFunction( data ) ) {
+               if ( isFunction( data ) ) {
                        type = type || callback;
                        callback = data;
                        data = undefined;
@@ -9350,7 +9383,7 @@ jQuery.fn.extend( {
                var wrap;
 
                if ( this[ 0 ] ) {
-                       if ( jQuery.isFunction( html ) ) {
+                       if ( isFunction( html ) ) {
                                html = html.call( this[ 0 ] );
                        }
 
@@ -9376,7 +9409,7 @@ jQuery.fn.extend( {
        },
 
        wrapInner: function( html ) {
-               if ( jQuery.isFunction( html ) ) {
+               if ( isFunction( html ) ) {
                        return this.each( function( i ) {
                                jQuery( this ).wrapInner( html.call( this, i ) );
                        } );
@@ -9396,10 +9429,10 @@ jQuery.fn.extend( {
        },
 
        wrap: function( html ) {
-               var isFunction = jQuery.isFunction( html );
+               var htmlIsFunction = isFunction( html );
 
                return this.each( function( i ) {
-                       jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
+                       jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
                } );
        },
 
@@ -9491,7 +9524,8 @@ jQuery.ajaxTransport( function( options ) {
                                        return function() {
                                                if ( callback ) {
                                                        callback = errorCallback = xhr.onload =
-                                                               xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;
+                                                               xhr.onerror = xhr.onabort = xhr.ontimeout =
+                                                                       xhr.onreadystatechange = null;
 
                                                        if ( type === "abort" ) {
                                                                xhr.abort();
@@ -9531,7 +9565,7 @@ jQuery.ajaxTransport( function( options ) {
 
                                // Listen to events
                                xhr.onload = callback();
-                               errorCallback = xhr.onerror = callback( "error" );
+                               errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
 
                                // Support: IE 9 only
                                // Use onreadystatechange to replace onabort
@@ -9685,7 +9719,7 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
        if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
 
                // Get callback name, remembering preexisting value associated with it
-               callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+               callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
                        s.jsonpCallback() :
                        s.jsonpCallback;
 
@@ -9736,7 +9770,7 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
                        }
 
                        // Call if it was a function and we have a response
-                       if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+                       if ( responseContainer && isFunction( overwritten ) ) {
                                overwritten( responseContainer[ 0 ] );
                        }
 
@@ -9828,7 +9862,7 @@ jQuery.fn.load = function( url, params, callback ) {
        }
 
        // If it's a function
-       if ( jQuery.isFunction( params ) ) {
+       if ( isFunction( params ) ) {
 
                // We assume that it's the callback
                callback = params;
@@ -9936,7 +9970,7 @@ jQuery.offset = {
                        curLeft = parseFloat( curCSSLeft ) || 0;
                }
 
-               if ( jQuery.isFunction( options ) ) {
+               if ( isFunction( options ) ) {
 
                        // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
                        options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
@@ -9959,6 +9993,8 @@ jQuery.offset = {
 };
 
 jQuery.fn.extend( {
+
+       // offset() relates an element's border box to the document origin
        offset: function( options ) {
 
                // Preserve chaining for setter
@@ -9970,7 +10006,7 @@ jQuery.fn.extend( {
                                } );
                }
 
-               var doc, docElem, rect, win,
+               var rect, win,
                        elem = this[ 0 ];
 
                if ( !elem ) {
@@ -9985,50 +10021,52 @@ jQuery.fn.extend( {
                        return { top: 0, left: 0 };
                }
 
+               // Get document-relative position by adding viewport scroll to viewport-relative gBCR
                rect = elem.getBoundingClientRect();
-
-               doc = elem.ownerDocument;
-               docElem = doc.documentElement;
-               win = doc.defaultView;
-
+               win = elem.ownerDocument.defaultView;
                return {
-                       top: rect.top + win.pageYOffset - docElem.clientTop,
-                       left: rect.left + win.pageXOffset - docElem.clientLeft
+                       top: rect.top + win.pageYOffset,
+                       left: rect.left + win.pageXOffset
                };
        },
 
+       // position() relates an element's margin box to its offset parent's padding box
+       // This corresponds to the behavior of CSS absolute positioning
        position: function() {
                if ( !this[ 0 ] ) {
                        return;
                }
 
-               var offsetParent, offset,
+               var offsetParent, offset, doc,
                        elem = this[ 0 ],
                        parentOffset = { top: 0, left: 0 };
 
-               // Fixed elements are offset from window (parentOffset = {top:0, left: 0},
-               // because it is its only offset parent
+               // position:fixed elements are offset from the viewport, which itself always has zero offset
                if ( jQuery.css( elem, "position" ) === "fixed" ) {
 
-                       // Assume getBoundingClientRect is there when computed position is fixed
+                       // Assume position:fixed implies availability of getBoundingClientRect
                        offset = elem.getBoundingClientRect();
 
                } else {
+                       offset = this.offset();
 
-                       // Get *real* offsetParent
-                       offsetParent = this.offsetParent();
+                       // Account for the *real* offset parent, which can be the document or its root element
+                       // when a statically positioned element is identified
+                       doc = elem.ownerDocument;
+                       offsetParent = elem.offsetParent || doc.documentElement;
+                       while ( offsetParent &&
+                               ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
+                               jQuery.css( offsetParent, "position" ) === "static" ) {
 
-                       // Get correct offsets
-                       offset = this.offset();
-                       if ( !nodeName( offsetParent[ 0 ], "html" ) ) {
-                               parentOffset = offsetParent.offset();
+                               offsetParent = offsetParent.parentNode;
                        }
+                       if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
 
-                       // Add offsetParent borders
-                       parentOffset = {
-                               top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ),
-                               left: parentOffset.left + jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true )
-                       };
+                               // Incorporate borders into its offset, since they are outside its content origin
+                               parentOffset = jQuery( offsetParent ).offset();
+                               parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
+                               parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
+                       }
                }
 
                // Subtract parent offsets and element margins
@@ -10070,7 +10108,7 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function(
 
                        // Coalesce documents and windows
                        var win;
-                       if ( jQuery.isWindow( elem ) ) {
+                       if ( isWindow( elem ) ) {
                                win = elem;
                        } else if ( elem.nodeType === 9 ) {
                                win = elem.defaultView;
@@ -10128,7 +10166,7 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
                        return access( this, function( elem, type, value ) {
                                var doc;
 
-                               if ( jQuery.isWindow( elem ) ) {
+                               if ( isWindow( elem ) ) {
 
                                        // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
                                        return funcName.indexOf( "outer" ) === 0 ?
@@ -10162,6 +10200,28 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
 } );
 
 
+jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
+       "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+       "change select submit keydown keypress keyup contextmenu" ).split( " " ),
+       function( i, name ) {
+
+       // Handle event binding
+       jQuery.fn[ name ] = function( data, fn ) {
+               return arguments.length > 0 ?
+                       this.on( name, null, data, fn ) :
+                       this.trigger( name );
+       };
+} );
+
+jQuery.fn.extend( {
+       hover: function( fnOver, fnOut ) {
+               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+       }
+} );
+
+
+
+
 jQuery.fn.extend( {
 
        bind: function( types, data, fn ) {
@@ -10183,6 +10243,37 @@ jQuery.fn.extend( {
        }
 } );
 
+// Bind a function to a context, optionally partially applying any
+// arguments.
+// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
+// However, it is not slated for removal any time soon
+jQuery.proxy = function( fn, context ) {
+       var tmp, args, proxy;
+
+       if ( typeof context === "string" ) {
+               tmp = fn[ context ];
+               context = fn;
+               fn = tmp;
+       }
+
+       // Quick check to determine if target is callable, in the spec
+       // this throws a TypeError, but we will just return undefined.
+       if ( !isFunction( fn ) ) {
+               return undefined;
+       }
+
+       // Simulated bind
+       args = slice.call( arguments, 2 );
+       proxy = function() {
+               return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+       };
+
+       // Set the guid of unique handler to the same of original handler, so it can be removed
+       proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+       return proxy;
+};
+
 jQuery.holdReady = function( hold ) {
        if ( hold ) {
                jQuery.readyWait++;
@@ -10193,6 +10284,26 @@ jQuery.holdReady = function( hold ) {
 jQuery.isArray = Array.isArray;
 jQuery.parseJSON = JSON.parse;
 jQuery.nodeName = nodeName;
+jQuery.isFunction = isFunction;
+jQuery.isWindow = isWindow;
+jQuery.camelCase = camelCase;
+jQuery.type = toType;
+
+jQuery.now = Date.now;
+
+jQuery.isNumeric = function( obj ) {
+
+       // As of jQuery 3.0, isNumeric is limited to
+       // strings and numbers (primitives or objects)
+       // that can be coerced to finite numbers (gh-2662)
+       var type = jQuery.type( obj );
+       return ( type === "number" || type === "string" ) &&
+
+               // parseFloat NaNs numeric-cast false positives ("")
+               // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+               // subtraction forces infinities to NaN
+               !isNaN( obj - parseFloat( obj ) );
+};
 
 
 
index 284b21a..16994f5 100644 (file)
@@ -14,7 +14,6 @@
  *   "mediawiki" module, and will remain a default/implicit dependency for all
  *   regular modules, just like jquery and wikibits already are.
  */
-/* globals mw */
 ( function () {
        'use strict';
 
index e86aff6..d1fbff0 100644 (file)
@@ -4,7 +4,7 @@
  * @class mw.errorLogger
  * @singleton
  */
-( function ( mw ) {
+( function () {
        'use strict';
 
        mw.errorLogger = {
@@ -55,4 +55,4 @@
        };
 
        mw.errorLogger.installGlobalHandler( window );
-}( mediaWiki ) );
+}() );
index e13c6fa..21b4f10 100644 (file)
@@ -7,15 +7,16 @@
         *
         * @constructor
         * @param {Object} [config] Configuration options
-        * @cfg {Object} columns Required object representing the column labels and associated
-        *  tags in the matrix.
-        * @cfg {Object} rows Required object representing the row labels and associated
-        *  tags in the matrix.
-        * @cfg {string[]} [forcedOn] An array of column-row tags to be displayed as
-        *  enabled but unavailable to change
-        * @cfg {string[]} [forcedOff] An array of column-row tags to be displayed as
-        *  disnabled but unavailable to change
-        * @cfg {Object} Object mapping row label to tooltip content
+        * @cfg {Object} columns Required object mapping column labels (as HTML) to
+        *  their tags.
+        * @cfg {Object} rows Required object mapping row labels (as HTML) to their
+        *  tags.
+        * @cfg {string[]} [forcedOn] Array of column-row tags to be displayed as
+        *  enabled but unavailable to change.
+        * @cfg {string[]} [forcedOff] Array of column-row tags to be displayed as
+        *  disabled but unavailable to change.
+        * @cfg {Object} [tooltips] Optional object mapping row labels to tooltips
+        *  (as text, will be escaped).
         */
        mw.widgets.CheckMatrixWidget = function MWWCheckMatrixWidget( config ) {
                var $headRow = $( '<tr>' ),
                this.forcedOff = config.forcedOff || [];
 
                // Build header
-               $headRow.append( $( '<td>' ).html( '&#160;' ) );
+               $headRow.append( $( '<td>' ).text( '\u00A0' ) );
 
                // Iterate over the columns object (ignore the value)
                $.each( this.columns, function ( columnLabel ) {
-                       $headRow.append( $( '<td>' ).text( columnLabel ) );
+                       $headRow.append( $( '<td>' ).html( columnLabel ) );
                } );
                $table.append( $headRow );
 
@@ -50,7 +51,7 @@
                                labelField = new OO.ui.FieldLayout(
                                        new OO.ui.Widget(), // Empty widget, since we don't have the checkboxes here
                                        {
-                                               label: rowLabel,
+                                               label: new OO.ui.HtmlSnippet( rowLabel ),
                                                help: widget.tooltips[ rowLabel ],
                                                align: 'inline'
                                        }
index e665403..ee3a656 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * Base library for MediaWiki.
  *
- * Exposed globally as `mediaWiki` with `mw` as shortcut.
+ * Exposed globally as `mw`, with `mediaWiki` as alias.
  *
  * @class mw
  * @alternateClassName mediaWiki
                log.deprecate = !Object.defineProperty ? function ( obj, key, val ) {
                        obj[ key ] = val;
                } : function ( obj, key, val, msg, logName ) {
-                       var logged = new StringSet();
-                       logName = logName || key;
-                       msg = 'Use of "' + logName + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' );
-                       function uniqueTrace() {
-                               var trace = new Error().stack;
-                               if ( logged.has( trace ) ) {
-                                       return false;
+                       var stacks;
+                       function maybeLog() {
+                               var name,
+                                       trace = new Error().stack;
+                               if ( !stacks ) {
+                                       stacks = new StringSet();
+                               }
+                               if ( !stacks.has( trace ) ) {
+                                       stacks.add( trace );
+                                       name = logName || key;
+                                       mw.track( 'mw.deprecate', name );
+                                       mw.log.warn(
+                                               'Use of "' + name + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' )
+                                       );
                                }
-                               logged.add( trace );
-                               return true;
                        }
                        // Support: Safari 5.0
                        // Throws "not supported on DOM Objects" for Node or Element objects (incl. document)
                                        configurable: true,
                                        enumerable: true,
                                        get: function () {
-                                               if ( uniqueTrace() ) {
-                                                       mw.track( 'mw.deprecate', logName );
-                                                       mw.log.warn( msg );
-                                               }
+                                               maybeLog();
                                                return val;
                                        },
                                        set: function ( newVal ) {
-                                               if ( uniqueTrace() ) {
-                                                       mw.track( 'mw.deprecate', logName );
-                                                       mw.log.warn( msg );
-                                               }
+                                               maybeLog();
                                                val = newVal;
                                        }
                                } );
                         *   - resolve: failed to sort dependencies for a module in mw.loader.load
                         *   - store-eval: could not evaluate module code cached in localStorage
                         *   - store-localstorage-init: localStorage or JSON parse error in mw.loader.store.init
-                        *   - store-localstorage-json: JSON conversion error in mw.loader.store.set
-                        *   - store-localstorage-update: localStorage or JSON conversion error in mw.loader.store.update
+                        *   - store-localstorage-json: JSON conversion error in mw.loader.store
+                        *   - store-localstorage-update: localStorage conversion error in mw.loader.store.
                         */
 
                        /**
 
                                // The current module became 'ready'.
                                if ( registry[ module ].state === 'ready' ) {
-                                       // Save it to the module store.
-                                       mw.loader.store.set( module, registry[ module ] );
+                                       // Queue it for later syncing to the module store.
+                                       mw.loader.store.add( module );
                                        // Recursively execute all dependent modules that were already loaded
                                        // (waiting for execution) and no longer have unsatisfied dependencies.
                                        for ( m in registry ) {
                                        // Allow multiple registration
                                        if ( typeof module === 'object' ) {
                                                resolveIndexedDependencies( module );
+                                               // module is an array of arrays
                                                for ( i = 0; i < module.length; i++ ) {
                                                        // module is an array of module names
-                                                       if ( typeof module[ i ] === 'string' ) {
-                                                               mw.loader.register( module[ i ] );
-                                                       // module is an array of arrays
-                                                       } else if ( typeof module[ i ] === 'object' ) {
-                                                               mw.loader.register.apply( mw.loader, module[ i ] );
-                                                       }
+                                                       mw.loader.register.apply( mw.loader, module[ i ] );
                                                }
                                                return;
                                        }
                                        // to module implementations.
                                        items: {},
 
+                                       // Names of modules to be stored during the next update.
+                                       // See add() and update().
+                                       queue: [],
+
                                        // Cache hit stats
                                        stats: { hits: 0, misses: 0, expired: 0, failed: 0 },
 
                                        },
 
                                        /**
-                                        * Stringify a module and queue it for storage.
+                                        * Queue the name of a module that the next update should consider storing.
                                         *
+                                        * @since 1.32
                                         * @param {string} module Module name
-                                        * @param {Object} descriptor The module's descriptor as set in the registry
                                         */
-                                       set: function ( module, descriptor ) {
-                                               var args, key, src;
-
+                                       add: function ( module ) {
                                                if ( !this.enabled ) {
                                                        return;
                                                }
+                                               this.queue.push( module );
+                                               this.requestUpdate();
+                                       },
+
+                                       /**
+                                        * Add the contents of the named module to the in-memory store.
+                                        *
+                                        * This method does not guarantee that the module will be stored.
+                                        * Inspection of the module's meta data and size will ultimately decide that.
+                                        *
+                                        * This method is considered internal to mw.loader.store and must only
+                                        * be called if the store is enabled.
+                                        *
+                                        * @private
+                                        * @param {string} module Module name
+                                        */
+                                       set: function ( module ) {
+                                               var key, args, src,
+                                                       descriptor = mw.loader.moduleRegistry[ module ];
 
                                                key = getModuleKey( module );
 
                                                        // Already stored a copy of this exact version
                                                        key in this.items ||
                                                        // Module failed to load
+                                                       !descriptor ||
                                                        descriptor.state !== 'ready' ||
                                                        // Unversioned, private, or site-/user-specific
                                                        !descriptor.version ||
                                                        return;
                                                }
                                                this.items[ key ] = src;
-                                               this.update();
                                        },
 
                                        /**
                                        },
 
                                        /**
-                                        * Sync in-memory store back to localStorage.
+                                        * Request a sync of the in-memory store back to persisted localStorage.
+                                        *
+                                        * This function debounces updates. The debouncing logic should account
+                                        * for the following factors:
+                                        *
+                                        * - Writing to localStorage is an expensive operation that must not happen
+                                        *   during the critical path of initialising and executing module code.
+                                        *   Instead, it should happen at a later time after modules have been given
+                                        *   time and priority to do their thing first.
+                                        *
+                                        * - This method is called from mw.loader.store.add(), which will be called
+                                        *   hundreds of times on a typical page, including within the same call-stack
+                                        *   and eventloop-tick. This is because responses from load.php happen in
+                                        *   batches. As such, we want to allow all modules from the same load.php
+                                        *   response to be written to disk with a single flush, not many.
                                         *
-                                        * This function debounces updates. When called with a flush already pending, the
-                                        * scheduled flush is postponed. The call to localStorage.setItem will be keep
-                                        * being deferred until the page is quiescent for 2 seconds.
+                                        * - Repeatedly deleting and creating timers is non-trivial.
                                         *
-                                        * Because localStorage is shared by all pages from the same origin, if multiple
-                                        * pages are loaded with different module sets, the possibility exists that
-                                        * modules saved by one page will be clobbered by another. The only impact of this
-                                        * is minor (merely a less efficient cache use) and the problem would be corrected
-                                        * by subsequent page views.
+                                        * - localStorage is shared by all pages from the same origin, if multiple
+                                        *   pages are loaded with different module sets, the possibility exists that
+                                        *   modules saved by one page will be clobbered by another. The impact of
+                                        *   this is minor, it merely causes a less efficient cache use, and the
+                                        *   problem would be corrected by subsequent page views.
                                         *
+                                        * This method is considered internal to mw.loader.store and must only
+                                        * be called if the store is enabled.
+                                        *
+                                        * @private
                                         * @method
                                         */
-                                       update: ( function () {
-                                               var timer, hasPendingWrites = false;
+                                       requestUpdate: ( function () {
+                                               var hasPendingWrites = false;
 
                                                function flushWrites() {
                                                        var data, key;
-                                                       if ( !mw.loader.store.enabled ) {
-                                                               return;
-                                                       }
 
+                                                       // Remove anything from the in-memory store that came from previous page
+                                                       // loads that no longer corresponds with current module names and versions.
                                                        mw.loader.store.prune();
+                                                       // Process queued module names, serialise their contents to the in-memory store.
+                                                       while ( mw.loader.store.queue.length ) {
+                                                               mw.loader.store.set( mw.loader.store.queue.shift() );
+                                                       }
+
                                                        key = mw.loader.store.getStoreKey();
                                                        try {
                                                                // Replacing the content of the module store might fail if the new
                                                                } );
                                                        }
 
+                                                       // Let the next call to requestUpdate() create a new timer.
                                                        hasPendingWrites = false;
                                                }
 
-                                               function request() {
-                                                       // If another mw.loader.store.set()/update() call happens in the narrow
-                                                       // time window between requestIdleCallback() and flushWrites firing, ignore it.
-                                                       // It'll be saved by the already-scheduled flush.
-                                                       if ( !hasPendingWrites ) {
-                                                               hasPendingWrites = true;
-                                                               mw.requestIdleCallback( flushWrites );
-                                                       }
+                                               function onTimeout() {
+                                                       // Defer the actual write via requestIdleCallback
+                                                       mw.requestIdleCallback( flushWrites );
                                                }
 
                                                return function () {
-                                                       // Cancel the previous timer (if it hasn't fired yet)
-                                                       clearTimeout( timer );
-                                                       timer = setTimeout( request, 2000 );
+                                                       // On the first call to requestUpdate(), create a timer that
+                                                       // waits at least two seconds, then calls onTimeout.
+                                                       // The main purpose is to allow the current batch of load.php
+                                                       // responses to complete before we do anything. This batch can
+                                                       // trigger many hundreds of calls to requestUpdate().
+                                                       if ( !hasPendingWrites ) {
+                                                               hasPendingWrites = true;
+                                                               setTimeout( onTimeout, 2000 );
+                                                       }
                                                };
                                        }() )
                                }
index a3f3c68..af59c7f 100644 (file)
@@ -6,7 +6,7 @@
  * @author Michael Dale <mdale@wikimedia.org>
  * @author Trevor Parscal <tparscal@wikimedia.org>
  */
-( function ( mw ) {
+( function () {
        /* global console */
        /* eslint-disable no-console */
        var original = mw.log;
@@ -22,4 +22,4 @@
                mw.log.error = original.error;
                mw.log.deprecate = original.deprecate;
        }
-}( mediaWiki ) );
+}() );
index 650092b..9f8c598 100644 (file)
@@ -1,4 +1,4 @@
-( function ( mw ) {
+( function () {
        var maxBusy = 50;
 
        mw.requestIdleCallbackInternal = function ( callback ) {
@@ -49,4 +49,4 @@
        // Note: Polyfill was previously disabled due to
        // https://bugs.chromium.org/p/chromium/issues/detail?id=647870
        // See also <http://codepen.io/Krinkle/full/XNGEvv>
-}( mediaWiki ) );
+}() );
index 5e9b6ab..588750c 100644 (file)
@@ -4,7 +4,6 @@
  * @author Timo Tijhof
  * @since 1.32
  */
-/* global mw */
 ( function () {
        'use strict';
 
index 03141b9..8c79c94 100644 (file)
@@ -4,7 +4,7 @@
  * - Beware: This file MUST parse without errors on even the most ancient of browsers!
  */
 /* eslint-disable vars-on-top, no-unmodified-loop-condition */
-/* global mw, isCompatible, $VARS, $CODE */
+/* global isCompatible, $VARS, $CODE */
 
 /**
  * See <https://www.mediawiki.org/wiki/Compatibility#Browsers>
index 383d430..51a3f07 100644 (file)
@@ -1670,14 +1670,10 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
         */
        private function resetDB( $db, $tablesUsed ) {
                if ( $db ) {
-                       // NOTE: Do not reset the slot_roles and content_models tables, but let them
-                       // leak across tests. Resetting them would require to reset all NamedTableStore
-                       // instances for these tables, of which there may be several beyond the ones
-                       // known to MediaWikiServices. See T202641.
                        $userTables = [ 'user', 'user_groups', 'user_properties', 'actor' ];
                        $pageTables = [
                                'page', 'revision', 'ip_changes', 'revision_comment_temp', 'comment', 'archive',
-                               'revision_actor_temp', 'slots', 'content',
+                               'revision_actor_temp', 'slots', 'content', 'content_models', 'slot_roles',
                        ];
                        $coreDBDataTables = array_merge( $userTables, $pageTables );
 
@@ -1708,6 +1704,8 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
                        }
 
                        if ( array_intersect( $tablesUsed, $coreDBDataTables ) ) {
+                               // Reset services that may contain information relating to the truncated tables
+                               $this->overrideMwServices();
                                // Re-add core DB data that was deleted
                                $this->addCoreDBData();
                        }
@@ -1739,10 +1737,15 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
                        $db->delete( $tableName, '*', __METHOD__ );
                }
 
-               if ( in_array( $db->getType(), [ 'postgres', 'sqlite' ], true ) ) {
+               if ( $db instanceof DatabasePostgres || $db instanceof DatabaseSqlite ) {
                        // Reset the table's sequence too.
                        $db->resetSequenceForTable( $tableName, __METHOD__ );
                }
+
+               // re-initialize site_stats table
+               if ( $tableName === 'site_stats' ) {
+                       SiteStatsInit::doPlaceholderInit();
+               }
        }
 
        private static function unprefixTable( &$tableName, $ind, $prefix ) {
diff --git a/tests/phpunit/data/templates/parentvars.mustache b/tests/phpunit/data/templates/parentvars.mustache
new file mode 100644 (file)
index 0000000..aa732c0
--- /dev/null
@@ -0,0 +1,4 @@
+{{foo}}
+{{#bar}}
+       {{foo}} {{baz}}
+{{/bar}}
diff --git a/tests/phpunit/includes/Storage/NameTableStoreFactoryTest.php b/tests/phpunit/includes/Storage/NameTableStoreFactoryTest.php
new file mode 100644 (file)
index 0000000..f377993
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+namespace MediaWiki\Tests\Storage;
+
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\NameTableStore;
+use MediaWiki\Storage\NameTableStoreFactory;
+use MediaWikiTestCase;
+use Wikimedia\Rdbms\ILBFactory;
+use Wikimedia\Rdbms\ILoadBalancer;
+
+/**
+ * @covers MediaWiki\Storage\NameTableStoreFactory
+ * @group Database
+ */
+class NameTableStoreFactoryTest extends MediaWikiTestCase {
+       /**
+        * @return \PHPUnit_Framework_MockObject_MockObject|ILoadBalancer
+        */
+       private function getMockLoadBalancer() {
+               return $this->getMockBuilder( ILoadBalancer::class )
+                       ->disableOriginalConstructor()->getMock();
+       }
+
+       /**
+        * @return \PHPUnit_Framework_MockObject_MockObject|ILBFactory
+        */
+       private function getMockLoadBalancerFactory( $expectedWiki ) {
+               $mock = $this->getMockBuilder( ILBFactory::class )
+                       ->disableOriginalConstructor()->getMock();
+
+               $mock->expects( $this->once() )
+                       ->method( 'getMainLB' )
+                       ->with( $this->equalTo( $expectedWiki ) )
+                       ->willReturnCallback( function ( $domain ) use ( $expectedWiki ) {
+                               return $this->getMockLoadBalancer();
+                       } );
+
+               return $mock;
+       }
+
+       public static function provideTestGet() {
+               return [
+                       [
+                               'change_tag_def',
+                               false,
+                               false,
+                       ],
+                       [
+                               'content_models',
+                               false,
+                               false,
+                       ],
+                       [
+                               'slot_roles',
+                               false,
+                               false,
+                       ],
+                       [
+                               'change_tag_def',
+                               'test7245',
+                               'test7245',
+                       ],
+               ];
+       }
+
+       /** @dataProvider provideTestGet */
+       public function testGet( $tableName, $wiki, $expectedWiki ) {
+               $services = MediaWikiServices::getInstance();
+               $db = wfGetDB( DB_MASTER );
+               if ( $wiki === false ) {
+                       $wiki2 = $db->getWikiID();
+               } else {
+                       $wiki2 = $wiki;
+               }
+               $names = new NameTableStoreFactory(
+                       $this->getMockLoadBalancerFactory( $expectedWiki ),
+                       $services->getMainWANObjectCache(),
+                       LoggerFactory::getInstance( 'NameTableStoreFactory' )
+               );
+
+               $table = $names->get( $tableName, $wiki );
+               $table2 = $names->get( $tableName, $wiki2 );
+               $this->assertSame( $table, $table2 );
+               $this->assertInstanceOf( NameTableStore::class, $table );
+       }
+
+       /*
+        * The next three integration tests verify that the schema information is correct by loading
+        * the relevant information from the database.
+        */
+
+       public function testIntegratedGetChangeTagDef() {
+               $services = MediaWikiServices::getInstance();
+               $factory = $services->getNameTableStoreFactory();
+               $store = $factory->getChangeTagDef();
+               $this->assertType( 'array', $store->getMap() );
+       }
+
+       public function testIntegratedGetContentModels() {
+               $services = MediaWikiServices::getInstance();
+               $factory = $services->getNameTableStoreFactory();
+               $store = $factory->getContentModels();
+               $this->assertType( 'array', $store->getMap() );
+       }
+
+       public function testIntegratedGetSlotRoles() {
+               $services = MediaWikiServices::getInstance();
+               $factory = $services->getNameTableStoreFactory();
+               $store = $factory->getSlotRoles();
+               $this->assertType( 'array', $store->getMap() );
+       }
+}
index 3f8bd4b..1d8771b 100644 (file)
@@ -8,6 +8,7 @@ use MediaWiki\Logger\Spi as LoggerSpi;
 use MediaWiki\Storage\BlobStore;
 use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\NameTableStore;
+use MediaWiki\Storage\NameTableStoreFactory;
 use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\RevisionStoreFactory;
 use MediaWiki\Storage\SqlBlobStore;
@@ -25,6 +26,7 @@ class RevisionStoreFactoryTest extends MediaWikiTestCase {
                new RevisionStoreFactory(
                        $this->getMockLoadBalancerFactory(),
                        $this->getMockBlobStoreFactory(),
+                       $this->getNameTableStoreFactory(),
                        $this->getHashWANObjectCache(),
                        $this->getMockCommentStore(),
                        ActorMigration::newMigration(),
@@ -53,6 +55,7 @@ class RevisionStoreFactoryTest extends MediaWikiTestCase {
        ) {
                $lbFactory = $this->getMockLoadBalancerFactory();
                $blobStoreFactory = $this->getMockBlobStoreFactory();
+               $nameTableStoreFactory = $this->getNameTableStoreFactory();
                $cache = $this->getHashWANObjectCache();
                $commentStore = $this->getMockCommentStore();
                $actorMigration = ActorMigration::newMigration();
@@ -61,6 +64,7 @@ class RevisionStoreFactoryTest extends MediaWikiTestCase {
                $factory = new RevisionStoreFactory(
                        $lbFactory,
                        $blobStoreFactory,
+                       $nameTableStoreFactory,
                        $cache,
                        $commentStore,
                        $actorMigration,
@@ -138,6 +142,16 @@ class RevisionStoreFactoryTest extends MediaWikiTestCase {
                return $mock;
        }
 
+       /**
+        * @return NameTableStoreFactory
+        */
+       private function getNameTableStoreFactory() {
+               return new NameTableStoreFactory(
+                       $this->getMockLoadBalancerFactory(),
+                       $this->getHashWANObjectCache(),
+                       new NullLogger() );
+       }
+
        /**
         * @return \PHPUnit_Framework_MockObject_MockObject|CommentStore
         */
index 90bd57a..5307ca9 100644 (file)
@@ -110,13 +110,15 @@ class RevisionStoreTest extends MediaWikiTestCase {
                        $this->setExpectedException( MWException::class );
                }
 
+               $nameTables = MediaWikiServices::getInstance()->getNameTableStoreFactory();
+
                $store = new RevisionStore(
                        $this->getMockLoadBalancer(),
                        $this->getMockSqlBlobStore(),
                        $this->getHashWANObjectCache(),
                        $this->getMockCommentStore(),
-                       MediaWikiServices::getInstance()->getContentModelStore(),
-                       MediaWikiServices::getInstance()->getSlotRoleStore(),
+                       $nameTables->getContentModels(),
+                       $nameTables->getSlotRoles(),
                        $migrationMode,
                        MediaWikiServices::getInstance()->getActorMigration()
                );
@@ -508,17 +510,19 @@ class RevisionStoreTest extends MediaWikiTestCase {
                $blobStore = $this->getMockSqlBlobStore();
                $cache = $this->getHashWANObjectCache();
                $commentStore = $this->getMockCommentStore();
-               $contentModelStore = MediaWikiServices::getInstance()->getContentModelStore();
-               $slotRoleStore = MediaWikiServices::getInstance()->getSlotRoleStore();
+               $services = MediaWikiServices::getInstance();
+               $nameTables = $services->getNameTableStoreFactory();
+               $contentModelStore = $nameTables->getContentModels();
+               $slotRoleStore = $nameTables->getSlotRoles();
                $store = new RevisionStore(
                        $loadBalancer,
                        $blobStore,
                        $cache,
                        $commentStore,
-                       MediaWikiServices::getInstance()->getContentModelStore(),
-                       MediaWikiServices::getInstance()->getSlotRoleStore(),
+                       $nameTables->getContentModels(),
+                       $nameTables->getSlotRoles(),
                        $migration,
-                       MediaWikiServices::getInstance()->getActorMigration()
+                       $services->getActorMigration()
                );
                if ( !$expectException ) {
                        $store = TestingAccessWrapper::newFromObject( $store );
index ccccf0f..cb321bb 100644 (file)
@@ -105,6 +105,17 @@ class TemplateParserTest extends MediaWikiTestCase {
                                false,
                                'Exception',
                        ],
+                       [
+                               'parentvars',
+                               [
+                                       'foo' => 'f',
+                                       'bar' => [
+                                               [ 'baz' => 'x' ],
+                                               [ 'baz' => 'y' ]
+                                       ]
+                               ],
+                               "f\n\n\tf x\n\n\tf y\n\n"
+                       ]
                ];
        }
 
index 3ef4f05..c770029 100644 (file)
@@ -510,7 +510,7 @@ class ChangeTagsTest extends MediaWikiTestCase {
                $dbw = wfGetDB( DB_MASTER );
                $dbw->delete( 'change_tag', '*' );
                $dbw->delete( 'change_tag_def', '*' );
-               MediaWikiServices::getInstance()->resetServiceForTesting( 'ChangeTagDefStore' );
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'NameTableStoreFactory' );
 
                $rcId = 123;
                ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
@@ -564,7 +564,7 @@ class ChangeTagsTest extends MediaWikiTestCase {
                $dbw = wfGetDB( DB_MASTER );
                $dbw->delete( 'change_tag', '*' );
                $dbw->delete( 'change_tag_def', '*' );
-               MediaWikiServices::getInstance()->resetServiceForTesting( 'ChangeTagDefStore' );
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'NameTableStoreFactory' );
 
                $rcId = 123;
                ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
@@ -582,7 +582,7 @@ class ChangeTagsTest extends MediaWikiTestCase {
                $dbw = wfGetDB( DB_MASTER );
                $dbw->delete( 'change_tag', '*' );
                $dbw->delete( 'change_tag_def', '*' );
-               MediaWikiServices::getInstance()->resetServiceForTesting( 'ChangeTagDefStore' );
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'NameTableStoreFactory' );
 
                $rcId = 123;
                ChangeTags::updateTags( [ 'tag1', 'tag2' ], [], $rcId );
index 86956f2..aa3f820 100644 (file)
@@ -572,6 +572,7 @@ mw.loader.register( [
                $version1 = $module->getVersionHash( $context );
                $module = new ResourceLoaderStartupModule();
                $version2 = $module->getVersionHash( $context );
+
                $this->setMwGlobals( 'wgArticlePath', '/w3' );
                $module = new ResourceLoaderStartupModule();
                $version3 = $module->getVersionHash( $context );
@@ -590,8 +591,7 @@ mw.loader.register( [
        }
 
        /**
-        * @covers ResourceLoaderStartupModule::getAllModuleHashes
-        * @covers ResourceLoaderStartupModule::getDefinitionSummary
+        * @covers ResourceLoaderStartupModule
         */
        public function testGetVersionHash_varyModule() {
                $context1 = $this->getResourceLoaderContext();
@@ -621,10 +621,11 @@ mw.loader.register( [
                $module = new ResourceLoaderStartupModule();
                $version3 = $module->getVersionHash( $context3 );
 
-               $this->assertEquals(
+               // Module name *is* significant (T201686)
+               $this->assertNotEquals(
                        $version1,
                        $version2,
-                       'Module name is insignificant'
+                       'Module name is significant'
                );
 
                $this->assertNotEquals(
@@ -634,4 +635,32 @@ mw.loader.register( [
                );
        }
 
+       /**
+        * @covers ResourceLoaderStartupModule
+        */
+       public function testGetVersionHash_varyDeps() {
+               $context = $this->getResourceLoaderContext();
+               $rl = $context->getResourceLoader();
+               $rl->register( [
+                       'test.a' => new ResourceLoaderTestModule( [ 'dependencies' => [ 'x', 'y' ] ] ),
+               ] );
+               $module = new ResourceLoaderStartupModule();
+               $version1 = $module->getVersionHash( $context );
+
+               $context = $this->getResourceLoaderContext();
+               $rl = $context->getResourceLoader();
+               $rl->register( [
+                       'test.a' => new ResourceLoaderTestModule( [ 'dependencies' => [ 'x', 'z' ] ] ),
+               ] );
+               $module = new ResourceLoaderStartupModule();
+               $version2 = $module->getVersionHash( $context );
+
+               // Dependencies *are* significant (T201686)
+               $this->assertNotEquals(
+                       $version1,
+                       $version2,
+                       'Dependencies are significant'
+               );
+       }
+
 }
index 1a1affa..ae5ddb5 100644 (file)
@@ -1,15 +1,12 @@
 ( function ( mw, $ ) {
        QUnit.module( 'mediawiki.loader', QUnit.newMwEnvironment( {
                setup: function ( assert ) {
-                       mw.loader.store.enabled = false;
-
                        // Expose for load.mock.php
                        mw.loader.testFail = function ( reason ) {
                                assert.ok( false, reason );
                        };
                },
                teardown: function () {
-                       mw.loader.store.enabled = false;
                        // Teardown for StringSet shim test
                        if ( this.nativeSet ) {
                                window.Set = this.nativeSet;
 
        QUnit.test( 'Stale response caching - T117587', function ( assert ) {
                var count = 0;
-               mw.loader.store.enabled = true;
+               // Enable store and stub timeout/idle scheduling
+               this.sandbox.stub( mw.loader.store, 'enabled', true );
+               this.sandbox.stub( window, 'setTimeout', function ( fn ) {
+                       fn();
+               } );
+               this.sandbox.stub( mw, 'requestIdleCallback', function ( fn ) {
+                       fn();
+               } );
+
                mw.loader.register( 'test.stale', 'v2' );
                assert.strictEqual( mw.loader.store.get( 'test.stale' ), false, 'Not in store' );
 
                                // After implementing, registry contains version as implemented by the response.
                                assert.strictEqual( mw.loader.getVersion( 'test.stale' ), 'v1', 'Override version' );
                                assert.strictEqual( mw.loader.getState( 'test.stale' ), 'ready' );
-                               assert.ok( mw.loader.store.get( 'test.stale' ), 'In store' );
+                               assert.strictEqual( typeof mw.loader.store.get( 'test.stale' ), 'string', 'In store' );
                        } )
                        .then( function () {
                                // Reset run time, but keep mw.loader.store
 
        QUnit.test( 'Stale response caching - backcompat', function ( assert ) {
                var script = 0;
-               mw.loader.store.enabled = true;
+               // Enable store and stub timeout/idle scheduling
+               this.sandbox.stub( mw.loader.store, 'enabled', true );
+               this.sandbox.stub( window, 'setTimeout', function ( fn ) {
+                       fn();
+               } );
+               this.sandbox.stub( mw, 'requestIdleCallback', function ( fn ) {
+                       fn();
+               } );
+
                mw.loader.register( 'test.stalebc', 'v2' );
                assert.strictEqual( mw.loader.store.get( 'test.stalebc' ), false, 'Not in store' );
 
                        .then( function () {
                                assert.strictEqual( script, 1, 'module script ran' );
                                assert.strictEqual( mw.loader.getState( 'test.stalebc' ), 'ready' );
-                               assert.ok( mw.loader.store.get( 'test.stalebc' ), 'In store' );
+                               assert.strictEqual( typeof mw.loader.store.get( 'test.stalebc' ), 'string', 'In store' );
                        } )
                        .then( function () {
                                // Reset run time, but keep mw.loader.store