Merge "Add urlencode for URL encoding of lang values"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 26 Jul 2017 14:23:16 +0000 (14:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 26 Jul 2017 14:23:17 +0000 (14:23 +0000)
357 files changed:
INSTALL
RELEASE-NOTES-1.30
autoload.php
composer.json
docs/extension.schema.v1.json
docs/extension.schema.v2.json
includes/Block.php
includes/DefaultSettings.php
includes/EditPage.php
includes/Feed.php
includes/FileDeleteForm.php
includes/HistoryBlob.php
includes/LinkFilter.php
includes/Linker.php
includes/MWNamespace.php
includes/MagicWord.php
includes/MediaWiki.php
includes/MimeMagic.php
includes/Preferences.php
includes/Revision.php
includes/Sanitizer.php
includes/ServiceWiring.php
includes/Setup.php
includes/SiteStats.php
includes/Title.php
includes/WikiMap.php
includes/Xml.php
includes/XmlJsCode.php
includes/actions/Action.php
includes/api/ApiBase.php
includes/api/ApiEmailUser.php
includes/api/ApiMain.php
includes/api/ApiPageSet.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryInfo.php
includes/api/ApiResult.php
includes/api/ApiUsageException.php
includes/api/i18n/fr.json
includes/api/i18n/he.json
includes/api/i18n/ko.json
includes/api/i18n/pl.json
includes/api/i18n/zh-hant.json
includes/auth/AuthenticationResponse.php
includes/cache/MessageCache.php
includes/changes/ChangesFeed.php
includes/changes/ChangesListStringOptionsFilter.php
includes/changes/EnhancedChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/collation/IcuCollation.php
includes/collation/NumericUppercaseCollation.php
includes/config/EtcdConfig.php
includes/content/ContentHandler.php
includes/context/DerivativeContext.php
includes/db/CloneDatabase.php
includes/debug/MWDebug.php
includes/deferred/LinksUpdate.php
includes/deferred/SearchUpdate.php
includes/diff/DifferenceEngine.php
includes/edit/PreparedEdit.php [new file with mode: 0644]
includes/exception/HttpError.php
includes/exception/MWExceptionRenderer.php
includes/export/WikiExporter.php
includes/filerepo/ForeignAPIRepo.php
includes/htmlform/HTMLForm.php
includes/htmlform/fields/HTMLSelectAndOtherField.php
includes/htmlform/fields/HTMLSelectOrOtherField.php
includes/http/Http.php
includes/import/ImportStreamSource.php
includes/installer/DatabaseUpdater.php
includes/installer/MysqlInstaller.php
includes/installer/SqliteInstaller.php
includes/installer/WebInstaller.php
includes/installer/i18n/csb.json
includes/installer/i18n/fr.json
includes/installer/i18n/pt.json
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueFederated.php
includes/jobqueue/JobQueueSecondTestQueue.php [new file with mode: 0644]
includes/libs/CSSMin.php
includes/libs/IP.php
includes/libs/MemoizedCallable.php
includes/libs/StringUtils.php
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/APCUBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/rdbms/loadbalancer/ILoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/logging/LogPage.php
includes/logging/LogPager.php
includes/mail/UserMailer.php
includes/media/DjVuImage.php
includes/media/Exif.php
includes/media/SVGMetadataExtractor.php
includes/page/Article.php
includes/page/WikiPage.php
includes/pager/IndexPager.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/profiler/output/ProfilerOutput.php
includes/rcfeed/RedisPubSubFeedEngine.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderModule.php
includes/search/SearchDatabase.php
includes/search/SearchResultSet.php
includes/skins/Skin.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/SpecialPage.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialImport.php
includes/specials/SpecialListusers.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialRedirect.php
includes/specials/SpecialSearch.php
includes/specials/SpecialSpecialpages.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/pagers/UsersPager.php
includes/tidy/Balancer.php
includes/tidy/RemexCompatMunger.php
includes/user/BotPassword.php
includes/user/User.php
includes/widget/SearchInputWidget.php
languages/ConverterRule.php
languages/Language.php
languages/data/Names.php
languages/i18n/ar.json
languages/i18n/ast.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/cdo.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/csb.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/din.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fa.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gl.json
languages/i18n/gor.json
languages/i18n/gsw.json
languages/i18n/ha.json
languages/i18n/hak.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/inh.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/li.json
languages/i18n/lv.json
languages/i18n/mg.json
languages/i18n/mk.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/pnb.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/rif.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/sd.json
languages/i18n/shi.json
languages/i18n/skr-arab.json [new file with mode: 0644]
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/su.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/tet.json
languages/i18n/tg-cyrl.json
languages/i18n/tt-cyrl.json
languages/i18n/uk.json
languages/i18n/vi.json
languages/i18n/yi.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesSkr.php [new file with mode: 0644]
languages/messages/MessagesSkr_arab.php [new file with mode: 0644]
maintenance/Maintenance.php
maintenance/addRFCandPMIDInterwiki.php
maintenance/deleteDefaultMessages.php
maintenance/fixDoubleRedirects.php
maintenance/generateSitemap.php
maintenance/language/checkLanguage.inc
maintenance/moveBatch.php
maintenance/nukeNS.php
maintenance/nukePage.php
maintenance/populateCategory.php
maintenance/populateInterwiki.php
maintenance/populatePPSortKey.php [new file with mode: 0644]
maintenance/rebuildFileCache.php
maintenance/recountCategories.php [new file with mode: 0644]
maintenance/refreshLinks.php
maintenance/sqlite.inc
maintenance/updateRestrictions.php
maintenance/userOptions.inc
mw-config/config.js
package.json
phpcs.xml
resources/Resources.php
resources/lib/qunitjs/qunit.css
resources/lib/qunitjs/qunit.js
resources/src/jquery/jquery.badge.js
resources/src/jquery/jquery.colorUtil.js
resources/src/jquery/jquery.makeCollapsible.js
resources/src/jquery/jquery.mwExtension.js
resources/src/jquery/jquery.suggestions.js
resources/src/jquery/jquery.tablesorter.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.action/mediawiki.action.history.js
resources/src/mediawiki.action/mediawiki.action.view.metadata.js
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.DatePopupWidget.less [new file with mode: 0644]
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterTagMultiselectWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.LiveUpdateButtonWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.SavedLinksListItemWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ValuePickerWidget.less [new file with mode: 0644]
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js [new file with mode: 0644]
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitPopupWidget.js [new file with mode: 0644]
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js [new file with mode: 0644]
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DatePopupWidget.js [new file with mode: 0644]
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SavedLinksListWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.js [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.contributions.js
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js
resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
resources/src/mediawiki.widgets.visibleByteLimit/mediawiki.widgets.visibleByteLimit.js
resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchWidget.js
resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
resources/src/mediawiki/api/rollback.js
resources/src/mediawiki/api/upload.js
resources/src/mediawiki/htmlform/hide-if.js
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.RegExp.js
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.debug.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.hlist-allskins.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.hlist.css
resources/src/mediawiki/mediawiki.jqueryMsg.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.notification.js
resources/src/mediawiki/mediawiki.requestIdleCallback.js
resources/src/mediawiki/mediawiki.searchSuggest.js
resources/src/mediawiki/mediawiki.toc.js
resources/src/mediawiki/page/patrol.ajax.js
resources/src/mediawiki/page/rollback.js
resources/src/oojs-ui-local.js
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
tests/phan/stubs/wikidiff.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/ResourceLoaderTestCase.php
tests/phpunit/includes/DeprecatedGlobalTest.php
tests/phpunit/includes/GitInfoTest.php
tests/phpunit/includes/PreferencesTest.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/SiteStatsTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiErrorFormatterTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/changetags/ChangeTagsTest.php [new file with mode: 0644]
tests/phpunit/includes/config/ConfigFactoryTest.php
tests/phpunit/includes/config/EtcdConfigTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php [deleted file]
tests/phpunit/includes/db/DatabaseSQLTest.php [deleted file]
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/db/DatabaseTest.php [deleted file]
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php [new file with mode: 0644]
tests/phpunit/includes/page/WikiPageTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/site/TestSites.php
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
tests/phpunit/includes/specials/SpecialRecentchangesTest.php
tests/phpunit/structure/DatabaseIntegrationTest.php [new file with mode: 0644]
tests/phpunit/suite.xml
tests/qunit/data/callMwLoaderTestCallback.js [deleted file]
tests/qunit/data/mwLoaderTestCallback.js [new file with mode: 0644]
tests/qunit/data/qunitOkCall.js [deleted file]
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js
tests/selenium/wdio.conf.js

diff --git a/INSTALL b/INSTALL
index 90da381..13c9c62 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -8,7 +8,7 @@ Starting with MediaWiki 1.2.0, it's possible to install and configure the wiki
 Required software:
 * Web server with PHP 5.5.9 or higher.
 * A SQL server, the following types are supported
-** MySQL 5.0.3 or higher
+** MySQL 5.5.8 or higher
 ** PostgreSQL 8.3 or higher
 ** SQLite 3.3.7 or higher
 ** Oracle 9.0.1 or higher
index 453368b..c5ab81a 100644 (file)
@@ -5,6 +5,10 @@ THIS IS NOT A RELEASE YET
 MediaWiki 1.30 is an alpha-quality branch and is not recommended for use in
 production.
 
+=== MySQL version requirement in 1.30 ===
+As of 1.30, MediaWiki now requires MySQL 5.5.8 or higher (see Compatibility
+section).
+
 === Configuration changes in 1.30 ===
 * The "C.UTF-8" locale should be used for $wgShellLocale, if available, to avoid
   unexpected behavior when code uses locale-sensitive string comparisons. For
@@ -37,15 +41,18 @@ production.
   enabled by setting $wgUsePigLatinVariant to true.
 * Added RecentChangesPurgeRows hook to allow extensions to purge data that
   depends on the recentchanges table.
+* Added JS config values wgDiffOldId/wgDiffNewId to the output of diff pages.
 
 === Languages updated in 1.30 ===
 
 * Support for kbp (Kabɩyɛ / Kabiyè) was added.
+* Support for skr (Saraiki, سرائیکی) was added.
 
 === External library changes in 1.30 ===
 
 ==== Upgraded external libraries ====
 * mediawiki/mediawiki-codesniffer updated to 0.8.1.
+* wikimedia/composer-merge-plugin updated to 1.4.1.
 
 ==== New external libraries ====
 * The class \TestingAccessWrapper has been moved to the external library
@@ -147,7 +154,7 @@ Oracle and Microsoft SQL Server.
 
 The supported versions are:
 
-* MySQL 5.0.3 or later
+* MySQL 5.5.8 or later
 * PostgreSQL 8.3 or later
 * SQLite 3.3.7 or later
 * Oracle 9.0.1 or later
index 30ec1d7..510eeee 100644 (file)
@@ -679,6 +679,7 @@ $wgAutoloadLocalClasses = [
        'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueReadOnlyError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
+       'JobQueueSecondTestQueue' => __DIR__ . '/includes/jobqueue/JobQueueSecondTestQueue.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
        'JpegHandler' => __DIR__ . '/includes/media/Jpeg.php',
@@ -875,6 +876,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Auth\\UsernameAuthenticationRequest' => __DIR__ . '/includes/auth/UsernameAuthenticationRequest.php',
        'MediaWiki\\Diff\\ComplexityException' => __DIR__ . '/includes/diff/ComplexityException.php',
        'MediaWiki\\Diff\\WordAccumulator' => __DIR__ . '/includes/diff/WordAccumulator.php',
+       'MediaWiki\\Edit\\PreparedEdit' => __DIR__ . '/includes/edit/PreparedEdit.php',
        'MediaWiki\\HeaderCallback' => __DIR__ . '/includes/HeaderCallback.php',
        'MediaWiki\\Interwiki\\ClassicInterwikiLookup' => __DIR__ . '/includes/interwiki/ClassicInterwikiLookup.php',
        'MediaWiki\\Interwiki\\InterwikiLookup' => __DIR__ . '/includes/interwiki/InterwikiLookup.php',
@@ -1117,6 +1119,7 @@ $wgAutoloadLocalClasses = [
        'PopulateInterwiki' => __DIR__ . '/maintenance/populateInterwiki.php',
        'PopulateLogSearch' => __DIR__ . '/maintenance/populateLogSearch.php',
        'PopulateLogUsertext' => __DIR__ . '/maintenance/populateLogUsertext.php',
+       'PopulatePPSortKey' => __DIR__ . '/maintenance/populatePPSortKey.php',
        'PopulateParentId' => __DIR__ . '/maintenance/populateParentId.php',
        'PopulateRecentChangesSource' => __DIR__ . '/maintenance/populateRecentChangesSource.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
@@ -1190,6 +1193,7 @@ $wgAutoloadLocalClasses = [
        'RecentChange' => __DIR__ . '/includes/changes/RecentChange.php',
        'RecentChangesUpdateJob' => __DIR__ . '/includes/jobqueue/jobs/RecentChangesUpdateJob.php',
        'RecompressTracked' => __DIR__ . '/maintenance/storage/recompressTracked.php',
+       'RecountCategories' => __DIR__ . '/maintenance/recountCategories.php',
        'RedirectSpecialArticle' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
        'RedirectSpecialPage' => __DIR__ . '/includes/specialpage/RedirectSpecialPage.php',
        'RedisBagOStuff' => __DIR__ . '/includes/libs/objectcache/RedisBagOStuff.php',
index 8262880..d04e9f5 100644 (file)
@@ -33,7 +33,7 @@
                "wikimedia/base-convert": "1.0.1",
                "wikimedia/cdb": "1.4.1",
                "wikimedia/cldr-plural-rule-parser": "1.0.0",
-               "wikimedia/composer-merge-plugin": "1.4.0",
+               "wikimedia/composer-merge-plugin": "1.4.1",
                "wikimedia/html-formatter": "1.0.1",
                "wikimedia/ip-set": "1.1.0",
                "wikimedia/php-session-serializer": "1.0.4",
@@ -52,8 +52,8 @@
                "hamcrest/hamcrest-php": "^2.0",
                "jakub-onderka/php-parallel-lint": "0.9.2",
                "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a",
-               "justinrainbow/json-schema": "~3.0",
-               "mediawiki/mediawiki-codesniffer": "0.8.1",
+               "justinrainbow/json-schema": "~5.2",
+               "mediawiki/mediawiki-codesniffer": "0.10.1",
                "monolog/monolog": "~1.22.1",
                "nikic/php-parser": "2.1.0",
                "nmred/kafka-php": "0.1.5",
index f241cf5..7cfebca 100644 (file)
@@ -39,7 +39,7 @@
                "url": {
                        "type": "string",
                        "description": "URL to the homepage for the extension.",
-                       "format": "uri"
+                       "format": "uri-reference"
                },
                "description": {
                        "type": "string",
index 518a65c..75a4f2c 100644 (file)
@@ -40,7 +40,7 @@
                "url": {
                        "type": "string",
                        "description": "URL to the homepage for the extension.",
-                       "format": "uri"
+                       "format": "uri-reference"
                },
                "description": {
                        "type": "string",
index 2c935df..2a04879 100644 (file)
@@ -485,7 +485,7 @@ class Block {
 
                # Periodic purge via commit hooks
                if ( mt_rand( 0, 9 ) == 0 ) {
-                       Block::purgeExpired();
+                       self::purgeExpired();
                }
 
                $row = $this->getDatabaseArray();
@@ -778,12 +778,12 @@ class Block {
                # It's okay to autoblock. Go ahead and insert/update the block...
 
                # Do not add a *new* block if the IP is already blocked.
-               $ipblock = Block::newFromTarget( $autoblockIP );
+               $ipblock = self::newFromTarget( $autoblockIP );
                if ( $ipblock ) {
                        # Check if the block is an autoblock and would exceed the user block
                        # if renewed. If so, do nothing, otherwise prolong the block time...
                        if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
-                               $this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
+                               $this->mExpiry > self::getAutoblockExpiry( $ipblock->mTimestamp )
                        ) {
                                # Reset block timestamp to now and its expiry to
                                # $wgAutoblockExpiry in the future
@@ -810,11 +810,11 @@ class Block {
 
                if ( $this->mExpiry == 'infinity' ) {
                        # Original block was indefinite, start an autoblock now
-                       $autoblock->mExpiry = Block::getAutoblockExpiry( $timestamp );
+                       $autoblock->mExpiry = self::getAutoblockExpiry( $timestamp );
                } else {
                        # If the user is already blocked with an expiry date, we don't
                        # want to pile on top of that.
-                       $autoblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $timestamp ) );
+                       $autoblock->mExpiry = min( $this->mExpiry, self::getAutoblockExpiry( $timestamp ) );
                }
 
                # Insert the block...
@@ -870,7 +870,7 @@ class Block {
        public function updateTimestamp() {
                if ( $this->mAuto ) {
                        $this->mTimestamp = wfTimestamp();
-                       $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
+                       $this->mExpiry = self::getAutoblockExpiry( $this->mTimestamp );
 
                        $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'ipblocks',
@@ -1111,8 +1111,8 @@ class Block {
         */
        public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
                list( $target, $type ) = self::parseTarget( $specificTarget );
-               if ( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) {
-                       return Block::newFromID( $target );
+               if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
+                       return self::newFromID( $target );
 
                } elseif ( $target === null && $vagueTarget == '' ) {
                        # We're not going to find anything useful here
@@ -1122,7 +1122,7 @@ class Block {
 
                } elseif ( in_array(
                        $type,
-                       [ Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ] )
+                       [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
                ) {
                        $block = new Block();
                        $block->fromMaster( $fromMaster );
@@ -1189,7 +1189,7 @@ class Block {
                }
                $selectFields = array_merge(
                        [ 'ipb_range_start', 'ipb_range_end' ],
-                       Block::selectFields()
+                       self::selectFields()
                );
                $rows = $db->select( 'ipblocks',
                        $selectFields,
@@ -1350,12 +1350,12 @@ class Block {
                        # off validation checking (which would exclude IP addresses)
                        return [
                                User::newFromName( IP::sanitizeIP( $target ), false ),
-                               Block::TYPE_IP
+                               self::TYPE_IP
                        ];
 
                } elseif ( IP::isValidBlock( $target ) ) {
                        # Can't create a User from an IP range
-                       return [ IP::sanitizeRange( $target ), Block::TYPE_RANGE ];
+                       return [ IP::sanitizeRange( $target ), self::TYPE_RANGE ];
                }
 
                # Consider the possibility that this is not a username at all
@@ -1370,11 +1370,11 @@ class Block {
                        # Note that since numbers are valid usernames, a $target of "12345" will be
                        # considered a User.  If you want to pass a block ID, prepend a hash "#12345",
                        # since hash characters are not valid in usernames or titles generally.
-                       return [ $userObj, Block::TYPE_USER ];
+                       return [ $userObj, self::TYPE_USER ];
 
                } elseif ( preg_match( '/^#\d+$/', $target ) ) {
                        # Autoblock reference in the form "#12345"
-                       return [ substr( $target, 1 ), Block::TYPE_AUTO ];
+                       return [ substr( $target, 1 ), self::TYPE_AUTO ];
 
                } else {
                        # WTF?
index 6ce9a66..f35715e 100644 (file)
@@ -8301,6 +8301,20 @@ $wgUpdateRowsPerQuery = 100;
  */
 $wgExternalDiffEngine = false;
 
+/**
+ * wikidiff2 supports detection of changes in moved paragraphs.
+ * This setting controls the maximum number of paragraphs to compare before it bails out.
+ * Supported values:
+ * * 0: detection of moved paragraphs is disabled
+ * * int > 0: maximum number of paragraphs to compare
+ * Note: number of paragraph comparisons is in O(n^2).
+ * This setting is only effective if the wikidiff2 PHP/HHVM module is used as diffengine.
+ * See $wgExternalDiffEngine.
+ *
+ * @since 1.30
+ */
+$wgWikiDiff2MovedParagraphDetectionCutoff = 0;
+
 /**
  * Disable redirects to special pages and interwiki redirects, which use a 302
  * and have no "redirected from" link.
index 814c248..229a36a 100644 (file)
@@ -422,8 +422,6 @@ class EditPage {
         * @param Article $article
         */
        public function __construct( Article $article ) {
-               global $wgOOUIEditPage;
-
                $this->mArticle = $article;
                $this->page = $article->getPage(); // model object
                $this->mTitle = $article->getTitle();
@@ -434,7 +432,7 @@ class EditPage {
                $handler = ContentHandler::getForModelID( $this->contentModel );
                $this->contentFormat = $handler->getDefaultFormat();
 
-               $this->oouiEnabled = $wgOOUIEditPage;
+               $this->oouiEnabled = $this->context->getConfig()->get( 'OOUIEditPage' );
        }
 
        /**
@@ -2785,7 +2783,7 @@ class EditPage {
                $wgOut->addHTML( $this->editFormTextBeforeContent );
 
                if ( !$this->isCssJsSubpage && $showToolbar && $wgUser->getOption( 'showtoolbar' ) ) {
-                       $wgOut->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
+                       $wgOut->addHTML( self::getEditToolbar( $this->mTitle ) );
                }
 
                if ( $this->blankArticle ) {
@@ -3494,6 +3492,10 @@ HTML
                }
        }
 
+       /**
+        * Inserts optional text shown below edit and upload forms. Can be used to offer special characters not present on
+        * most keyboards for copying/pasting.
+        */
        protected function showEditTools() {
                global $wgOut;
                $wgOut->addHTML( '<div class="mw-editTools">' .
index 189fd9f..f76a634 100644 (file)
@@ -54,8 +54,6 @@ class FeedItem {
        public $rssIsPermalink = false;
 
        /**
-        * Constructor
-        *
         * @param string|Title $title Item's title
         * @param string $description
         * @param string $url URL uniquely designating the item.
index e7b4a1f..8a1cd35 100644 (file)
@@ -47,8 +47,6 @@ class FileDeleteForm {
        private $oldimage = '';
 
        /**
-        * Constructor
-        *
         * @param File $file File object we're deleting
         */
        public function __construct( $file ) {
index 56cf815..51bd7a9 100644 (file)
@@ -76,9 +76,6 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob {
        public $mMaxSize = 10000000;
        public $mMaxCount = 100;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                if ( !function_exists( 'gzdeflate' ) ) {
                        throw new MWException( "Need zlib support to read or write this "
index 2f50558..790e2be 100644 (file)
@@ -50,7 +50,7 @@ class LinkFilter {
 
                $text = $content->getNativeData();
 
-               $regex = LinkFilter::makeRegex( $filterEntry );
+               $regex = self::makeRegex( $filterEntry );
                return preg_match( $regex, $text );
        }
 
index f2e4ac4..4aae3ba 100644 (file)
@@ -1328,7 +1328,7 @@ class Linker {
                Title $title, $text, $wikiId = null, $options = []
        ) {
                if ( $wikiId !== null && !$title->isExternal() ) {
-                       $link = Linker::makeExternalLink(
+                       $link = self::makeExternalLink(
                                WikiMap::getForeignURL(
                                        $wikiId,
                                        $title->getNamespace() === 0
@@ -1341,7 +1341,7 @@ class Linker {
                                /* escape = */ false // Already escaped
                        );
                } else {
-                       $link = Linker::link( $title, $text, [], [], $options );
+                       $link = self::link( $title, $text, [], [], $options );
                }
 
                return $link;
@@ -2021,7 +2021,7 @@ class Linker {
                }
 
                if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
-                       return Linker::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
+                       return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
                } else {
                        if ( $rev->getId() ) {
                                // RevDelete links using revision ID are stable across
@@ -2040,7 +2040,7 @@ class Linker {
                                        'ids' => $rev->getTimestamp()
                                ];
                        }
-                       return Linker::revDeleteLink( $query,
+                       return self::revDeleteLink( $query,
                                $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
                }
        }
index 89cb616..97dba26 100644 (file)
@@ -370,7 +370,7 @@ class MWNamespace {
         */
        public static function getSubjectNamespaces() {
                return array_filter(
-                       MWNamespace::getValidNamespaces(),
+                       self::getValidNamespaces(),
                        'MWNamespace::isSubject'
                );
        }
@@ -383,7 +383,7 @@ class MWNamespace {
         */
        public static function getTalkNamespaces() {
                return array_filter(
-                       MWNamespace::getValidNamespaces(),
+                       self::getValidNamespaces(),
                        'MWNamespace::isTalk'
                );
        }
index ee95918..1703179 100644 (file)
@@ -664,7 +664,7 @@ class MagicWord {
                $search = [];
                $replace = [];
                foreach ( $magicarr as $id => $replacement ) {
-                       $mw = MagicWord::get( $id );
+                       $mw = self::get( $id );
                        $search[] = $mw->getRegex();
                        $replace[] = $replacement;
                }
index 4df4d76..4e47184 100644 (file)
@@ -609,6 +609,7 @@ class MediaWiki {
                        $lbFactory->hasOrMadeRecentMasterChanges( INF )
                ) ? self::getUrlDomainDistance( $output->getRedirect(), $context ) : false;
 
+               $allowHeaders = !( $output->isDisabled() || headers_sent() );
                if ( $urlDomainDistance === 'local' || $urlDomainDistance === 'remote' ) {
                        // OutputPage::output() will be fast; $postCommitWork will not be useful for
                        // masking the latency of syncing DB positions accross all datacenters synchronously.
@@ -616,7 +617,7 @@ class MediaWiki {
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_ASYNC;
                        $cpPosTime = microtime( true );
                        // Client's next request should see 1+ positions with this DBMasterPos::asOf() time
-                       if ( $urlDomainDistance === 'local' ) {
+                       if ( $urlDomainDistance === 'local' && $allowHeaders ) {
                                // Client will stay on this domain, so set an unobtrusive cookie
                                $expires = time() + ChronologyProtector::POSITION_TTL;
                                $options = [ 'prefix' => '' ];
@@ -633,7 +634,7 @@ class MediaWiki {
                        // OutputPage::output() is fairly slow; run it in $postCommitWork to mask
                        // the latency of syncing DB positions accross all datacenters synchronously
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_SYNC;
-                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) ) {
+                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) && $allowHeaders ) {
                                $cpPosTime = microtime( true );
                                // Set a cookie in case the DB position store cannot sync accross datacenters.
                                // This will at least cover the common case of the user staying on the domain.
index 8670729..a2a44bb 100644 (file)
@@ -35,7 +35,7 @@ class MimeMagic extends MimeAnalyzer {
                $instance = MediaWikiServices::getInstance()->getMimeAnalyzer();
                Assert::postcondition(
                        $instance instanceof MimeMagic,
-                       __METHOD__ . ' should return an instance of ' . MimeMagic::class
+                       __METHOD__ . ' should return an instance of ' . self::class
                );
                return $instance;
        }
index 008963b..7efbef1 100644 (file)
@@ -1316,7 +1316,7 @@ class Preferences {
                $formClass = 'PreferencesForm',
                array $remove = []
        ) {
-               $formDescriptor = Preferences::getPreferences( $user, $context );
+               $formDescriptor = self::getPreferences( $user, $context );
                if ( count( $remove ) ) {
                        $removeKeys = array_flip( $remove );
                        $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
index c3782ba..537b7c1 100644 (file)
@@ -559,8 +559,6 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Constructor
-        *
         * @param object|array $row Either a database row or an array
         * @throws MWException
         * @access private
@@ -1004,7 +1002,7 @@ class Revision implements IDBAccessObject {
 
                return RecentChange::newFromConds(
                        [
-                               'rc_user_text' => $this->getUserText( Revision::RAW ),
+                               'rc_user_text' => $this->getUserText( self::RAW ),
                                'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
                                'rc_this_oldid' => $this->getId()
                        ],
@@ -1468,7 +1466,7 @@ class Revision implements IDBAccessObject {
                                ? $this->getPreviousRevisionId( $dbw )
                                : $this->mParentId,
                        'rev_sha1'       => $this->mSha1 === null
-                               ? Revision::base36Sha1( $this->mText )
+                               ? self::base36Sha1( $this->mText )
                                : $this->mSha1,
                ];
 
@@ -1557,7 +1555,7 @@ class Revision implements IDBAccessObject {
                        }
                }
 
-               $content = $this->getContent( Revision::RAW );
+               $content = $this->getContent( self::RAW );
                $prefixedDBkey = $title->getPrefixedDBkey();
                $revId = $this->mId;
 
index b08bc69..2def06a 100644 (file)
@@ -465,7 +465,7 @@ class Sanitizer {
                extract( self::getRecognizedTagData( $extratags, $removetags ) );
 
                # Remove HTML comments
-               $text = Sanitizer::removeHTMLcomments( $text );
+               $text = self::removeHTMLcomments( $text );
                $bits = explode( '<', $text );
                $text = str_replace( '>', '&gt;', array_shift( $bits ) );
                if ( !MWTidy::isEnabled() ) {
@@ -583,12 +583,12 @@ class Sanitizer {
                                                        call_user_func_array( $processCallback, [ &$params, $args ] );
                                                }
 
-                                               if ( !Sanitizer::validateTag( $params, $t ) ) {
+                                               if ( !self::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
                                                # Strip non-approved attributes from the tag
-                                               $newparams = Sanitizer::fixTagAttributes( $params, $t );
+                                               $newparams = self::fixTagAttributes( $params, $t );
                                        }
                                        if ( !$badtag ) {
                                                $rest = str_replace( '>', '&gt;', $rest );
@@ -629,11 +629,11 @@ class Sanitizer {
                                                                call_user_func_array( $warnCallback, [ 'deprecated-self-close-category' ] );
                                                        }
                                                }
-                                               if ( !Sanitizer::validateTag( $params, $t ) ) {
+                                               if ( !self::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
-                                               $newparams = Sanitizer::fixTagAttributes( $params, $t );
+                                               $newparams = self::fixTagAttributes( $params, $t );
                                                if ( !$badtag ) {
                                                        if ( $brace === '/>' && !isset( $htmlsingleonly[$t] ) ) {
                                                                # Interpret self-closing tags as empty tags even when
@@ -710,7 +710,7 @@ class Sanitizer {
         * @return bool
         */
        static function validateTag( $params, $element ) {
-               $params = Sanitizer::decodeTagAttributes( $params );
+               $params = self::decodeTagAttributes( $params );
 
                if ( $element == 'meta' || $element == 'link' ) {
                        if ( !isset( $params['itemprop'] ) ) {
@@ -746,8 +746,8 @@ class Sanitizer {
         * @todo Check for unique id attribute :P
         */
        static function validateTagAttributes( $attribs, $element ) {
-               return Sanitizer::validateAttributes( $attribs,
-                       Sanitizer::attributeWhitelist( $element ) );
+               return self::validateAttributes( $attribs,
+                       self::attributeWhitelist( $element ) );
        }
 
        /**
@@ -795,12 +795,12 @@ class Sanitizer {
                        # Strip javascript "expression" from stylesheets.
                        # https://msdn.microsoft.com/en-us/library/ms537634.aspx
                        if ( $attribute == 'style' ) {
-                               $value = Sanitizer::checkCss( $value );
+                               $value = self::checkCss( $value );
                        }
 
                        # Escape HTML id attributes
                        if ( $attribute === 'id' ) {
-                               $value = Sanitizer::escapeId( $value, 'noninitial' );
+                               $value = self::escapeId( $value, 'noninitial' );
                        }
 
                        # Escape HTML id reference lists
@@ -809,7 +809,7 @@ class Sanitizer {
                                || $attribute === 'aria-labelledby'
                                || $attribute === 'aria-owns'
                        ) {
-                               $value = Sanitizer::escapeIdReferenceList( $value, 'noninitial' );
+                               $value = self::escapeIdReferenceList( $value, 'noninitial' );
                        }
 
                        // RDFa and microdata properties allow URLs, URIs and/or CURIs.
@@ -907,7 +907,7 @@ class Sanitizer {
         */
        public static function normalizeCss( $value ) {
                // Decode character references like &#123;
-               $value = Sanitizer::decodeCharReferences( $value );
+               $value = self::decodeCharReferences( $value );
 
                // Decode escape sequences and line continuation
                // See the grammar in the CSS 2 spec, appendix D.
@@ -1087,14 +1087,14 @@ class Sanitizer {
                        return '';
                }
 
-               $decoded = Sanitizer::decodeTagAttributes( $text );
-               $stripped = Sanitizer::validateTagAttributes( $decoded, $element );
+               $decoded = self::decodeTagAttributes( $text );
+               $stripped = self::validateTagAttributes( $decoded, $element );
 
                if ( $sorted ) {
                        ksort( $stripped );
                }
 
-               return Sanitizer::safeEncodeTagAttributes( $stripped );
+               return self::safeEncodeTagAttributes( $stripped );
        }
 
        /**
@@ -1124,7 +1124,7 @@ class Sanitizer {
         * @return string HTML-encoded text fragment
         */
        static function safeEncodeAttribute( $text ) {
-               $encValue = Sanitizer::encodeAttribute( $text );
+               $encValue = self::encodeAttribute( $text );
 
                # Templates and links may be expanded in later parsing,
                # creating invalid or dangerous output. Suppress this.
@@ -1186,7 +1186,7 @@ class Sanitizer {
                global $wgExperimentalHtmlIds;
                $options = (array)$options;
 
-               $id = Sanitizer::decodeCharReferences( $id );
+               $id = self::decodeCharReferences( $id );
 
                if ( $wgExperimentalHtmlIds && !in_array( 'legacy', $options ) ) {
                        $id = preg_replace( '/[ \t\n\r\f_\'"&#%]+/', '_', $id );
@@ -1238,7 +1238,7 @@ class Sanitizer {
 
                # Escape each token as an id
                foreach ( $references as &$ref ) {
-                       $ref = Sanitizer::escapeId( $ref, $options );
+                       $ref = self::escapeId( $ref, $options );
                }
 
                # Merge the array back to a space delimited list string
@@ -1275,7 +1275,7 @@ class Sanitizer {
         * @return string Escaped input
         */
        static function escapeHtmlAllowEntities( $html ) {
-               $html = Sanitizer::decodeCharReferences( $html );
+               $html = self::decodeCharReferences( $html );
                # It seems wise to escape ' as well as ", as a matter of course.  Can't
                # hurt. Use ENT_SUBSTITUTE so that incorrectly truncated multibyte characters
                # don't cause the entire string to disappear.
@@ -1317,14 +1317,14 @@ class Sanitizer {
 
                foreach ( $pairs as $set ) {
                        $attribute = strtolower( $set[1] );
-                       $value = Sanitizer::getTagAttributeCallback( $set );
+                       $value = self::getTagAttributeCallback( $set );
 
                        // Normalize whitespace
                        $value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
                        $value = trim( $value );
 
                        // Decode character references
-                       $attribs[$attribute] = Sanitizer::decodeCharReferences( $value );
+                       $attribs[$attribute] = self::decodeCharReferences( $value );
                }
                return $attribs;
        }
@@ -1340,7 +1340,7 @@ class Sanitizer {
                $attribs = [];
                foreach ( $assoc_array as $attribute => $value ) {
                        $encAttribute = htmlspecialchars( $attribute );
-                       $encValue = Sanitizer::safeEncodeAttribute( $value );
+                       $encValue = self::safeEncodeAttribute( $value );
 
                        $attribs[] = "$encAttribute=\"$encValue\"";
                }
@@ -1427,11 +1427,11 @@ class Sanitizer {
        static function normalizeCharReferencesCallback( $matches ) {
                $ret = null;
                if ( $matches[1] != '' ) {
-                       $ret = Sanitizer::normalizeEntity( $matches[1] );
+                       $ret = self::normalizeEntity( $matches[1] );
                } elseif ( $matches[2] != '' ) {
-                       $ret = Sanitizer::decCharReference( $matches[2] );
+                       $ret = self::decCharReference( $matches[2] );
                } elseif ( $matches[3] != '' ) {
-                       $ret = Sanitizer::hexCharReference( $matches[3] );
+                       $ret = self::hexCharReference( $matches[3] );
                }
                if ( is_null( $ret ) ) {
                        return htmlspecialchars( $matches[0] );
@@ -1468,7 +1468,7 @@ class Sanitizer {
         */
        static function decCharReference( $codepoint ) {
                $point = intval( $codepoint );
-               if ( Sanitizer::validateCodepoint( $point ) ) {
+               if ( self::validateCodepoint( $point ) ) {
                        return sprintf( '&#%d;', $point );
                } else {
                        return null;
@@ -1481,7 +1481,7 @@ class Sanitizer {
         */
        static function hexCharReference( $codepoint ) {
                $point = hexdec( $codepoint );
-               if ( Sanitizer::validateCodepoint( $point ) ) {
+               if ( self::validateCodepoint( $point ) ) {
                        return sprintf( '&#x%x;', $point );
                } else {
                        return null;
@@ -1550,11 +1550,11 @@ class Sanitizer {
         */
        static function decodeCharReferencesCallback( $matches ) {
                if ( $matches[1] != '' ) {
-                       return Sanitizer::decodeEntity( $matches[1] );
+                       return self::decodeEntity( $matches[1] );
                } elseif ( $matches[2] != '' ) {
-                       return Sanitizer::decodeChar( intval( $matches[2] ) );
+                       return self::decodeChar( intval( $matches[2] ) );
                } elseif ( $matches[3] != '' ) {
-                       return Sanitizer::decodeChar( hexdec( $matches[3] ) );
+                       return self::decodeChar( hexdec( $matches[3] ) );
                }
                # Last case should be an ampersand by itself
                return $matches[0];
@@ -1568,7 +1568,7 @@ class Sanitizer {
         * @private
         */
        static function decodeChar( $codepoint ) {
-               if ( Sanitizer::validateCodepoint( $codepoint ) ) {
+               if ( self::validateCodepoint( $codepoint ) ) {
                        return UtfNormal\Utils::codepointToUtf8( $codepoint );
                } else {
                        return UtfNormal\Constants::UTF8_REPLACEMENT;
@@ -1601,7 +1601,7 @@ class Sanitizer {
         * @return array
         */
        static function attributeWhitelist( $element ) {
-               $list = Sanitizer::setupAttributeWhitelist();
+               $list = self::setupAttributeWhitelist();
                return isset( $list[$element] )
                        ? $list[$element]
                        : [];
@@ -1876,7 +1876,7 @@ class Sanitizer {
        static function cleanUrl( $url ) {
                # Normalize any HTML entities in input. They will be
                # re-escaped by makeExternalLink().
-               $url = Sanitizer::decodeCharReferences( $url );
+               $url = self::decodeCharReferences( $url );
 
                # Escape any control characters introduced by the above step
                $url = preg_replace_callback( '/[\][<>"\\x00-\\x20\\x7F\|]/',
index e1244e7..d048007 100644 (file)
@@ -287,7 +287,7 @@ return [
                return ObjectFactory::constructClassInstance( $conf['class'], [ $conf ] );
        },
 
-       'ParserCache' => function( MediaWikiServices $services ) {
+       'ParserCache' => function ( MediaWikiServices $services ) {
                $config = $services->getMainConfig();
                $cache = ObjectCache::getInstance( $config->get( 'ParserCacheType' ) );
                wfDebugLog( 'caches', 'parser: ' . get_class( $cache ) );
@@ -298,7 +298,7 @@ return [
                );
        },
 
-       'LinkCache' => function( MediaWikiServices $services ) {
+       'LinkCache' => function ( MediaWikiServices $services ) {
                return new LinkCache(
                        $services->getTitleFormatter(),
                        $services->getMainWANObjectCache()
index ac00fab..3d5bee2 100644 (file)
@@ -687,7 +687,7 @@ $messageMemc = wfGetMessageCacheStorage();
 /**
  * @deprecated since 1.30
  */
-$parserMemc = new DeprecatedGlobal( 'parserMemc', function() {
+$parserMemc = new DeprecatedGlobal( 'parserMemc', function () {
        return MediaWikiServices::getInstance()->getParserCache()->getCacheStorage();
 }, '1.30' );
 
index 6ce1aed..6a2d0e2 100644 (file)
@@ -34,9 +34,6 @@ class SiteStats {
        /** @var bool */
        private static $loaded = false;
 
-       /** @var int */
-       private static $jobs;
-
        /** @var int[] */
        private static $pageCount = [];
 
@@ -213,24 +210,24 @@ class SiteStats {
        }
 
        /**
+        * Total number of jobs in the job queue.
         * @return int
         */
        static function jobs() {
-               if ( !isset( self::$jobs ) ) {
-                       try{
-                               self::$jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
-                       } catch ( JobQueueError $e ) {
-                               self::$jobs = 0;
-                       }
-                       /**
-                        * Zero rows still do single row read for row that doesn't exist,
-                        * but people are annoyed by that
-                        */
-                       if ( self::$jobs == 1 ) {
-                               self::$jobs = 0;
-                       }
-               }
-               return self::$jobs;
+               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'SiteStats', 'jobscount' ),
+                       $cache::TTL_MINUTE,
+                       function ( $oldValue, &$ttl, array &$setOpts ) {
+                               try{
+                                       $jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
+                               } catch ( JobQueueError $e ) {
+                                       $jobs = 0;
+                               }
+                               return $jobs;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
        }
 
        /**
@@ -296,7 +293,6 @@ class SiteStatsInit {
        private $mUsers = null, $mFiles = null;
 
        /**
-        * Constructor
         * @param bool|IDatabase $database
         * - boolean: Whether to use the master DB
         * - IDatabase: Database connection to use
index 083a725..edfdaca 100644 (file)
@@ -272,7 +272,7 @@ class Title implements LinkTarget {
                }
 
                try {
-                       return Title::newFromTextThrow( strval( $text ), $defaultNamespace );
+                       return self::newFromTextThrow( strval( $text ), $defaultNamespace );
                } catch ( MalformedTitleException $ex ) {
                        return null;
                }
@@ -411,7 +411,7 @@ class Title implements LinkTarget {
                        __METHOD__
                );
                if ( $row !== false ) {
-                       $title = Title::newFromRow( $row );
+                       $title = self::newFromRow( $row );
                } else {
                        $title = null;
                }
@@ -439,7 +439,7 @@ class Title implements LinkTarget {
 
                $titles = [];
                foreach ( $res as $row ) {
-                       $titles[] = Title::newFromRow( $row );
+                       $titles[] = self::newFromRow( $row );
                }
                return $titles;
        }
@@ -541,7 +541,7 @@ class Title implements LinkTarget {
                }
 
                $t = new Title();
-               $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
+               $t->mDbkeyform = self::makeName( $ns, $title, $fragment, $interwiki, true );
 
                try {
                        $t->secureAndSplit();
@@ -557,10 +557,10 @@ class Title implements LinkTarget {
         * @return Title The new object
         */
        public static function newMainPage() {
-               $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
+               $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
                // Don't give fatal errors if the message is broken
                if ( !$title ) {
-                       $title = Title::newFromText( 'Main Page' );
+                       $title = self::newFromText( 'Main Page' );
                }
                return $title;
        }
@@ -933,7 +933,7 @@ class Title implements LinkTarget {
         */
        public function getContentModel( $flags = 0 ) {
                if ( !$this->mForcedContentModel
-                       && ( !$this->mContentModel || $flags === Title::GAID_FOR_UPDATE )
+                       && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE )
                        && $this->getArticleID( $flags )
                ) {
                        $linkCache = LinkCache::singleton();
@@ -1096,7 +1096,7 @@ class Title implements LinkTarget {
                        if ( $canonicalName ) {
                                $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
                                if ( $localName != $this->mDbkeyform ) {
-                                       return Title::makeTitle( NS_SPECIAL, $localName );
+                                       return self::makeTitle( NS_SPECIAL, $localName );
                                }
                        }
                }
@@ -1195,7 +1195,7 @@ class Title implements LinkTarget {
         * @return bool
         */
        public function isMainPage() {
-               return $this->equals( Title::newMainPage() );
+               return $this->equals( self::newMainPage() );
        }
 
        /**
@@ -1313,7 +1313,7 @@ class Title implements LinkTarget {
         * @return Title The object for the talk page
         */
        public function getTalkPage() {
-               return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
+               return self::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
        }
 
        /**
@@ -1328,7 +1328,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() == $subjectNS ) {
                        return $this;
                }
-               return Title::makeTitle( $subjectNS, $this->getDBkey() );
+               return self::makeTitle( $subjectNS, $this->getDBkey() );
        }
 
        /**
@@ -1388,7 +1388,7 @@ class Title implements LinkTarget {
                if ( !$this->hasFragment() ) {
                        return '';
                } else {
-                       return '#' . Title::escapeFragmentForURL( $this->getFragment() );
+                       return '#' . self::escapeFragmentForURL( $this->getFragment() );
                }
        }
 
@@ -1535,7 +1535,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getRootTitle() {
-               return Title::makeTitle( $this->getNamespace(), $this->getRootText() );
+               return self::makeTitle( $this->getNamespace(), $this->getRootText() );
        }
 
        /**
@@ -1575,7 +1575,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getBaseTitle() {
-               return Title::makeTitle( $this->getNamespace(), $this->getBaseText() );
+               return self::makeTitle( $this->getNamespace(), $this->getBaseText() );
        }
 
        /**
@@ -1611,7 +1611,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getSubpage( $text ) {
-               return Title::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
+               return self::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
        }
 
        /**
@@ -2847,7 +2847,7 @@ class Title implements LinkTarget {
                                        $page_id = $row->pr_page;
                                        $page_ns = $row->page_namespace;
                                        $page_title = $row->page_title;
-                                       $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
+                                       $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
                                        # Add groups needed for each restriction type if its not already there
                                        # Make sure this restriction type still exists
 
@@ -3172,7 +3172,7 @@ class Title implements LinkTarget {
                if ( $limit > -1 ) {
                        $options['LIMIT'] = $limit;
                }
-               $this->mSubpages = TitleArray::newFromResult(
+               return TitleArray::newFromResult(
                        $dbr->select( 'page',
                                [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
                                $conds,
@@ -3180,7 +3180,6 @@ class Title implements LinkTarget {
                                $options
                        )
                );
-               return $this->mSubpages;
        }
 
        /**
@@ -3329,7 +3328,7 @@ class Title implements LinkTarget {
         * @return int Int or 0 if the page doesn't exist
         */
        public function getLatestRevID( $flags = 0 ) {
-               if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
+               if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
                        return intval( $this->mLatestID );
                }
                if ( !$this->getArticleID( $flags ) ) {
@@ -3489,7 +3488,7 @@ class Title implements LinkTarget {
                if ( $res->numRows() ) {
                        $linkCache = LinkCache::singleton();
                        foreach ( $res as $row ) {
-                               $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
                                if ( $titleObj ) {
                                        $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
                                        $retVal[] = $titleObj;
@@ -3557,9 +3556,9 @@ class Title implements LinkTarget {
                $linkCache = LinkCache::singleton();
                foreach ( $res as $row ) {
                        if ( $row->page_id ) {
-                               $titleObj = Title::newFromRow( $row );
+                               $titleObj = self::newFromRow( $row );
                        } else {
-                               $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
+                               $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
                                $linkCache->addBadLinkObj( $titleObj );
                        }
                        $retVal[] = $titleObj;
@@ -3615,7 +3614,7 @@ class Title implements LinkTarget {
 
                $retVal = [];
                foreach ( $res as $row ) {
-                       $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
+                       $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
                }
                return $retVal;
        }
@@ -3827,7 +3826,7 @@ class Title implements LinkTarget {
                        }
                        # T16385: we need makeTitleSafe because the new page names may
                        # be longer than 255 characters.
-                       $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+                       $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
 
                        $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
                        if ( $success === true ) {
@@ -3989,7 +3988,7 @@ class Title implements LinkTarget {
                                        # Circular reference
                                        $stack[$parent] = [];
                                } else {
-                                       $nt = Title::newFromText( $parent );
+                                       $nt = self::newFromText( $parent );
                                        if ( $nt ) {
                                                $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
                                        }
index a03bc19..6a532e5 100644 (file)
@@ -115,7 +115,7 @@ class WikiMap {
         * @return string|int Wiki's name or $wiki_id if the wiki was not found
         */
        public static function getWikiName( $wikiID ) {
-               $wiki = WikiMap::getWiki( $wikiID );
+               $wiki = self::getWiki( $wikiID );
 
                if ( $wiki ) {
                        return $wiki->getDisplayName();
@@ -166,7 +166,7 @@ class WikiMap {
         * @return string|bool URL or false if the wiki was not found
         */
        public static function getForeignURL( $wikiID, $page, $fragmentId = null ) {
-               $wiki = WikiMap::getWiki( $wikiID );
+               $wiki = self::getWiki( $wikiID );
 
                if ( $wiki ) {
                        return $wiki->getFullUrl( $page, $fragmentId );
index d016433..16a5a9d 100644 (file)
@@ -225,7 +225,7 @@ class Xml {
                $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode;
                $options = "\n";
                foreach ( $languages as $code => $name ) {
-                       $options .= Xml::option( "$code - $name", $code, $code == $selected ) . "\n";
+                       $options .= self::option( "$code - $name", $code, $code == $selected ) . "\n";
                }
 
                $attrs = [ 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ];
@@ -235,8 +235,8 @@ class Xml {
                        $msg = wfMessage( 'yourlanguage' );
                }
                return [
-                       Xml::label( $msg->text(), $attrs['id'] ),
-                       Xml::tags( 'select', $attrs, $options )
+                       self::label( $msg->text(), $attrs['id'] ),
+                       self::tags( 'select', $attrs, $options )
                ];
        }
 
@@ -400,7 +400,7 @@ class Xml {
                $value = false, $attribs = []
        ) {
                return [
-                       Xml::label( $label, $id, $attribs ),
+                       self::label( $label, $id, $attribs ),
                        self::input( $name, $size, $value, [ 'id' => $id ] + $attribs )
                ];
        }
@@ -556,11 +556,11 @@ class Xml {
                        $attribs['tabindex'] = $tabindex;
                }
 
-               return Xml::openElement( 'select', $attribs )
+               return self::openElement( 'select', $attribs )
                        . "\n"
                        . $options
                        . "\n"
-                       . Xml::closeElement( 'select' );
+                       . self::closeElement( 'select' );
        }
 
        /**
@@ -575,15 +575,15 @@ class Xml {
         * @return string
         */
        public static function fieldset( $legend = false, $content = false, $attribs = [] ) {
-               $s = Xml::openElement( 'fieldset', $attribs ) . "\n";
+               $s = self::openElement( 'fieldset', $attribs ) . "\n";
 
                if ( $legend ) {
-                       $s .= Xml::element( 'legend', null, $legend ) . "\n";
+                       $s .= self::element( 'legend', null, $legend ) . "\n";
                }
 
                if ( $content !== false ) {
                        $s .= $content . "\n";
-                       $s .= Xml::closeElement( 'fieldset' ) . "\n";
+                       $s .= self::closeElement( 'fieldset' ) . "\n";
                }
 
                return $s;
@@ -644,7 +644,7 @@ class Xml {
         */
        public static function encodeJsCall( $name, $args, $pretty = false ) {
                foreach ( $args as &$arg ) {
-                       $arg = Xml::encodeJsVar( $arg, $pretty );
+                       $arg = self::encodeJsVar( $arg, $pretty );
                        if ( $arg === false ) {
                                return false;
                        }
@@ -702,7 +702,7 @@ class Xml {
                        $text .
                        '</html>';
 
-               return Xml::isWellFormed( $html );
+               return self::isWellFormed( $html );
        }
 
        /**
@@ -736,25 +736,25 @@ class Xml {
 
                foreach ( $fields as $labelmsg => $input ) {
                        $id = "mw-$labelmsg";
-                       $form .= Xml::openElement( 'tr', [ 'id' => $id ] );
+                       $form .= self::openElement( 'tr', [ 'id' => $id ] );
 
                        // TODO use a <label> here for accessibility purposes - will need
                        // to either not use a table to build the form, or find the ID of
                        // the input somehow.
 
-                       $form .= Xml::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() );
-                       $form .= Xml::openElement( 'td', [ 'class' => 'mw-input' ] )
-                               . $input . Xml::closeElement( 'td' );
-                       $form .= Xml::closeElement( 'tr' );
+                       $form .= self::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() );
+                       $form .= self::openElement( 'td', [ 'class' => 'mw-input' ] )
+                               . $input . self::closeElement( 'td' );
+                       $form .= self::closeElement( 'tr' );
                }
 
                if ( $submitLabel ) {
-                       $form .= Xml::openElement( 'tr' );
-                       $form .= Xml::tags( 'td', [], '' );
-                       $form .= Xml::openElement( 'td', [ 'class' => 'mw-submit' ] )
-                               . Xml::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
-                               . Xml::closeElement( 'td' );
-                       $form .= Xml::closeElement( 'tr' );
+                       $form .= self::openElement( 'tr' );
+                       $form .= self::tags( 'td', [], '' );
+                       $form .= self::openElement( 'td', [ 'class' => 'mw-submit' ] )
+                               . self::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
+                               . self::closeElement( 'td' );
+                       $form .= self::closeElement( 'tr' );
                }
 
                $form .= "</tbody></table>";
@@ -770,10 +770,10 @@ class Xml {
         * @return string
         */
        public static function buildTable( $rows, $attribs = [], $headers = null ) {
-               $s = Xml::openElement( 'table', $attribs );
+               $s = self::openElement( 'table', $attribs );
 
                if ( is_array( $headers ) ) {
-                       $s .= Xml::openElement( 'thead', $attribs );
+                       $s .= self::openElement( 'thead', $attribs );
 
                        foreach ( $headers as $id => $header ) {
                                $attribs = [];
@@ -782,9 +782,9 @@ class Xml {
                                        $attribs['id'] = $id;
                                }
 
-                               $s .= Xml::element( 'th', $attribs, $header );
+                               $s .= self::element( 'th', $attribs, $header );
                        }
-                       $s .= Xml::closeElement( 'thead' );
+                       $s .= self::closeElement( 'thead' );
                }
 
                foreach ( $rows as $id => $row ) {
@@ -794,10 +794,10 @@ class Xml {
                                $attribs['id'] = $id;
                        }
 
-                       $s .= Xml::buildTableRow( $attribs, $row );
+                       $s .= self::buildTableRow( $attribs, $row );
                }
 
-               $s .= Xml::closeElement( 'table' );
+               $s .= self::closeElement( 'table' );
 
                return $s;
        }
@@ -809,7 +809,7 @@ class Xml {
         * @return string
         */
        public static function buildTableRow( $attribs, $cells ) {
-               $s = Xml::openElement( 'tr', $attribs );
+               $s = self::openElement( 'tr', $attribs );
 
                foreach ( $cells as $id => $cell ) {
                        $attribs = [];
@@ -818,10 +818,10 @@ class Xml {
                                $attribs['id'] = $id;
                        }
 
-                       $s .= Xml::element( 'td', $attribs, $cell );
+                       $s .= self::element( 'td', $attribs, $cell );
                }
 
-               $s .= Xml::closeElement( 'tr' );
+               $s .= self::closeElement( 'tr' );
 
                return $s;
        }
index d1cd037..35a0607 100644 (file)
  * interpret a given string as being a JavaScript expression, instead of string
  * data.
  *
- * Example:
+ * @par Example:
+ * @code
+ *     Xml::encodeJsVar( new XmlJsCode( 'a + b' ) );
+ * @encode
  *
- *    Xml::encodeJsVar( new XmlJsCode( 'a + b' ) );
- *
- * Returns "a + b".
+ * This returns "a + b".
  *
  * @note As of 1.21, XmlJsCode objects cannot be nested inside objects or arrays. The sole
  *       exception is the $args argument to Xml::encodeJsCall() because Xml::encodeJsVar() is
index 88382b6..e8d9a3e 100644 (file)
@@ -151,7 +151,7 @@ abstract class Action implements MessageLocalizer {
                        return 'view';
                }
 
-               $action = Action::factory( $actionName, $context->getWikiPage(), $context );
+               $action = self::factory( $actionName, $context->getWikiPage(), $context );
                if ( $action instanceof Action ) {
                        return $action->getName();
                }
@@ -388,7 +388,7 @@ abstract class Action implements MessageLocalizer {
        public function addHelpLink( $to, $overrideBaseUrl = false ) {
                global $wgContLang;
                $msg = wfMessage( $wgContLang->lc(
-                       Action::getActionName( $this->getContext() )
+                       self::getActionName( $this->getContext() )
                        ) . '-helppage' );
 
                if ( !$msg->isDisabled() ) {
index bc3def8..034d243 100644 (file)
@@ -548,7 +548,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has this method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module.' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module.' );
                }
 
                return $this->getMain()->lacksSameOriginSecurity();
@@ -620,7 +620,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getResult() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getResult();
@@ -634,7 +634,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getErrorFormatter() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getErrorFormatter();
@@ -660,7 +660,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getContinuationManager() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getContinuationManager();
@@ -674,7 +674,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has setContinuationManager() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                $this->getMain()->setContinuationManager( $manager );
@@ -1059,7 +1059,7 @@ abstract class ApiBase extends ContextSource {
                if ( $type == 'boolean' ) {
                        if ( isset( $default ) && $default !== false ) {
                                // Having a default value of anything other than 'false' is not allowed
-                               ApiBase::dieDebug(
+                               self::dieDebug(
                                        __METHOD__,
                                        "Boolean param $encParamName's default is set to '$default'. " .
                                                'Boolean parameters must default to false.'
@@ -1070,13 +1070,13 @@ abstract class ApiBase extends ContextSource {
                } elseif ( $type == 'upload' ) {
                        if ( isset( $default ) ) {
                                // Having a default value is not allowed
-                               ApiBase::dieDebug(
+                               self::dieDebug(
                                        __METHOD__,
                                        "File upload param $encParamName's default is set to " .
                                                "'$default'. File upload parameters may not have a default." );
                        }
                        if ( $multi ) {
-                               ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                               self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                        }
                        $value = $this->getMain()->getUpload( $encParamName );
                        if ( !$value->exists() ) {
@@ -1138,7 +1138,7 @@ abstract class ApiBase extends ContextSource {
 
                $allSpecifier = ( is_string( $allowAll ) ? $allowAll : self::ALL_DEFAULT_STRING );
                if ( $allowAll && $multi && is_array( $type ) && in_array( $allSpecifier, $type, true ) ) {
-                       ApiBase::dieDebug(
+                       self::dieDebug(
                                __METHOD__,
                                "For param $encParamName, PARAM_ALL collides with a possible value" );
                }
@@ -1194,13 +1194,13 @@ abstract class ApiBase extends ContextSource {
                                                if ( !isset( $paramSettings[self::PARAM_MAX] )
                                                        || !isset( $paramSettings[self::PARAM_MAX2] )
                                                ) {
-                                                       ApiBase::dieDebug(
+                                                       self::dieDebug(
                                                                __METHOD__,
                                                                "MAX1 or MAX2 are not defined for the limit $encParamName"
                                                        );
                                                }
                                                if ( $multi ) {
-                                                       ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                                                       self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                                                }
                                                $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0;
                                                if ( $value == 'max' ) {
@@ -1221,7 +1221,7 @@ abstract class ApiBase extends ContextSource {
                                                break;
                                        case 'boolean':
                                                if ( $multi ) {
-                                                       ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                                                       self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                                                }
                                                break;
                                        case 'timestamp':
@@ -1255,7 +1255,7 @@ abstract class ApiBase extends ContextSource {
                                                }
                                                break;
                                        default:
-                                               ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
+                                               self::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
                                }
                        }
 
@@ -2077,19 +2077,19 @@ abstract class ApiBase extends ContextSource {
         * @return Message
         */
        public function getFinalSummary() {
-               $msg = ApiBase::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
+               $msg = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
                        $this->getModulePrefix(),
                        $this->getModuleName(),
                        $this->getModulePath(),
                ] );
                if ( !$msg->exists() ) {
                        wfDeprecated( 'API help "description" messages', '1.30' );
-                       $msg = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
+                       $msg = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
                                $this->getModulePath(),
                        ] );
-                       $msg = ApiBase::makeMessage( 'rawmessage', $this->getContext(), [
+                       $msg = self::makeMessage( 'rawmessage', $this->getContext(), [
                                preg_replace( '/\n.*/s', '', $msg->text() )
                        ] );
                }
@@ -2116,12 +2116,12 @@ abstract class ApiBase extends ContextSource {
                        $desc = (string)$desc;
                }
 
-               $summary = ApiBase::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
+               $summary = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
                        $this->getModulePrefix(),
                        $this->getModuleName(),
                        $this->getModulePath(),
                ] );
-               $extendedDescription = ApiBase::makeMessage(
+               $extendedDescription = self::makeMessage(
                        $this->getExtendedDescription(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
@@ -2133,7 +2133,7 @@ abstract class ApiBase extends ContextSource {
                        $msgs = [ $summary, $extendedDescription ];
                } else {
                        wfDeprecated( 'API help "description" messages', '1.30' );
-                       $description = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
+                       $description = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
                                $this->getModulePath(),
@@ -2165,10 +2165,10 @@ abstract class ApiBase extends ContextSource {
 
                if ( $this->needsToken() ) {
                        $params['token'] = [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => true,
-                               ApiBase::PARAM_SENSITIVE => true,
-                               ApiBase::PARAM_HELP_MSG => [
+                               self::PARAM_TYPE => 'string',
+                               self::PARAM_REQUIRED => true,
+                               self::PARAM_SENSITIVE => true,
+                               self::PARAM_HELP_MSG => [
                                        'api-help-param-token',
                                        $this->needsToken(),
                                ],
@@ -2205,7 +2205,7 @@ abstract class ApiBase extends ContextSource {
                }
                $desc = self::escapeWikiText( $desc );
 
-               $params = $this->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
+               $params = $this->getFinalParams( self::GET_VALUES_FOR_HELP );
                $msgs = [];
                foreach ( $params as $param => $settings ) {
                        if ( !is_array( $settings ) ) {
@@ -2224,15 +2224,15 @@ abstract class ApiBase extends ContextSource {
                                $d = implode( ' ', $d );
                        }
 
-                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
-                               $msg = $settings[ApiBase::PARAM_HELP_MSG];
+                       if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
+                               $msg = $settings[self::PARAM_HELP_MSG];
                        } else {
                                $msg = $this->msg( "apihelp-{$path}-param-{$param}" );
                                if ( !$msg->exists() ) {
                                        $msg = $this->msg( 'api-help-fallback-parameter', $d );
                                }
                        }
-                       $msg = ApiBase::makeMessage( $msg, $this->getContext(),
+                       $msg = self::makeMessage( $msg, $this->getContext(),
                                [ $prefix, $param, $name, $path ] );
                        if ( !$msg ) {
                                self::dieDebug( __METHOD__,
@@ -2240,11 +2240,11 @@ abstract class ApiBase extends ContextSource {
                        }
                        $msgs[$param] = [ $msg ];
 
-                       if ( isset( $settings[ApiBase::PARAM_TYPE] ) &&
-                               $settings[ApiBase::PARAM_TYPE] === 'submodule'
+                       if ( isset( $settings[self::PARAM_TYPE] ) &&
+                               $settings[self::PARAM_TYPE] === 'submodule'
                        ) {
-                               if ( isset( $settings[ApiBase::PARAM_SUBMODULE_MAP] ) ) {
-                                       $map = $settings[ApiBase::PARAM_SUBMODULE_MAP];
+                               if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
+                                       $map = $settings[self::PARAM_SUBMODULE_MAP];
                                } else {
                                        $prefix = $this->isMain() ? '' : ( $this->getModulePath() . '+' );
                                        $map = [];
@@ -2282,29 +2282,29 @@ abstract class ApiBase extends ContextSource {
                                        $arr[] = $m->setContext( $this->getContext() );
                                }
                                $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
-                       } elseif ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
-                               if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                       } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                               if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
                                }
-                               if ( !is_array( $settings[ApiBase::PARAM_TYPE] ) ) {
+                               if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
                                                'ApiBase::PARAM_TYPE is an array' );
                                }
 
-                               $valueMsgs = $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE];
-                               $deprecatedValues = isset( $settings[ApiBase::PARAM_DEPRECATED_VALUES] )
-                                       ? $settings[ApiBase::PARAM_DEPRECATED_VALUES]
+                               $valueMsgs = $settings[self::PARAM_HELP_MSG_PER_VALUE];
+                               $deprecatedValues = isset( $settings[self::PARAM_DEPRECATED_VALUES] )
+                                       ? $settings[self::PARAM_DEPRECATED_VALUES]
                                        : [];
 
-                               foreach ( $settings[ApiBase::PARAM_TYPE] as $value ) {
+                               foreach ( $settings[self::PARAM_TYPE] as $value ) {
                                        if ( isset( $valueMsgs[$value] ) ) {
                                                $msg = $valueMsgs[$value];
                                        } else {
                                                $msg = "apihelp-{$path}-paramvalue-{$param}-{$value}";
                                        }
-                                       $m = ApiBase::makeMessage( $msg, $this->getContext(),
+                                       $m = self::makeMessage( $msg, $this->getContext(),
                                                [ $prefix, $param, $name, $path, $value ] );
                                        if ( $m ) {
                                                $m = new ApiHelpParamValueMessage(
@@ -2321,13 +2321,13 @@ abstract class ApiBase extends ContextSource {
                                }
                        }
 
-                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
-                               if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
+                       if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
+                               if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
                                }
-                               foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $m ) {
-                                       $m = ApiBase::makeMessage( $m, $this->getContext(),
+                               foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
+                                       $m = self::makeMessage( $m, $this->getContext(),
                                                [ $prefix, $param, $name, $path ] );
                                        if ( $m ) {
                                                $msgs[$param][] = $m;
@@ -2614,6 +2614,7 @@ abstract class ApiBase extends ContextSource {
         * @param string $warning Warning message
         */
        public function setWarning( $warning ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $msg = new ApiRawMessage( $warning, 'warning' );
                $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg );
        }
@@ -2632,6 +2633,7 @@ abstract class ApiBase extends ContextSource {
         * @throws ApiUsageException always
         */
        public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithError(
                        new RawMessage( '$1', [ $description ] ),
                        $errorCode,
@@ -2651,6 +2653,7 @@ abstract class ApiBase extends ContextSource {
         * @throws MWException
         */
        public function getErrorFromStatus( $status, &$extraData = null ) {
+               wfDeprecated( __METHOD__, '1.29' );
                if ( $status->isGood() ) {
                        throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
                }
@@ -2860,6 +2863,7 @@ abstract class ApiBase extends ContextSource {
         * @return [ 'code' => code, 'info' => info ]
         */
        public function parseMsg( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                // Check whether someone passed the whole array, instead of one element as
                // documented. This breaks if it's actually an array of fallback keys, but
                // that's long-standing misbehavior introduced in r87627 to incorrectly
@@ -2889,6 +2893,7 @@ abstract class ApiBase extends ContextSource {
         * @throws ApiUsageException always
         */
        public function dieUsageMsg( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithError( $this->parseMsgInternal( $error ) );
        }
 
@@ -2901,6 +2906,7 @@ abstract class ApiBase extends ContextSource {
         * @since 1.21
         */
        public function dieUsageMsgOrDebug( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithErrorOrDebug( $this->parseMsgInternal( $error ) );
        }
 
index 72c7c35..4b4b76b 100644 (file)
@@ -71,7 +71,7 @@ class ApiEmailUser extends ApiBase {
                }
 
                $result = array_filter( [
-                       'result' => $retval->isGood() ? 'Success' : $retval->isOk() ? 'Warnings' : 'Failure',
+                       'result' => $retval->isGood() ? 'Success' : ( $retval->isOk() ? 'Warnings' : 'Failure' ),
                        'warnings' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'warning' ),
                        'errors' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'error' ),
                ] );
index 52f79ee..b7d4529 100644 (file)
@@ -1041,7 +1041,7 @@ class ApiMain extends ApiBase {
                        // None of the rest have any messages for non-error types
                } elseif ( $e instanceof UsageException ) {
                        // User entered incorrect parameters - generate error response
-                       $data = $e->getMessageArray();
+                       $data = MediaWiki\quietCall( [ $e, 'getMessageArray' ] );
                        $code = $data['code'];
                        $info = $data['info'];
                        unset( $data['code'], $data['info'] );
@@ -1829,7 +1829,7 @@ class ApiMain extends ApiBase {
                                ApiBase::PARAM_TYPE => 'submodule',
                        ],
                        'format' => [
-                               ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT,
+                               ApiBase::PARAM_DFLT => self::API_DEFAULT_FORMAT,
                                ApiBase::PARAM_TYPE => 'submodule',
                        ],
                        'maxlag' => [
index 599b3de..cfac761 100644 (file)
@@ -121,7 +121,7 @@ class ApiPageSet extends ApiBase {
        public function __construct( ApiBase $dbSource, $flags = 0, $defaultNamespace = NS_MAIN ) {
                parent::__construct( $dbSource->getMain(), $dbSource->getModuleName() );
                $this->mDbSource = $dbSource;
-               $this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0;
+               $this->mAllowGenerator = ( $flags & self::DISABLE_GENERATORS ) == 0;
                $this->mDefaultNamespace = $defaultNamespace;
 
                $this->mParams = $this->extractRequestParams();
@@ -166,7 +166,7 @@ class ApiPageSet extends ApiBase {
                        }
                        // Create a temporary pageset to store generator's output,
                        // add any additional fields generator may need, and execute pageset to populate titles/pageids
-                       $tmpPageSet = new ApiPageSet( $dbSource, ApiPageSet::DISABLE_GENERATORS );
+                       $tmpPageSet = new ApiPageSet( $dbSource, self::DISABLE_GENERATORS );
                        $generator->setGeneratorMode( $tmpPageSet );
                        $this->mCacheMode = $generator->getCacheMode( $generator->extractRequestParams() );
 
index b2664df..bfd5b17 100644 (file)
@@ -677,7 +677,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ApiBase::PARAM_DFLT => -1,
                                ApiBase::PARAM_HELP_MSG => [
                                        'apihelp-query+imageinfo-param-urlwidth',
-                                       ApiQueryImageInfo::TRANSFORM_LIMIT,
+                                       self::TRANSFORM_LIMIT,
                                ],
                        ],
                        'urlheight' => [
index 6b8f98c..ecdebd4 100644 (file)
@@ -128,7 +128,7 @@ class ApiQueryInfo extends ApiQueryBase {
         * @deprecated since 1.24
         */
        public static function resetTokenCache() {
-               ApiQueryInfo::$cachedTokens = [];
+               self::$cachedTokens = [];
        }
 
        /**
@@ -144,11 +144,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['edit'] ) ) {
-                       ApiQueryInfo::$cachedTokens['edit'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['edit'] ) ) {
+                       self::$cachedTokens['edit'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['edit'];
+               return self::$cachedTokens['edit'];
        }
 
        /**
@@ -161,11 +161,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['delete'] ) ) {
-                       ApiQueryInfo::$cachedTokens['delete'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['delete'] ) ) {
+                       self::$cachedTokens['delete'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['delete'];
+               return self::$cachedTokens['delete'];
        }
 
        /**
@@ -178,11 +178,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['protect'] ) ) {
-                       ApiQueryInfo::$cachedTokens['protect'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['protect'] ) ) {
+                       self::$cachedTokens['protect'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['protect'];
+               return self::$cachedTokens['protect'];
        }
 
        /**
@@ -195,11 +195,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['move'] ) ) {
-                       ApiQueryInfo::$cachedTokens['move'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['move'] ) ) {
+                       self::$cachedTokens['move'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['move'];
+               return self::$cachedTokens['move'];
        }
 
        /**
@@ -212,11 +212,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['block'] ) ) {
-                       ApiQueryInfo::$cachedTokens['block'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['block'] ) ) {
+                       self::$cachedTokens['block'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['block'];
+               return self::$cachedTokens['block'];
        }
 
        /**
@@ -237,11 +237,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['email'] ) ) {
-                       ApiQueryInfo::$cachedTokens['email'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['email'] ) ) {
+                       self::$cachedTokens['email'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['email'];
+               return self::$cachedTokens['email'];
        }
 
        /**
@@ -254,11 +254,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['import'] ) ) {
-                       ApiQueryInfo::$cachedTokens['import'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['import'] ) ) {
+                       self::$cachedTokens['import'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['import'];
+               return self::$cachedTokens['import'];
        }
 
        /**
@@ -271,11 +271,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['watch'] ) ) {
-                       ApiQueryInfo::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' );
+               if ( !isset( self::$cachedTokens['watch'] ) ) {
+                       self::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' );
                }
 
-               return ApiQueryInfo::$cachedTokens['watch'];
+               return self::$cachedTokens['watch'];
        }
 
        /**
@@ -288,11 +288,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['options'] ) ) {
-                       ApiQueryInfo::$cachedTokens['options'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['options'] ) ) {
+                       self::$cachedTokens['options'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['options'];
+               return self::$cachedTokens['options'];
        }
 
        public function execute() {
index 6734740..468d878 100644 (file)
@@ -287,12 +287,12 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
-               if ( ( $flags & ApiResult::NO_VALIDATE ) !== ApiResult::NO_VALIDATE ) {
+               if ( ( $flags & self::NO_VALIDATE ) !== self::NO_VALIDATE ) {
                        $value = self::validateValue( $value );
                }
 
                if ( $name === null ) {
-                       if ( $flags & ApiResult::ADD_ON_TOP ) {
+                       if ( $flags & self::ADD_ON_TOP ) {
                                array_unshift( $arr, $value );
                        } else {
                                array_push( $arr, $value );
@@ -301,8 +301,8 @@ class ApiResult implements ApiSerializable {
                }
 
                $exists = isset( $arr[$name] );
-               if ( !$exists || ( $flags & ApiResult::OVERRIDE ) ) {
-                       if ( !$exists && ( $flags & ApiResult::ADD_ON_TOP ) ) {
+               if ( !$exists || ( $flags & self::OVERRIDE ) ) {
+                       if ( !$exists && ( $flags & self::ADD_ON_TOP ) ) {
                                $arr = [ $name => $value ] + $arr;
                        } else {
                                $arr[$name] = $value;
@@ -403,13 +403,13 @@ class ApiResult implements ApiSerializable {
         * @since 1.21 int $flags replaced boolean $override
         */
        public function addValue( $path, $name, $value, $flags = 0 ) {
-               $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
+               $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
 
-               if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+               if ( $this->checkingSize && !( $flags & self::NO_SIZE_CHECK ) ) {
                        // self::size needs the validated value. Then flag
                        // to not re-validate later.
                        $value = self::validateValue( $value );
-                       $flags |= ApiResult::NO_VALIDATE;
+                       $flags |= self::NO_VALIDATE;
 
                        $newsize = $this->size + self::size( $value );
                        if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
@@ -459,7 +459,7 @@ class ApiResult implements ApiSerializable {
                        $name = array_pop( $path );
                }
                $ret = self::unsetValue( $this->path( $path, 'dummy' ), $name );
-               if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+               if ( $this->checkingSize && !( $flags & self::NO_SIZE_CHECK ) ) {
                        $newsize = $this->size - self::size( $ret );
                        $this->size = max( $newsize, 0 );
                }
@@ -511,7 +511,7 @@ class ApiResult implements ApiSerializable {
        public function addParsedLimit( $moduleName, $limit ) {
                // Add value, allowing overwriting
                $this->addValue( 'limits', $moduleName, $limit,
-                       ApiResult::OVERRIDE | ApiResult::NO_SIZE_CHECK );
+                       self::OVERRIDE | self::NO_SIZE_CHECK );
        }
 
        /**@}*/
@@ -551,7 +551,7 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public function addContentField( $path, $name, $flags = 0 ) {
-               $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
+               $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
                self::setContentField( $arr, $name, $flags );
        }
 
@@ -1156,7 +1156,7 @@ class ApiResult implements ApiSerializable {
                $bools = [];
                foreach ( $vars as $k => $v ) {
                        if ( is_array( $v ) || is_object( $v ) ) {
-                               $vars[$k] = ApiResult::addMetadataToResultVars( (array)$v, is_object( $v ) );
+                               $vars[$k] = self::addMetadataToResultVars( (array)$v, is_object( $v ) );
                        } elseif ( is_bool( $v ) ) {
                                // Better here to use real bools even in BC formats
                                $bools[] = $k;
@@ -1176,22 +1176,22 @@ class ApiResult implements ApiSerializable {
                        // Get the list of keys we actually care about. Unfortunately, we can't support
                        // certain keys that conflict with ApiResult metadata.
                        $keys = array_diff( array_keys( $vars ), [
-                               ApiResult::META_TYPE, ApiResult::META_PRESERVE_KEYS, ApiResult::META_KVP_KEY_NAME,
-                               ApiResult::META_INDEXED_TAG_NAME, ApiResult::META_BC_BOOLS
+                               self::META_TYPE, self::META_PRESERVE_KEYS, self::META_KVP_KEY_NAME,
+                               self::META_INDEXED_TAG_NAME, self::META_BC_BOOLS
                        ] );
 
                        return [
-                               ApiResult::META_TYPE => 'kvp',
-                               ApiResult::META_KVP_KEY_NAME => 'key',
-                               ApiResult::META_PRESERVE_KEYS => $keys,
-                               ApiResult::META_BC_BOOLS => $bools,
-                               ApiResult::META_INDEXED_TAG_NAME => 'var',
+                               self::META_TYPE => 'kvp',
+                               self::META_KVP_KEY_NAME => 'key',
+                               self::META_PRESERVE_KEYS => $keys,
+                               self::META_BC_BOOLS => $bools,
+                               self::META_INDEXED_TAG_NAME => 'var',
                        ] + $vars;
                } else {
                        return [
-                               ApiResult::META_TYPE => 'array',
-                               ApiResult::META_BC_BOOLS => $bools,
-                               ApiResult::META_INDEXED_TAG_NAME => 'value',
+                               self::META_TYPE => 'array',
+                               self::META_BC_BOOLS => $bools,
+                               self::META_INDEXED_TAG_NAME => 'value',
                        ] + $vars;
                }
        }
index 9dc1f92..fb49e2d 100644 (file)
@@ -45,6 +45,10 @@ class UsageException extends MWException {
                $this->mCodestr = $codestr;
                $this->mExtraData = $extradata;
 
+               if ( !$this instanceof ApiUsageException ) {
+                       wfDeprecated( __METHOD__, '1.29' );
+               }
+
                // This should never happen, so throw an exception about it that will
                // hopefully get logged with a backtrace (T138585)
                if ( !is_string( $codestr ) || $codestr === '' ) {
@@ -58,6 +62,7 @@ class UsageException extends MWException {
         * @return string
         */
        public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
                return $this->mCodestr;
        }
 
@@ -65,6 +70,7 @@ class UsageException extends MWException {
         * @return array
         */
        public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
                $result = [
                        'code' => $this->mCodestr,
                        'info' => $this->getMessage()
@@ -183,6 +189,7 @@ class ApiUsageException extends UsageException implements ILocalizedException {
         * @inheritdoc
         */
        public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
                return $this->getApiMessage()->getApiCode();
        }
 
@@ -192,6 +199,7 @@ class ApiUsageException extends UsageException implements ILocalizedException {
         * @inheritdoc
         */
        public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
                $enMsg = clone $this->getApiMessage();
                $enMsg->inLanguage( 'en' )->useDatabase( false );
 
index e419711..b73d537 100644 (file)
@@ -27,7 +27,8 @@
                        "Trial",
                        "Pols12",
                        "The RedBurn",
-                       "Umherirrender"
+                       "Umherirrender",
+                       "Thibaut120094"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].",
        "apihelp-parse-paramvalue-prop-sections": "Fournit les sections dans le wikitexte analysé.",
        "apihelp-parse-paramvalue-prop-revid": "Ajoute l’ID de révision de la page analysée.",
        "apihelp-parse-paramvalue-prop-displaytitle": "Ajoute le titre du wikitexte analysé.",
-       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">Désuet.</span> Fournit les éléments à mettre dans le <code>&lt;head&gt;</code> de la page.",
+       "apihelp-parse-paramvalue-prop-headitems": "Fournit les éléments à mettre dans le <code>&lt;head&gt;</code> de la page.",
        "apihelp-parse-paramvalue-prop-headhtml": "Fournit le <code>&lt;head&gt;</code> analysé de la page.",
        "apihelp-parse-paramvalue-prop-modules": "Fournit les modules ResourceLoader utilisés sur la page. Pour les charger, utiliser <code>mw.loader.using()</code>. Soit <kbd>jsconfigvars</kbd> soit <kbd>encodedjsconfigvars</kbd> doit être demandé avec <kbd>modules</kbd>.",
        "apihelp-parse-paramvalue-prop-jsconfigvars": "Fournit les variables de configuration JavaScript spécifiques à la page. Pour les appliquer, utiliser <code>mw.config.set()</code>.",
        "apihelp-query+filearchive-paramvalue-prop-mime": "Ajoute le MIME de l’image.",
        "apihelp-query+filearchive-paramvalue-prop-mediatype": "Ajoute le type de média de l’image.",
        "apihelp-query+filearchive-paramvalue-prop-metadata": "Liste les métadonnées Exif pour la version de l’image.",
-       "apihelp-query+filearchive-paramvalue-prop-bitdepth": "Ajoute la profondeur de bit de la version.",
+       "apihelp-query+filearchive-paramvalue-prop-bitdepth": "Ajoute la profondeur de bits de la version.",
        "apihelp-query+filearchive-paramvalue-prop-archivename": "Ajoute le nom de fichier de la version d’archive pour les versions autres que la dernière.",
        "apihelp-query+filearchive-example-simple": "Afficher une liste de tous les fichiers supprimés",
        "apihelp-query+filerepoinfo-summary": "Renvoyer les méta-informations sur les référentiels d’images configurés dans le wiki.",
        "apihelp-query+recentchanges-paramvalue-prop-ids": "Ajoute l’ID de la page, l’ID des modifications récentes et l’ID de l’ancienne et de la nouvelle révision.",
        "apihelp-query+recentchanges-paramvalue-prop-sizes": "Ajoute l’ancienne et la nouvelle taille de la page en octets.",
        "apihelp-query+recentchanges-paramvalue-prop-redirect": "Marque la modification si la page est une redirection.",
-       "apihelp-query+recentchanges-paramvalue-prop-patrolled": "Marque les modifications patrouillables comme patrouillées ou non.",
+       "apihelp-query+recentchanges-paramvalue-prop-patrolled": "Marque les modifications à relire comme relues ou pas.",
        "apihelp-query+recentchanges-paramvalue-prop-loginfo": "Ajoute les informations du journal (Id du journal, type de trace, etc.) aux entrées du journal.",
        "apihelp-query+recentchanges-paramvalue-prop-tags": "Liste les balises de l’entrée.",
        "apihelp-query+recentchanges-paramvalue-prop-sha1": "Ajoute la somme de contrôle du contenu pour les entrées associées à une révision.",
        "apihelp-query+recentchanges-param-toponly": "Lister uniquement les modifications qui sont de la dernière révision.",
        "apihelp-query+recentchanges-param-generaterevisions": "Utilisé comme générateur, générer des IDs de révision plutôt que des titres.\nLes entrées de modification récentes sans IDs de révision associé (par ex. la plupart des entrées de journaux) ne généreront rien.",
        "apihelp-query+recentchanges-example-simple": "Lister les modifications récentes",
-       "apihelp-query+recentchanges-example-generator": "Obtenir l’information de page sur les modifications récentes non patrouillées",
+       "apihelp-query+recentchanges-example-generator": "Obtenir l’information de page sur les modifications récentes non relues.",
        "apihelp-query+redirects-summary": "Renvoie toutes les redirections vers les pages données.",
        "apihelp-query+redirects-param-prop": "Quelles propriétés récupérer :",
        "apihelp-query+redirects-paramvalue-prop-pageid": "ID de page de chaque redirection.",
        "apihelp-query+revisions+base-paramvalue-prop-parsedcomment": "Commentaire analysé de l’utilisateur sur la révision.",
        "apihelp-query+revisions+base-paramvalue-prop-content": "Texte de la révision.",
        "apihelp-query+revisions+base-paramvalue-prop-tags": "Balises de la révision.",
-       "apihelp-query+revisions+base-paramvalue-prop-parsetree": "L’arbre d’analyse XML du contenu de la révision (nécessite le modèle de contenu <code>$1</code>).",
+       "apihelp-query+revisions+base-paramvalue-prop-parsetree": "<span class=\"apihelp-deprecated\">Deprecated.</span> Utiliser  <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> ou <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd> à la place. L’arbre d’analyse XML du contenu de la révision (nécessite le modèle de contenu <code>$1</code>).",
        "apihelp-query+revisions+base-param-limit": "Limiter le nombre de révisions retournées.",
-       "apihelp-query+revisions+base-param-expandtemplates": "Développer les modèles dans le contenu de la révision (nécessite $1prop=content).",
-       "apihelp-query+revisions+base-param-generatexml": "Générer l’arbre d’analyse XML pour le contenu de la révision (nécessite $1prop=content ; remplacé par <kbd>$1prop=parsetree</kbd>).",
-       "apihelp-query+revisions+base-param-parse": "Analyser le contenu de la révision (nécessite $1prop=content). Pour des raisons de performance, si cette option est utilisée, $1limit est forcé à 1.",
+       "apihelp-query+revisions+base-param-expandtemplates": "Utiliser <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> à la place. Développer les modèles dans le contenu de la révision (nécessite $1prop=content).",
+       "apihelp-query+revisions+base-param-generatexml": "Utiliser <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> ou <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd> à la place. Générer l’arbre d’analyse XML pour le contenu de la révision (nécessite $1prop=content).",
+       "apihelp-query+revisions+base-param-parse": "Utiliser <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd> à la place. Analyser le contenu de la révision (nécessite $1prop=content). Pour des raisons de performance, si cette option est utilisée, $1limit est forcé à 1.",
        "apihelp-query+revisions+base-param-section": "Récupérer uniquement le contenu de ce numéro de section.",
-       "apihelp-query+revisions+base-param-diffto": "ID de révision à prendre pour comparer chaque révision. Utiliser <kbd>prev</kbd>, <kbd>next</kbd> et <kbd>cur</kbd> pour la version précédente, suivante et actuelle respectivement.",
-       "apihelp-query+revisions+base-param-difftotext": "Texte auquel comparer chaque révision. Compare uniquement un nombre limité de révisions. Écrase <var>$1diffto</var>. Si <var>$1section</var> est positionné, seule cette section sera comparée avec ce texte.",
-       "apihelp-query+revisions+base-param-difftotextpst": "Effectuer une transformation avant enregistrement sur le texte avant de le comparer. Valide uniquement quand c’est utilisé avec <var>$1difftotext</var>.",
+       "apihelp-query+revisions+base-param-diffto": "Utiliser <kbd>[[Special:ApiHelp/compare|action=compare]]</kbd> à la place. ID de révision à prendre pour comparer chaque révision. Utiliser <kbd>prev</kbd>, <kbd>next</kbd> et <kbd>cur</kbd> pour la version précédente, suivante et actuelle respectivement.",
+       "apihelp-query+revisions+base-param-difftotext": "Utiliser <kbd>[[Special:ApiHelp/compare|action=compare]]</kbd> à la place. Texte auquel comparer chaque révision. Compare uniquement un nombre limité de révisions. Écrase <var>$1diffto</var>. Si <var>$1section</var> est positionné, seule cette section sera comparée avec ce texte.",
+       "apihelp-query+revisions+base-param-difftotextpst": "Utiliser <kbd>[[Special:ApiHelp/compare|action=compare]]</kbd> à la place. Effectuer une transformation avant enregistrement sur le texte avant de le comparer. Valide uniquement quand utilisé avec <var>$1difftotext</var>.",
        "apihelp-query+revisions+base-param-contentformat": "Format de sérialisation utilisé pour <var>$1difftotext</var> et attendu pour la sortie du contenu.",
        "apihelp-query+search-summary": "Effectuer une recherche en texte intégral.",
        "apihelp-query+search-param-search": "Rechercher les titres de page ou le contenu correspondant à cette valeur. Vous pouvez utiliser la chaîne de recherche pour invoquer des fonctionnalités de recherche spéciales, selon ce que le serveur de recherche du wiki implémente.",
        "apihelp-query+usercontribs-paramvalue-prop-size": "Ajoute la nouvelle taille de la modification.",
        "apihelp-query+usercontribs-paramvalue-prop-sizediff": "Ajoute le delta de taille de la modification par rapport à son parent.",
        "apihelp-query+usercontribs-paramvalue-prop-flags": "Ajoute les marques de la modification.",
-       "apihelp-query+usercontribs-paramvalue-prop-patrolled": "Marque les modifications patrouillées.",
+       "apihelp-query+usercontribs-paramvalue-prop-patrolled": "Marque les modifications relues.",
        "apihelp-query+usercontribs-paramvalue-prop-tags": "Liste les balises de la modification.",
        "apihelp-query+usercontribs-param-show": "Afficher uniquement les éléments correspondant à ces critères, par ex. les modifications non mineures uniquement : <kbd>$2show=!minor</kbd>.\n\nSi <kbd>$2show=patrolled</kbd> ou <kbd>$2show=!patrolled</kbd> est positionné, les révisions plus anciennes que <var>[[mw:Special:MyLanguage/Manual:$wgRCMaxAge|$wgRCMaxAge]]</var> ($1 {{PLURAL:$1|seconde|secondes}}) ne seront pas affichées.",
        "apihelp-query+usercontribs-param-tag": "Lister uniquement les révisions marquées avec cette balise.",
        "apihelp-query+userinfo-paramvalue-prop-rights": "Liste tous les droits qu’a l’utilisateur actuel.",
        "apihelp-query+userinfo-paramvalue-prop-changeablegroups": "Liste les groupes pour lesquels l’utilisateur actuel peut ajouter ou supprimer.",
        "apihelp-query+userinfo-paramvalue-prop-options": "Liste toutes les préférences qu’a définies l’utilisateur actuel.",
-       "apihelp-query+userinfo-paramvalue-prop-preferencestoken": "<span class=\"apihelp-deprecated\">Obsolete.</span> Obtenir un jeton pour modifier les préférences de l’utilisateur actuel.",
+       "apihelp-query+userinfo-paramvalue-prop-preferencestoken": "Obtenir un jeton pour modifier les préférences de l’utilisateur actuel.",
        "apihelp-query+userinfo-paramvalue-prop-editcount": "Ajoute le compteur de modifications de l’utilisateur actuel.",
        "apihelp-query+userinfo-paramvalue-prop-ratelimits": "Liste toutes les limites de débit s’appliquant à l’utilisateur actuel.",
        "apihelp-query+userinfo-paramvalue-prop-realname": "Ajoute le vrai nom de l’utilisateur actuel.",
        "apihelp-query+watchlist-paramvalue-prop-comment": "Ajoute le commentaire de la modification.",
        "apihelp-query+watchlist-paramvalue-prop-parsedcomment": "Ajoute le commentaire analysé de la modification.",
        "apihelp-query+watchlist-paramvalue-prop-timestamp": "Ajoute l’horodatage de la modification.",
-       "apihelp-query+watchlist-paramvalue-prop-patrol": "Marque les modifications patrouillées.",
+       "apihelp-query+watchlist-paramvalue-prop-patrol": "Marque les modifications relues.",
        "apihelp-query+watchlist-paramvalue-prop-sizes": "Ajoute les tailles ancienne et nouvelle de la page.",
        "apihelp-query+watchlist-paramvalue-prop-notificationtimestamp": "Ajoute l’horodatage de la dernière notification de la modification à l’utilisateur.",
        "apihelp-query+watchlist-paramvalue-prop-loginfo": "Ajoute l’information de trace le cas échéant.",
        "api-format-title": "Résultat de l’API de MediaWiki",
        "api-format-prettyprint-header": "Voici la représentation HTML du format $1. HTML est utile pour le débogage, mais inapproprié pour être utilisé dans une application.\n\nSpécifiez le paramètre <var>format</var> pour modifier le format de sortie. Pour voir la représentation non HTML du format $1, mettez <kbd>format=$2</kbd>.\n\nVoyez la [[mw:Special:MyLanguage/API|documentation complète]], ou l’[[Special:ApiHelp/main|aide de l’API]] pour plus d’information.",
        "api-format-prettyprint-header-only-html": "Ceci est une représentation HTML à des fins de débogage, et n’est pas approprié pour une utilisation applicative.\n\nVoir la [[mw:Special:MyLanguage/API|documentation complète]], ou l’[[Special:ApiHelp/main|aide de l’API]] pour plus d’information.",
+       "api-format-prettyprint-header-hyperlinked": "Voici la représentation HTML du format $1. HTML est bien pour le débogage, mais inapproprié pour être utilisé dans une application.\n\nSpécifiez le paramètre <var>format</var> pour modifier le format de sortie. Pour voir la représentation non HTML du format $1, mettez [$3 <kbd>format=$2</kbd>].\n\nVoir la [[mw:API|documentation complète]], ou l’ [[Special:ApiHelp/main|aide de l’API]] pour plus d’information.",
        "api-format-prettyprint-status": "Cette réponse serait retournée avec l'état HTTP $1 $2.",
        "api-login-fail-aborted": "L’authentification nécessite une interaction avec l’utilisateur, qui n’est pas prise en charge par <kbd>action=login</kbd>. Pour pouvoir se connecter avec <kbd>action=login</kbd>, voyez [[Special:BotPasswords]]. Pour continuer à utiliser la connexion du compte principal, voyez <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "api-login-fail-aborted-nobotpw": "L’authentification nécessite une interaction avec l’utilisateur, qui n’est pas prise en charge par <kbd>action=login</kbd>. Pour se connecter, voyez <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apierror-notarget": "Vous n’avez pas spécifié une cible valide pour cette action.",
        "apierror-notpatrollable": "La révision r$1 ne peut pas être patrouillée car elle est trop ancienne.",
        "apierror-nouploadmodule": "Aucun module de téléversement défini.",
-       "apierror-offline": "Impossible de continuer du fait de problèmes de connexion au réseau. Assurez-vous d’avoir une connexion internet active et réessayez.",
+       "apierror-offline": "Impossible de continuer du fait de problèmes de connexion au réseau. Assurez-vous d’avoir une connexion internet opérationnelle et réessayez.",
        "apierror-opensearch-json-warnings": "Les avertissements ne peuvent pas être représentés dans le format JSON OpenSearch.",
        "apierror-pagecannotexist": "L’espace de noms ne permet pas de pages réelles.",
        "apierror-pagedeleted": "La page a été supprimée depuis que vous avez récupéré son horodatage.",
index 6fea718..8918620 100644 (file)
        "apihelp-query+langlinks-param-lang": "להחזיר רק קישורי שפה עם קוד השפה הזה.",
        "apihelp-query+langlinks-param-title": "קישור לחיפוש. חובה להשתמש עם <var>$1lang</var>.",
        "apihelp-query+langlinks-param-dir": "באיזה כיוון לרשום.",
-       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות.",
+       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות.",
        "apihelp-query+langlinks-example-simple": "קבלת קישורים בין־לשוניים מהדף <kbd>Main Page</kbd>.",
        "apihelp-query+links-summary": "החזרת כל הקישורים מהדפים שצוינו.",
        "apihelp-query+links-param-namespace": "להציג קישורים רק במרחבי השם האלה.",
        "apihelp-query+siteinfo-param-filteriw": "החזרה רק של עיולים מקומיים או רק של עיולים לא מקומיים ממפת הבינוויקי.",
        "apihelp-query+siteinfo-param-showalldb": "רשימת כל שרתי מסד הנתונים, לא רק אלה שהכי מתעכבים.",
        "apihelp-query+siteinfo-param-numberingroup": "רשימת מספרי משתמשים בקבוצות משתמשים.",
-       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
+       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
        "apihelp-query+siteinfo-example-simple": "איזור מידע על האתר.",
        "apihelp-query+siteinfo-example-interwiki": "אחזור תחיליות בינוויקי מקומיות.",
        "apihelp-query+siteinfo-example-replag": "בדיקת שיהוי השכפול הנוכחי.",
index 158aa91..391c06c 100644 (file)
        "apihelp-resetpassword-example-email": "<kbd>user@example.com</kbd> 이메일 주소를 가진 모든 사용자에 대해 비밀번호 재설정 이메일을 보냅니다.",
        "apihelp-revisiondelete-summary": "판을 삭제하거나 되살립니다.",
        "apihelp-revisiondelete-param-reason": "삭제 또는 복구 이유.",
+       "apihelp-rollback-summary": "문서의 마지막 편집을 취소합니다.",
        "apihelp-rollback-param-tags": "되돌리기를 적용하기 위해 태그합니다.",
        "apihelp-rollback-param-watchlist": "현재 사용자의 주시목록에서 문서를 무조건적으로 추가하거나 제거하거나, 환경 설정을 사용하거나 주시를 변경하지 않습니다.",
        "apihelp-rollback-example-simple": "<kbd>Project:대문</kbd> 문서의 <kbd>예시</kbd>의 마지막 판을 되돌리기",
        "apihelp-setpagelanguage-param-lang": "문서를 변경할 언어의 언어 코드입니다. 문서를 위키의 기본 콘텐츠 언어로 재설정하려면 <kbd>default</kbd>를 사용하십시오.",
        "apihelp-setpagelanguage-param-reason": "변경 이유.",
        "apihelp-setpagelanguage-example-language": "<kbd>Main Page</kbd>의 언어를 바스크어로 변경합니다.",
+       "apihelp-stashedit-summary": "공유된 캐시에서 편집을 준비합니다.",
        "apihelp-stashedit-param-sectiontitle": "새 문단을 위한 제목.",
        "apihelp-stashedit-param-text": "문서 내용.",
        "apihelp-stashedit-param-contentmodel": "새 콘텐츠의 콘텐츠 모델.",
index 7876b3a..9b509ee 100644 (file)
@@ -57,6 +57,7 @@
        "apihelp-compare-param-fromtitle": "Pierwszy tytuł do porównania.",
        "apihelp-compare-param-fromid": "ID pierwszej strony do porównania.",
        "apihelp-compare-param-fromrev": "Pierwsza wersja do porównania.",
+       "apihelp-compare-param-frompst": "Dokonaj zapisu wersji roboczej transformacji przeprowadzonej na <var>fromtext</var>.",
        "apihelp-compare-param-totitle": "Drugi tytuł do porównania.",
        "apihelp-compare-param-toid": "Numer drugiej strony do porównania.",
        "apihelp-compare-param-torev": "Druga wersja do porównania.",
index 1dfeb34..a311728 100644 (file)
@@ -11,7 +11,8 @@
                        "Winstonyin",
                        "Arthur2e5",
                        "烈羽",
-                       "Corainn"
+                       "Corainn",
+                       "A2093064"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|說明文件]]\n* [[mw:API:FAQ|常見問題]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 郵寄清單]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 報告錯誤及請求功能]\n</div>\n<strong>狀態資訊:</strong>本頁所展示的所有功能都應正常工作,但是API仍在開發當中,將會隨時變化。請訂閱[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 郵件清單]以便得到更新通知。\n\n<strong>錯誤的請求:</strong>當API收到錯誤的請求時,會發出以「MediaWiki-API-Error」為鍵的HTTP頭欄位,隨後頭欄位的值與錯誤碼將會被設為相同的值。詳細資訊請參閱[[mw:API:Errors_and_warnings|API: 錯誤與警告]]。\n\n<strong>測試:</strong>要簡化API請求的測試過程,請見[[Special:ApiSandbox]]。",
@@ -57,7 +58,7 @@
        "apihelp-createaccount-summary": "建立新使用者帳號。",
        "apihelp-createaccount-param-name": "使用者名稱。",
        "apihelp-createaccount-param-password": "密碼 (若有設定 <var>$1mailpassword</var> 則可略過)。",
-       "apihelp-createaccount-param-domain": "å¤\96é\83¨èª\8d証使用的網域 (選填)。",
+       "apihelp-createaccount-param-domain": "å¤\96é\83¨èª\8dè­\89使用的網域 (選填)。",
        "apihelp-createaccount-param-token": "在第一次請求時已取得的帳號建立金鑰。",
        "apihelp-createaccount-param-email": "使用者的電子郵件地址 (選填) 。",
        "apihelp-createaccount-param-realname": "使用者的真實姓名 (選填)。",
index 6684fb9..956c985 100644 (file)
@@ -133,7 +133,7 @@ class AuthenticationResponse {
         */
        public static function newPass( $username = null ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::PASS;
+               $ret->status = self::PASS;
                $ret->username = $username;
                return $ret;
        }
@@ -145,7 +145,7 @@ class AuthenticationResponse {
         */
        public static function newFail( Message $msg ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::FAIL;
+               $ret->status = self::FAIL;
                $ret->message = $msg;
                $ret->messageType = 'error';
                return $ret;
@@ -158,7 +158,7 @@ class AuthenticationResponse {
         */
        public static function newRestart( Message $msg ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::RESTART;
+               $ret->status = self::RESTART;
                $ret->message = $msg;
                return $ret;
        }
@@ -169,7 +169,7 @@ class AuthenticationResponse {
         */
        public static function newAbstain() {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::ABSTAIN;
+               $ret->status = self::ABSTAIN;
                return $ret;
        }
 
@@ -189,7 +189,7 @@ class AuthenticationResponse {
                }
 
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::UI;
+               $ret->status = self::UI;
                $ret->neededRequests = $reqs;
                $ret->message = $msg;
                $ret->messageType = $msgtype;
@@ -209,7 +209,7 @@ class AuthenticationResponse {
                }
 
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::REDIRECT;
+               $ret->status = self::REDIRECT;
                $ret->neededRequests = $reqs;
                $ret->redirectTarget = $redirectTarget;
                $ret->redirectApiData = $redirectApiData;
index ad1fffb..f9f9a08 100644 (file)
@@ -816,7 +816,7 @@ class MessageCache {
                }
 
                // Normalise title-case input (with some inlining)
-               $lckey = MessageCache::normalizeKey( $key );
+               $lckey = self::normalizeKey( $key );
 
                Hooks::run( 'MessageCache::get', [ &$lckey ] );
 
index cffb59a..99dc899 100644 (file)
@@ -31,8 +31,6 @@ class ChangesFeed {
        public $format, $type, $titleMsg, $descMsg;
 
        /**
-        * Constructor
-        *
         * @param string $format Feed's format (either 'rss' or 'atom')
         * @param string $type Type of feed (for cache keys)
         */
index 6754d67..76d0bef 100644 (file)
@@ -19,10 +19,12 @@ class ChangesListStringOptionsFilter extends ChangesListFilter {
         * @inheritdoc
         */
        public function isSelected( FormOptions $opts ) {
-               $values = explode(
-                       ChangesListStringOptionsFilterGroup::SEPARATOR,
-                       $opts[ $this->getGroup()->getName() ]
-               );
+               $option = $opts[ $this->getGroup()->getName() ];
+               if ( $option === ChangesListStringOptionsFilterGroup::ALL ) {
+                       return true;
+               }
+
+               $values = explode( ChangesListStringOptionsFilterGroup::SEPARATOR, $option );
                return in_array( $this->getName(), $values );
        }
 }
index 30c6995..55cb9ed 100644 (file)
@@ -102,15 +102,17 @@ class EnhancedChangesList extends ChangesList {
                        $rc->mAttribs['rc_timestamp'],
                        $this->getUser()
                );
+               if ( $this->lastdate === '' ) {
+                       $this->lastdate = $date;
+               }
 
                $ret = '';
 
-               # If it's a new day, add the headline and flush the cache
-               if ( $date != $this->lastdate ) {
-                       # Process current cache
+               # If it's a new day, flush the cache and update $this->lastdate
+               if ( $date !== $this->lastdate ) {
+                       # Process current cache (uses $this->lastdate to generate a heading)
                        $ret = $this->recentChangesBlock();
                        $this->rc_cache = [];
-                       $ret .= Xml::element( 'h4', null, $date ) . "\n";
                        $this->lastdate = $date;
                }
 
@@ -763,7 +765,11 @@ class EnhancedChangesList extends ChangesList {
                        }
                }
 
-               return '<div>' . $blockOut . '</div>';
+               if ( $blockOut === '' ) {
+                       return '';
+               }
+               // $this->lastdate is kept up to date by recentChangesLine()
+               return Xml::element( 'h4', null, $this->lastdate ) . "\n<div>" . $blockOut . '</div>';
        }
 
        /**
index e8e35a3..5fad8fd 100644 (file)
@@ -130,7 +130,7 @@ class RecentChange {
                if ( is_array( $type ) ) {
                        $retval = [];
                        foreach ( $type as $t ) {
-                               $retval[] = RecentChange::parseToRCType( $t );
+                               $retval[] = self::parseToRCType( $t );
                        }
 
                        return $retval;
@@ -459,7 +459,7 @@ class RecentChange {
 
                $change = $change instanceof RecentChange
                        ? $change
-                       : RecentChange::newFromId( $change );
+                       : self::newFromId( $change );
 
                if ( !$change instanceof RecentChange ) {
                        return null;
index c9b5f96..2eb9b22 100644 (file)
@@ -643,24 +643,32 @@ class ChangeTags {
         * Handles selecting tags, and filtering.
         * Needs $tables to be set up properly, so we can figure out which join conditions to use.
         *
+        * WARNING: If $filter_tag contains more than one tag, this function will add DISTINCT,
+        * which may cause performance problems for your query unless you put the ID field of your
+        * table at the end of the ORDER BY, and set a GROUP BY equal to the ORDER BY. For example,
+        * if you had ORDER BY foo_timestamp DESC, you will now need GROUP BY foo_timestamp, foo_id
+        * ORDER BY foo_timestamp DESC, foo_id DESC.
+        *
         * @param string|array $tables Table names, see Database::select
         * @param string|array $fields Fields used in query, see Database::select
         * @param string|array $conds Conditions used in query, see Database::select
         * @param array $join_conds Join conditions, see Database::select
-        * @param array $options Options, see Database::select
-        * @param bool|string $filter_tag Tag to select on
+        * @param string|array $options Options, see Database::select
+        * @param string|array $filter_tag Tag(s) to select on
         *
         * @throws MWException When unable to determine appropriate JOIN condition for tagging
         */
        public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
-                                                                               &$join_conds, &$options, $filter_tag = false ) {
-               global $wgRequest, $wgUseTagFilter;
+                                                                               &$join_conds, &$options, $filter_tag = '' ) {
+               global $wgUseTagFilter;
 
-               if ( $filter_tag === false ) {
-                       $filter_tag = $wgRequest->getVal( 'tagfilter' );
-               }
+               // Normalize to arrays
+               $tables = (array)$tables;
+               $fields = (array)$fields;
+               $conds = (array)$conds;
+               $options = (array)$options;
 
-               // Figure out which conditions can be done.
+               // Figure out which ID field to use
                if ( in_array( 'recentchanges', $tables ) ) {
                        $join_cond = 'ct_rc_id=rc_id';
                } elseif ( in_array( 'logging', $tables ) ) {
@@ -683,7 +691,13 @@ class ChangeTags {
 
                        $tables[] = 'change_tag';
                        $join_conds['change_tag'] = [ 'INNER JOIN', $join_cond ];
-                       $conds['ct_tag'] = explode( '|', $filter_tag );
+                       $conds['ct_tag'] = $filter_tag;
+                       if (
+                               is_array( $filter_tag ) && count( $filter_tag ) > 1 &&
+                               !in_array( 'DISTINCT', $options )
+                       ) {
+                               $options[] = 'DISTINCT';
+                       }
                }
        }
 
@@ -962,7 +976,7 @@ class ChangeTags {
 
                // tags cannot contain commas (used as a delimiter in tag_summary table),
                // pipe (used as a delimiter between multiple tags in
-               // modifyDisplayQuery), or slashes (would break tag description messages in
+               // SpecialRecentchanges and friends), or slashes (would break tag description messages in
                // MediaWiki namespace)
                if ( strpos( $tag, ',' ) !== false || strpos( $tag, '|' ) !== false
                        || strpos( $tag, '/' ) !== false ) {
index e0eb1c1..d93362b 100644 (file)
@@ -22,7 +22,7 @@
  * @since 1.16.3
  */
 class IcuCollation extends Collation {
-       const FIRST_LETTER_VERSION = 2;
+       const FIRST_LETTER_VERSION = 3;
 
        /** @var Collator */
        private $primaryCollator;
@@ -180,7 +180,7 @@ class IcuCollation extends Collation {
                'mk' => [ "Ѓ", "Ќ" ],
                'ml' => [],
                'mn' => [],
-               'mo' => [ "Ă", "Â", "Î", "Ş", "Ţ" ], // not in libicu
+               'mo' => [ "Ă", "Â", "Î", "Ș", "Ț" ], // not in libicu
                'mr' => [ "\xe0\xa4\x82", "\xe0\xa4\x83", "ळ", "क्ष", "ज्ञ" ],
                'ms' => [],
                'mt' => [ "Ċ", "Ġ", "Għ", "Ħ", "Ż" ],
@@ -196,9 +196,9 @@ class IcuCollation extends Collation {
                'pl' => [ "Ą", "Ć", "Ę", "Ł", "Ń", "Ó", "Ś", "Ź", "Ż" ],
                'pt' => [],
                'rm' => [], // not in libicu
-               'ro' => [ "Ă", "Â", "Î", "Ş", "Ţ" ],
+               'ro' => [ "Ă", "Â", "Î", "Ș", "Ț" ],
                'ru' => [],
-               'rup' => [ "Ă", "Â", "Î", "Ľ", "Ń", "Ş", "Ţ" ], // not in libicu
+               'rup' => [ "Ă", "Â", "Î", "Ľ", "Ń", "Ș", "Ț" ], // not in libicu
                'sco' => [],
                'se' => [
                        'Á', 'Č', 'Ʒ', 'Ǯ', 'Đ', 'Ǧ', 'Ǥ', 'Ǩ', 'Ŋ',
@@ -535,7 +535,7 @@ class IcuCollation extends Collation {
         * @return string|bool
         */
        static function getUnicodeVersionForICU() {
-               $icuVersion = IcuCollation::getICUVersion();
+               $icuVersion = self::getICUVersion();
                if ( !$icuVersion ) {
                        return false;
                }
index 2d2ca47..8dd7a38 100644 (file)
@@ -40,8 +40,6 @@ class NumericUppercaseCollation extends UppercaseCollation {
        private $digitTransformLang;
 
        /**
-        * Constructor
-        *
         * @param $lang Language How to convert digits.
         *  For example, if given language "my" than ၇ is treated like 7.
         *
index c57eba7..6605c38 100644 (file)
@@ -243,7 +243,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
 
                $info = json_decode( $rbody, true );
                if ( $info === null || !isset( $info['node']['nodes'] ) ) {
-                       return [ null, $rcode, "Unexpected JSON response; missing 'nodes' list.", false ];
+                       return [ null, "Unexpected JSON response; missing 'nodes' list.", false ];
                }
 
                $config = [];
index f85b00d..8603360 100644 (file)
@@ -136,7 +136,7 @@ abstract class ContentHandler {
                        $modelId = $title->getContentModel();
                }
 
-               $handler = ContentHandler::getForModelID( $modelId );
+               $handler = self::getForModelID( $modelId );
 
                return $handler->unserializeContent( $text, $format );
        }
@@ -240,7 +240,7 @@ abstract class ContentHandler {
        public static function getForTitle( Title $title ) {
                $modelId = $title->getContentModel();
 
-               return ContentHandler::getForModelID( $modelId );
+               return self::getForModelID( $modelId );
        }
 
        /**
@@ -256,7 +256,7 @@ abstract class ContentHandler {
        public static function getForContent( Content $content ) {
                $modelId = $content->getModel();
 
-               return ContentHandler::getForModelID( $modelId );
+               return self::getForModelID( $modelId );
        }
 
        /**
@@ -293,8 +293,8 @@ abstract class ContentHandler {
        public static function getForModelID( $modelId ) {
                global $wgContentHandlers;
 
-               if ( isset( ContentHandler::$handlers[$modelId] ) ) {
-                       return ContentHandler::$handlers[$modelId];
+               if ( isset( self::$handlers[$modelId] ) ) {
+                       return self::$handlers[$modelId];
                }
 
                if ( empty( $wgContentHandlers[$modelId] ) ) {
@@ -327,9 +327,9 @@ abstract class ContentHandler {
                wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
                        . ': ' . get_class( $handler ) );
 
-               ContentHandler::$handlers[$modelId] = $handler;
+               self::$handlers[$modelId] = $handler;
 
-               return ContentHandler::$handlers[$modelId];
+               return self::$handlers[$modelId];
        }
 
        /**
@@ -372,7 +372,7 @@ abstract class ContentHandler {
                $formats = [];
 
                foreach ( $wgContentHandlers as $model => $class ) {
-                       $handler = ContentHandler::getForModelID( $model );
+                       $handler = self::getForModelID( $model );
                        $formats = array_merge( $formats, $handler->getSupportedFormats() );
                }
 
index 0d0c149..6e3eda6 100644 (file)
@@ -75,7 +75,6 @@ class DerivativeContext extends ContextSource implements MutableContext {
        private $timing;
 
        /**
-        * Constructor
         * @param IContextSource $context Context to inherit from
         */
        public function __construct( IContextSource $context ) {
index 6d18444..3d22c03 100644 (file)
@@ -46,8 +46,6 @@ class CloneDatabase {
        private $db;
 
        /**
-        * Constructor
-        *
         * @param IMaintainableDatabase $db A database subclass
         * @param array $tablesToClone An array of tables to clone, unprefixed
         * @param string $newTablePrefix Prefix to assign to the tables
index e67a0b3..012837f 100644 (file)
@@ -425,7 +425,7 @@ class MWDebug {
                $html = '';
 
                if ( self::$enabled ) {
-                       MWDebug::log( 'MWDebug output complete' );
+                       self::log( 'MWDebug output complete' );
                        $debugInfo = self::getDebugInfo( $context );
 
                        // Cannot use OutputPage::addJsConfigVars because those are already outputted
@@ -495,7 +495,7 @@ class MWDebug {
                        }
                }
 
-               MWDebug::log( 'MWDebug output complete' );
+               self::log( 'MWDebug output complete' );
                $debugInfo = self::getDebugInfo( $context );
 
                ApiResult::setIndexedTagName( $debugInfo, 'debuginfo' );
index 072c1e0..c6facd9 100644 (file)
@@ -102,8 +102,6 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        private $db;
 
        /**
-        * Constructor
-        *
         * @param Title $title Title of the page we're updating
         * @param ParserOutput $parserOutput Output from a full parse of this page
         * @param bool $recursive Queue jobs for recursive updates?
index b9a259b..2766bcb 100644 (file)
@@ -44,8 +44,6 @@ class SearchUpdate implements DeferrableUpdate {
        private $page;
 
        /**
-        * Constructor
-        *
         * @param int $id Page id to update
         * @param Title|string $title Title of page to update
         * @param Content|string|bool $c Content of the page to update. Default: false.
index 0b58cc1..d4bee29 100644 (file)
@@ -104,7 +104,6 @@ class DifferenceEngine extends ContextSource {
        /**#@-*/
 
        /**
-        * Constructor
         * @param IContextSource $context Context to use, anything else will be ignored
         * @param int $old Old ID we want to show and diff with.
         * @param string|int $new Either revision ID or 'prev' or 'next'. Default: 0.
@@ -377,6 +376,11 @@ class DifferenceEngine extends ContextSource {
                        }
                }
 
+               $out->addJsConfigVars( [
+                       'wgDiffOldId' => $this->mOldid,
+                       'wgDiffNewId' => $this->mNewid,
+               ] );
+
                # Make "next revision link"
                # Skip next link on the top revision
                if ( $samePage && !$this->mNewRev->isCurrent() ) {
@@ -908,10 +912,35 @@ class DifferenceEngine extends ContextSource {
                        $wgExternalDiffEngine = false;
                }
 
+               // Better external diff engine, the 2 may some day be dropped
+               // This one does the escaping and segmenting itself
                if ( function_exists( 'wikidiff2_do_diff' ) && $wgExternalDiffEngine === false ) {
-                       # Better external diff engine, the 2 may some day be dropped
-                       # This one does the escaping and segmenting itself
-                       $text = wikidiff2_do_diff( $otext, $ntext, 2 );
+                       $wikidiff2Version = phpversion( 'wikidiff2' );
+                       if (
+                               $wikidiff2Version !== false &&
+                               version_compare( $wikidiff2Version, '0.3.0', '>=' )
+                       ) {
+                               $text = wikidiff2_do_diff(
+                                       $otext,
+                                       $ntext,
+                                       2,
+                                       $this->getConfig()->get( 'WikiDiff2MovedParagraphDetectionCutoff' )
+                               );
+                       } else {
+                               // Don't pass the 4th parameter for compatibility with older versions of wikidiff2
+                               $text = wikidiff2_do_diff(
+                                       $otext,
+                                       $ntext,
+                                       2
+                               );
+
+                               // Log a warning in case the configuration value is set to not silently ignore it
+                               if ( $this->getConfig()->get( 'WikiDiff2MovedParagraphDetectionCutoff' ) > 0 ) {
+                                       wfLogWarning( '$wgWikiDiff2MovedParagraphDetectionCutoff is set but has no
+                                               effect since the used version of WikiDiff2 does not support it.' );
+                               }
+                       }
+
                        $text .= $this->debug( 'wikidiff2' );
 
                        return $text;
diff --git a/includes/edit/PreparedEdit.php b/includes/edit/PreparedEdit.php
new file mode 100644 (file)
index 0000000..62624f4
--- /dev/null
@@ -0,0 +1,113 @@
+<?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\Edit;
+
+use Content;
+use ParserOptions;
+use ParserOutput;
+
+/**
+ * Represents information returned by WikiPage::prepareContentForEdit()
+ *
+ * @since 1.30
+ */
+class PreparedEdit {
+
+       /**
+        * Time this prepared edit was made
+        *
+        * @var string
+        */
+       public $timestamp;
+
+       /**
+        * Revision ID
+        *
+        * @var int|null
+        */
+       public $revid;
+
+       /**
+        * Content after going through pre-save transform
+        *
+        * @var Content|null
+        */
+       public $pstContent;
+
+       /**
+        * Content format
+        *
+        * @var string
+        */
+       public $format;
+
+       /**
+        * Parser options used to get parser output
+        *
+        * @var ParserOptions
+        */
+       public $popts;
+
+       /**
+        * Parser output
+        *
+        * @var ParserOutput|null
+        */
+       public $output;
+
+       /**
+        * Content that is being saved (before PST)
+        *
+        * @var Content
+        */
+       public $newContent;
+
+       /**
+        * Current content of the page, if any
+        *
+        * @var Content|null
+        */
+       public $oldContent;
+
+       /**
+        * $newContent in text form
+        *
+        * @var string
+        * @deprecated since 1.21
+        */
+       public $newText;
+
+       /**
+        * $oldContent in text from
+        *
+        * @var string
+        * @deprecated since 1.21
+        */
+       public $oldText;
+
+       /**
+        * $pstContent in text form
+        *
+        * @var string
+        * @deprecated since 1.21
+        */
+       public $pst;
+}
index 48bc3bd..f464d8a 100644 (file)
@@ -31,8 +31,6 @@ class HttpError extends MWException {
        private $httpCode, $header, $content;
 
        /**
-        * Constructor
-        *
         * @param int $httpCode HTTP status code to send to the client
         * @param string|Message $content Content of the message
         * @param string|Message|null $header Content of the header (\<title\> and \<h1\>)
index 2eb821a..60d760f 100644 (file)
@@ -211,7 +211,7 @@ class MWExceptionRenderer {
                                "\nBacktrace:\n" .
                                MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
                } else {
-                       return self::getShowBacktraceError( $e );
+                       return self::getShowBacktraceError( $e ) . "\n";
                }
        }
 
@@ -242,7 +242,7 @@ class MWExceptionRenderer {
                        $vars[] = '$wgShowDBErrorBacktrace = true;';
                }
                $vars = implode( ' and ', $vars );
-               return "Set $vars at the bottom of LocalSettings.php to show detailed debugging information\n";
+               return "Set $vars at the bottom of LocalSettings.php to show detailed debugging information.";
        }
 
        /**
index a307468..943aa04 100644 (file)
@@ -91,8 +91,8 @@ class WikiExporter {
         * @param int $buffer One of WikiExporter::BUFFER or WikiExporter::STREAM
         * @param int $text One of WikiExporter::TEXT or WikiExporter::STUB
         */
-       function __construct( $db, $history = WikiExporter::CURRENT,
-                       $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
+       function __construct( $db, $history = self::CURRENT,
+                       $buffer = self::BUFFER, $text = self::TEXT ) {
                $this->db = $db;
                $this->history = $history;
                $this->buffer = $buffer;
@@ -272,7 +272,7 @@ class WikiExporter {
                        # Get logging table name for logging.* clause
                        $logging = $this->db->tableName( 'logging' );
 
-                       if ( $this->buffer == WikiExporter::STREAM ) {
+                       if ( $this->buffer == self::STREAM ) {
                                $prev = $this->db->bufferResults( false );
                        }
                        $result = null; // Assuring $result is not undefined, if exception occurs early
@@ -284,7 +284,7 @@ class WikiExporter {
                                        [ 'ORDER BY' => 'log_id', 'USE INDEX' => [ 'logging' => 'PRIMARY' ] ]
                                );
                                $this->outputLogStream( $result );
-                               if ( $this->buffer == WikiExporter::STREAM ) {
+                               if ( $this->buffer == self::STREAM ) {
                                        $this->db->bufferResults( $prev );
                                }
                        } catch ( Exception $e ) {
@@ -303,7 +303,7 @@ class WikiExporter {
 
                                // Putting database back in previous buffer mode
                                try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                       if ( $this->buffer == self::STREAM ) {
                                                $this->db->bufferResults( $prev );
                                        }
                                } catch ( Exception $e2 ) {
@@ -341,10 +341,10 @@ class WikiExporter {
                                if ( !empty( $this->history['limit'] ) ) {
                                        $opts['LIMIT'] = intval( $this->history['limit'] );
                                }
-                       } elseif ( $this->history & WikiExporter::FULL ) {
+                       } elseif ( $this->history & self::FULL ) {
                                # Full history dumps...
                                # query optimization for history stub dumps
-                               if ( $this->text == WikiExporter::STUB && $orderRevs ) {
+                               if ( $this->text == self::STUB && $orderRevs ) {
                                        $tables = [ 'revision', 'page' ];
                                        $opts[] = 'STRAIGHT_JOIN';
                                        $opts['ORDER BY'] = [ 'rev_page ASC', 'rev_id ASC' ];
@@ -353,13 +353,13 @@ class WikiExporter {
                                } else {
                                        $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page' ];
                                }
-                       } elseif ( $this->history & WikiExporter::CURRENT ) {
+                       } elseif ( $this->history & self::CURRENT ) {
                                # Latest revision dumps...
                                if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
                                        $this->do_list_authors( $cond );
                                }
                                $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' ];
-                       } elseif ( $this->history & WikiExporter::STABLE ) {
+                       } elseif ( $this->history & self::STABLE ) {
                                # "Stable" revision dumps...
                                # Default JOIN, to be overridden...
                                $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' ];
@@ -367,7 +367,7 @@ class WikiExporter {
                                if ( Hooks::run( 'WikiExporter::dumpStableQuery', [ &$tables, &$opts, &$join ] ) ) {
                                        throw new MWException( __METHOD__ . " given invalid history dump type." );
                                }
-                       } elseif ( $this->history & WikiExporter::RANGE ) {
+                       } elseif ( $this->history & self::RANGE ) {
                                # Dump of revisions within a specified range
                                $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page' ];
                                $opts['ORDER BY'] = [ 'rev_page ASC', 'rev_id ASC' ];
@@ -381,12 +381,12 @@ class WikiExporter {
                                $opts['USE INDEX']['page'] = 'PRIMARY';
                        }
                        # Build text join options
-                       if ( $this->text != WikiExporter::STUB ) { // 1-pass
+                       if ( $this->text != self::STUB ) { // 1-pass
                                $tables[] = 'text';
                                $join['text'] = [ 'INNER JOIN', 'rev_text_id=old_id' ];
                        }
 
-                       if ( $this->buffer == WikiExporter::STREAM ) {
+                       if ( $this->buffer == self::STREAM ) {
                                $prev = $this->db->bufferResults( false );
                        }
                        $result = null; // Assuring $result is not undefined, if exception occurs early
@@ -399,7 +399,7 @@ class WikiExporter {
                                # Output dump results
                                $this->outputPageStream( $result );
 
-                               if ( $this->buffer == WikiExporter::STREAM ) {
+                               if ( $this->buffer == self::STREAM ) {
                                        $this->db->bufferResults( $prev );
                                }
                        } catch ( Exception $e ) {
@@ -418,7 +418,7 @@ class WikiExporter {
 
                                // Putting database back in previous buffer mode
                                try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                       if ( $this->buffer == self::STREAM ) {
                                                $this->db->bufferResults( $prev );
                                        }
                                } catch ( Exception $e2 ) {
index 43f1d21..56ccc64 100644 (file)
@@ -528,7 +528,7 @@ class ForeignAPIRepo extends FileRepo {
                }
 
                $req = MWHttpRequest::factory( $url, $options, __METHOD__ );
-               $req->setUserAgent( ForeignAPIRepo::getUserAgent() );
+               $req->setUserAgent( self::getUserAgent() );
                $status = $req->execute();
 
                if ( $status->isOK() ) {
index 399147b..61d0d89 100644 (file)
@@ -285,7 +285,7 @@ class HTMLForm extends ContextSource {
                                return ObjectFactory::constructClassInstance( OOUIHTMLForm::class, $arguments );
                        default:
                                /** @var HTMLForm $form */
-                               $form = ObjectFactory::constructClassInstance( HTMLForm::class, $arguments );
+                               $form = ObjectFactory::constructClassInstance( self::class, $arguments );
                                $form->setDisplayFormat( $displayFormat );
                                return $form;
                }
index 9af60e5..38b487a 100644 (file)
@@ -63,8 +63,70 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                return "$select<br />\n$textbox";
        }
 
+       protected function getOOUIModules() {
+               return [ 'mediawiki.widgets.SelectWithInputWidget' ];
+       }
+
        public function getInputOOUI( $value ) {
-               return false;
+               $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.SelectWithInputWidget.styles' );
+
+               # TextInput
+               $textAttribs = [
+                       'id' => $this->mID . '-other',
+                       'name' => $this->mName . '-other',
+                       'size' => $this->getSize(),
+                       'class' => [ 'mw-htmlform-select-and-other-field' ],
+                       'data-id-select' => $this->mID,
+                       'value' => $value[2],
+               ];
+
+               $allowedParams = [
+                       'required',
+                       'autofocus',
+                       'multiple',
+                       'disabled',
+                       'tabindex',
+                       'maxlength',
+               ];
+
+               $textAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $textAttribs['classes'] = [ $this->mClass ];
+               }
+
+               # DropdownInput
+               $dropdownInputAttribs = [
+                       'name' => $this->mName,
+                       'id' => $this->mID,
+                       'options' => $this->getOptionsOOUI(),
+                       'value' => $value[1],
+               ];
+
+               $allowedParams = [
+                       'tabindex',
+                       'disabled',
+               ];
+
+               $dropdownInputAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $dropdownInputAttribs['classes'] = [ $this->mClass ];
+               }
+
+               return $this->getInputWidget( [
+                       'textinput' => $textAttribs,
+                       'dropdowninput' => $dropdownInputAttribs,
+                       'or' => false,
+               ] );
+       }
+
+       public function getInputWidget( $params ) {
+               return new Mediawiki\Widget\SelectWithInputWidget( $params );
        }
 
        /**
index bb41079..a009b28 100644 (file)
@@ -64,8 +64,80 @@ class HTMLSelectOrOtherField extends HTMLTextField {
                return "$select<br />\n$textbox";
        }
 
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+
+       protected function getOOUIModules() {
+               return [ 'mediawiki.widgets.SelectWithInputWidget' ];
+       }
+
        public function getInputOOUI( $value ) {
-               return false;
+               $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.SelectWithInputWidget.styles' );
+
+               $valInSelect = false;
+               if ( $value !== false ) {
+                       $value = strval( $value );
+                       $valInSelect = in_array(
+                               $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
+                       );
+               }
+
+               # DropdownInput
+               $dropdownAttribs = [
+                       'id' => $this->mID,
+                       'name' => $this->mName,
+                       'options' => $this->getOptionsOOUI(),
+                       'value' => $valInSelect ? $value : 'other',
+                       'class' => [ 'mw-htmlform-select-or-other' ],
+               ];
+
+               $allowedParams = [
+                       'disabled',
+                       'tabindex',
+               ];
+
+               $dropdownAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               # TextInput
+               $textAttribs = [
+                       'id' => $this->mID . '-other',
+                       'name' => $this->mName . '-other',
+                       'size' => $this->getSize(),
+                       'value' => $valInSelect ? '' : $value,
+               ];
+
+               $allowedParams = [
+                       'required',
+                       'autofocus',
+                       'multiple',
+                       'disabled',
+                       'tabindex',
+                       'maxlength',
+               ];
+
+               $textAttribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $textAttribs['classes'] = [ $this->mClass ];
+               }
+               if ( $this->mPlaceholder !== '' ) {
+                       $textAttribs['placeholder'] = $this->mPlaceholder;
+               }
+
+               return $this->getInputWidget( [
+                       'textinput' => $textAttribs,
+                       'dropdowninput' => $dropdownAttribs,
+                       'or' => true,
+               ] );
+       }
+
+       public function getInputWidget( $params ) {
+               return new Mediawiki\Widget\SelectWithInputWidget( $params );
        }
 
        /**
index 4f21ce2..c10b312 100644 (file)
@@ -106,7 +106,7 @@ class Http {
                        $options['timeout'] = $args[1];
                        $caller = __METHOD__;
                }
-               return Http::request( 'GET', $url, $options, $caller );
+               return self::request( 'GET', $url, $options, $caller );
        }
 
        /**
@@ -119,7 +119,7 @@ class Http {
         * @return string|bool false on error
         */
        public static function post( $url, $options = [], $caller = __METHOD__ ) {
-               return Http::request( 'POST', $url, $options, $caller );
+               return self::request( 'POST', $url, $options, $caller );
        }
 
        /**
index 8034400..94a2b93 100644 (file)
@@ -93,7 +93,7 @@ class ImportStreamSource implements ImportSource {
                }
                $fname = $upload['tmp_name'];
                if ( is_uploaded_file( $fname ) ) {
-                       return ImportStreamSource::newFromFile( $fname );
+                       return self::newFromFile( $fname );
                } else {
                        return Status::newFatal( 'importnofile' );
                }
@@ -178,6 +178,6 @@ class ImportStreamSource implements ImportSource {
 
                $url = wfAppendQuery( $link, $params );
                # For interwikis, use POST to avoid redirects.
-               return ImportStreamSource::newFromURL( $url, "POST" );
+               return self::newFromURL( $url, "POST" );
        }
 }
index e5cbb7c..7b6ac5e 100644 (file)
@@ -83,6 +83,7 @@ abstract class DatabaseUpdater {
                FixDefaultJsonContentPages::class,
                CleanupEmptyCategories::class,
                AddRFCAndPMIDInterwiki::class,
+               PopulatePPSortKey::class
        ];
 
        /**
@@ -105,8 +106,6 @@ abstract class DatabaseUpdater {
        protected $holdContentHandlerUseDB = true;
 
        /**
-        * Constructor
-        *
         * @param Database $db To perform updates on
         * @param bool $shared Whether to perform updates on shared tables
         * @param Maintenance $maintenance Maintenance object which created us
index cbb79f8..0250b6f 100644 (file)
@@ -51,7 +51,7 @@ class MysqlInstaller extends DatabaseInstaller {
 
        public $supportedEngines = [ 'InnoDB', 'MyISAM' ];
 
-       public $minimumVersion = '5.0.3';
+       public $minimumVersion = '5.5.8';
 
        public $webUserPrivs = [
                'DELETE',
index 3943374..0f639ba 100644 (file)
@@ -32,7 +32,8 @@ use Wikimedia\Rdbms\DBConnectionError;
  * @since 1.17
  */
 class SqliteInstaller extends DatabaseInstaller {
-       const MINIMUM_VERSION = '3.3.7';
+
+       public $minimumVersion = '3.3.7';
 
        /**
         * @var DatabaseSqlite
@@ -60,8 +61,8 @@ class SqliteInstaller extends DatabaseInstaller {
                $result = Status::newGood();
                // Bail out if SQLite is too old
                $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
-               if ( version_compare( $db->getServerVersion(), self::MINIMUM_VERSION, '<' ) ) {
-                       $result->fatal( 'config-outdated-sqlite', $db->getServerVersion(), self::MINIMUM_VERSION );
+               if ( version_compare( $db->getServerVersion(), $this->minimumVersion, '<' ) ) {
+                       $result->fatal( 'config-outdated-sqlite', $db->getServerVersion(), $this->minimumVersion );
                }
                // Check for FTS3 full-text search module
                if ( DatabaseSqlite::getFulltextSearchModule() != 'FTS3' ) {
index a311ce9..27300f3 100644 (file)
@@ -703,7 +703,7 @@ class WebInstaller extends Installer {
                        "<span class=\"config-help-field-hint\" title=\"" .
                        wfMessage( 'config-help-tooltip' )->escaped() . "\">" .
                        wfMessage( 'config-help' )->escaped() . "</span>\n" .
-                       "<span class=\"config-help-field-data\">" . $html . "</span>\n" .
+                       "<div class=\"config-help-field-data\">" . $html . "</div>\n" .
                        "</div>\n";
        }
 
index 2ba47f1..defaf1b 100644 (file)
@@ -1,4 +1,64 @@
 {
-       "@metadata": [],
-       "mainpagetext": "'''MediaWiki òsta zainstalowónô.'''"
+       "@metadata": {
+               "authors": [
+                       "Kaszeba"
+               ]
+       },
+       "config-desc": "Jinstalownik MediaWiki",
+       "config-title": "Jinstalowanié MediaWiki $1",
+       "config-information": "Wëdowiédzô",
+       "config-localsettings-key": "Klucz aktualizacëji:",
+       "config-localsettings-badkey": "Lëchi klucz aktualizacëji.",
+       "config-session-error": "Fela zrëszeniô sesëji – $1",
+       "config-your-language": "Twój jãzëk:",
+       "config-your-language-help": "Wybierzë jãzëk procesu jinstalacëji.",
+       "config-wiki-language": "Jãzëk wiki:",
+       "config-back": "← Nazôd",
+       "config-continue": "Dali →",
+       "config-page-language": "Jãzëk",
+       "config-page-welcome": "Witómë w MediaWiki!",
+       "config-page-dbconnect": "Sparłãczë z bazą pòdôwków",
+       "config-page-upgrade": "Zaktualnienié jinstalacëji",
+       "config-page-dbsettings": "Nastôw bazë pòdôwków",
+       "config-page-name": "Miono",
+       "config-page-options": "Òptacëje",
+       "config-page-install": "Wjinstalëjë",
+       "config-page-complete": "Fardich!",
+       "config-page-restart": "Zrëszë jinstalacëjã znowa",
+       "config-page-readme": "Spòdlowô wëdowiédzô",
+       "config-page-releasenotes": "Wëdowiédzô ò wersëji",
+       "config-page-copying": "Kòpérowanié",
+       "config-page-upgradedoc": "Zaktualnienié",
+       "config-page-existingwiki": "Egyzstëjącô wiki",
+       "config-restart": "Jo, zrëszë znowa",
+       "config-env-php": "PHP $1 je wjinastalowóné",
+       "config-env-hhvm": "HHVM $1 je wjinastalowóné",
+       "config-memory-raised": "Paraméter PHP <code>memory_limit</code> $1 òstôł zwikszony do $2.",
+       "config-xcache": "[Http://trac.lighttpd.net/xcache/ XCache] je wjinstalowóny",
+       "config-apc": "[Http://www.php.net/apc APC] je wjinstalowóny",
+       "config-apcu": "[http://www.php.net/apcu APCu] je wjinstalowóny",
+       "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] je wjinstalowóny",
+       "config-diff3-bad": "Felënk GNU diff3.",
+       "config-mysql-innodb": "InnoDB",
+       "config-mysql-myisam": "MyISAM",
+       "config-mysql-binary": "binarny",
+       "config-mysql-utf8": "UTF‐8",
+       "config-site-name": "Miono wiki:",
+       "config-site-name-blank": "Wpiszë miono starnów.",
+       "config-ns-other-default": "MòjôWiki",
+       "config-admin-box": "Kònto sprôwnika",
+       "config-admin-name": "Twòjé miono brëkòwnika:",
+       "config-admin-password": "Parola:",
+       "config-admin-password-confirm": "Pòwtórzë parolã:",
+       "config-admin-name-blank": "Wpiszë miono brëkòwnika, chtëren bãdze sprôwnikã.",
+       "config-admin-email": "E-mailowô adresa:",
+       "config-license-none": "Felënk stopczi z licencëją",
+       "config-email-settings": "Nastôwë e-mail",
+       "config-logo": "Adresa URL logo:",
+       "config-skins": "Wëzdrzatk",
+       "config-skins-use-as-default": "Ùżëjë tegò wëzdrzatkù za domëslny",
+       "config-install-step-done": "fardich",
+       "config-install-step-failed": "nie dzrzëło sã",
+       "config-help": "pòmòc",
+       "mainpagetext": "<strong>MediaWiki òsta wjinstalowónô.<strong>"
 }
index db21da6..9122967 100644 (file)
@@ -81,7 +81,7 @@
        "config-outdated-sqlite": "'''Attention''': vous avez SQLite $1, qui est inférieur à la version minimale requise $2. SQLite sera indisponible.",
        "config-no-fts3": "'''Attention :''' SQLite est compilé sans le module [//sqlite.org/fts3.html FTS3] ; les fonctions de recherche ne seront pas disponibles sur ce moteur.",
        "config-pcre-old": "'''Fatal :''' PCRE $1 ou ultérieur est nécessaire.\nVotre binaire PHP est lié avec PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/Plus d’information sur PCRE].",
-       "config-pcre-no-utf8": "'''Erreur fatale''': Le module PCRE de PHP semble être compilé sans le support PCRE_UTF8.\nMédiaWiki nécessite la gestion d’UTF-8 pour fonctionner correctement.",
+       "config-pcre-no-utf8": "<strong>Erreur fatale</strong>: le module PCRE de PHP semble être compilé sans la prise en charge de PCRE_UTF8.\nMédiaWiki a besoin de la gestion d’UTF-8 pour fonctionner correctement.",
        "config-memory-raised": "Le paramètre <code>memory_limit</code> de PHP était à $1, porté à $2.",
        "config-memory-bad": "'''Attention :''' Le paramètre <code>memory_limit</code> de PHP est à $1.\nCette valeur est probablement trop faible.\nIl est possible que l’installation échoue !",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] est installé",
@@ -89,7 +89,7 @@
        "config-apcu": "[http://www.php.net/apcu APCu] est installé",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] est installé",
        "config-no-cache-apcu": "<strong>Attention :</strong> Impossible de trouver [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nLa mise en cache d'objets n'est pas activée.",
-       "config-mod-security": "'''Attention''': Votre serveur web a [http://modsecurity.org/ mod_security] activé. S'il est mal configuré, cela peut poser des problèmes à MediaWiki ou à d'autres applications qui permettent aux utilisateurs de publier un contenu quelconque.\nReportez-vous à [http://modsecurity.org/documentation/ la documentation de mod_security] ou contactez le support de votre hébergeur si vous rencontrez des erreurs aléatoires.",
+       "config-mod-security": "'''Attention''': Votre serveur web a [http://modsecurity.org/ mod_security] activé. S’il est mal configuré, cela peut poser des problèmes à MediaWiki ou à d’autres applications qui permettent aux utilisateurs de publier un contenu quelconque.\nReportez-vous à [http://modsecurity.org/documentation/ la documentation de mod_security] ou contactez le soutien de votre hébergeur si vous rencontrez des erreurs aléatoires.",
        "config-diff3-bad": "GNU diff3 introuvable.",
        "config-git": "Logiciel de contrôle de version Git trouvé : <code>$1</code>.",
        "config-git-bad": "Logiciel de contrôle de version Git non trouvé.",
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQL Server",
        "config-support-info": "MediaWiki prend en charge ces systèmes de bases de données :\n\n$1\n\nSi vous ne voyez pas le système de base de données que vous essayez d’utiliser ci-dessous, alors suivez les instructions ci-dessus (voir liens) pour activer la prise en charge.",
-       "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] est le premier choix pour MediaWiki et est le mieux pris en charge. MediaWiki fonctionne aussi avec [{{int:version-db-mariadb-url}} MariaDB] et [{{int:version-db-percona-url}} Percona Server], qui sont compatibles avec MySQL. ([http://www.php.net/manual/en/mysqli.installation.php Comment compiler PHP avec le support MySQL])",
-       "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] est un système de base de données populaire et ''open source'' qui peut être une alternative à MySQL ([http://www.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec le support de PostgreSQL]).",
+       "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] est le premier choix pour MediaWiki et est le mieux pris en charge. MediaWiki fonctionne aussi avec [{{int:version-db-mariadb-url}} MariaDB] et [{{int:version-db-percona-url}} Percona Server], qui sont compatibles avec MySQL. ([http://www.php.net/manual/en/mysqli.installation.php Comment compiler PHP avec la prise en charge de MySQL])",
+       "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] est un système de base de données populaire en ''source ouverte'' qui peut être une alternative à MySQL ([http://www.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec la prise en charge de PostgreSQL]).",
        "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien pris en charge. ([http://www.php.net/manual/fr/pdo.installation.php Comment compiler PHP avec la prise en charge de SQLite], en utilisant PDO)",
-       "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] est un système commercial de gestion de base de données d’entreprise. ([http://www.php.net/manual/en/oci8.installation.php Comment compiler PHP avec le support OCI8])",
-       "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] est une base de données commerciale d’entreprise pour Windows. ([http://www.php.net/manual/en/sqlsrv.installation.php Comment compiler PHP avec le support de SQLSRV])",
+       "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] est un système commercial de gestion de base de données d’entreprise. ([http://www.php.net/manual/en/oci8.installation.php Comment compiler PHP avec la prise en charge d’OCI8])",
+       "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] est une base de données commerciale d’entreprise pour Windows. ([http://www.php.net/manual/en/sqlsrv.installation.php Comment compiler PHP avec la prise en charge de SQLSRV])",
        "config-header-mysql": "Paramètres de MySQL",
        "config-header-postgres": "Paramètres de PostgreSQL",
        "config-header-sqlite": "Paramètres de SQLite",
index 4f44ab8..686ebd2 100644 (file)
        "config-admin-name-invalid": "O nome de utilizador especificado \"<nowiki>$1</nowiki>\" é inválido.\nIntroduza um nome de utilizador diferente.",
        "config-admin-password-blank": "Introduza uma palavra-passe para a conta de administrador.",
        "config-admin-password-mismatch": "As duas palavras-passe que introduziu não coincidem.",
-       "config-admin-email": "Correio electrónico:",
+       "config-admin-email": "Correio eletrónico:",
        "config-admin-email-help": "Introduza aqui um correio eletrónico que lhe permita receber mensagens de outros utilizadores da wiki, reiniciar a sua palavra-passe e receber notificações de alterações às suas páginas vigiadas. Pode deixar o campo vazio.",
        "config-admin-error-user": "Ocorreu um erro interno ao criar um administrador com o nome \"<nowiki>$1</nowiki>\".",
        "config-admin-error-password": "Ocorreu um erro interno ao definir uma palavra-passe para o administrador \"<nowiki>$1</nowiki>\": <pre>$2</pre>",
-       "config-admin-error-bademail": "Introduziu um correio electrónico inválido",
+       "config-admin-error-bademail": "Introduziu um correio eletrónico inválido",
        "config-subscribe": "Subscrever a [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce lista de divulgação de anúncios de lançamento].",
        "config-subscribe-help": "Esta é uma lista de divulgação de baixo volume para anúncios de lançamento de versões novas, incluindo anúncios de segurança importantes.\nDeve subscrevê-la e atualizar a sua instalação MediaWiki quando são lançadas versões novas.",
-       "config-subscribe-noemail": "Tentou subscrever a lista de divulgação dos anúncios de novas versões, sem fornecer um endereço de correio electrónico.\nPara subscrever esta lista de divulgação tem de fornecer um endereço de correio electrónico.",
+       "config-subscribe-noemail": "Tentou subscrever a lista de divulgação dos anúncios de novas versões, sem fornecer um endereço de correio eletrónico.\nPara subscrever esta lista de divulgação tem de fornecer um endereço de correio eletrónico.",
        "config-pingback": "Partilhar dados sobre esta instalação com os programadores do MediaWiki.",
        "config-pingback-help": "Se selecionar esta opção, o MediaWiki fará periodicamente um <i>ping</i> a https://www.mediawiki.org com dados básicos acerca desta instância do MediaWiki. Estes dados incluem, por exemplo, o tipo de sistema, a versão do PHP e a base de dados que escolheu. A Wikimedia Foundation partilha estes dados com os programadores do MediaWiki, para ajudar a guiar o esforço de desenvolvimento futuro. Para o seu sistema, serão enviados os seguintes dados:\n<pre>$1</pre>",
        "config-almost-done": "Está quase a terminar!\nAgora pode ignorar as restantes configurações e instalar já a wiki.",
        "config-license-pd": "Domínio Público",
        "config-license-cc-choose": "Selecionar uma licença personalizada Creative Commons",
        "config-license-help": "Muitas wikis de acesso público licenciam todas as colaborações com uma [http://freedomdefined.org/Definition licença livre].\nIsto ajuda a criar um sentido de propriedade da comunidade e encoraja as colaborações a longo prazo.\nTal não é geralmente necessário nas wikis privadas ou corporativas.\n\nSe pretende que seja possível usar textos da Wikipédia na sua wiki e que seja possível a Wikipédia aceitar textos copiados da sua wiki, deve escolher a licença <strong>{{int:config-license-cc-by-sa}}</strong>..\n\nA licença anterior da Wikipédia era a licença GNU Free Documentation License.\nA GFDL é uma licença válida, mas de difícil compreensão.\nTambém é difícil reutilizar conteúdos licenciados com a GFDL.",
-       "config-email-settings": "Definições do correio electrónico",
+       "config-email-settings": "Definições do correio eletrónico",
        "config-enable-email": "Ativar mensagens eletrónicas de saída",
        "config-enable-email-help": "Se quer que o correio eletrónico funcione, as [http://www.php.net/manual/en/mail.configuration.php definições de correio eletrónico do PHP] têm de estar configuradas corretamente.\nSe não pretende viabilizar qualquer funcionalidade de correio eletrónico, pode desativá-lo aqui.",
        "config-email-user": "Ativar mensagens eletrónicas entre utilizadores",
        "config-email-watchlist-help": "Permitir que os utilizadores recebam notificações de alterações às suas páginas vigiadas, se tiverem ativado esta funcionalidade nas suas preferências.",
        "config-email-auth": "Ativar autenticação do correio eletrónico",
        "config-email-auth-help": "Se esta opção for ativada, os utilizadores têm de confirmar o seu endereço de correio eletrónico usando um link que lhes é enviado sempre que o definirem ou alterarem.\nSó os endereços de correio eletrónico autenticados podem receber mensagens eletrónicas dos outros utilizadores ou alterar as mensagens de notificação.\nÉ '''recomendado''' que esta opção seja ativada nas wikis de acesso público para impedir o uso abusivo das funcionalidades de correio eletrónico.",
-       "config-email-sender": "Endereço de correio electrónico de retorno:",
-       "config-email-sender-help": "Introduza o endereço de correio electrónico que será usado como endereço de retorno nas mensagens electrónicas de saída.\nÉ para este endereço que serão enviadas as mensagens que não podem ser entregues.\nMuitos servidores de correio electrónico exigem que pelo menos a parte do nome do domínio seja válida. \\",
+       "config-email-sender": "Endereço de correio eletrónico de retorno:",
+       "config-email-sender-help": "Introduza o endereço de correio eletrónico que será usado como endereço de retorno nas mensagens eletrónicas de saída.\nÉ para este endereço que serão enviadas as mensagens que não podem ser entregues.\nMuitos servidores de correio eletrónico exigem que pelo menos a parte do nome do domínio seja válida. \\",
        "config-upload-settings": "Carregamento de imagens e ficheiros",
        "config-upload-enable": "Possibilitar o carregamento de ficheiros",
        "config-upload-help": "O carregamento de ficheiros expõe o seu servidor a riscos de segurança.\nPara mais informações, leia a [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security secção sobre segurança] do Manual Técnico.\n\nPara permitir o carregamento de ficheiros, altere as permissões do subdiretório <code>images</code> no diretório de raiz do MediaWiki para que o servidor de Internet possa escrever nele.\nDepois ative esta opção.",
index d20a233..1f4f179 100644 (file)
@@ -378,7 +378,7 @@ abstract class JobQueue {
                // Flag this job as an old duplicate based on its "root" job...
                try {
                        if ( $job && $this->isRootJobOldDuplicate( $job ) ) {
-                               JobQueue::incrStats( 'dupe_pops', $this->type );
+                               self::incrStats( 'dupe_pops', $this->type );
                                $job = DuplicateJob::newFromJob( $job ); // convert to a no-op
                        }
                } catch ( Exception $e ) {
index cefe74d..b7cc133 100644 (file)
@@ -768,7 +768,7 @@ class JobQueueDB extends JobQueue {
        protected function getDB( $index ) {
                $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                $lb = ( $this->cluster !== false )
-                       ? $lbFactory->getExternalLB( $this->cluster, $this->wiki )
+                       ? $lbFactory->getExternalLB( $this->cluster )
                        : $lbFactory->getMainLB( $this->wiki );
 
                return $lb->getConnectionRef( $index, [], $this->wiki );
index 7fdd617..44721d9 100644 (file)
@@ -28,7 +28,7 @@
  * For example, one can set $wgJobTypeConf['refreshLinks'] to point to a
  * JobQueueFederated instance, which itself would consist of three JobQueueRedis
  * instances, each using their own redis server. This would allow for the jobs
- * to be split (evenly or based on weights) accross multiple servers if a single
+ * to be split (evenly or based on weights) across multiple servers if a single
  * server becomes impractical or expensive. Different JobQueue classes can be mixed.
  *
  * The basic queue configuration (e.g. "order", "claimTTL") of a federated queue
diff --git a/includes/jobqueue/JobQueueSecondTestQueue.php b/includes/jobqueue/JobQueueSecondTestQueue.php
new file mode 100644 (file)
index 0000000..a1935df
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * A wrapper for the JobQueue that delegates all the method calls to a single,
+ * main queue, and also pushes all the jobs to a second job queue that's being
+ * debugged.
+ *
+ * This class was temporary added to test transitioning to the JobQueueEventBus
+ * and will removed after the transition is completed. This code is only needed
+ * while we are testing the new infrastructure to be able to compare the results
+ * between the queue implementations and make sure that they process the same jobs,
+ * deduplicate correctly, compare the delays, backlogs and make sure no jobs are lost.
+ * When the new infrastructure is well tested this will not be needed any more.
+ *
+ * @deprecated since 1.30
+ * @since 1.30
+ */
+class JobQueueSecondTestQueue extends JobQueue {
+
+       /**
+        * @var JobQueue
+        */
+       private $mainQueue;
+
+       /**
+        * @var JobQueue
+        */
+       private $debugQueue;
+
+       protected function __construct( array $params ) {
+               if ( !isset( $params['mainqueue'] ) ) {
+                       throw new MWException( "mainqueue parameter must be provided to the debug queue" );
+               }
+
+               if ( !isset( $params['debugqueue'] ) ) {
+                       throw new MWException( "debugqueue parameter must be provided to the debug queue" );
+               }
+
+               $conf = [ 'wiki' => $params['wiki'], 'type' => $params['type'] ];
+               $this->mainQueue = JobQueue::factory( $params['mainqueue'] + $conf );
+               $this->debugQueue = JobQueue::factory( $params['debugqueue'] + $conf );
+
+               // We need to construct parent after creating the main and debug queue
+               // because super constructor calls some methods we delegate to the main queue.
+               parent::__construct( $params );
+       }
+
+       /**
+        * Get the allowed queue orders for configuration validation
+        *
+        * @return array Subset of (random, timestamp, fifo, undefined)
+        */
+       protected function supportedOrders() {
+               return $this->mainQueue->supportedOrders();
+       }
+
+       /**
+        * Get the default queue order to use if configuration does not specify one
+        *
+        * @return string One of (random, timestamp, fifo, undefined)
+        */
+       protected function optimalOrder() {
+               return $this->mainQueue->optimalOrder();
+       }
+
+       /**
+        * Find out if delayed jobs are supported for configuration validation
+        *
+        * @return bool Whether delayed jobs are supported
+        */
+       protected function supportsDelayedJobs() {
+               return $this->mainQueue->supportsDelayedJobs();
+       }
+
+       /**
+        * @see JobQueue::isEmpty()
+        * @return bool
+        */
+       protected function doIsEmpty() {
+               return $this->mainQueue->doIsEmpty();
+       }
+
+       /**
+        * @see JobQueue::getSize()
+        * @return int
+        */
+       protected function doGetSize() {
+               return $this->mainQueue->doGetSize();
+       }
+
+       /**
+        * @see JobQueue::getAcquiredCount()
+        * @return int
+        */
+       protected function doGetAcquiredCount() {
+               return $this->mainQueue->doGetAcquiredCount();
+       }
+
+       /**
+        * @see JobQueue::getDelayedCount()
+        * @return int
+        */
+       protected function doGetDelayedCount() {
+               return $this->mainQueue->doGetDelayedCount();
+       }
+
+       /**
+        * @see JobQueue::getAbandonedCount()
+        * @return int
+        */
+       protected function doGetAbandonedCount() {
+               return $this->mainQueue->doGetAbandonedCount();
+       }
+
+       /**
+        * @see JobQueue::batchPush()
+        * @param IJobSpecification[] $jobs
+        * @param int $flags
+        */
+       protected function doBatchPush( array $jobs, $flags ) {
+               $this->mainQueue->doBatchPush( $jobs, $flags );
+
+               try {
+                       $this->debugQueue->doBatchPush( $jobs, $flags );
+               } catch ( Exception $exception ) {
+                       MWExceptionHandler::logException( $exception );
+               }
+       }
+
+       /**
+        * @see JobQueue::pop()
+        * @return Job|bool
+        */
+       protected function doPop() {
+               return $this->mainQueue->doPop();
+       }
+
+       /**
+        * @see JobQueue::ack()
+        * @param Job $job
+        */
+       protected function doAck( Job $job ) {
+               return $this->mainQueue->doAck( $job );
+       }
+
+       /**
+        * @see JobQueue::deduplicateRootJob()
+        * @param IJobSpecification $job
+        * @throws MWException
+        * @return bool
+        */
+       protected function doDeduplicateRootJob( IJobSpecification $job ) {
+               return $this->mainQueue->doDeduplicateRootJob( $job );
+       }
+
+       /**
+        * @see JobQueue::isRootJobOldDuplicate()
+        * @param Job $job
+        * @return bool
+        */
+       protected function doIsRootJobOldDuplicate( Job $job ) {
+               return $this->mainQueue->doIsRootJobOldDuplicate( $job );
+       }
+
+       /**
+        * @param string $signature Hash identifier of the root job
+        * @return string
+        */
+       protected function getRootJobCacheKey( $signature ) {
+               return $this->mainQueue->getRootJobCacheKey( $signature );
+       }
+
+       /**
+        * @see JobQueue::delete()
+        * @throws MWException
+        */
+       protected function doDelete() {
+               return $this->mainQueue->doDelete();
+       }
+
+       /**
+        * @see JobQueue::waitForBackups()
+        * @return void
+        */
+       protected function doWaitForBackups() {
+               $this->mainQueue->doWaitForBackups();
+       }
+
+       /**
+        * @see JobQueue::flushCaches()
+        * @return void
+        */
+       protected function doFlushCaches() {
+               $this->mainQueue->doFlushCaches();
+       }
+
+       /**
+        * Get an iterator to traverse over all available jobs in this queue.
+        * This does not include jobs that are currently acquired or delayed.
+        * Note: results may be stale if the queue is concurrently modified.
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        */
+       public function getAllQueuedJobs() {
+               return $this->mainQueue->getAllQueuedJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all delayed jobs in this queue.
+        * Note: results may be stale if the queue is concurrently modified.
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.22
+        */
+       public function getAllDelayedJobs() {
+               return $this->mainQueue->getAllDelayedJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all claimed jobs in this queue
+        *
+        * Callers should be quick to iterator over it or few results
+        * will be returned due to jobs being acknowledged and deleted
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.26
+        */
+       public function getAllAcquiredJobs() {
+               return $this->mainQueue->getAllAcquiredJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all abandoned jobs in this queue
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.25
+        */
+       public function getAllAbandonedJobs() {
+               return $this->mainQueue->getAllAbandonedJobs();
+       }
+
+       /**
+        * Do not use this function outside of JobQueue/JobQueueGroup
+        *
+        * @return string
+        * @since 1.22
+        */
+       public function getCoalesceLocationInternal() {
+               return $this->mainQueue->getCoalesceLocationInternal();
+       }
+
+       /**
+        * @see JobQueue::getSiblingQueuesWithJobs()
+        * @param array $types List of queues types
+        * @return array|null (list of queue types) or null if unsupported
+        */
+       protected function doGetSiblingQueuesWithJobs( array $types ) {
+               return $this->mainQueue->doGetSiblingQueuesWithJobs( $types );
+       }
+
+       /**
+        * @see JobQueue::getSiblingQueuesSize()
+        * @param array $types List of queues types
+        * @return array|null (list of queue types) or null if unsupported
+        */
+       protected function doGetSiblingQueueSizes( array $types ) {
+               return $this->mainQueue->doGetSiblingQueueSizes( $types );
+       }
+
+       /**
+        * @throws JobQueueReadOnlyError
+        */
+       protected function assertNotReadOnly() {
+               $this->mainQueue->assertNotReadOnly();
+       }
+}
index ea0f1b7..9e060cd 100644 (file)
@@ -78,7 +78,12 @@ class CSSMin {
                                $url = $match['file'][0];
 
                                // Skip fully-qualified and protocol-relative URLs and data URIs
-                               if ( substr( $url, 0, 2 ) === '//' || parse_url( $url, PHP_URL_SCHEME ) ) {
+                               // Also skips the rare `behavior` property specifying application's default behavior
+                               if (
+                                       substr( $url, 0, 2 ) === '//' ||
+                                       parse_url( $url, PHP_URL_SCHEME ) ||
+                                       substr( $url, 0, 9 ) === '#default#'
+                               ) {
                                        break;
                                }
 
@@ -252,7 +257,7 @@ class CSSMin {
                // quotation marks (e.g. "foo /* bar").
                $comments = [];
 
-               $pattern = '/(?!' . CSSMin::EMBED_REGEX . ')(' . CSSMin::COMMENT_REGEX . ')/s';
+               $pattern = '/(?!' . self::EMBED_REGEX . ')(' . self::COMMENT_REGEX . ')/s';
 
                $source = preg_replace_callback(
                        $pattern,
@@ -355,7 +360,7 @@ class CSSMin {
                        }, $source );
 
                // Re-insert comments
-               $pattern = '/' . CSSMin::PLACEHOLDER . '(\d+)x/';
+               $pattern = '/' . self::PLACEHOLDER . '(\d+)x/';
                $source = preg_replace_callback( $pattern, function ( $match ) use ( &$comments ) {
                        return $comments[ $match[1] ];
                }, $source );
@@ -474,7 +479,12 @@ class CSSMin {
 
                // Pass thru fully-qualified and protocol-relative URLs and data URIs, as well as local URLs if
                // we can't expand them.
-               if ( self::isRemoteUrl( $url ) || self::isLocalUrl( $url ) ) {
+               // Also skips the rare `behavior` property specifying application's default behavior
+               if (
+                       self::isRemoteUrl( $url ) ||
+                       self::isLocalUrl( $url ) ||
+                       substr( $url, 0, 9 ) === '#default#'
+               ) {
                        return $url;
                }
 
index e8b0e6a..b22f06d 100644 (file)
@@ -549,7 +549,7 @@ class IP {
         */
        private static function parseCIDR6( $range ) {
                # Explode into <expanded IP,range>
-               $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
+               $parts = explode( '/', self::sanitizeIP( $range ), 2 );
                if ( count( $parts ) != 2 ) {
                        return [ false, false ];
                }
@@ -590,7 +590,7 @@ class IP {
         */
        private static function parseRange6( $range ) {
                # Expand any IPv6 IP
-               $range = IP::sanitizeIP( $range );
+               $range = self::sanitizeIP( $range );
                // CIDR notation...
                if ( strpos( $range, '/' ) !== false ) {
                        list( $network, $bits ) = self::parseCIDR6( $range );
@@ -732,8 +732,8 @@ class IP {
        public static function getSubnet( $ip ) {
                $matches = [];
                $subnet = false;
-               if ( IP::isIPv6( $ip ) ) {
-                       $parts = IP::parseRange( "$ip/64" );
+               if ( self::isIPv6( $ip ) ) {
+                       $parts = self::parseRange( "$ip/64" );
                        $subnet = $parts[0];
                } elseif ( preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
                        // IPv4
index 6b4281f..01adeab 100644 (file)
@@ -1,4 +1,24 @@
 <?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
+ * @author Ori Livneh
+ */
+
 /**
  * APC-backed and APCu-backed function memoization
  *
  * MemoizedCallable::call( 'range', array( 5, 8 ) );  // same
  * @endcode
  *
- * 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
- * @author Ori Livneh
  * @since 1.27
  */
 class MemoizedCallable {
index cffb5a3..9638706 100644 (file)
@@ -276,7 +276,7 @@ class StringUtils {
 
                // Replace instances of the separator inside HTML-like tags with the placeholder
                $replacer = new DoubleReplacer( $separator, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+               $cleaned = self::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
 
                // Explode, then put the replaced separators back in
                $items = explode( $separator, $cleaned );
@@ -303,7 +303,7 @@ class StringUtils {
 
                // Replace instances of the separator inside HTML-like tags with the placeholder
                $replacer = new DoubleReplacer( $search, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+               $cleaned = self::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
 
                // Explode, then put the replaced separators back in
                $cleaned = str_replace( $search, $replace, $cleaned );
index 9bfcee7..e41c3a2 100644 (file)
@@ -42,8 +42,6 @@ class APCBagOStuff extends BagOStuff {
        const KEY_SUFFIX = ':2';
 
        /**
-        * Constructor
-        *
         * Available parameters are:
         *   - nativeSerialize:     If true, pass objects to apc_store(), and trust it
         *                          to serialize them correctly. If false, serialize
index 6e6a3ad..a26e560 100644 (file)
@@ -28,8 +28,6 @@
  */
 class APCUBagOStuff extends APCBagOStuff {
        /**
-        * Constructor
-        *
         * Available parameters are:
         *   - nativeSerialize:     If true, pass objects to apcu_store(), and trust it
         *                          to serialize them correctly. If false, serialize
index c568e7b..e3e66d5 100644 (file)
@@ -29,8 +29,6 @@
 class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
        /**
-        * Constructor
-        *
         * Available parameters are:
         *   - servers:             The list of IP:port combinations holding the memcached servers.
         *   - persistent:          Whether to use a persistent connection
index 687c67c..d94578d 100644 (file)
@@ -181,6 +181,12 @@ class MultiWriteBagOStuff extends BagOStuff {
                $ret = true;
                $args = array_slice( func_get_args(), 3 );
 
+               if ( $count > 1 && $asyncWrites ) {
+                       // Deep-clone $args to prevent misbehavior when something writes an
+                       // object to the BagOStuff then modifies it afterwards, e.g. T168040.
+                       $args = unserialize( serialize( $args ) );
+               }
+
                foreach ( $this->caches as $i => $cache ) {
                        if ( $i >= $count ) {
                                break; // ignore the lower tiers
index acb4dce..fc50961 100644 (file)
@@ -233,12 +233,13 @@ interface ILoadBalancer {
 
        /**
         * Open a connection to the server given by the specified index
-        * Index must be an actual index into the array.
-        * If the server is already open, returns it.
+        *
+        * The index must be an actual index into the array. If a connection to the server is
+        * already open and not considered an "in use" foreign connection, this simply returns it.
         *
         * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
         *
-        * @param int $i Server index or DB_MASTER/DB_REPLICA
+        * @param int $i Server index (does not support DB_MASTER/DB_REPLICA)
         * @param string|bool $domain Domain ID, or false for the current domain
         * @return Database|bool Returns false on errors
         * @throws DBAccessError
index 0b70010..72217da 100644 (file)
@@ -41,7 +41,7 @@ use Exception;
 class LoadBalancer implements ILoadBalancer {
        /** @var array[] Map of (server index => server config array) */
        private $mServers;
-       /** @var Database[][][] Map of local/foreignUsed/foreignFree => server index => IDatabase array */
+       /** @var Database[][][] Map of local/foreignUsed/foreignFree => server index => IDatabase[] */
        private $mConns;
        /** @var float[] Map of (server index => weight) */
        private $mLoads;
@@ -126,6 +126,10 @@ class LoadBalancer implements ILoadBalancer {
        /** @var integer Seconds to cache master server read-only status */
        const TTL_CACHE_READONLY = 5;
 
+       const KEY_LOCAL = 'local';
+       const KEY_FOREIGN_FREE = 'foreignFree';
+       const KEY_FOREIGN_INUSE = 'foreignInUse';
+
        public function __construct( array $params ) {
                if ( !isset( $params['servers'] ) ) {
                        throw new InvalidArgumentException( __CLASS__ . ': missing servers parameter' );
@@ -148,9 +152,9 @@ class LoadBalancer implements ILoadBalancer {
 
                $this->mReadIndex = -1;
                $this->mConns = [
-                       'local'       => [],
-                       'foreignUsed' => [],
-                       'foreignFree' => []
+                       self::KEY_LOCAL => [],
+                       self::KEY_FOREIGN_INUSE => [],
+                       self::KEY_FOREIGN_FREE => []
                ];
                $this->mLoads = [];
                $this->mWaitForPos = false;
@@ -711,19 +715,19 @@ class LoadBalancer implements ILoadBalancer {
                }
 
                $domain = $conn->getDomainID();
-               if ( !isset( $this->mConns['foreignUsed'][$serverIndex][$domain] ) ) {
+               if ( !isset( $this->mConns[self::KEY_FOREIGN_INUSE][$serverIndex][$domain] ) ) {
                        throw new InvalidArgumentException( __METHOD__ .
                                ": connection $serverIndex/$domain not found; it may have already been freed." );
-               } elseif ( $this->mConns['foreignUsed'][$serverIndex][$domain] !== $conn ) {
+               } elseif ( $this->mConns[self::KEY_FOREIGN_INUSE][$serverIndex][$domain] !== $conn ) {
                        throw new InvalidArgumentException( __METHOD__ .
                                ": connection $serverIndex/$domain mismatched; it may have already been freed." );
                }
                $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
                if ( $refCount <= 0 ) {
-                       $this->mConns['foreignFree'][$serverIndex][$domain] = $conn;
-                       unset( $this->mConns['foreignUsed'][$serverIndex][$domain] );
-                       if ( !$this->mConns['foreignUsed'][$serverIndex] ) {
-                               unset( $this->mConns[ 'foreignUsed' ][$serverIndex] ); // clean up
+                       $this->mConns[self::KEY_FOREIGN_FREE][$serverIndex][$domain] = $conn;
+                       unset( $this->mConns[self::KEY_FOREIGN_INUSE][$serverIndex][$domain] );
+                       if ( !$this->mConns[self::KEY_FOREIGN_INUSE][$serverIndex] ) {
+                               unset( $this->mConns[ self::KEY_FOREIGN_INUSE ][$serverIndex] ); // clean up
                        }
                        $this->connLogger->debug( __METHOD__ . ": freed connection $serverIndex/$domain" );
                } else {
@@ -772,8 +776,8 @@ class LoadBalancer implements ILoadBalancer {
 
                if ( $domain !== false ) {
                        $conn = $this->openForeignConnection( $i, $domain );
-               } elseif ( isset( $this->mConns['local'][$i][0] ) ) {
-                       $conn = $this->mConns['local'][$i][0];
+               } elseif ( isset( $this->mConns[self::KEY_LOCAL][$i][0] ) ) {
+                       $conn = $this->mConns[self::KEY_LOCAL][$i][0];
                } else {
                        if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
                                throw new InvalidArgumentException( "No server with index '$i'." );
@@ -785,7 +789,7 @@ class LoadBalancer implements ILoadBalancer {
                        $serverName = $this->getServerName( $i );
                        if ( $conn->isOpen() ) {
                                $this->connLogger->debug( "Connected to database $i at '$serverName'." );
-                               $this->mConns['local'][$i][0] = $conn;
+                               $this->mConns[self::KEY_LOCAL][$i][0] = $conn;
                        } else {
                                $this->connLogger->warning( "Failed to connect to database $i at '$serverName'." );
                                $this->errorConnection = $conn;
@@ -830,20 +834,20 @@ class LoadBalancer implements ILoadBalancer {
                $dbName = $domainInstance->getDatabase();
                $prefix = $domainInstance->getTablePrefix();
 
-               if ( isset( $this->mConns['foreignUsed'][$i][$domain] ) ) {
-                       // Reuse an already-used connection
-                       $conn = $this->mConns['foreignUsed'][$i][$domain];
+               if ( isset( $this->mConns[self::KEY_FOREIGN_INUSE][$i][$domain] ) ) {
+                       // Reuse an in-use connection for the same domain that is not in-use
+                       $conn = $this->mConns[self::KEY_FOREIGN_INUSE][$i][$domain];
                        $this->connLogger->debug( __METHOD__ . ": reusing connection $i/$domain" );
-               } elseif ( isset( $this->mConns['foreignFree'][$i][$domain] ) ) {
-                       // Reuse a free connection for the same domain
-                       $conn = $this->mConns['foreignFree'][$i][$domain];
-                       unset( $this->mConns['foreignFree'][$i][$domain] );
-                       $this->mConns['foreignUsed'][$i][$domain] = $conn;
+               } elseif ( isset( $this->mConns[self::KEY_FOREIGN_FREE][$i][$domain] ) ) {
+                       // Reuse a free connection for the same domain that is not in-use
+                       $conn = $this->mConns[self::KEY_FOREIGN_FREE][$i][$domain];
+                       unset( $this->mConns[self::KEY_FOREIGN_FREE][$i][$domain] );
+                       $this->mConns[self::KEY_FOREIGN_INUSE][$i][$domain] = $conn;
                        $this->connLogger->debug( __METHOD__ . ": reusing free connection $i/$domain" );
-               } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
+               } elseif ( !empty( $this->mConns[self::KEY_FOREIGN_FREE][$i] ) ) {
                        // Reuse a connection from another domain
-                       $conn = reset( $this->mConns['foreignFree'][$i] );
-                       $oldDomain = key( $this->mConns['foreignFree'][$i] );
+                       $conn = reset( $this->mConns[self::KEY_FOREIGN_FREE][$i] );
+                       $oldDomain = key( $this->mConns[self::KEY_FOREIGN_FREE][$i] );
                        // The empty string as a DB name means "don't care".
                        // DatabaseMysqlBase::open() already handle this on connection.
                        if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
@@ -853,8 +857,8 @@ class LoadBalancer implements ILoadBalancer {
                                $conn = false;
                        } else {
                                $conn->tablePrefix( $prefix );
-                               unset( $this->mConns['foreignFree'][$i][$oldDomain] );
-                               $this->mConns['foreignUsed'][$i][$domain] = $conn;
+                               unset( $this->mConns[self::KEY_FOREIGN_FREE][$i][$oldDomain] );
+                               $this->mConns[self::KEY_FOREIGN_INUSE][$i][$domain] = $conn;
                                $this->connLogger->debug( __METHOD__ .
                                        ": reusing free connection from $oldDomain for $domain" );
                        }
@@ -874,7 +878,7 @@ class LoadBalancer implements ILoadBalancer {
                                $conn = false;
                        } else {
                                $conn->tablePrefix( $prefix );
-                               $this->mConns['foreignUsed'][$i][$domain] = $conn;
+                               $this->mConns[self::KEY_FOREIGN_INUSE][$i][$domain] = $conn;
                                $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
                        }
                }
@@ -1085,9 +1089,9 @@ class LoadBalancer implements ILoadBalancer {
                } );
 
                $this->mConns = [
-                       'local' => [],
-                       'foreignFree' => [],
-                       'foreignUsed' => [],
+                       self::KEY_LOCAL => [],
+                       self::KEY_FOREIGN_FREE => [],
+                       self::KEY_FOREIGN_INUSE => [],
                ];
                $this->connsOpened = 0;
        }
@@ -1621,13 +1625,19 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function setDomainPrefix( $prefix ) {
-               if ( $this->mConns['foreignUsed'] ) {
-                       // Do not switch connections to explicit foreign domains unless marked as free
-                       $domains = [];
-                       foreach ( $this->mConns['foreignUsed'] as $i => $connsByDomain ) {
-                               $domains = array_merge( $domains, array_keys( $connsByDomain ) );
+               // Find connections to explicit foreign domains still marked as in-use...
+               $domainsInUse = [];
+               $this->forEachOpenConnection( function ( IDatabase $conn ) use ( &$domainsInUse ) {
+                       // Once reuseConnection() is called on a handle, its reference count goes from 1 to 0.
+                       // Until then, it is still in use by the caller (explicitly or via DBConnRef scope).
+                       if ( $conn->getLBInfo( 'foreignPoolRefCount' ) > 0 ) {
+                               $domainsInUse[] = $conn->getDomainID();
                        }
-                       $domains = implode( ', ', $domains );
+               } );
+
+               // Do not switch connections to explicit foreign domains unless marked as safe
+               if ( $domainsInUse ) {
+                       $domains = implode( ', ', $domainsInUse );
                        throw new DBUnexpectedError( null,
                                "Foreign domain connections are still in use ($domains)." );
                }
index f2b1670..ec97b50 100644 (file)
@@ -72,8 +72,6 @@ class LogPage {
        private $target;
 
        /**
-        * Constructor
-        *
         * @param string $type One of '', 'block', 'protect', 'rights', 'delete',
         *   'upload', 'move'
         * @param bool $rc Whether to update recent changes as well as the logging table
@@ -207,7 +205,7 @@ class LogPage {
         * @return bool
         */
        public static function isLogType( $type ) {
-               return in_array( $type, LogPage::validTypes() );
+               return in_array( $type, self::validTypes() );
        }
 
        /**
@@ -350,7 +348,7 @@ class LogPage {
                $this->action = $action;
                $this->target = $target;
                $this->comment = $comment;
-               $this->params = LogPage::makeParamBlob( $params );
+               $this->params = self::makeParamBlob( $params );
 
                if ( $doer === null ) {
                        global $wgUser;
index 11dce31..f79fcfa 100644 (file)
@@ -49,8 +49,6 @@ class LogPager extends ReverseChronologicalPager {
        public $mLogEventsList;
 
        /**
-        * Constructor
-        *
         * @param LogEventsList $list
         * @param string|array $types Log types to show
         * @param string $performer The user who made the log entries
index 3858f27..1f8489f 100644 (file)
@@ -175,18 +175,18 @@ class UserMailer {
                                // first send to non-split address list, then to split addresses one by one
                                $status = Status::newGood();
                                if ( $to ) {
-                                       $status->merge( UserMailer::sendInternal(
+                                       $status->merge( self::sendInternal(
                                                $to, $from, $subject, $body, $options ) );
                                }
                                foreach ( $splitTo as $newTo ) {
-                                       $status->merge( UserMailer::sendInternal(
+                                       $status->merge( self::sendInternal(
                                                [ $newTo ], $from, $subject, $body, $options ) );
                                }
                                return $status;
                        }
                }
 
-               return UserMailer::sendInternal( $to, $from, $subject, $body, $options );
+               return self::sendInternal( $to, $from, $subject, $body, $options );
        }
 
        /**
index 57b5b36..d25111c 100644 (file)
@@ -40,8 +40,6 @@ class DjVuImage {
        const DJVUTXT_MEMORY_LIMIT = 300000;
 
        /**
-        * Constructor
-        *
         * @param string $filename The DjVu file name.
         */
        function __construct( $filename ) {
index 9bfbc96..cd457f0 100644 (file)
@@ -96,8 +96,6 @@ class Exif {
        private $byteOrder;
 
        /**
-        * Constructor
-        *
         * @param string $file Filename.
         * @param string $byteOrder Type of byte ordering either 'BE' (Big Endian)
         *   or 'LE' (Little Endian). Default ''.
@@ -120,162 +118,162 @@ class Exif {
                        # TIFF Rev. 6.0 Attribute Information (p22)
                        'IFD0' => [
                                # Tags relating to image structure
-                               'ImageWidth' => Exif::SHORT_OR_LONG, # Image width
-                               'ImageLength' => Exif::SHORT_OR_LONG, # Image height
-                               'BitsPerSample' => [ Exif::SHORT, 3 ], # Number of bits per component
+                               'ImageWidth' => self::SHORT_OR_LONG, # Image width
+                               'ImageLength' => self::SHORT_OR_LONG, # Image height
+                               'BitsPerSample' => [ self::SHORT, 3 ], # Number of bits per component
                                # "When a primary image is JPEG compressed, this designation is not"
                                # "necessary and is omitted." (p23)
-                               'Compression' => Exif::SHORT, # Compression scheme #p23
-                               'PhotometricInterpretation' => Exif::SHORT, # Pixel composition #p23
-                               'Orientation' => Exif::SHORT, # Orientation of image #p24
-                               'SamplesPerPixel' => Exif::SHORT, # Number of components
-                               'PlanarConfiguration' => Exif::SHORT, # Image data arrangement #p24
-                               'YCbCrSubSampling' => [ Exif::SHORT, 2 ], # Subsampling ratio of Y to C #p24
-                               'YCbCrPositioning' => Exif::SHORT, # Y and C positioning #p24-25
-                               'XResolution' => Exif::RATIONAL, # Image resolution in width direction
-                               'YResolution' => Exif::RATIONAL, # Image resolution in height direction
-                               'ResolutionUnit' => Exif::SHORT, # Unit of X and Y resolution #(p26)
+                               'Compression' => self::SHORT, # Compression scheme #p23
+                               'PhotometricInterpretation' => self::SHORT, # Pixel composition #p23
+                               'Orientation' => self::SHORT, # Orientation of image #p24
+                               'SamplesPerPixel' => self::SHORT, # Number of components
+                               'PlanarConfiguration' => self::SHORT, # Image data arrangement #p24
+                               'YCbCrSubSampling' => [ self::SHORT, 2 ], # Subsampling ratio of Y to C #p24
+                               'YCbCrPositioning' => self::SHORT, # Y and C positioning #p24-25
+                               'XResolution' => self::RATIONAL, # Image resolution in width direction
+                               'YResolution' => self::RATIONAL, # Image resolution in height direction
+                               'ResolutionUnit' => self::SHORT, # Unit of X and Y resolution #(p26)
 
                                # Tags relating to recording offset
-                               'StripOffsets' => Exif::SHORT_OR_LONG, # Image data location
-                               'RowsPerStrip' => Exif::SHORT_OR_LONG, # Number of rows per strip
-                               'StripByteCounts' => Exif::SHORT_OR_LONG, # Bytes per compressed strip
-                               'JPEGInterchangeFormat' => Exif::SHORT_OR_LONG, # Offset to JPEG SOI
-                               'JPEGInterchangeFormatLength' => Exif::SHORT_OR_LONG, # Bytes of JPEG data
+                               'StripOffsets' => self::SHORT_OR_LONG, # Image data location
+                               'RowsPerStrip' => self::SHORT_OR_LONG, # Number of rows per strip
+                               'StripByteCounts' => self::SHORT_OR_LONG, # Bytes per compressed strip
+                               'JPEGInterchangeFormat' => self::SHORT_OR_LONG, # Offset to JPEG SOI
+                               'JPEGInterchangeFormatLength' => self::SHORT_OR_LONG, # Bytes of JPEG data
 
                                # Tags relating to image data characteristics
-                               'TransferFunction' => Exif::IGNORE, # Transfer function
-                               'WhitePoint' => [ Exif::RATIONAL, 2 ], # White point chromaticity
-                               'PrimaryChromaticities' => [ Exif::RATIONAL, 6 ], # Chromaticities of primarities
+                               'TransferFunction' => self::IGNORE, # Transfer function
+                               'WhitePoint' => [ self::RATIONAL, 2 ], # White point chromaticity
+                               'PrimaryChromaticities' => [ self::RATIONAL, 6 ], # Chromaticities of primarities
                                # Color space transformation matrix coefficients #p27
-                               'YCbCrCoefficients' => [ Exif::RATIONAL, 3 ],
-                               'ReferenceBlackWhite' => [ Exif::RATIONAL, 6 ], # Pair of black and white reference values
+                               'YCbCrCoefficients' => [ self::RATIONAL, 3 ],
+                               'ReferenceBlackWhite' => [ self::RATIONAL, 6 ], # Pair of black and white reference values
 
                                # Other tags
-                               'DateTime' => Exif::ASCII, # File change date and time
-                               'ImageDescription' => Exif::ASCII, # Image title
-                               'Make' => Exif::ASCII, # Image input equipment manufacturer
-                               'Model' => Exif::ASCII, # Image input equipment model
-                               'Software' => Exif::ASCII, # Software used
-                               'Artist' => Exif::ASCII, # Person who created the image
-                               'Copyright' => Exif::ASCII, # Copyright holder
+                               'DateTime' => self::ASCII, # File change date and time
+                               'ImageDescription' => self::ASCII, # Image title
+                               'Make' => self::ASCII, # Image input equipment manufacturer
+                               'Model' => self::ASCII, # Image input equipment model
+                               'Software' => self::ASCII, # Software used
+                               'Artist' => self::ASCII, # Person who created the image
+                               'Copyright' => self::ASCII, # Copyright holder
                        ],
 
                        # Exif IFD Attribute Information (p30-31)
                        'EXIF' => [
                                # @todo NOTE: Nonexistence of this field is taken to mean nonconformance
                                # to the Exif 2.1 AND 2.2 standards
-                               'ExifVersion' => Exif::UNDEFINED, # Exif version
-                               'FlashPixVersion' => Exif::UNDEFINED, # Supported Flashpix version #p32
+                               'ExifVersion' => self::UNDEFINED, # Exif version
+                               'FlashPixVersion' => self::UNDEFINED, # Supported Flashpix version #p32
 
                                # Tags relating to Image Data Characteristics
-                               'ColorSpace' => Exif::SHORT, # Color space information #p32
+                               'ColorSpace' => self::SHORT, # Color space information #p32
 
                                # Tags relating to image configuration
-                               'ComponentsConfiguration' => Exif::UNDEFINED, # Meaning of each component #p33
-                               'CompressedBitsPerPixel' => Exif::RATIONAL, # Image compression mode
-                               'PixelYDimension' => Exif::SHORT_OR_LONG, # Valid image height
-                               'PixelXDimension' => Exif::SHORT_OR_LONG, # Valid image width
+                               'ComponentsConfiguration' => self::UNDEFINED, # Meaning of each component #p33
+                               'CompressedBitsPerPixel' => self::RATIONAL, # Image compression mode
+                               'PixelYDimension' => self::SHORT_OR_LONG, # Valid image height
+                               'PixelXDimension' => self::SHORT_OR_LONG, # Valid image width
 
                                # Tags relating to related user information
-                               'MakerNote' => Exif::IGNORE, # Manufacturer notes
-                               'UserComment' => Exif::UNDEFINED, # User comments #p34
+                               'MakerNote' => self::IGNORE, # Manufacturer notes
+                               'UserComment' => self::UNDEFINED, # User comments #p34
 
                                # Tags relating to related file information
-                               'RelatedSoundFile' => Exif::ASCII, # Related audio file
+                               'RelatedSoundFile' => self::ASCII, # Related audio file
 
                                # Tags relating to date and time
-                               'DateTimeOriginal' => Exif::ASCII, # Date and time of original data generation #p36
-                               'DateTimeDigitized' => Exif::ASCII, # Date and time of original data generation
-                               'SubSecTime' => Exif::ASCII, # DateTime subseconds
-                               'SubSecTimeOriginal' => Exif::ASCII, # DateTimeOriginal subseconds
-                               'SubSecTimeDigitized' => Exif::ASCII, # DateTimeDigitized subseconds
+                               'DateTimeOriginal' => self::ASCII, # Date and time of original data generation #p36
+                               'DateTimeDigitized' => self::ASCII, # Date and time of original data generation
+                               'SubSecTime' => self::ASCII, # DateTime subseconds
+                               'SubSecTimeOriginal' => self::ASCII, # DateTimeOriginal subseconds
+                               'SubSecTimeDigitized' => self::ASCII, # DateTimeDigitized subseconds
 
                                # Tags relating to picture-taking conditions (p31)
-                               'ExposureTime' => Exif::RATIONAL, # Exposure time
-                               'FNumber' => Exif::RATIONAL, # F Number
-                               'ExposureProgram' => Exif::SHORT, # Exposure Program #p38
-                               'SpectralSensitivity' => Exif::ASCII, # Spectral sensitivity
-                               'ISOSpeedRatings' => Exif::SHORT, # ISO speed rating
-                               'OECF' => Exif::IGNORE,
+                               'ExposureTime' => self::RATIONAL, # Exposure time
+                               'FNumber' => self::RATIONAL, # F Number
+                               'ExposureProgram' => self::SHORT, # Exposure Program #p38
+                               'SpectralSensitivity' => self::ASCII, # Spectral sensitivity
+                               'ISOSpeedRatings' => self::SHORT, # ISO speed rating
+                               'OECF' => self::IGNORE,
                                # Optoelectronic conversion factor. Note: We don't have support for this atm.
-                               'ShutterSpeedValue' => Exif::SRATIONAL, # Shutter speed
-                               'ApertureValue' => Exif::RATIONAL, # Aperture
-                               'BrightnessValue' => Exif::SRATIONAL, # Brightness
-                               'ExposureBiasValue' => Exif::SRATIONAL, # Exposure bias
-                               'MaxApertureValue' => Exif::RATIONAL, # Maximum land aperture
-                               'SubjectDistance' => Exif::RATIONAL, # Subject distance
-                               'MeteringMode' => Exif::SHORT, # Metering mode #p40
-                               'LightSource' => Exif::SHORT, # Light source #p40-41
-                               'Flash' => Exif::SHORT, # Flash #p41-42
-                               'FocalLength' => Exif::RATIONAL, # Lens focal length
-                               'SubjectArea' => [ Exif::SHORT, 4 ], # Subject area
-                               'FlashEnergy' => Exif::RATIONAL, # Flash energy
-                               'SpatialFrequencyResponse' => Exif::IGNORE, # Spatial frequency response. Not supported atm.
-                               'FocalPlaneXResolution' => Exif::RATIONAL, # Focal plane X resolution
-                               'FocalPlaneYResolution' => Exif::RATIONAL, # Focal plane Y resolution
-                               'FocalPlaneResolutionUnit' => Exif::SHORT, # Focal plane resolution unit #p46
-                               'SubjectLocation' => [ Exif::SHORT, 2 ], # Subject location
-                               'ExposureIndex' => Exif::RATIONAL, # Exposure index
-                               'SensingMethod' => Exif::SHORT, # Sensing method #p46
-                               'FileSource' => Exif::UNDEFINED, # File source #p47
-                               'SceneType' => Exif::UNDEFINED, # Scene type #p47
-                               'CFAPattern' => Exif::IGNORE, # CFA pattern. not supported atm.
-                               'CustomRendered' => Exif::SHORT, # Custom image processing #p48
-                               'ExposureMode' => Exif::SHORT, # Exposure mode #p48
-                               'WhiteBalance' => Exif::SHORT, # White Balance #p49
-                               'DigitalZoomRatio' => Exif::RATIONAL, # Digital zoom ration
-                               'FocalLengthIn35mmFilm' => Exif::SHORT, # Focal length in 35 mm film
-                               'SceneCaptureType' => Exif::SHORT, # Scene capture type #p49
-                               'GainControl' => Exif::SHORT, # Scene control #p49-50
-                               'Contrast' => Exif::SHORT, # Contrast #p50
-                               'Saturation' => Exif::SHORT, # Saturation #p50
-                               'Sharpness' => Exif::SHORT, # Sharpness #p50
-                               'DeviceSettingDescription' => Exif::IGNORE,
+                               'ShutterSpeedValue' => self::SRATIONAL, # Shutter speed
+                               'ApertureValue' => self::RATIONAL, # Aperture
+                               'BrightnessValue' => self::SRATIONAL, # Brightness
+                               'ExposureBiasValue' => self::SRATIONAL, # Exposure bias
+                               'MaxApertureValue' => self::RATIONAL, # Maximum land aperture
+                               'SubjectDistance' => self::RATIONAL, # Subject distance
+                               'MeteringMode' => self::SHORT, # Metering mode #p40
+                               'LightSource' => self::SHORT, # Light source #p40-41
+                               'Flash' => self::SHORT, # Flash #p41-42
+                               'FocalLength' => self::RATIONAL, # Lens focal length
+                               'SubjectArea' => [ self::SHORT, 4 ], # Subject area
+                               'FlashEnergy' => self::RATIONAL, # Flash energy
+                               'SpatialFrequencyResponse' => self::IGNORE, # Spatial frequency response. Not supported atm.
+                               'FocalPlaneXResolution' => self::RATIONAL, # Focal plane X resolution
+                               'FocalPlaneYResolution' => self::RATIONAL, # Focal plane Y resolution
+                               'FocalPlaneResolutionUnit' => self::SHORT, # Focal plane resolution unit #p46
+                               'SubjectLocation' => [ self::SHORT, 2 ], # Subject location
+                               'ExposureIndex' => self::RATIONAL, # Exposure index
+                               'SensingMethod' => self::SHORT, # Sensing method #p46
+                               'FileSource' => self::UNDEFINED, # File source #p47
+                               'SceneType' => self::UNDEFINED, # Scene type #p47
+                               'CFAPattern' => self::IGNORE, # CFA pattern. not supported atm.
+                               'CustomRendered' => self::SHORT, # Custom image processing #p48
+                               'ExposureMode' => self::SHORT, # Exposure mode #p48
+                               'WhiteBalance' => self::SHORT, # White Balance #p49
+                               'DigitalZoomRatio' => self::RATIONAL, # Digital zoom ration
+                               'FocalLengthIn35mmFilm' => self::SHORT, # Focal length in 35 mm film
+                               'SceneCaptureType' => self::SHORT, # Scene capture type #p49
+                               'GainControl' => self::SHORT, # Scene control #p49-50
+                               'Contrast' => self::SHORT, # Contrast #p50
+                               'Saturation' => self::SHORT, # Saturation #p50
+                               'Sharpness' => self::SHORT, # Sharpness #p50
+                               'DeviceSettingDescription' => self::IGNORE,
                                # Device settings description. This could maybe be supported. Need to find an
                                # example file that uses this to see if it has stuff of interest in it.
-                               'SubjectDistanceRange' => Exif::SHORT, # Subject distance range #p51
+                               'SubjectDistanceRange' => self::SHORT, # Subject distance range #p51
 
-                               'ImageUniqueID' => Exif::ASCII, # Unique image ID
+                               'ImageUniqueID' => self::ASCII, # Unique image ID
                        ],
 
                        # GPS Attribute Information (p52)
                        'GPS' => [
-                               'GPSVersion' => Exif::UNDEFINED,
+                               'GPSVersion' => self::UNDEFINED,
                                # Should be an array of 4 Exif::BYTE's. However php treats it as an undefined
                                # Note exif standard calls this GPSVersionID, but php doesn't like the id suffix
-                               'GPSLatitudeRef' => Exif::ASCII, # North or South Latitude #p52-53
-                               'GPSLatitude' => [ Exif::RATIONAL, 3 ], # Latitude
-                               'GPSLongitudeRef' => Exif::ASCII, # East or West Longitude #p53
-                               'GPSLongitude' => [ Exif::RATIONAL, 3 ], # Longitude
-                               'GPSAltitudeRef' => Exif::UNDEFINED,
+                               'GPSLatitudeRef' => self::ASCII, # North or South Latitude #p52-53
+                               'GPSLatitude' => [ self::RATIONAL, 3 ], # Latitude
+                               'GPSLongitudeRef' => self::ASCII, # East or West Longitude #p53
+                               'GPSLongitude' => [ self::RATIONAL, 3 ], # Longitude
+                               'GPSAltitudeRef' => self::UNDEFINED,
                                # Altitude reference. Note, the exif standard says this should be an EXIF::Byte,
                                # but php seems to disagree.
-                               'GPSAltitude' => Exif::RATIONAL, # Altitude
-                               'GPSTimeStamp' => [ Exif::RATIONAL, 3 ], # GPS time (atomic clock)
-                               'GPSSatellites' => Exif::ASCII, # Satellites used for measurement
-                               'GPSStatus' => Exif::ASCII, # Receiver status #p54
-                               'GPSMeasureMode' => Exif::ASCII, # Measurement mode #p54-55
-                               'GPSDOP' => Exif::RATIONAL, # Measurement precision
-                               'GPSSpeedRef' => Exif::ASCII, # Speed unit #p55
-                               'GPSSpeed' => Exif::RATIONAL, # Speed of GPS receiver
-                               'GPSTrackRef' => Exif::ASCII, # Reference for direction of movement #p55
-                               'GPSTrack' => Exif::RATIONAL, # Direction of movement
-                               'GPSImgDirectionRef' => Exif::ASCII, # Reference for direction of image #p56
-                               'GPSImgDirection' => Exif::RATIONAL, # Direction of image
-                               'GPSMapDatum' => Exif::ASCII, # Geodetic survey data used
-                               'GPSDestLatitudeRef' => Exif::ASCII, # Reference for latitude of destination #p56
-                               'GPSDestLatitude' => [ Exif::RATIONAL, 3 ], # Latitude destination
-                               'GPSDestLongitudeRef' => Exif::ASCII, # Reference for longitude of destination #p57
-                               'GPSDestLongitude' => [ Exif::RATIONAL, 3 ], # Longitude of destination
-                               'GPSDestBearingRef' => Exif::ASCII, # Reference for bearing of destination #p57
-                               'GPSDestBearing' => Exif::RATIONAL, # Bearing of destination
-                               'GPSDestDistanceRef' => Exif::ASCII, # Reference for distance to destination #p57-58
-                               'GPSDestDistance' => Exif::RATIONAL, # Distance to destination
-                               'GPSProcessingMethod' => Exif::UNDEFINED, # Name of GPS processing method
-                               'GPSAreaInformation' => Exif::UNDEFINED, # Name of GPS area
-                               'GPSDateStamp' => Exif::ASCII, # GPS date
-                               'GPSDifferential' => Exif::SHORT, # GPS differential correction
+                               'GPSAltitude' => self::RATIONAL, # Altitude
+                               'GPSTimeStamp' => [ self::RATIONAL, 3 ], # GPS time (atomic clock)
+                               'GPSSatellites' => self::ASCII, # Satellites used for measurement
+                               'GPSStatus' => self::ASCII, # Receiver status #p54
+                               'GPSMeasureMode' => self::ASCII, # Measurement mode #p54-55
+                               'GPSDOP' => self::RATIONAL, # Measurement precision
+                               'GPSSpeedRef' => self::ASCII, # Speed unit #p55
+                               'GPSSpeed' => self::RATIONAL, # Speed of GPS receiver
+                               'GPSTrackRef' => self::ASCII, # Reference for direction of movement #p55
+                               'GPSTrack' => self::RATIONAL, # Direction of movement
+                               'GPSImgDirectionRef' => self::ASCII, # Reference for direction of image #p56
+                               'GPSImgDirection' => self::RATIONAL, # Direction of image
+                               'GPSMapDatum' => self::ASCII, # Geodetic survey data used
+                               'GPSDestLatitudeRef' => self::ASCII, # Reference for latitude of destination #p56
+                               'GPSDestLatitude' => [ self::RATIONAL, 3 ], # Latitude destination
+                               'GPSDestLongitudeRef' => self::ASCII, # Reference for longitude of destination #p57
+                               'GPSDestLongitude' => [ self::RATIONAL, 3 ], # Longitude of destination
+                               'GPSDestBearingRef' => self::ASCII, # Reference for bearing of destination #p57
+                               'GPSDestBearing' => self::RATIONAL, # Bearing of destination
+                               'GPSDestDistanceRef' => self::ASCII, # Reference for distance to destination #p57-58
+                               'GPSDestDistance' => self::RATIONAL, # Distance to destination
+                               'GPSProcessingMethod' => self::UNDEFINED, # Name of GPS processing method
+                               'GPSAreaInformation' => self::UNDEFINED, # Name of GPS area
+                               'GPSDateStamp' => self::ASCII, # GPS date
+                               'GPSDifferential' => self::SHORT, # GPS differential correction
                        ],
                ];
 
@@ -761,43 +759,43 @@ class Exif {
                }
                // Does not work if not typecast
                switch ( (string)$etype ) {
-                       case (string)Exif::BYTE:
+                       case (string)self::BYTE:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isByte( $val );
-                       case (string)Exif::ASCII:
+                       case (string)self::ASCII:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isASCII( $val );
-                       case (string)Exif::SHORT:
+                       case (string)self::SHORT:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isShort( $val );
-                       case (string)Exif::LONG:
+                       case (string)self::LONG:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isLong( $val );
-                       case (string)Exif::RATIONAL:
+                       case (string)self::RATIONAL:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isRational( $val );
-                       case (string)Exif::SHORT_OR_LONG:
+                       case (string)self::SHORT_OR_LONG:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isShort( $val ) || $this->isLong( $val );
-                       case (string)Exif::UNDEFINED:
+                       case (string)self::UNDEFINED:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isUndefined( $val );
-                       case (string)Exif::SLONG:
+                       case (string)self::SLONG:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isSlong( $val );
-                       case (string)Exif::SRATIONAL:
+                       case (string)self::SRATIONAL:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isSrational( $val );
-                       case (string)Exif::IGNORE:
+                       case (string)self::IGNORE:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return false;
index 2cf4d23..9b22cbe 100644 (file)
@@ -58,8 +58,6 @@ class SVGReader {
        private $languagePrefixes = [];
 
        /**
-        * Constructor
-        *
         * Creates an SVGReader drawing from the source provided
         * @param string $source URI from which to read
         * @throws MWException|Exception
index 16328bc..e2cf2cf 100644 (file)
@@ -1594,7 +1594,7 @@ class Article implements Page {
                        [ 'delete', $this->getTitle()->getPrefixedText() ] )
                ) {
                        # Flag to hide all contents of the archived revisions
-                       $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
+                       $suppress = $request->getCheck( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
 
                        $this->doDelete( $reason, $suppress );
 
@@ -1669,28 +1669,28 @@ class Article implements Page {
                $title = $this->getTitle();
                $ctx = $this->getContext();
                $outputPage = $ctx->getOutput();
-               $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
                $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
                $outputPage->addBacklinkSubtitle( $title );
                $outputPage->setRobotPolicy( 'noindex,nofollow' );
+
                $backlinkCache = $title->getBacklinkCache();
                if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
                        $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                'deleting-backlinks-warning' );
                }
+
+               $subpageQueryLimit = 51;
+               $subpages = $title->getSubpages( $subpageQueryLimit );
+               $subpageCount = count( $subpages );
+               if ( $subpageCount > 0 ) {
+                       $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
+                               [ 'deleting-subpages-warning', Message::numParam( $subpageCount ) ] );
+               }
                $outputPage->addWikiMsg( 'confirmdeletetext' );
 
                Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
 
                $user = $this->getContext()->getUser();
-               if ( $user->isAllowed( 'suppressrevision' ) ) {
-                       $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
-                               Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
-                                       'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
-                               Html::closeElement( 'div' );
-               } else {
-                       $suppress = '';
-               }
                $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
 
                $outputPage->enableOOUI();
@@ -1761,6 +1761,21 @@ class Article implements Page {
                        );
                }
 
+               if ( $user->isAllowed( 'suppressrevision' ) ) {
+                       $fields[] = new OOUI\FieldLayout(
+                               new OOUI\CheckboxInputWidget( [
+                                       'name' => 'wpSuppress',
+                                       'inputId' => 'wpSuppress',
+                                       'tabIndex' => 4,
+                               ] ),
+                               [
+                                       'label' => $ctx->msg( 'revdelete-suppress' )->text(),
+                                       'align' => 'inline',
+                                       'infusable' => true,
+                               ]
+                       );
+               }
+
                $fields[] = new OOUI\FieldLayout(
                        new OOUI\ButtonInputWidget( [
                                'name' => 'wpConfirmB',
index 3ba2d2e..c05ba45 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 
+use MediaWiki\Edit\PreparedEdit;
 use \MediaWiki\Logger\LoggerFactory;
 use \MediaWiki\MediaWikiServices;
 use Wikimedia\Rdbms\FakeResultWrapper;
@@ -49,7 +50,7 @@ class WikiPage implements Page, IDBAccessObject {
        public $mLatest = false;             // !< Integer (false means "not loaded")
        /**@}}*/
 
-       /** @var stdClass Map of cache fields (text, parser output, ect) for a proposed/new edit */
+       /** @var PreparedEdit Map of cache fields (text, parser output, ect) for a proposed/new edit */
        public $mPreparedEdit = false;
 
        /**
@@ -781,7 +782,7 @@ class WikiPage implements Page, IDBAccessObject {
         * Determine whether a page would be suitable for being counted as an
         * article in the site_stats table based on the title & its content
         *
-        * @param object|bool $editInfo (false): object returned by prepareTextForEdit(),
+        * @param PreparedEdit|bool $editInfo (false): object returned by prepareTextForEdit(),
         *   if false, the current database state will be used
         * @return bool
         */
@@ -1606,7 +1607,7 @@ class WikiPage implements Page, IDBAccessObject {
                $meta = [
                        'bot' => ( $flags & EDIT_FORCE_BOT ),
                        'minor' => ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' ),
-                       'serialized' => $editInfo->pst,
+                       'serialized' => $pstContent->serialize( $serialFormat ),
                        'serialFormat' => $serialFormat,
                        'baseRevId' => $baseRevId,
                        'oldRevision' => $old_revision,
@@ -1960,7 +1961,9 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Prepare content which is about to be saved.
-        * Returns a stdClass with source, pst and output members
+        *
+        * Prior to 1.30, this returned a stdClass object with the same class
+        * members.
         *
         * @param Content $content
         * @param Revision|int|null $revision Revision object. For backwards compatibility, a
@@ -1969,7 +1972,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string|null $serialFormat
         * @param bool $useCache Check shared prepared edit cache
         *
-        * @return object
+        * @return PreparedEdit
         *
         * @since 1.21
         */
@@ -2019,7 +2022,7 @@ class WikiPage implements Page, IDBAccessObject {
                $popts = ParserOptions::newFromUserAndLang( $user, $wgContLang );
                Hooks::run( 'ArticlePrepareTextForEdit', [ $this, $popts ] );
 
-               $edit = (object)[];
+               $edit = new PreparedEdit();
                if ( $cachedEdit ) {
                        $edit->timestamp = $cachedEdit->timestamp;
                } else {
@@ -2970,7 +2973,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Clear caches
-               WikiPage::onArticleDelete( $this->mTitle );
+               self::onArticleDelete( $this->mTitle );
                ResourceLoaderWikiModule::invalidateModuleCache(
                        $this->mTitle, $revision, null, wfWikiID()
                );
index 0b867ef..6620c47 100644 (file)
@@ -737,6 +737,6 @@ abstract class IndexPager extends ContextSource implements Pager {
         * @return bool
         */
        protected function getDefaultDirections() {
-               return IndexPager::DIR_ASCENDING;
+               return self::DIR_ASCENDING;
        }
 }
index 5da2546..b035b02 100644 (file)
@@ -227,7 +227,7 @@ class Parser {
         * @var string Deprecated accessor for the strip marker prefix.
         * @deprecated since 1.26; use Parser::MARKER_PREFIX instead.
         */
-       public $mUniqPrefix = Parser::MARKER_PREFIX;
+       public $mUniqPrefix = self::MARKER_PREFIX;
 
        /**
         * @var array Array with the language name of each language link (i.e. the
@@ -1295,7 +1295,7 @@ class Parser {
                        if ( !$frame->depth ) {
                                $flag = 0;
                        } else {
-                               $flag = Parser::PTD_FOR_INCLUSION;
+                               $flag = self::PTD_FOR_INCLUSION;
                        }
                        $dom = $this->preprocessToDom( $text, $flag );
                        $text = $frame->expand( $dom );
index 96a4368..73a9927 100644 (file)
@@ -927,7 +927,6 @@ class ParserOptions {
        }
 
        /**
-        * Constructor
         * @warning For interaction with the parser cache, use
         *  WikiPage::makeParserOptions(), ContentHandler::makeParserOptions(), or
         *  ParserOptions::newCanonical() instead.
index 10ac192..cfb0c3e 100644 (file)
@@ -253,7 +253,7 @@ class ParserOutput extends CacheTime {
                $text = $this->mText;
                if ( $this->mEditSectionTokens ) {
                        $text = preg_replace_callback(
-                               ParserOutput::EDITSECTION_REGEX,
+                               self::EDITSECTION_REGEX,
                                function ( $m ) {
                                        global $wgOut, $wgLang;
                                        $editsectionPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) );
@@ -274,7 +274,7 @@ class ParserOutput extends CacheTime {
                                $text
                        );
                } else {
-                       $text = preg_replace( ParserOutput::EDITSECTION_REGEX, '', $text );
+                       $text = preg_replace( self::EDITSECTION_REGEX, '', $text );
                }
 
                // If you have an old cached version of this class - sorry, you can't disable the TOC
index ddf084e..20b0780 100644 (file)
@@ -31,7 +31,6 @@ abstract class ProfilerOutput {
        protected $params = [];
 
        /**
-        * Constructor
         * @param Profiler $collector The actual profiler
         * @param array $params Configuration array, passed down from $wgProfiler
         */
index 4c011be..f0fcd7d 100644 (file)
  * 'rc'. If the URI contains a query string, its parameters will be parsed
  * as RedisConnectionPool options.
  *
- * @example
+ * @par Example:
+ * @code
  * $wgRCFeeds['redis'] = array(
  *      'formatter' => 'JSONRCFeedFormatter',
  *      'uri'       => "redis://127.0.0.1:6379/rc.$wgDBname",
  * );
+ * @encode
  *
  * @since 1.22
  */
index 8553116..2f29200 100644 (file)
@@ -178,7 +178,7 @@ class ResourceLoader implements LoggerAwareInterface {
         * @return string Filtered data, or a comment containing an error message
         */
        public static function filter( $filter, $data, array $options = [] ) {
-               if ( strpos( $data, ResourceLoader::FILTER_NOMIN ) !== false ) {
+               if ( strpos( $data, self::FILTER_NOMIN ) !== false ) {
                        return $data;
                }
 
@@ -1079,7 +1079,7 @@ MESSAGE;
                                                                // mw.loader.implement will use globalEval if scripts is a string.
                                                                // Minify manually here, because general response minification is
                                                                // not effective due it being a string literal, not a function.
-                                                               if ( !ResourceLoader::inDebugMode() ) {
+                                                               if ( !self::inDebugMode() ) {
                                                                        $scripts = self::filter( 'minify-js', $scripts ); // T107377
                                                                }
                                                        } else {
@@ -1139,7 +1139,7 @@ MESSAGE;
                } else {
                        if ( count( $states ) ) {
                                $this->errors[] = 'Problematic modules: ' .
-                                       FormatJson::encode( $states, ResourceLoader::inDebugMode() );
+                                       FormatJson::encode( $states, self::inDebugMode() );
                        }
                }
 
@@ -1214,7 +1214,7 @@ MESSAGE;
                ];
                self::trimArray( $module );
 
-               return Xml::encodeJsCall( 'mw.loader.implement', $module, ResourceLoader::inDebugMode() );
+               return Xml::encodeJsCall( 'mw.loader.implement', $module, self::inDebugMode() );
        }
 
        /**
@@ -1228,7 +1228,7 @@ MESSAGE;
                return Xml::encodeJsCall(
                        'mw.messages.set',
                        [ (object)$messages ],
-                       ResourceLoader::inDebugMode()
+                       self::inDebugMode()
                );
        }
 
@@ -1285,13 +1285,13 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.state',
                                [ $name ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                } else {
                        return Xml::encodeJsCall(
                                'mw.loader.state',
                                [ $name, $state ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                }
        }
@@ -1317,7 +1317,7 @@ MESSAGE;
                return Xml::encodeJsCall(
                        "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )",
                        [ $name, $version, $dependencies, $group, $source ],
-                       ResourceLoader::inDebugMode()
+                       self::inDebugMode()
                );
        }
 
@@ -1409,7 +1409,7 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.register',
                                [ $name ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                } else {
                        $registration = [ $name, $version, $dependencies, $group, $source, $skip ];
@@ -1417,7 +1417,7 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.register',
                                $registration,
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                }
        }
@@ -1441,13 +1441,13 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.addSource',
                                [ $id ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                } else {
                        return Xml::encodeJsCall(
                                'mw.loader.addSource',
                                [ $id, $loadUrl ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                }
        }
@@ -1494,7 +1494,7 @@ MESSAGE;
                return Xml::encodeJsCall(
                        'mw.config.set',
                        [ $configuration ],
-                       ResourceLoader::inDebugMode()
+                       self::inDebugMode()
                );
        }
 
index 197ac51..06f9841 100644 (file)
@@ -149,9 +149,7 @@ class ResourceLoaderClientHtml {
                                continue;
                        }
 
-                       $group = $module->getGroup();
-
-                       if ( $group === 'private' ) {
+                       if ( $module->shouldEmbedModule( $this->context ) ) {
                                // Embed via mw.loader.implement per T36907.
                                $data['embed']['general'][] = $name;
                                // Avoid duplicate request from mw.loader
@@ -186,7 +184,7 @@ class ResourceLoaderClientHtml {
                                // Avoid needless request for empty module
                                $data['states'][$name] = 'ready';
                        } else {
-                               if ( $group === 'private' ) {
+                               if ( $module->shouldEmbedModule( $this->context ) ) {
                                        // Embed via style element
                                        $data['embed']['styles'][] = $name;
                                        // Avoid duplicate request from mw.loader
@@ -392,62 +390,75 @@ class ResourceLoaderClientHtml {
                foreach ( $sortedModules as $source => $groups ) {
                        foreach ( $groups as $group => $grpModules ) {
                                $context = self::makeContext( $mainContext, $group, $only, $extraQuery );
-                               $context->setModules( array_keys( $grpModules ) );
-
-                               if ( $group === 'private' ) {
-                                       // Decide whether to use style or script element
-                                       if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
-                                               $chunks[] = Html::inlineStyle(
-                                                       $rl->makeModuleResponse( $context, $grpModules )
-                                               );
-                                       } else {
-                                               $chunks[] = ResourceLoader::makeInlineScript(
-                                                       $rl->makeModuleResponse( $context, $grpModules )
-                                               );
-                                       }
-                                       continue;
-                               }
-
-                               // See if we have one or more raw modules
-                               $isRaw = false;
-                               foreach ( $grpModules as $key => $module ) {
-                                       $isRaw |= $module->isRaw();
-                               }
 
-                               // Special handling for the user group; because users might change their stuff
-                               // on-wiki like user pages, or user preferences; we need to find the highest
-                               // timestamp of these user-changeable modules so we can ensure cache misses on change
-                               // This should NOT be done for the site group (T29564) because anons get that too
-                               // and we shouldn't be putting timestamps in CDN-cached HTML
-                               if ( $group === 'user' ) {
-                                       // Must setModules() before makeVersionQuery()
-                                       $context->setVersion( $rl->makeVersionQuery( $context ) );
+                               // Separate sets of linked and embedded modules while preserving order
+                               $moduleSets = [];
+                               $idx = -1;
+                               foreach ( $grpModules as $name => $module ) {
+                                       $shouldEmbed = $module->shouldEmbedModule( $context );
+                                       if ( !$moduleSets || $moduleSets[$idx][0] !== $shouldEmbed ) {
+                                               $moduleSets[++$idx] = [ $shouldEmbed, [] ];
+                                       }
+                                       $moduleSets[$idx][1][$name] = $module;
                                }
 
-                               $url = $rl->createLoaderURL( $source, $context, $extraQuery );
-
-                               // Decide whether to use 'style' or 'script' element
-                               if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
-                                       $chunk = Html::linkedStyle( $url );
-                               } else {
-                                       if ( $context->getRaw() || $isRaw ) {
-                                               $chunk = Html::element( 'script', [
-                                                       // In SpecialJavaScriptTest, QUnit must load synchronous
-                                                       'async' => !isset( $extraQuery['sync'] ),
-                                                       'src' => $url
-                                               ] );
+                               // Link/embed each set
+                               foreach ( $moduleSets as list( $embed, $moduleSet ) ) {
+                                       $context->setModules( array_keys( $moduleSet ) );
+                                       if ( $embed ) {
+                                               // Decide whether to use style or script element
+                                               if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
+                                                       $chunks[] = Html::inlineStyle(
+                                                               $rl->makeModuleResponse( $context, $moduleSet )
+                                                       );
+                                               } else {
+                                                       $chunks[] = ResourceLoader::makeInlineScript(
+                                                               $rl->makeModuleResponse( $context, $moduleSet )
+                                                       );
+                                               }
                                        } else {
-                                               $chunk = ResourceLoader::makeInlineScript(
-                                                       Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
-                                               );
+                                               // See if we have one or more raw modules
+                                               $isRaw = false;
+                                               foreach ( $moduleSet as $key => $module ) {
+                                                       $isRaw |= $module->isRaw();
+                                               }
+
+                                               // Special handling for the user group; because users might change their stuff
+                                               // on-wiki like user pages, or user preferences; we need to find the highest
+                                               // timestamp of these user-changeable modules so we can ensure cache misses on change
+                                               // This should NOT be done for the site group (T29564) because anons get that too
+                                               // and we shouldn't be putting timestamps in CDN-cached HTML
+                                               if ( $group === 'user' ) {
+                                                       // Must setModules() before makeVersionQuery()
+                                                       $context->setVersion( $rl->makeVersionQuery( $context ) );
+                                               }
+
+                                               $url = $rl->createLoaderURL( $source, $context, $extraQuery );
+
+                                               // Decide whether to use 'style' or 'script' element
+                                               if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
+                                                       $chunk = Html::linkedStyle( $url );
+                                               } else {
+                                                       if ( $context->getRaw() || $isRaw ) {
+                                                               $chunk = Html::element( 'script', [
+                                                                       // In SpecialJavaScriptTest, QUnit must load synchronous
+                                                                       'async' => !isset( $extraQuery['sync'] ),
+                                                                       'src' => $url
+                                                               ] );
+                                                       } else {
+                                                               $chunk = ResourceLoader::makeInlineScript(
+                                                                       Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
+                                                               );
+                                                       }
+                                               }
+
+                                               if ( $group == 'noscript' ) {
+                                                       $chunks[] = Html::rawElement( 'noscript', [], $chunk );
+                                               } else {
+                                                       $chunks[] = $chunk;
+                                               }
                                        }
                                }
-
-                               if ( $group == 'noscript' ) {
-                                       $chunks[] = Html::rawElement( 'noscript', [], $chunk );
-                               } else {
-                                       $chunks[] = $chunk;
-                               }
                        }
                }
 
index 1c767fa..1608901 100644 (file)
@@ -918,6 +918,20 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                return false;
        }
 
+       /**
+        * Check whether this module should be embeded rather than linked
+        *
+        * Modules returning true here will be embedded rather than loaded by
+        * ResourceLoaderClientHtml.
+        *
+        * @since 1.30
+        * @param ResourceLoaderContext $context
+        * @return bool
+        */
+       public function shouldEmbedModule( ResourceLoaderContext $context ) {
+               return $this->getGroup() === 'private';
+       }
+
        /** @var JSParser Lazy-initialized; use self::javaScriptParser() */
        private static $jsParser;
        private static $parseCacheVersion = 1;
index 1d7a4a3..643c2c1 100644 (file)
@@ -35,7 +35,6 @@ class SearchDatabase extends SearchEngine {
        protected $db;
 
        /**
-        * Constructor
         * @param IDatabase $db The database to search from
         */
        public function __construct( IDatabase $db = null ) {
index 978db27..89d2299 100644 (file)
@@ -264,4 +264,14 @@ class SearchResultSet {
                $result->setExtensionData( $this->extraData[$id] );
                return $this->extraData[$id];
        }
+
+       /**
+        * @return int|null The offset the current page starts at. Typically
+        *  this should be null to allow the UI to decide on its own, but in
+        *  special cases like interleaved AB tests specifying explicitly is
+        *  necessary.
+        */
+       public function getOffset() {
+               return null;
+       }
 }
index a8f9d0c..40905a5 100644 (file)
@@ -95,7 +95,7 @@ abstract class Skin extends ContextSource {
        static function normalizeKey( $key ) {
                global $wgDefaultSkin, $wgFallbackSkin;
 
-               $skinNames = Skin::getSkinNames();
+               $skinNames = self::getSkinNames();
 
                // Make keys lowercase for case-insensitive matching.
                $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
index b85d272..645fbb2 100644 (file)
@@ -86,8 +86,6 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                'filters' => [
                                        [
                                                'name' => 'hideliu',
-                                               'label' => 'rcfilters-filter-registered-label',
-                                               'description' => 'rcfilters-filter-registered-description',
                                                // rcshowhideliu-show, rcshowhideliu-hide,
                                                // wlshowhideliu
                                                'showHideSuffix' => 'showhideliu',
@@ -97,16 +95,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                ) {
                                                        $conds[] = 'rc_user = 0';
                                                },
-                                               'cssClassSuffix' => 'liu',
-                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
-                                                       return $rc->getAttribute( 'rc_user' );
-                                               },
+                                               'isReplacedInStructuredUi' => true,
 
                                        ],
                                        [
                                                'name' => 'hideanons',
-                                               'label' => 'rcfilters-filter-unregistered-label',
-                                               'description' => 'rcfilters-filter-unregistered-description',
                                                // rcshowhideanons-show, rcshowhideanons-hide,
                                                // wlshowhideanons
                                                'showHideSuffix' => 'showhideanons',
@@ -116,10 +109,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                ) {
                                                        $conds[] = 'rc_user != 0';
                                                },
-                                               'cssClassSuffix' => 'anon',
-                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
-                                                       return !$rc->getAttribute( 'rc_user' );
-                                               },
+                                               'isReplacedInStructuredUi' => true,
                                        ]
                                ],
                        ],
@@ -128,9 +118,26 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                'name' => 'userExpLevel',
                                'title' => 'rcfilters-filtergroup-userExpLevel',
                                'class' => ChangesListStringOptionsFilterGroup::class,
-                               // Excludes unregistered users
-                               'isFullCoverage' => false,
+                               'isFullCoverage' => true,
                                'filters' => [
+                                       [
+                                               'name' => 'unregistered',
+                                               'label' => 'rcfilters-filter-user-experience-level-unregistered-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-unregistered-description',
+                                               'cssClassSuffix' => 'user-unregistered',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$rc->getAttribute( 'rc_user' );
+                                               }
+                                       ],
+                                       [
+                                               'name' => 'registered',
+                                               'label' => 'rcfilters-filter-user-experience-level-registered-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-registered-description',
+                                               'cssClassSuffix' => 'user-registered',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_user' );
+                                               }
+                                       ],
                                        [
                                                'name' => 'newcomer',
                                                'label' => 'rcfilters-filter-user-experience-level-newcomer-label',
@@ -632,19 +639,10 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                $this->registerFiltersFromDefinitions( [ $unstructuredGroupDefinition ] );
 
                $userExperienceLevel = $this->getFilterGroup( 'userExpLevel' );
-
-               $registration = $this->getFilterGroup( 'registration' );
-               $anons = $registration->getFilter( 'hideanons' );
-
-               // This means there is a conflict between any item in user experience level
-               // being checked and only anons being *shown* (hideliu=1&hideanons=0 in the
-               // URL, or equivalent).
-               $userExperienceLevel->conflictsWith(
-                       $anons,
-                       'rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global',
-                       'rcfilters-filtergroup-user-experience-level-conflicts-unregistered',
-                       'rcfilters-filter-unregistered-conflicts-user-experience-level'
-               );
+               $registered = $userExperienceLevel->getFilter( 'registered' );
+               $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'newcomer' ) );
+               $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'learner' ) );
+               $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'experienced' ) );
 
                $categoryFilter = $changeTypeGroup->getFilter( 'hidecategorization' );
                $logactionsFilter = $changeTypeGroup->getFilter( 'hidelog' );
@@ -1337,15 +1335,35 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                        $wgLearnerMemberSince,
                        $wgExperiencedUserMemberSince;
 
-               $LEVEL_COUNT = 3;
+               $LEVEL_COUNT = 5;
 
-               // If all levels are selected, all logged-in users are included (but no
-               // anons), so we can short-circuit.
+               // If all levels are selected, don't filter
                if ( count( $selectedExpLevels ) === $LEVEL_COUNT ) {
+                       return;
+               }
+
+               // both 'registered' and 'unregistered', experience levels, if any, are included in 'registered'
+               if (
+                       in_array( 'registered', $selectedExpLevels ) &&
+                       in_array( 'unregistered', $selectedExpLevels )
+               ) {
+                       return;
+               }
+
+               // 'registered' but not 'unregistered', experience levels, if any, are included in 'registered'
+               if (
+                       in_array( 'registered', $selectedExpLevels ) &&
+                       !in_array( 'unregistered', $selectedExpLevels )
+               ) {
                        $conds[] = 'rc_user != 0';
                        return;
                }
 
+               if ( $selectedExpLevels === [ 'unregistered' ] ) {
+                       $conds[] = 'rc_user = 0';
+                       return;
+               }
+
                $tables[] = 'user';
                $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ];
 
@@ -1373,24 +1391,39 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                        IDatabase::LIST_AND
                );
 
+               $conditions = [];
+
+               if ( in_array( 'unregistered', $selectedExpLevels ) ) {
+                       $selectedExpLevels = array_diff( $selectedExpLevels, [ 'unregistered' ] );
+                       $conditions[] = 'rc_user = 0';
+               }
+
                if ( $selectedExpLevels === [ 'newcomer' ] ) {
-                       $conds[] = "NOT ( $aboveNewcomer )";
+                       $conditions[] = "NOT ( $aboveNewcomer )";
                } elseif ( $selectedExpLevels === [ 'learner' ] ) {
-                       $conds[] = $dbr->makeList(
+                       $conditions[] = $dbr->makeList(
                                [ $aboveNewcomer, "NOT ( $aboveLearner )" ],
                                IDatabase::LIST_AND
                        );
                } elseif ( $selectedExpLevels === [ 'experienced' ] ) {
-                       $conds[] = $aboveLearner;
+                       $conditions[] = $aboveLearner;
                } elseif ( $selectedExpLevels === [ 'learner', 'newcomer' ] ) {
-                       $conds[] = "NOT ( $aboveLearner )";
+                       $conditions[] = "NOT ( $aboveLearner )";
                } elseif ( $selectedExpLevels === [ 'experienced', 'newcomer' ] ) {
-                       $conds[] = $dbr->makeList(
+                       $conditions[] = $dbr->makeList(
                                [ "NOT ( $aboveNewcomer )", $aboveLearner ],
                                IDatabase::LIST_OR
                        );
                } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) {
-                       $conds[] = $aboveNewcomer;
+                       $conditions[] = $aboveNewcomer;
+               } elseif ( $selectedExpLevels === [ 'experienced', 'learner', 'newcomer' ] ) {
+                       $conditions[] = 'rc_user != 0';
+               }
+
+               if ( count( $conditions ) > 1 ) {
+                       $conds[] = $dbr->makeList( $conditions, IDatabase::LIST_OR );
+               } elseif ( count( $conditions ) === 1 ) {
+                       $conds[] = reset( $conditions );
                }
        }
 }
index 67c14d8..4c3ca54 100644 (file)
@@ -383,7 +383,7 @@ class SpecialPage implements MessageLocalizer {
                        return true;
                } elseif ( $securityStatus === AuthManager::SEC_REAUTH ) {
                        $request = $this->getRequest();
-                       $title = SpecialPage::getTitleFor( 'Userlogin' );
+                       $title = self::getTitleFor( 'Userlogin' );
                        $query = [
                                'returnto' => $this->getFullTitle()->getPrefixedDBkey(),
                                'returntoquery' => wfArrayToCgi( array_diff_key( $request->getQueryValues(),
index e7c9423..9028787 100644 (file)
@@ -28,9 +28,6 @@
  */
 class SpecialActiveUsers extends SpecialPage {
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct( 'Activeusers' );
        }
index 4056709..9e66447 100644 (file)
@@ -33,9 +33,6 @@ class SpecialAllMessages extends SpecialPage {
         */
        protected $table;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct( 'Allmessages' );
        }
index 17f6cca..4d84e31 100644 (file)
@@ -44,8 +44,6 @@ class SpecialAllPages extends IncludableSpecialPage {
        protected $nsfromMsg = 'allpagesfrom';
 
        /**
-        * Constructor
-        *
         * @param string $name Name of the special page, as seen in links and URLs (default: 'Allpages')
         */
        function __construct( $name = 'Allpages' ) {
index a2930fc..a827e89 100644 (file)
@@ -46,9 +46,6 @@ class SpecialImport extends SpecialPage {
        private $pageLinkDepth;
        private $importSources;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct( 'Import', 'import' );
        }
index 1a8dccf..dee2968 100644 (file)
@@ -29,9 +29,7 @@
  * @ingroup SpecialPage
  */
 class SpecialListUsers extends IncludableSpecialPage {
-       /**
-        * Constructor
-        */
+
        public function __construct() {
                parent::__construct( 'Listusers' );
        }
index 15bbffd..1248007 100644 (file)
@@ -315,7 +315,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $opts = parent::getDefaultOptions();
                $user = $this->getUser();
 
-               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+               $opts->add( 'days', $user->getIntOption( 'rcdays' ), FormOptions::FLOAT );
                $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
                $opts->add( 'from', '' );
 
@@ -359,7 +359,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
                                $opts['limit'] = $m[1];
                        }
-                       if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) {
+                       if ( preg_match( '/^days=(\d+(?:\.\d+)?)$/', $bit, $m ) ) {
                                $opts['days'] = $m[1];
                        }
                        if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
@@ -388,7 +388,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
 
                // Calculate cutoff
                $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
-               $cutoff_unixtime = $cutoff_unixtime - ( $cutoff_unixtime % 86400 );
                $cutoff = $dbr->timestamp( $cutoff_unixtime );
 
                $fromValid = preg_match( '/^[0-9]{14}$/', $opts['from'] );
@@ -430,13 +429,14 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $fields[] = 'page_latest';
                $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
 
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $fields,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
                if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
@@ -449,13 +449,24 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        return false;
                }
 
+               $orderByAndLimit = [
+                       'ORDER BY' => 'rc_timestamp DESC',
+                       'LIMIT' => $opts['limit']
+               ];
+               if ( in_array( 'DISTINCT', $query_options ) ) {
+                       // ChangeTags::modifyDisplayQuery() adds DISTINCT when filtering on multiple tags.
+                       // In order to prevent DISTINCT from causing query performance problems,
+                       // we have to GROUP BY the primary key. This in turn requires us to add
+                       // the primary key to the end of the ORDER BY, and the old ORDER BY to the
+                       // start of the GROUP BY
+                       $orderByAndLimit['ORDER BY'] = 'rc_timestamp DESC, rc_id DESC';
+                       $orderByAndLimit['GROUP BY'] = 'rc_timestamp, rc_id';
+               }
                // array_merge() is used intentionally here so that hooks can, should
                // they so desire, override the ORDER BY / LIMIT condition(s); prior to
                // MediaWiki 1.26 this used to use the plus operator instead, which meant
                // that extensions weren't able to change these conditions
-               $query_options = array_merge( [
-                       'ORDER BY' => 'rc_timestamp DESC',
-                       'LIMIT' => $opts['limit'] ], $query_options );
+               $query_options = array_merge( $orderByAndLimit, $query_options );
                $rows = $dbr->select(
                        $tables,
                        $fields,
@@ -669,6 +680,15 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                                [ 'class' => 'rcfilters-container' ]
                        );
 
+                       $loadingContainer = Html::rawElement(
+                               'div',
+                               [ 'class' => 'rcfilters-spinner' ],
+                               Html::element(
+                                       'div',
+                                       [ 'class' => 'rcfilters-spinner-bounce' ]
+                               )
+                       );
+
                        // Wrap both with rcfilters-head
                        $this->getOutput()->addHTML(
                                Html::rawElement(
@@ -677,6 +697,9 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                                        $rcfilterContainer . $rcoptions
                                )
                        );
+
+                       // Add spinner
+                       $this->getOutput()->addHTML( $loadingContainer );
                } else {
                        $this->getOutput()->addHTML( $rcoptions );
                }
index b3b9210..fee336e 100644 (file)
@@ -103,15 +103,33 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                        $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
                        $select[] = 'page_latest';
                }
+
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $select,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
+               if ( $dbr->unionSupportsOrderAndLimit() ) {
+                       if ( count( $tagFilter ) > 1 ) {
+                               // ChangeTags::modifyDisplayQuery() will have added DISTINCT.
+                               // To prevent this from causing query performance problems, we need to add
+                               // a GROUP BY, and add rc_id to the ORDER BY.
+                               $order = [
+                                       'GROUP BY' => 'rc_timestamp, rc_id',
+                                       'ORDER BY' => 'rc_timestamp DESC, rc_id DESC'
+                               ];
+                       } else {
+                               $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
+                       }
+               } else {
+                       $order = [];
+               }
+
                if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
                        $opts )
                ) {
@@ -181,12 +199,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                                }
                        }
 
-                       if ( $dbr->unionSupportsOrderAndLimit() ) {
-                               $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-                       } else {
-                               $order = [];
-                       }
-
                        $query = $dbr->selectSQLText(
                                array_merge( $tables, [ $link_table ] ),
                                $select,
index 1d1df6a..3273046 100644 (file)
@@ -33,16 +33,18 @@ class SpecialRedirect extends FormSpecialPage {
        /**
         * The type of the redirect (user/file/revision)
         *
+        * Example value: `'user'`
+        *
         * @var string $mType
-        * @example 'user'
         */
        protected $mType;
 
        /**
         * The identifier/value for the redirect (which id, which file)
         *
+        * Example value: `'42'`
+        *
         * @var string $mValue
-        * @example '42'
         */
        protected $mValue;
 
index e5adeb5..eeb8823 100644 (file)
@@ -435,9 +435,17 @@ class SpecialSearch extends SpecialPage {
 
                // prev/next links
                if ( $totalRes > $this->limit || $this->offset ) {
+                       // Allow matches to define the correct offset, as interleaved
+                       // AB testing may require a different next page offset.
+                       if ( $textMatches && $textMatches->getOffset() !== null ) {
+                               $offset = $textMatches->getOffset();
+                       } else {
+                               $offset = $this->offset;
+                       }
+
                        $prevnext = $this->getLanguage()->viewPrevNext(
                                $this->getPageTitle(),
-                               $this->offset,
+                               $offset,
                                $this->limit,
                                $this->powerSearchOptions() + [ 'search' => $term ],
                                $this->limit + $this->offset >= $totalRes
index 451669c..4f29082 100644 (file)
@@ -130,9 +130,29 @@ class SpecialSpecialpages extends UnlistedSpecialPage {
                        );
                }
 
-               if ( $includesRestrictedPages || $includesCachedPages ) {
-                       $out->wrapWikiMsg( "<h2 class=\"mw-specialpages-note-top\">$1</h2>", 'specialpages-note-top' );
-                       $out->wrapWikiMsg( "<div class=\"mw-specialpages-notes\">\n$1\n</div>", 'specialpages-note' );
+               // add legend
+               $notes = [];
+               if ( $includesRestrictedPages ) {
+                       $restricedMsg = $this->msg( 'specialpages-note-restricted' );
+                       if ( !$restricedMsg->isDisabled() ) {
+                               $notes[] = $restricedMsg->plain();
+                       }
+               }
+               if ( $includesCachedPages ) {
+                       $cachedMsg = $this->msg( 'specialpages-note-cached' );
+                       if ( !$cachedMsg->isDisabled() ) {
+                               $notes[] = $cachedMsg->plain();
+                       }
+               }
+               if ( $notes !== [] ) {
+                       $out->wrapWikiMsg(
+                               "<h2 class=\"mw-specialpages-note-top\">$1</h2>", 'specialpages-note-top'
+                       );
+                       $out->addWikiText(
+                               "<div class=\"mw-specialpages-notes\">\n" .
+                               implode( "\n", $notes ) .
+                               "\n</div>"
+                       );
                }
        }
 }
index 810f8fb..e7478ee 100644 (file)
@@ -669,13 +669,7 @@ class SpecialUndelete extends SpecialPage {
 
                $archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
                Hooks::run( 'UndeleteForm::showHistory', [ &$archive, $this->mTargetObj ] );
-               /*
-               $text = $archive->getLastRevisionText();
-               if( is_null( $text ) ) {
-                       $out->addWikiMsg( 'nohistory' );
-                       return;
-               }
-               */
+
                $out->addHTML( '<div class="mw-undelete-history">' );
                if ( $this->mAllowed ) {
                        $out->addWikiMsg( 'undeletehistory' );
@@ -858,11 +852,12 @@ class SpecialUndelete extends SpecialPage {
                        $misc = Html::hidden( 'target', $this->mTarget );
                        $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
                        $history .= $misc;
-               }
-
-               $form->appendContent( new OOUI\HtmlSnippet( $history ) );
 
-               $out->addHTML( $form );
+                       $form->appendContent( new OOUI\HtmlSnippet( $history ) );
+                       $out->addHTML( $form );
+               } else {
+                       $out->addHTML( $history );
+               }
 
                return true;
        }
index 073e58d..4cdc78f 100644 (file)
@@ -33,7 +33,6 @@ use MediaWiki\MediaWikiServices;
  */
 class SpecialUpload extends SpecialPage {
        /**
-        * Constructor : initialise object
         * Get data POSTed through the form and assign them to the object
         * @param WebRequest $request Data posted.
         */
index 7fa03ba..10baadf 100644 (file)
@@ -275,17 +275,6 @@ class UsersPager extends AlphabeticPager {
                        $groupOptions[ $groupText ] = $group;
                }
 
-               $optionsDefault = [];
-               if ( $this->editsOnly ) {
-                       $optionsDefault[] = 'editsOnly';
-               }
-               if ( $this->creationSort ) {
-                       $optionsDefault[] = 'creationSort';
-               }
-               if ( $this->mDefaultDirection ) {
-                       $optionsDefault[] = 'desc';
-               }
-
                $formDescriptor = [
                        'user' => [
                                'class' => 'HTMLUserTextField',
@@ -300,14 +289,26 @@ class UsersPager extends AlphabeticPager {
                                'class' => 'HTMLSelectField',
                                'options' => $groupOptions,
                        ],
-                       'options' => [
-                               'class' => 'HTMLMultiSelectField',
-                               'options' => [
-                                       $this->msg( 'listusers-editsonly' )->text() => 'editsOnly',
-                                       $this->msg( 'listusers-creationsort' )->text() => 'creationSort',
-                                       $this->msg( 'listusers-desc' )->text() => 'desc'
-                               ],
-                               'default' => $optionsDefault
+                       'editsOnly' => [
+                               'type' => 'check',
+                               'label' => $this->msg( 'listusers-editsonly' )->text(),
+                               'name' => 'editsOnly',
+                               'id' => 'editsOnly',
+                               'value' => $this->editsOnly
+                       ],
+                       'creationSort' => [
+                               'type' => 'check',
+                               'label' => $this->msg( 'listusers-creationsort' )->text(),
+                               'name' => 'creationSort',
+                               'id' => 'creationSort',
+                               'value' => $this->creationSort
+                       ],
+                       'desc' => [
+                               'type' => 'check',
+                               'label' => $this->msg( 'listusers-desc' )->text(),
+                               'name' => 'desc',
+                               'id' => 'desc',
+                               'value' => $this->mDefaultDirection
                        ],
                        'limithiddenfield' => [
                                'class' => 'HTMLHiddenField',
index b0c12e4..4852ce5 100644 (file)
@@ -2156,7 +2156,7 @@ class Balancer {
                if (
                        $this->allowComments &&
                        !( $this->inRCDATA || $this->inRAWTEXT ) &&
-                       preg_match( Balancer::VALID_COMMENT_REGEX, $x, $regs, PREG_OFFSET_CAPTURE ) &&
+                       preg_match( self::VALID_COMMENT_REGEX, $x, $regs, PREG_OFFSET_CAPTURE ) &&
                        // verify EOF condition where necessary
                        ( $regs[4][1] < 0 || !$this->bitsIterator->valid() )
                ) {
index dbcf568..a797398 100644 (file)
@@ -81,8 +81,6 @@ class RemexCompatMunger implements TreeHandler {
        ];
 
        /**
-        * Constructor
-        *
         * @param Serializer $serializer
         */
        public function __construct( Serializer $serializer ) {
index 9a955fb..25625e7 100644 (file)
@@ -411,7 +411,7 @@ class BotPassword implements IDBAccessObject {
         * @return array|false
         */
        public static function canonicalizeLoginData( $username, $password ) {
-               $sep = BotPassword::getSeparator();
+               $sep = self::getSeparator();
                // the strlen check helps minimize the password information obtainable from timing
                if ( strlen( $password ) >= 32 && strpos( $username, $sep ) !== false ) {
                        // the separator is not valid in new usernames but might appear in legacy ones
index a1119fa..fa84c94 100644 (file)
@@ -305,12 +305,6 @@ class User implements IDBAccessObject {
        /** @var integer User::READ_* constant bitfield used to load data */
        protected $queryFlagsUsed = self::READ_NORMAL;
 
-       /** @var string Indicates type of block (used for eventlogging)
-        * Permitted values: 'cookie-block', 'proxy-block', 'openproxy-block', 'xff-block',
-        * 'config-block'
-        */
-       public $blockTrigger = false;
-
        public static $idCacheByName = [];
 
        /**
@@ -609,7 +603,7 @@ class User implements IDBAccessObject {
                        ]
                );
 
-               return $id ? User::newFromId( $id ) : null;
+               return $id ? self::newFromId( $id ) : null;
        }
 
        /**
@@ -848,7 +842,7 @@ class User implements IDBAccessObject {
                global $wgContLang, $wgMaxNameChars;
 
                if ( $name == ''
-                       || User::isIP( $name )
+                       || self::isIP( $name )
                        || strpos( $name, '/' ) !== false
                        || strlen( $name ) > $wgMaxNameChars
                        || $name != $wgContLang->ucfirst( $name )
@@ -1115,17 +1109,17 @@ class User implements IDBAccessObject {
                        case false:
                                break;
                        case 'valid':
-                               if ( !User::isValidUserName( $name ) ) {
+                               if ( !self::isValidUserName( $name ) ) {
                                        $name = false;
                                }
                                break;
                        case 'usable':
-                               if ( !User::isUsableName( $name ) ) {
+                               if ( !self::isUsableName( $name ) ) {
                                        $name = false;
                                }
                                break;
                        case 'creatable':
-                               if ( !User::isCreatableName( $name ) ) {
+                               if ( !self::isCreatableName( $name ) ) {
                                        $name = false;
                                }
                                break;
@@ -1597,7 +1591,7 @@ class User implements IDBAccessObject {
                // since extensions may change the set of searchable namespaces depending
                // on user groups/permissions.
                foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
-                       $defOpt['searchNs' . $nsnum] = (boolean)$val;
+                       $defOpt['searchNs' . $nsnum] = (bool)$val;
                }
                $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
 
@@ -1676,7 +1670,6 @@ class User implements IDBAccessObject {
                                        'address' => $ip,
                                        'systemBlock' => 'proxy',
                                ] );
-                               $this->blockTrigger = 'proxy-block';
                        } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
                                $block = new Block( [
                                        'byText' => wfMessage( 'sorbs' )->text(),
@@ -1684,7 +1677,6 @@ class User implements IDBAccessObject {
                                        'address' => $ip,
                                        'systemBlock' => 'dnsbl',
                                ] );
-                               $this->blockTrigger = 'openproxy-block';
                        }
                }
 
@@ -1703,7 +1695,6 @@ class User implements IDBAccessObject {
                                # Mangle the reason to alert the user that the block
                                # originated from matching the X-Forwarded-For header.
                                $block->mReason = wfMessage( 'xffblockreason', $block->mReason )->text();
-                               $this->blockTrigger = 'xff-block';
                        }
                }
 
@@ -1719,7 +1710,6 @@ class User implements IDBAccessObject {
                                'anonOnly' => true,
                                'systemBlock' => 'wgSoftBlockRanges',
                        ] );
-                       $this->blockTrigger = 'config-block';
                }
 
                if ( $block instanceof Block ) {
@@ -1733,7 +1723,6 @@ class User implements IDBAccessObject {
                        $this->mBlockedby = '';
                        $this->mHideName = 0;
                        $this->mAllowUsertalk = false;
-                       $this->blockTrigger = false;
                }
 
                // Avoid PHP 7.1 warning of passing $this by reference
@@ -1766,7 +1755,6 @@ class User implements IDBAccessObject {
                                $useBlockCookie = ( $config->get( 'CookieSetOnAutoblock' ) === true );
                                if ( $blockIsValid && $useBlockCookie ) {
                                        // Use the block.
-                                       $this->blockTrigger = 'cookie-block';
                                        return $tmpBlock;
                                } else {
                                        // If the block is not valid, remove the cookie.
@@ -2224,7 +2212,7 @@ class User implements IDBAccessObject {
         * @return int The user's ID; 0 if the user is anonymous or nonexistent
         */
        public function getId() {
-               if ( $this->mId === null && $this->mName !== null && User::isIP( $this->mName ) ) {
+               if ( $this->mId === null && $this->mName !== null && self::isIP( $this->mName ) ) {
                        // Special case, we know the user is anonymous
                        return 0;
                } elseif ( !$this->isItemLoaded( 'id' ) ) {
@@ -4143,7 +4131,7 @@ class User implements IDBAccessObject {
                }
                $dbw->insert( 'user', $fields, __METHOD__, [ 'IGNORE' ] );
                if ( $dbw->affectedRows() ) {
-                       $newUser = User::newFromId( $dbw->insertId() );
+                       $newUser = self::newFromId( $dbw->insertId() );
                } else {
                        $newUser = null;
                }
@@ -5048,7 +5036,7 @@ class User implements IDBAccessObject {
                        // Do nothing
                } elseif ( $wgGroupsAddToSelf[$group] === true ) {
                        // No idea WHY this would be used, but it's there
-                       $groups['add-self'] = User::getAllGroups();
+                       $groups['add-self'] = self::getAllGroups();
                } elseif ( is_array( $wgGroupsAddToSelf[$group] ) ) {
                        $groups['add-self'] = $wgGroupsAddToSelf[$group];
                }
@@ -5056,7 +5044,7 @@ class User implements IDBAccessObject {
                if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
                        // Do nothing
                } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
-                       $groups['remove-self'] = User::getAllGroups();
+                       $groups['remove-self'] = self::getAllGroups();
                } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
                        $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
                }
@@ -5077,7 +5065,7 @@ class User implements IDBAccessObject {
                        // compatibility with old "userrights lets you change
                        // everything")
                        // Using array_merge to make the groups reindexed
-                       $all = array_merge( User::getAllGroups() );
+                       $all = array_merge( self::getAllGroups() );
                        return [
                                'add' => $all,
                                'remove' => $all,
@@ -5503,7 +5491,7 @@ class User implements IDBAccessObject {
                global $wgLang;
 
                $groups = [];
-               foreach ( User::getGroupsWithPermission( $permission ) as $group ) {
+               foreach ( self::getGroupsWithPermission( $permission ) as $group ) {
                        $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(), 'wiki' );
                }
 
index 47d3717..90792fc 100644 (file)
@@ -10,10 +10,12 @@ namespace MediaWiki\Widget;
 /**
  * Search input widget.
  */
-class SearchInputWidget extends \OOUI\TextInputWidget {
+class SearchInputWidget extends TitleInputWidget {
 
        protected $pushPending = false;
        protected $performSearchOnClick = true;
+       protected $validateTitle = false;
+       protected $highlightFirst = false;
        protected $dataLocation = 'header';
 
        /**
@@ -28,6 +30,7 @@ class SearchInputWidget extends \OOUI\TextInputWidget {
         */
        public function __construct( array $config = [] ) {
                $config = array_merge( [
+                       'maxLength' => null,
                        'icon' => 'search',
                ], $config );
 
index e51a8ed..8dfe00f 100644 (file)
@@ -38,8 +38,6 @@ class ConverterRule {
        public $mUnidtable = [];// array of the translation in each variant
 
        /**
-        * Constructor
-        *
         * @param string $text The text between -{ and }-
         * @param LanguageConverter $converter
         */
index 83dff65..12f26c3 100644 (file)
@@ -208,11 +208,11 @@ class Language {
         * @return Language
         */
        protected static function newFromCode( $code, $fallback = false ) {
-               if ( !Language::isValidCode( $code ) ) {
+               if ( !self::isValidCode( $code ) ) {
                        throw new MWException( "Invalid language code \"$code\"" );
                }
 
-               if ( !Language::isValidBuiltInCode( $code ) ) {
+               if ( !self::isValidBuiltInCode( $code ) ) {
                        // It's not possible to customise this code with class files, so
                        // just return a Language object. This is to support uselang= hacks.
                        $lang = new Language;
@@ -228,9 +228,9 @@ class Language {
                }
 
                // Keep trying the fallback list until we find an existing class
-               $fallbacks = Language::getFallbacksFor( $code );
+               $fallbacks = self::getFallbacksFor( $code );
                foreach ( $fallbacks as $fallbackCode ) {
-                       if ( !Language::isValidBuiltInCode( $fallbackCode ) ) {
+                       if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
                                throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
                        }
 
@@ -829,7 +829,7 @@ class Language {
                global $wgExtraLanguageNames, $wgUsePigLatinVariant;
 
                // If passed an invalid language code to use, fallback to en
-               if ( $inLanguage !== null && !Language::isValidCode( $inLanguage ) ) {
+               if ( $inLanguage !== null && !self::isValidCode( $inLanguage ) ) {
                        $inLanguage = 'en';
                }
 
@@ -1192,7 +1192,7 @@ class Language {
                                case 'D':
                                        $usedDay = true;
                                        $s .= $this->getWeekdayAbbreviation(
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
                                        );
                                        break;
                                case 'j':
@@ -1223,7 +1223,7 @@ class Language {
                                case 'l':
                                        $usedDay = true;
                                        $s .= $this->getWeekdayName(
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
                                        );
                                        break;
                                case 'F':
@@ -1404,36 +1404,36 @@ class Language {
                                case 'O':
                                case 'P':
                                case 'T':
-                                       $s .= Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'w':
                                case 'N':
                                case 'z':
                                        $usedDay = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'W':
                                        $usedWeek = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 't':
                                        $usedMonth = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'L':
                                        $usedIsLeapYear = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'o':
                                        $usedISOYear = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'U':
                                        $usedSecond = true;
                                        // fall through
                                case 'I':
                                case 'Z':
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case '\\':
                                        # Backslash escaping
@@ -1467,7 +1467,7 @@ class Language {
                                        $s .= $num;
                                        $raw = false;
                                } elseif ( $roman ) {
-                                       $s .= Language::romanNumeral( $num );
+                                       $s .= self::romanNumeral( $num );
                                        $roman = false;
                                } elseif ( $hebrewNum ) {
                                        $s .= self::hebrewNumeral( $num );
@@ -1509,7 +1509,7 @@ class Language {
                                substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
                        if ( $usedWeek ) {
                                $possibleTtls[] =
-                                       ( 7 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 +
+                                       ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 +
                                        $timeRemainingInDay;
                        } elseif ( $usedISOYear ) {
                                // December 28th falls on the last ISO week of the year, every year.
@@ -1519,29 +1519,29 @@ class Language {
                                        substr( $ts, 0, 4 ) . '1228',
                                        $zone ?: new DateTimeZone( 'UTC' )
                                )->format( 'W' );
-                               $currentISOWeek = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'W' );
+                               $currentISOWeek = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'W' );
                                $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
                                $timeRemainingInWeek =
-                                       ( 7 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400
+                                       ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400
                                        + $timeRemainingInDay;
                                $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
                        }
 
                        if ( $usedMonth ) {
                                $possibleTtls[] =
-                                       ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) -
+                                       ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) -
                                                substr( $ts, 6, 2 ) ) * 86400
                                        + $timeRemainingInDay;
                        } elseif ( $usedYear ) {
                                $possibleTtls[] =
-                                       ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
+                                       ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
                                        + $timeRemainingInDay;
                        } elseif ( $usedIsLeapYear ) {
                                $year = substr( $ts, 0, 4 );
                                $timeRemainingInYear =
-                                       ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
+                                       ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
                                        + $timeRemainingInDay;
                                $mod = $year % 4;
                                if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
@@ -3956,7 +3956,7 @@ class Language {
         * @return string Text, wrapped in LRE...PDF or RLE...PDF or nothing
         */
        public function embedBidi( $text = '' ) {
-               $dir = Language::strongDirFromContent( $text );
+               $dir = self::strongDirFromContent( $text );
                if ( $dir === 'ltr' ) {
                        // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING
                        return self::$lre . $text . self::$pdf;
@@ -4264,7 +4264,7 @@ class Language {
                        $this->mParentLanguage = null;
                        return null;
                }
-               $lang = Language::factory( $code );
+               $lang = self::factory( $code );
                if ( !$lang->hasVariant( $this->getCode() ) ) {
                        $this->mParentLanguage = null;
                        return null;
@@ -4420,7 +4420,7 @@ class Language {
         * @return array Non-empty array, ending in "en"
         */
        public static function getFallbacksFor( $code ) {
-               if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) {
+               if ( $code === 'en' || !self::isValidBuiltInCode( $code ) ) {
                        return [];
                }
                // For unknown languages, fallbackSequence returns an empty array,
@@ -4821,7 +4821,7 @@ class Language {
         */
        public function getCompiledPluralRules() {
                $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
-               $fallbacks = Language::getFallbacksFor( $this->mCode );
+               $fallbacks = self::getFallbacksFor( $this->mCode );
                if ( !$pluralRules ) {
                        foreach ( $fallbacks as $fallbackCode ) {
                                $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
@@ -4840,7 +4840,7 @@ class Language {
         */
        public function getPluralRules() {
                $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
-               $fallbacks = Language::getFallbacksFor( $this->mCode );
+               $fallbacks = self::getFallbacksFor( $this->mCode );
                if ( !$pluralRules ) {
                        foreach ( $fallbacks as $fallbackCode ) {
                                $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
@@ -4859,7 +4859,7 @@ class Language {
         */
        public function getPluralRuleTypes() {
                $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
-               $fallbacks = Language::getFallbacksFor( $this->mCode );
+               $fallbacks = self::getFallbacksFor( $this->mCode );
                if ( !$pluralRuleTypes ) {
                        foreach ( $fallbacks as $fallbackCode ) {
                                $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
index 00d91ce..19ff2a4 100644 (file)
@@ -383,6 +383,8 @@ class Names {
                'si' => 'සිංහල', # Sinhalese
                'simple' => 'Simple English', # Simple English
                'sk' => 'slovenčina', # Slovak
+               'skr' => 'سرائیکی', # Saraiki (multiple scripts - defaults to Arabic)
+               'skr-arab' => 'سرائیکی', # Saraiki (Arabic script)
                'sl' => 'slovenščina', # Slovenian
                'sli' => 'Schläsch', # Lower Selisian
                'sm' => 'Gagana Samoa', # Samoan
index c974d75..74ee9e6 100644 (file)
        "rcfilters-noresults-conflict": "لا نتائج تم العثور عليها لأن محددات البحث تعارض بعضها البعض",
        "rcfilters-state-message-subset": "هذا المرشح ليس له تأثير لأن نتائجه متضمنة في {{PLURAL:$2|المرشح التالي|المرشحات التالية}} الأكثر عمومية (جرب التعليم لتمييزه): $1",
        "rcfilters-state-message-fullcoverage": "اختيار كل المرشحات في مجموعة له نفس التأثير كاختيار لا شيء، لذا فهذا المرشح ليس له تأثير. المجموعة تتضمن: $1",
-       "rcfilters-filtergroup-registration": "تسجيل المستخدم",
-       "rcfilters-filter-registered-label": "مسجل",
-       "rcfilters-filter-registered-description": "المحررون مسجلو الدخول.",
-       "rcfilters-filter-unregistered-label": "غير مسجل",
-       "rcfilters-filter-unregistered-description": "المحررون غير مسجلي الدخول.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "هذا المرشح يتعارض مع {{PLURAL:$2|مرشح الخبرة التالي|مرشحات الخبرة التالية}}، و {{PLURAL:$2|الذي يعطي|التي تعطي}} فقط المستخدمين المسجلين: $1",
        "rcfilters-filtergroup-authorship": "ملكية التعديلات",
        "rcfilters-filter-editsbyself-label": "تعديلاتك الشخصية",
        "rcfilters-filter-editsbyself-description": "مساهماتك الشخصية.",
        "rcfilters-filter-editsbyother-label": "التغييرات بواسطة الآخرين",
        "rcfilters-filter-editsbyother-description": "كل التغييرات باستثناء تغييراتك.",
        "rcfilters-filtergroup-userExpLevel": "مستوى الخبرة (للمستخدمين المسجلين فقط)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "مرشحات الخبرة تعطي فقط المستخدمين المسجلين، لذا فهذا المرشح يتعارض مع مرشح \"غير المسجلين\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "مرشح \"غير المسجلين\" يتعارض مع واحد أو أكثر من مرشحات الخبرة، والتي تعطي المستخدمين المسجلين فقط. المرشحات المتعارضة معلم عليها في مساحة المرشحات النشطة، بالأعلى.",
+       "rcfilters-filter-user-experience-level-registered-label": "مسجل",
+       "rcfilters-filter-user-experience-level-registered-description": "المحررون مسجلو الدخول.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "غير مسجل",
+       "rcfilters-filter-user-experience-level-unregistered-description": "المحررون غير مسجلي الدخول.",
        "rcfilters-filter-user-experience-level-newcomer-label": "القادمون الجدد",
        "rcfilters-filter-user-experience-level-newcomer-description": "أقل من 10 تعديلات و4 أيام من النشاط.",
        "rcfilters-filter-user-experience-level-learner-label": "المتعلمون",
index 4062b17..c098ef2 100644 (file)
        "rcfilters-legend-heading": "<strong>Llista d'abreviatures:</strong>",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzaos",
+       "rcfilters-limit-title": "Cambios a amosar",
+       "rcfilters-limit-shownum": "Amosar los últimos $1 cambios",
+       "rcfilters-days-title": "Últimos díes",
+       "rcfilters-hours-title": "Últimes hores",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|díes}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|hores}}",
        "rcfilters-quickfilters": "Filtros guardaos",
        "rcfilters-quickfilters-placeholder-title": "Entá nun se guardaron enllaces",
        "rcfilters-quickfilters-placeholder-description": "Pa guardar les preferencies del filtru y volver a usales sero, pulsia nel iconu del marcador del área de Filtru Activu más abaxo.",
        "rcfilters-invalid-filter": "Filtru inválidu",
        "rcfilters-empty-filter": "Nun hai filtros activos. Amuésense toles contribuciones.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "¿Qué ye esto?",
+       "rcfilters-filterlist-whatsthis": "¿Como funciona esto?",
        "rcfilters-filterlist-feedbacklink": "Comentar sobro los nuevos filtros (beta)",
        "rcfilters-highlightbutton-title": "Resaltar resultaos",
        "rcfilters-highlightmenu-title": "Seleiciona un color",
        "rcfilters-noresults-conflict": "Nun s'alcontraron resultaos porque los criterios de gueta tán en conflictu",
        "rcfilters-state-message-subset": "Esti filtru nun fai efeutu porque los resultaos inclúyense ente los {{PLURAL:$2|del siguiente filtru más ampliu|de los siguientes filtros más amplios}} (tenta resaltalu pa estremalu): $1",
        "rcfilters-state-message-fullcoverage": "Seleicionar tolos filtros d'un grupu ye igual que nun seleicionar nengunu, de manera qu'esti filtru nun tien efeutu. El grupu incluye: $1",
-       "rcfilters-filtergroup-registration": "Rexistru del usuariu",
-       "rcfilters-filter-registered-label": "Rexistraos",
-       "rcfilters-filter-registered-description": "Editores coneutaos.",
-       "rcfilters-filter-unregistered-label": "Non rexistraos",
-       "rcfilters-filter-unregistered-description": "Editores ensin coneutar.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Esti filtru fai conflictu {{PLURAL:$2|col siguiente filtru|colos siguientes filtros}} Experience, qu'{{PLURAL:$2|alcuentra|alcuentren}} sólo usuarios rexistraos: $1",
        "rcfilters-filtergroup-authorship": "Autoría de la contribución",
        "rcfilters-filter-editsbyself-label": "Cambios de to",
        "rcfilters-filter-editsbyself-description": "Contribuciones de to.",
        "rcfilters-filter-editsbyother-label": "Cambios d'otros",
        "rcfilters-filter-editsbyother-description": "Tolos cambios menos los de to.",
-       "rcfilters-filtergroup-userExpLevel": "Nivel d'esperiencia (solo pa usuarios rexistraos)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Los filtros Experience alcuentren sólo usuarios rexistraos, de manera qu'esti filtru entra en conflictu col filtru «Sin rexistrar».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "El filtru «Ensin rexistrar» tien un conflictu con un filtru Experience o más, qu'alcuentren sólo usuarios rexistraos. Los filtros que tienen un conflictu márquense nel área de Filtros activos, más arriba.",
+       "rcfilters-filtergroup-userExpLevel": "Rexistru d'usuarios y esperiencia",
+       "rcfilters-filter-user-experience-level-registered-label": "Rexistraos",
+       "rcfilters-filter-user-experience-level-registered-description": "Editores coneutaos.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Non rexistraos",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que nun tán coneutaos.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Recién llegaos",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 ediciones y 4 díes d'actividá.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editores rexistraos con menos de 10 ediciones y 4 díes d'actividá.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
-       "rcfilters-filter-user-experience-level-learner-description": "Más esperiencia que los «Recién llegaos», pero menos que los «Usuarios espertos».",
+       "rcfilters-filter-user-experience-level-learner-description": "Editores rexistraos con esperiencia ente «Recién llegaos» y «Usuarios espertos».",
        "rcfilters-filter-user-experience-level-experienced-label": "Usuarios espertos",
-       "rcfilters-filter-user-experience-level-experienced-description": "Más de 30 díes d'actividá y 500 ediciones.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Editores rexistraos con más de 500 ediciones y 30 díes d'actividá.",
        "rcfilters-filtergroup-automated": "Contribuciones automátiques",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Ediciones feches con ferramientes automátiques.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "El filtru «Ediciones menores» fai conflictu con un filtru «Tipu de cambiu» o más, porque dellos tipos de cambiu nun pueden designase como «menores». Los filtros que faen conflictu tan marcaos nel área de Filtros Activos, más arriba.",
        "rcfilters-hideminor-conflicts-typeofchange": "Dellos tipos de cambiu nun pueden designase como «menores», de manera qu'esti filtru fai conflictu colos siguientes filtros «Tipu de cambiu»: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Esti filtru de «Tipu de cambiu» fai conflictu col filtru «Ediciones menores». Dellos tipos de cambiu nun pueden designase como «menores».",
-       "rcfilters-filtergroup-lastRevision": "Última revisión",
+       "rcfilters-filtergroup-lastRevision": "Últimes revisiones",
        "rcfilters-filter-lastrevision-label": "Última revisión",
-       "rcfilters-filter-lastrevision-description": "El cambio más recien d'una páxina.",
-       "rcfilters-filter-previousrevision-label": "Revisiones anteriores",
-       "rcfilters-filter-previousrevision-description": "Tolos cambios que nun son los más recien d'una páxina.",
+       "rcfilters-filter-lastrevision-description": "Sólo el cambiu más recien d'una páxina.",
+       "rcfilters-filter-previousrevision-label": "Non la cabera revisión",
+       "rcfilters-filter-previousrevision-description": "Tolos cambios que nun son la «cabera revisión».",
        "rcfilters-filter-excluded": "Escluíu",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
+       "rcfilters-exclude-button-off": "Torgar los seleicionaos",
+       "rcfilters-exclude-button-on": "Torgando los seleicionaos",
        "rcfilters-view-tags": "Ediciones etiquetaes",
        "rcfilters-view-namespaces-tooltip": "Filtriar los resultaos por espaciu de nomes",
        "rcfilters-view-tags-tooltip": "Filtriar los resultaos usando les etiquetes d'edición",
        "delete-warning-toobig": "Esta páxina tien un historial d'ediciones grande, más de $1 {{PLURAL:$1|revisión|revisiones}}.\nEsborralu pue perturbar les operaciones de la base de datos de {{SITENAME}};\nobra con precaución.",
        "deleteprotected": "Nun pues desaniciar esta páxina porque ta protexida.",
        "deleting-backlinks-warning": "<strong>Avisu:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Otres páxines]] enllacen a, o trescluyen de, la páxina que tas a piques de desaniciar.",
+       "deleting-subpages-warning": "<strong>Avisu:</strong> La páxina que vas desaniciar tien [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|una subpáxina|$1 subpáxines|51=más de 50 subpáxines}}]].",
        "rollback": "Revertir ediciones",
        "rollbacklink": "revertir",
        "rollbacklinkcount": "revertir $1 {{PLURAL:$1|edición|ediciones}}",
        "fileduplicatesearch-noresults": "Nun s'alcontró dengún ficheru nomáu «$1».",
        "specialpages": "Páxines especiales",
        "specialpages-note-top": "Lleenda",
-       "specialpages-note": "* Páxines especiales normales.\n* <span class=\"mw-specialpagerestricted\">Páxines especiales restrinxíes.</span>",
        "specialpages-group-maintenance": "Informes de mantenimientu",
        "specialpages-group-other": "Otres páxines especiales",
        "specialpages-group-login": "Entrar / crear cuenta",
index 44e67df..246ddaa 100644 (file)
        "thu": "Кс",
        "fri": "Йм",
        "sat": "Шб",
-       "january": "Ò\93инÑ\83аÑ\80",
-       "february": "февраль",
-       "march": "маÑ\80Ñ\82",
-       "april": "апÑ\80елÑ\8c",
-       "may_long": "май (Ò»абанай)",
-       "june": "иÑ\8eнÑ\8c",
-       "july": "иÑ\8eлÑ\8c",
-       "august": "авгÑ\83Ñ\81Ñ\82",
-       "september": "сентябрь",
-       "october": "окÑ\82Ñ\8fбÑ\80Ñ\8c",
-       "november": "ноÑ\8fбÑ\80Ñ\8c",
-       "december": "декабÑ\80Ñ\8c",
-       "january-gen": "Ò\93инÑ\83аÑ\80",
-       "february-gen": "февраль",
-       "march-gen": "маÑ\80Ñ\82",
-       "april-gen": "апÑ\80елÑ\8c",
-       "may-gen": "май",
-       "june-gen": "иÑ\8eнÑ\8c",
-       "july-gen": "иÑ\8eлÑ\8c",
-       "august-gen": "авгÑ\83Ñ\81Ñ\82",
-       "september-gen": "сентябрь",
-       "october-gen": "окÑ\82Ñ\8fбÑ\80Ñ\8c",
-       "november-gen": "ноÑ\8fбÑ\80Ñ\8c",
-       "december-gen": "декабÑ\80Ñ\8c",
-       "jan": "Ò\93ин",
-       "feb": "фев",
-       "mar": "мар",
-       "apr": "апр",
-       "may": "май",
-       "jun": "июн",
-       "jul": "июл",
-       "aug": "авг",
-       "sep": "сен",
-       "oct": "окт",
-       "nov": "ноя",
-       "dec": "дек",
+       "january": "Ò\92инÑ\83аÑ\80 (ÒºÑ\8bÑ\83Ñ\8bÒ\93ай)",
+       "february": "Февраль (Шаҡай)",
+       "march": "Ð\9cаÑ\80Ñ\82 (Ð\91Ñ\83Ñ\80анай)",
+       "april": "Ð\90пÑ\80елÑ\8c (Ð\90лаÒ\93аÑ\80ай)",
+       "may_long": "Ð\9cай (Òºабанай)",
+       "june": "Ð\98Ñ\8eнÑ\8c (ÒºÓ©Ñ\82ай)",
+       "july": "Ð\98Ñ\8eлÑ\8c (Ð\9cайай)",
+       "august": "Ð\90вгÑ\83Ñ\81Ñ\82 (УÑ\80аÒ\93ай)",
+       "september": "Сентябрь (Һарысай)",
+       "october": "Ð\9eкÑ\82Ñ\8fбÑ\80Ñ\8c (ҠаÑ\80аÑ\81ай)",
+       "november": "Ð\9dоÑ\8fбÑ\80Ñ\8c (Ò Ñ\8bÑ\80паÒ\93ай)",
+       "december": "Ð\94екабÑ\80Ñ\8c (Ð\90Ò¡Ñ\8aÑ\8eлай)",
+       "january-gen": "Ò\92инÑ\83аÑ\80 (ÒºÑ\8bÑ\83Ñ\8bÒ\93ай)",
+       "february-gen": "Февраль (Шаҡай)",
+       "march-gen": "Ð\9cаÑ\80Ñ\82 (Ð\91Ñ\83Ñ\80анай)",
+       "april-gen": "Ð\90пÑ\80елÑ\8c (Ð\90лаÒ\93аÑ\80ай)",
+       "may-gen": "Ð\9cай (Һабанай)",
+       "june-gen": "Ð\98Ñ\8eнÑ\8c (ÒºÓ©Ñ\82ай)",
+       "july-gen": "Ð\98Ñ\8eлÑ\8c (Ð\9cайай)",
+       "august-gen": "Ð\90вгÑ\83Ñ\81Ñ\82 (УÑ\80аÒ\93ай)",
+       "september-gen": "Сентябрь (Һарысай)",
+       "october-gen": "Ð\9eкÑ\82Ñ\8fбÑ\80Ñ\8c (ҠаÑ\80аÑ\81ай)",
+       "november-gen": "Ð\9dоÑ\8fбÑ\80Ñ\8c (Ò Ñ\8bÑ\80паÒ\93ай)",
+       "december-gen": "Ð\94екабÑ\80Ñ\8c (Ð\90Ò¡Ñ\8aÑ\8eлай)",
+       "jan": "Ò\92ин",
+       "feb": "Фев",
+       "mar": "Ð\9cар",
+       "apr": "Ð\90пр",
+       "may": "Ð\9cай",
+       "jun": "Ð\98юн",
+       "jul": "Ð\98юл",
+       "aug": "Ð\90вг",
+       "sep": "Сен",
+       "oct": "Ð\9eкт",
+       "nov": "Ð\9dоя",
+       "dec": "Ð\94ек",
        "january-date": "Ғинуар $1",
        "february-date": "Февраль $1",
        "march-date": "Март $1",
        "september-date": "Сентябрь $1",
        "october-date": "Октябрь $1",
        "november-date": "Ноябрь $1",
-       "december-date": "СенÑ\82Ñ\8fбрь $1",
+       "december-date": "Ð\94екабрь $1",
        "period-am": "ТК",
        "period-pm": "ТС",
        "pagecategories": "{{PLURAL:$1|1=Категория|Категориялар}}",
        "category-empty": "\"Был категория әлегә буш.\"",
        "hidden-categories": "{{PLURAL:$1|Йәшерен категория|Йәшерен категориялар}}",
        "hidden-category-category": "Йәшерен категориялар",
-       "category-subcat-count": "{{PLURAL:$2|Был категорияла тик киләһе эске категория ғына бар.|Барлығы $2 категориянан, был категорияла киләһе  {{PLURAL:$1|эске категория|$1 эске категория}} күрһәтелә.}}",
+       "category-subcat-count": "{{PLURAL:$2|Был категорияла тик киләһе эске категория ғына бар.|Барлығы $2 категориянан, был категорияла киләһе {{PLURAL:$1|эске категория|$1 эске категория}} күрһәтелә.}}",
        "category-subcat-count-limited": "Был категорияға киләһе {{PLURAL:$1|эске категория|$1 эске категория}} ингән.",
        "category-article-count": "{{PLURAL:$2|1=Был категорияла бер генә бит бар.|Категориялағы $2 биттең $1 бите күрһәтелгән.}}",
        "category-article-count-limited": "Был категорияла {{PLURAL:$1|$1 бит}} бар.",
        "category-file-count": "{{PLURAL:$2|Был категорияла бер генә файл бар.|Категориялағы $2 файлдың {{PLURAL:$1|$1 файлы күрһәтелгән}}.}}",
        "category-file-count-limited": "Был категорияла {{PLURAL:$1|$1 файл}} бар.",
-       "listingcontinuesabbrev": "(дауамы)",
+       "listingcontinuesabbrev": "дауамы",
        "index-category": "Индексланған биттәр",
        "noindex-category": "Индексланмаған биттәр",
        "broken-file-category": "Файлға һылтанмалары эшләмәгән биттәр",
        "newwindow": "(яңы биттә)",
        "cancel": "Кире алырға",
        "moredotdotdot": "Дауамы...",
-       "morenotlisted": "Был исемлек тулы түгел",
+       "morenotlisted": "Был исемлек тулы түгел.",
        "mypage": "Бит",
        "mytalk": "Әңгәмә",
        "anontalk": "Әңгәмә",
        "navigation": "Төп йүнәлештәр",
        "and": "&#32;һәм",
-       "faq": "ЙБҺ",
+       "faq": "ЙБҺ (ЧаВо)",
        "actions": "Ғәмәлдәр",
        "namespaces": "Исем арауыҡтары",
        "variants": "Варианттар",
        "tagline": "{{SITENAME}} проектынан",
        "help": "Белешмә",
        "search": "Эҙләү",
-       "search-ignored-headings": " #<!-- был юлды нисек бар шулай ҡалдырығыҙ --> <pre>\n# Эҙләүҙәр инҡар иткән атамалар.\n# Атамаһы булған бит индексланғас та, үҙгәртмәләр үҙ көсөнә инәсәк.\n# Буш төҙәтеү менән һеҙ битте яңынан индекслата алаһығыҙ\n# Синтаксис шулай күренә:\n#   * Ошо символға «#» башланған юлдың аҙағына тиклем комментарий була\n#   * Һәр буш булмаған юл - инҡар ителгәндең атамаһы, быға регистр ҙа инә\nИҫкәрмәләр\nҺылтанмалар\nҠарағыҙ шулай уҡ\n#</pre> <!-- был юлды шул көйө ҡалдырығыҙ -->",
+       "search-ignored-headings": " #<!-- был юлды нисек бар, шулай ҡалдырығыҙ --> <pre>\n# Эҙләүҙәр инҡар иткән атамалар.\n# Атамаһы булған бит индексланғас та, үҙгәртмәләр үҙ көсөнә инәсәк.\n# Буш төҙәтеү менән һеҙ битте яңынан индекслата алаһығыҙ\n# Синтаксис шулай күренә:\n#   * Ошо символға «#» башланған юлдың аҙағына тиклем комментарий була\n#   * Һәр буш булмаған юл - инҡар ителгәндең атамаһы, быға регистр ҙа инә\nИҫкәрмәләр\nҺылтанмалар\nҠарағыҙ шулай уҡ\n#</pre> <!-- был юлды шул көйө ҡалдырығыҙ -->",
        "searchbutton": "Эҙләү",
        "go": "Күсеү",
        "searcharticle": "Күсеү",
        "updatedmarker": "һуңғы инеүемдән һуң яңыртылған",
        "printableversion": "Баҫтырыу өлгөһө",
        "permalink": "Даими һылтанма",
-       "print": "Баҫыу",
+       "print": "Ð\91аҫÑ\82Ñ\8bÑ\80Ñ\8bÑ\83",
        "view": "Ҡарау",
        "view-foreign": "$1 сайтында ҡарау",
        "edit": "Үҙгәртеү",
        "edit-local": "Локаль тасуирламаны үҙгәртергә",
-       "create": "Төҙөргә",
+       "create": "Төҙөү",
        "create-local": "Локаль тасуирлама өҫтәргә",
-       "delete": "Юҡ  итергә",
+       "delete": "Юйырға",
        "undelete_short": "$1 {{PLURAL:$1|үҙгәртеүҙе}} тергеҙергә",
        "viewdeleted_short": "{{PLURAL:$1|1=1 юйылған үҙгәртеүҙе|$1 юйылған үҙгәртеүҙе}} ҡарау",
        "protect": "Һаҡларға",
        "talkpagelinktext": "әңг.",
        "specialpage": "Ярҙамсы бит",
        "personaltools": "Шәхси ҡоралдар",
-       "talk": "Әңгәмә",
+       "talk": "Фекер алышыу",
        "views": "Ҡарауҙар",
        "toolbox": "Ҡоралдар",
        "tool-link-userrights": "{{GENDER:$1|Ҡатнашыусы}} төркөмдәрен үҙгәртергә",
-       "tool-link-userrights-readonly": "{{GENDER:$1|Ҡатнашыусы|Ҡатнашыулар}} төркөмдәрен ҡарарға",
+       "tool-link-userrights-readonly": "{{GENDER:$1|Ҡатнашыусы}} төркөмдәрен ҡарарға",
        "tool-link-emailuser": "{{GENDER:$1|Ҡатнашыусыға}} хат яҙырға",
        "imagepage": "Файл битен ҡарарға",
        "mediawikipage": "Хәбәрҙәр битен ҡарарға",
        "jumpto": "Унда күсергә:",
        "jumptonavigation": "төп йүнәлештәр",
        "jumptosearch": "эҙләү",
-       "view-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 Ð°Ñ\80Ñ\82Ñ\8bÒ¡ Ñ\82ейÓ\99лгÓ\99н.\nБыл битте ҡарарға теләүселәр бик күп.\nБыл биткә һуңғарак кереп ҡарағыҙ.\n\n$1",
-       "generic-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 ÐºÓ©Ñ\81Ó©Ñ\80гÓ\99неÑ\88ле Ñ\8dÑ\88лÓ\99й.\nÐ\91Ñ\8bл Ð±Ð¾Ð»Ð´Ñ\8b Ò¡Ð°Ñ\80аÑ\80Ò\93а Ñ\82елÓ\99Ò¯Ñ\81елÓ\99Ñ\80 Ð±Ð¸Ðº ÐºÒ¯Ð¿.\nÐ\97инһаÑ\80, Ð±ер ни тиклем көтөгөҙ һәм һуңыраҡ тағы мөрәжәғәт итеп ҡарағыҙ.",
+       "view-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 ÐºÓ©Ñ\81Ó©Ñ\80гÓ\99неÑ\88ле Ñ\8dÑ\88лÓ\99й.\nБыл битте ҡарарға теләүселәр бик күп.\nБыл биткә һуңғарак кереп ҡарағыҙ.\n\n$1",
+       "generic-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 ÐºÓ©Ñ\81Ó©Ñ\80гÓ\99неÑ\88ле Ñ\8dÑ\88лÓ\99й.\nÐ\91Ñ\8bл Ð±Ð¾Ð»Ð´Ñ\8b Ò¡Ð°Ñ\80аÑ\80Ò\93а Ñ\82елÓ\99Ò¯Ñ\81елÓ\99Ñ\80 Ð±Ð¸Ðº ÐºÒ¯Ð¿.\nÐ\91ер ни тиклем көтөгөҙ һәм һуңыраҡ тағы мөрәжәғәт итеп ҡарағыҙ.",
        "pool-timeout": "Блоклауҙы көтөү ваҡыты үтте",
        "pool-queuefull": "Һорауҙар сираты тулы",
        "pool-errorunknown": "Билдәһеҙ хата",
        "disclaimerpage": "Project:Яуаплылыҡтан баш тартыу",
        "edithelp": "Төҙәтеү белешмәһе",
        "helppage-top-gethelp": "Ярҙам",
-       "mainpage": "Ð\91аÑ\88 Ð±ит",
+       "mainpage": "Ð\91аÑ\88 Ð\91ит",
        "mainpage-description": "Баш бит",
        "policy-url": "Project:Ҡағиҙәләр",
        "portal": "Берләшмә",
        "badaccess": "Кереү хатаһы",
        "badaccess-group0": "Һоратылған ғәмәлде үтәй алмайһығыҙ.",
        "badaccess-groups": "Һоратылған ғәмәлде киләһе {{PLURAL:$2|1=төркөм|төркөмдәр}} ҡулланыусылары ғына башҡара ала: $1.",
-       "versionrequired": "MediaWiki-ның $1 версияһы кәрәкле",
+       "versionrequired": "MediaWiki-ның $1 версияһы кәрәк",
        "versionrequiredtext": "Был бит менән эшләү өсөн MediaWiki-ның $1 версияһы кәрәк. [[Special:Version|Ҡулланылған версия тураһында мәғлүмәт битен]] ҡара.",
        "ok": "Тамам",
        "pagetitle": "{{SITENAME}} проектынан",
        "retrievedfrom": "Сығанағы — «$1»",
-       "youhavenewmessages": "Яңы $1 бар ($2).",
+       "youhavenewmessages": "{{PLURAL:$3|Һеҙгә}} $1 ($2) бар.",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Һеҙгә}} {{PLURAL:$3|$3 ҡатнашыусыһынан}} $1 килде ($2).",
        "youhavenewmessagesmanyusers": "Һеҙгә күп ҡатнашыусынан $1 бар ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|1=яңы хәбәр|яңы хәбәр}}",
        "botpasswords-updated-body": "$1 роботы өсөн $2 ҡулланыусыһы серһүҙе яңыртылды.",
        "botpasswords-deleted-title": "Робот серһүҙе юйылды.",
        "botpasswords-deleted-body": "$1 роботы өсөн $2 ҡулланыусыһы серһүҙе юйылды.",
-       "botpasswords-newpassword": "Ð\98неү Ó©Ñ\81өн Ñ\8fÒ£Ñ\8b Ñ\81еÑ\80Ò»Ò¯Ò\99 <strong>$1</strong> â\80\94 <strong>$2</strong>. <em>Ð\90Ñ\80Ñ\82абан Ò¡Ñ\83лланÑ\8bÑ\83 Ó©Ñ\81өн Ñ\8fÒ»Ñ\8bп Ð°Ð»Ñ\8bÒ\93Ñ\8bÒ\99.</em><strong>$3</strong> Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8b Ð¸Ñ\81еме <strong>$4</strong> Ð¿Ð°Ñ\80олÑ\8c Ñ\81иÑ\84аÑ\82Ñ\8b)",
+       "botpasswords-newpassword": "Ð\98неү Ó©Ñ\81өн Ñ\8fÒ£Ñ\8b Ñ\81еÑ\80Ò»Ò¯Ò\99 <strong>$1</strong> â\80\94 <strong>$2</strong>. <em>Ð\90Ñ\80Ñ\82абан Ò¡Ñ\83лланÑ\8bÑ\83 Ó©Ñ\81өн Ñ\8fÒ\99Ñ\8bп Ð°Ð»Ñ\8bÒ\93Ñ\8bÒ\99.</em> <br /> (Ð\98Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ð¼ÐµÐ½Ó\99н Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8bнÑ\8bÒ£ Ð¸Ñ\81еме Ð±ÐµÑ\80 Ð±Ñ\83лÑ\8bÑ\83Ñ\8bн Ñ\82алап Ð¸Ñ\82кÓ\99н Ð¸Ò«ÐºÐµ Ð±Ð¾Ñ\82Ñ\82аÑ\80 Ó©Ñ\81өн, <strong>$3</strong> Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8b Ð¸Ñ\81еме Ð¸Ñ\82еп Ò»Ó\99м <strong>$4</strong> Ñ\81еÑ\80Ò»Ò¯Ò\99 Ð¸Ñ\82еп Ò¡Ñ\83ллана Ð°Ð»Ð°Ò»Ñ\8bÒ\93Ñ\8bÒ\99.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider ғәмәлдә түгел.",
        "botpasswords-restriction-failed": "Робот серһүҙе менән бәйле сәбәптәр булғанға инеү башҡарылманы.",
        "botpasswords-invalid-name": "Күрһәтелгән ҡулланыусы исемендә робот $1 серһүҙен бүлеүсе тамға юҡ.",
        "passwordreset-emailelement": "Ҡулланыусы исеме: \n$1\n\nВаҡытлыса серһүҙ: \n$2",
        "passwordreset-emailsentemail": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон почта аша хат ебәрелде.",
        "passwordreset-emailsentusername": "Әгәр был ҡатнашыусының исеменә бәйле  электрон почтаһының адресы булһа, ул саҡта  серһүҙҙе тергеҙеү өсөн  хат ебәреләсәк.",
+       "passwordreset-nocaller": "Мөрәжәғәт сығанағы күрһәтелергә тейеш",
+       "passwordreset-nosuchcaller": "Мөрәжәғәт сығанағы юҡ: $1",
+       "passwordreset-ignored": "Серһүҙҙе ташлау эшләнмәне. Бәлки, бер провайдер ҙа көйләнмәгәндер?",
        "passwordreset-invalidemail": "Электрон почта адресы ҡабул ителмәй",
+       "passwordreset-nodata": "Ҡатнашыусы исеме лә, электрон почта адресы ла күрһәтелмәгән",
        "changeemail": "Электрон почта адресын үҙгәртергә",
        "changeemail-header": "Электрон почта адресын үҙгәртеү",
        "changeemail-no-info": "Был биткә туранан ирешеү өсөн һеҙгә системала танылыу кәрәк.",
        "preview": "Ҡарап сығыу",
        "showpreview": "Ҡарап сығырға",
        "showdiff": "Индерелгән үҙгәрештәр",
-       "blankarticle": "<strong>Иҫкәртеү:</strong> Һеҙ булдырасаҡ бит буш.\nӘгәр тағы ла «$1» кнопкаға баҫһағыҙ, шул уҡ йөкмәткеле бит  яңынан барлыҡҡа киләсәк.",
+       "blankarticle": "<strong>Иҫкәртеү:</strong> Һеҙ булдырасаҡ бит буш.\nӘгәр тағы ла «$1» төймәһенә баҫһағыҙ, йөкмәткеһеҙ бит барлыҡҡа киләсәк.",
        "anoneditwarning": "<strong>Иғтибар!</strong> Һеҙ сайтта теркәлмәнегеҙ. Әгәр ҙә һеҙ ниндәй ҙә булһа төҙәтмәләр  йәки үҙгәртүҙәр индерһәгеҙ, һеҙҙең IP-адрес башҡаларға ла күрһәтеләсәк. Сайтҡа <strong>[$1 керһәгеҙ]</strong> йәки <strong>[$2 ҡуллануысы яҙмаһын төҙөһәгеҙ]</strong>, һеҙ индергән үҙгәртеүҙәр һеҙҙең ҡулланыусы яҙмағыҙға бәйләнгән була, шулай уҡ башҡа мөмкинлектәр ҙә тыуасаҡ.",
        "anonpreviewwarning": "''Һеҙ танылмағанһығыҙ. Яҙҙырыу ваҡытында IP-адресығыҙ был биттең үҙгәртеүҙәр тарихына яҙыласаҡ.''",
        "missingsummary": "'''Иҫкәртеү.''' Һеҙ үҙгәртеүҙергә ҡыҫҡа тасуирлама яҙманығыҙ. Ҡабаттан «Битте һаҡларға» төймәһенә баҫһағыҙ, үҙгәртеүҙәрегеҙ тасуирламаһыҙ һаҡланасаҡ.",
        "selfredirect": "<strong>Иғтибар:</strong> Һеҙ шул уҡ мәҡәләгә йүнәлтеү эшләйһегеҙ.\n «$1» төәмәһенә баҫһағыҙ тағы шул биткә йүнәлтеләсәк.",
        "missingcommenttext": "Зинһар, аҫҡа үҙ тасуирламағыҙҙы керетегеҙ.",
        "missingcommentheader": "'''Иҫкәртеү:''' Һеҙ был комментарий өсөн тема/исем яҙманығыҙ.\n«$1» төймәһенә ҡабат баҫыу менән үҙгәртеүҙерегеҙ исемһеҙ яҙыласаҡ.",
-       "summary-preview": "Буласаҡ тасуирлама:",
-       "subject-preview": "Тема/башлыҡты алдан ҡарау:",
+       "summary-preview": "Үҙгәртеүҙәр аңлатмаһын ҡарап сығыу:",
+       "subject-preview": "Теманы/баш исемде алдан ҡарау:",
        "previewerrortext": "Алдан ҡарау ваҡытында хата китте.",
        "blockedtitle": "Ҡулланыусы блокланған",
        "blockedtext": "'''Иҫәп яҙыуығыҙ йәки IP-адресығыҙ блокланған.'''\n\nБлоклаусы хаким: $1.\nБелдерелгән сәбәп: ''$2''.\n\n* Блоклау башланған ваҡыт: $8\n* Блоклау  аҙағы: $6\n* Блоклауҙар һаны: $7\n\nҺеҙ $1 йәки башҡа [[{{MediaWiki:Grouppage-sysop}}|хакимгә]] блоклау буйынса һорауҙарығыҙҙы ебәрә алаһығыҙ.\nИҫегеҙҙе тотоғоҙ: әгәр һеҙ теркәлмәгән һәм электрон почта адресығыҙҙы раҫламаған булһағыҙ ([[Special:Preferences|көйләүҙәрем битендә]]), хакимгә хат ебәрә алмайһығыҙ. Шулай ук блоклау ваҡытында һеҙҙең хат ебәреү мөмкинлегегеҙ сикләгән булырға ла мөмкин.\nҺеҙҙең IP-адрес — $3, блоклау идентификаторы — #$5.\nХаттарҙа был мәғлүмәттәрҙе күрһәтергә онотмағыҙ.",
        "readonlywarning": "<strong>КИҪӘТЕҮ: Техник хеҙмәтләндереү сәбәпле мәғлүмәттәр базаһы блокланған, шунлыҡтан үҙгәртеүҙәрегеҙҙе хәҙер һаҡлай алмайһығыҙ.<strong>\nТексты аҙаҡтан ҡулланыу өсөн башҡа файлда һаҡлап тора алаһығыҙ.\n\nХаким белдергән сәбәп: $1",
        "protectedpagewarning": "'''КИҪӘТЕҮ: Һеҙ был битте үҙгәртә алмайһығыҙ, был хоҡуҡҡа хакимдәр генә эйә.'''\nБелешмә өсөн түбәндә һуңғы үҙгәртеү тураһында мәғлүмәт бирелә:",
        "semiprotectedpagewarning": "'''Киҫәтеү:''' был бит һаҡланған. Уны теркәлгән ҡулланыусылар ғына үҙгәртә ала.\nБелешмә өсөн түбәндә һуңғы үҙгәртеү тураһында мәғлүмәт бирелә:",
-       "cascadeprotectedwarning": "<strong>Киҫәтеү:</strong> Был битте тик хакимдәр генә үҙгәртә ала.  Сөнки бит {{PLURAL:$1|каскадлы яҡлау исемлегенә индерелгән}}:",
+       "cascadeprotectedwarning": "<strong>Киҫәтеү:</strong> Был битте тик [[Special:ListGroupRights|махсус хоҡуҡлы]] ҡатнашыусылар ғына үҙгәртә ала.  Сөнки бит {{PLURAL:$1|1=түбәндәге каскадлы яҡлау битенә индерелгән}}:",
        "titleprotectedwarning": "'''Киҫәтеү: Бындый исемле бит һаҡланған, уны үҙгәртеү өсөн [[Special:ListGroupRights|тейешле хоҡуҡҡа]] эйә булыу кәрәк.'''\nБелешмә өсөн түбәндә һуңғы үҙгәртеү тураһында мәғлүмәт бирелә:",
        "templatesused": "Был биттә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыптар}}:",
        "templatesusedpreview": "Алдан ҡаралған биттә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыптар}}:",
        "invalid-content-data": "Тыйылған мәғлүмәт",
        "content-not-allowed-here": "\"$1\" эстәлеге [[$2]] бит өсөн ярамай",
        "editwarning-warning": "Икенсе биткә күсеү һеҙ индергән үҙгәрештәрҙең юғалыуына килтереүе мөмкин.\nӘгәр системала танылыу үтһәгеҙ, көйләүҙәрегеҙ битенең \"Мөхәррирләү\" бүлегендә был киҫәтеүҙе һүндерә алаһығыҙ.",
+       "editpage-invalidcontentmodel-title": "Йөкмәтке форматы ҡабул ителмәй",
        "editpage-notsupportedcontentformat-title": "Йөкмәтке форматы асылмай",
        "editpage-notsupportedcontentformat-text": "$1 эстәлеге форматы $2 моделе форматы менән тап килмәй.",
        "content-model-wikitext": "викитекст",
        "recentchangeslinked-feed": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-toolbox": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-title": "\"$1\" битенә бәйле үҙгәртеүҙәр",
-       "recentchangeslinked-summary": "Был күрһәтелгән бит һылтанма яһаған (йәки күрһәтелгән категорияға кергән) һуңғы үҙгәртеүҙәр исемлеге.\n[[Special:Watchlist|Күҙәтеү исемлегегеҙгә]] керә торған биттәр '''ҡалын''' итеп күрһәтелгән.",
+       "recentchangeslinked-summary": "Был күрһәтелгән бит һылтанма яһаған (йәки күрһәтелгән категорияға кергән) һуңғы үҙгәртеүҙәр исемлеге.\n[[Special:Watchlist|Күҙәтеү исемлегегеҙгә]] керә торған биттәр '''ҡалын''' итеп күрһәтелгән.",
        "recentchangeslinked-page": "Бит исеме:",
        "recentchangeslinked-to": "Киреһенсә, был биткә һылтанма яһаған биттәрҙәге үҙгәртеүҙәрҙе күрһәтергә",
        "recentchanges-page-added-to-category": "[[:$1]] категорияға өҫтәлгән",
        "tooltip-invert": "Һайланған исемдәр арауығындағы (һәм бәйле исемдәр арауығындағы, әгәр күрһәтелһә) биттәрҙәге үҙгәртеүҙәрҙе йәшерер өсөн был билдәне ҡуйығыҙ.",
        "tooltip-whatlinkshere-invert": "Был тамғаны һайланған исемдәр арауығындағы һылтанмаларҙы йәшереү өсөн ҡуйығыҙ.",
        "namespace_association": "Бәйле арауыҡ",
-       "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә(йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
+       "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә (йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
        "blanknamespace": "(Төп)",
        "contributions": "{{GENDER:$1|Ҡатнашыусы}} башҡарған эш",
        "contributions-title": "$1 исемле ҡатнашыусы башҡарған эш",
        "tooltip-pt-watchlist": "Һеҙ күҙәткән биттәр исемлеге",
        "tooltip-pt-mycontris": "{{GENDER:|Һеҙҙең}} төҙәтеүҙәр исемлеге",
        "tooltip-pt-anoncontribs": "Был IP-адрестан яһалған төҙәтеүҙәр",
-       "tooltip-pt-login": "Бында теркәлеү үтергә була, әммә был эш мәжбүри түгел.",
+       "tooltip-pt-login": "Бында теркәлеү үтергә була, әммә был эш мәжбүри түгел",
        "tooltip-pt-logout": "Сығырға",
-       "tooltip-pt-createaccount": "Ð\9cоÑ\82лаҡ Ð±Ñ\83лмаһа Ð»Ð°, ÒºÐµÒ\99гÓ\99 Ð¸Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ñ\82Ó©Ò\99Ó©Ñ\80гө Ò»Ó\99м Ñ\81иÑ\81Ñ\82емала Ñ\82анÑ\8bлÑ\8bÑ\80Ò\93а Ñ\82Ó\99ҡдим Ð¸Ñ\82Ó\99беÒ\99.",
+       "tooltip-pt-createaccount": "Ð\9cоÑ\82лаҡ Ð±Ñ\83лмаһа Ð»Ð°, ÒºÐµÒ\99гÓ\99 Ð¸Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ñ\82Ó©Ò\99Ó©Ñ\80гÓ\99 Ò»Ó\99м Ñ\81иÑ\81Ñ\82емала Ñ\82анÑ\8bлÑ\8bÑ\80Ò\93а Ñ\82Ó\99ҡдим Ð¸Ñ\82Ó\99беÒ\99",
        "tooltip-ca-talk": "Биттең эстәлеге тураһында фекерләшеү",
        "tooltip-ca-edit": "Был битте үҙгәртергә",
        "tooltip-ca-addsection": "Яңы бүлек эшләргә",
        "tooltip-watchlistedit-raw-submit": "Күҙәтеү исемлеген яңыртырға",
        "tooltip-recreate": "Битте юйылған булыуына ҡарамаҫтан тергеҙергә",
        "tooltip-upload": "Күсерә башларға",
-       "tooltip-rollback": "Бер баҫыу менән аҙаҡҡы мөхәррирләүсенең үҙгәртеүҙәрен кире ала.",
+       "tooltip-rollback": "Бер баҫыу менән аҙаҡҡы мөхәррирләүсенең үҙгәртеүҙәрен кире ала",
        "tooltip-undo": "\"Кире ал\" төҙәтеүҙе кире ала һәм төҙәтеү формаһын \"алдан байҡау\"ҙа күрһәтә. Һәм кире алыуҙың сәбәбен белдерергә була.",
        "tooltip-preferences-save": "Көйләүҙәрҙе һаҡларға",
        "tooltip-summary": "Ҡыҫҡаса тасуирлама керетегеҙ",
        "fileduplicatesearch-noresults": "\"$1\" исемле файл табылманы",
        "specialpages": "Махсус биттәр",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Ябай махсус биттәр.\n* <span class=\"mw-specialpagerestricted\">Сикле махсус биттәр.</span>\n* <span class=\"mw-specialpagecached\">Кешланған махсус биттәр (иҫкергән булыуы мөмкин).</span>",
        "specialpages-group-maintenance": "Техник хеҙмәтләндереү хисапламалары",
        "specialpages-group-other": "Башҡа махсус биттәр",
        "specialpages-group-login": "Танылыу йәки теркәлеү",
        "logentry-patrol-patrol": "$1 $3 битенең $4 версияһын {{GENDER:$2|тикшерҙе}}.",
        "logentry-patrol-patrol-auto": "$1 $3 битенең $4 версияһын автоматик рәүештә {{GENDER:$2|тикшерҙе}}.",
        "logentry-newusers-newusers": " {{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы",
-       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы.",
+       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы",
        "logentry-newusers-create2": "$1 {{GENDER:$2|ҡатнашыусы}} $3 иҫәп яҙмаһын булдырҙы",
        "logentry-newusers-byemail": "$1 {{GENDER:$2|}} $3 иҫәп яҙмаһын булдырҙы һәм серһүҙ электрон почта аша ебәрелде",
        "logentry-newusers-autocreate": "Автоматик рәүештә {{GENDER:$2| ҡатнашыусының}} $1 иҫәп яҙмаһы яһалды",
index d5b0bee..07fde8b 100644 (file)
        "rcfilters-legend-heading": "<strong>Сьпіс абрэвіятураў:</strong>",
        "rcfilters-activefilters": "Актыўныя фільтры",
        "rcfilters-advancedfilters": "Пашыраныя фільтры",
+       "rcfilters-limit-title": "Паказаць зьменаў",
+       "rcfilters-limit-shownum": "Паказаць апошнія $1 зьменаў",
+       "rcfilters-days-title": "Апошнія дні",
+       "rcfilters-hours-title": "Апошнія гадзіны",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|дзень|дні|дзён}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|гадзіна|гадзіны|гадзінаў}}",
        "rcfilters-quickfilters": "Захаваныя фільтры",
        "rcfilters-quickfilters-placeholder-title": "Спасылкі яшчэ не захаваныя",
        "rcfilters-quickfilters-placeholder-description": "Каб захаваць налады вашага фільтру і выкарыстаць іх пазьней, націсьніце на выяву закладкі ў зоне актыўнага фільтру ніжэй.",
        "rcfilters-invalid-filter": "Няслушны фільтар",
        "rcfilters-empty-filter": "Няма актыўных фільтраў. Паказаны ўвесь унёсак.",
        "rcfilters-filterlist-title": "Фільтры",
-       "rcfilters-filterlist-whatsthis": "ШÑ\82о Ð³Ñ\8dÑ\82а?",
+       "rcfilters-filterlist-whatsthis": "Як Ð³Ñ\8dÑ\82а Ð¿Ñ\80аÑ\86Ñ\83е?",
        "rcfilters-filterlist-feedbacklink": "Пакінуць водгук пра новыя (бэта) фільтры",
        "rcfilters-highlightbutton-title": "Вылучыць вынікі",
        "rcfilters-highlightmenu-title": "Абярыце колер",
        "rcfilters-noresults-conflict": "Няма вынікаў, бо крытэры пошуку супярэчаць адзін аднаму",
        "rcfilters-state-message-subset": "Гэты фільтар ня мае эфэкту, бо яго вынікі ўключаныя ў вынікі больш {{PLURAL:$2|шырокага фільтру|шырокіх фільтраў}} (паспрабуйце вылучэньне, каб вызначыць яго): $1",
        "rcfilters-state-message-fullcoverage": "Выбар усіх фільтраў у групе — тое ж самае, што і выбар ніводнага, адпаведна, гэты фільтар ня мае эфэкту. Група ўключае: $1",
-       "rcfilters-filtergroup-registration": "Рэгістрацыя ўдзельнікаў",
-       "rcfilters-filter-registered-label": "Зарэгістраваныя",
-       "rcfilters-filter-registered-description": "Рэдактары, якія ўвайшлі ў сыстэму.",
-       "rcfilters-filter-unregistered-label": "Незарэгістраваныя",
-       "rcfilters-filter-unregistered-description": "Рэдактары, якія не ўвайшлі ў сыстэму",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Гэты фільтар канфліктуе з {{PLURAL:$2|1=наступным фільтрам|наступнымі фільтрамі}} дасьведчаных удзельнікаў, {{PLURAL:$2|1=які знаходзіць|якія знаходзяць}} толькі зарэгістраваных удзельнікаў: $1",
        "rcfilters-filtergroup-authorship": "Аўтарства ўнёску",
        "rcfilters-filter-editsbyself-label": "Зьмены, зробленыя вамі",
        "rcfilters-filter-editsbyself-description": "Ваш уласны ўнёсак.",
        "rcfilters-filter-editsbyother-label": "Зьмены, зробленыя іншымі",
        "rcfilters-filter-editsbyother-description": "Усе зьмены, за выключэньнем вашых.",
-       "rcfilters-filtergroup-userExpLevel": "Узровень досьведу (толькі для зарэгістраваных удзельнікаў)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Гэты фільтар досьведу знаходзіць толькі зарэгістраваных удзельнікаў, такім чынам фільтар канфліктуе зь фільтрам «Незарэгістраваныя».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фільтар «Незарэгістраваныя» канфліктуе з адным ці некалькімі фільтрамі ўзроўню досьведу, якія шукаюць толькі зарэгістраваных удзельнікаў. Канфліктныя фільтры пазначаныя ў разьдзеле актыўных фільтраў вышэй.",
+       "rcfilters-filtergroup-userExpLevel": "Рэгістрацыя ўдзельнікаў і досьвед",
+       "rcfilters-filter-user-experience-level-registered-label": "Зарэгістраваныя",
+       "rcfilters-filter-user-experience-level-registered-description": "Рэдактары, якія ўвайшлі ў сыстэму.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Незарэгістраваныя",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Рэдактары, якія не ўвайшлі ў сыстэму",
        "rcfilters-filter-user-experience-level-newcomer-label": "Навічкі",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9cенÑ\88 Ð·Ð° 10 Ð¿Ñ\80авак Ñ\96 4 Ð´Ð½і актыўнасьці.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\80Ñ\8dдакÑ\82аÑ\80Ñ\8b Ð·Ñ\8c Ð¼ÐµÐ½Ñ\88 Ñ\87Ñ\8bм 10 Ð¿Ñ\80аÑ\9eкамÑ\96 Ñ\96 4 Ð´Ð½Ñ\8fмі актыўнасьці.",
        "rcfilters-filter-user-experience-level-learner-label": "Вучні",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\91олÑ\8cÑ\88 Ð´Ð¾Ñ\81Ñ\8cведÑ\83, чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
+       "rcfilters-filter-user-experience-level-learner-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\80Ñ\8dдакÑ\82аÑ\80Ñ\8b, Ñ\87Ñ\8bй Ð´Ð¾Ñ\81Ñ\8cвед Ð±Ð¾Ð»Ñ\8cÑ\88 чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
        "rcfilters-filter-user-experience-level-experienced-label": "Дасьведчаныя ўдзельнікі",
        "rcfilters-filter-user-experience-level-experienced-description": "Больш за 30 дзён актыўнасьці і 500 правак.",
        "rcfilters-filtergroup-automated": "Аўтаматычны ўнёсак",
        "pageinfo-language-change": "зьмяніць",
        "pageinfo-content-model": "Мадэль зьместу старонкі",
        "pageinfo-content-model-change": "зьмяніць",
-       "pageinfo-robot-policy": "Ð\86ндÑ\8dкÑ\81аÑ\86Ñ\8bÑ\8f Ð¿Ð°Ñ\88Ñ\83кавÑ\96камі",
+       "pageinfo-robot-policy": "Ð\86ндÑ\8dкÑ\81аÑ\86Ñ\8bÑ\8f Ð¿Ð¾Ñ\88Ñ\83кавÑ\8bмÑ\96 Ñ\80обаÑ\82амі",
        "pageinfo-robot-index": "Дазволеная",
        "pageinfo-robot-noindex": "Не дазволеная",
        "pageinfo-watchers": "Колькасьць назіральнікаў і назіральніц",
        "fileduplicatesearch-noresults": "Файл з назвай «$1» ня знойдзены.",
        "specialpages": "Спэцыяльныя старонкі",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звычайныя спэцыяльныя старонкі.\n* <strong class=\"mw-specialpagerestricted\">Спэцыяльныя старонкі з абмежаваным доступам.</strong>",
        "specialpages-group-maintenance": "Тэхнічныя справаздачы",
        "specialpages-group-other": "Іншыя спэцыяльныя старонкі",
        "specialpages-group-login": "Уваход / стварэньне рахунку",
        "changecredentials": "Зьмена ўліковых зьвестак",
        "changecredentials-submit": "Зьмяніць уліковыя зьвесткі",
        "changecredentials-invalidsubpage": "$1 не зьяўляецца слушным тыпам уліковых зьвестак.",
+       "changecredentials-success": "Вашыя ўліковыя зьвесткі былі зьмененыя.",
        "removecredentials": "Выдаленьне ўліковых зьвестак",
        "removecredentials-submit": "Выдаліць уліковыя зьвесткі",
+       "removecredentials-invalidsubpage": "$1 не зьяўляецца слушным тыпам уліковых зьвестак.",
+       "removecredentials-success": "Вашыя ўліковыя зьвесткі былі выдаленыя.",
        "credentialsform-provider": "Тып уліковых зьвестак:",
-       "credentialsform-account": "Назва рахунку:"
+       "credentialsform-account": "Назва рахунку:",
+       "cannotlink-no-provider-title": "Няма рахункаў для далучэньня",
+       "cannotlink-no-provider": "Няма рахункаў для далучэньня."
 }
index 9cd29ac..9a6dd50 100644 (file)
@@ -30,7 +30,9 @@
                        "SamGold",
                        "Liashko",
                        "Mechanizatar",
-                       "Artsiom91"
+                       "Artsiom91",
+                       "Andrus",
+                       "Da voli"
                ]
        },
        "tog-underline": "Падкрэсліваць спасылкі:",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (гл. асобна [[Special:NewPages|новыя старонкі]])",
        "recentchanges-submit": "Паказаць",
        "rcfilters-activefilters": "Актыўныя фільтры",
+       "rcfilters-days-title": "Апошнія дні",
+       "rcfilters-hours-title": "Апошнія гадзіны",
        "rcfilters-savedqueries-rename": "Перайменаваць",
        "rcfilters-savedqueries-setdefault": "Устанавіць прадвызначаным",
        "rcfilters-savedqueries-unsetdefault": "Зняць прадвызначэнне",
        "rcfilters-clear-all-filters": "Ачысціць усе фільтры",
        "rcfilters-invalid-filter": "Недапушчальны фільтр",
        "rcfilters-filterlist-title": "Фільтры",
-       "rcfilters-filterlist-whatsthis": "ШÑ\82о Ð³Ñ\8dÑ\82а?",
+       "rcfilters-filterlist-whatsthis": "Як Ð³Ñ\8dÑ\82а Ð¿Ñ\80аÑ\86Ñ\83е?",
        "rcfilters-highlightmenu-title": "Выбраць колер",
        "rcfilters-highlightmenu-help": "Выбраць колер для падсвечвання уласцівасці",
-       "rcfilters-filtergroup-registration": "Рэгістрацыя ўдзельніка",
-       "rcfilters-filter-registered-label": "Зарэгістраваны",
-       "rcfilters-filter-registered-description": "Залагіненыя рэдактары",
-       "rcfilters-filter-unregistered-label": "Незарэгістраваны",
        "rcfilters-filter-editsbyself-label": "Вашыя змены",
        "rcfilters-filter-editsbyself-description": "Ваш унёсак",
        "rcfilters-filter-editsbyother-label": "Змены іншых удзельнікаў",
        "rcfilters-filter-editsbyother-description": "Усе змены, акрамя Вашых",
        "rcfilters-filtergroup-userExpLevel": "Вопыт (толькі для зарэгістраваных удзельнікаў)",
-       "rcfilters-filter-user-experience-level-learner-description": "Болей дзён актыўнасці і правак, чым у «навічкоў», але меней чым у «дасведчаных удзельнікаў».",
-       "rcfilters-filter-user-experience-level-experienced-description": "Больш за 30 дзён актыўнасці і 500 правак.",
+       "rcfilters-filter-user-experience-level-registered-label": "Зарэгістраваны",
+       "rcfilters-filter-user-experience-level-registered-description": "Залагіненыя рэдактары",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Незарэгістраваны",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Зарэгістраваныя рэдактары з менш за 10 правак і 4 дзён актыўнасці.",
+       "rcfilters-filter-user-experience-level-learner-description": "Зарэгістраваныя рэдактары, вопыт якіх знаходзіцца паміж «навічкамі» і «дасведчанымі удзельнікамі».",
+       "rcfilters-filter-user-experience-level-experienced-description": "Зарэгістраваныя рэдактары з больш за 500 правак і 30 дзён актыўнасці.",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-humans-label": "Чалавек (не бот)",
        "rcfilters-filter-humans-description": "Праўкі, зробленыя людзьмі",
        "rcfilters-filter-unpatrolled-label": "Недагледжаны",
        "rcfilters-filtergroup-lastRevision": "Цяперашняя версія",
        "rcfilters-filter-lastrevision-label": "Актуальная версія",
+       "rcfilters-exclude-button-on": "За выключэннем выбранага",
        "rcnotefrom": "Ніжэй {{PLURAL:$5|паказана змяненне|паказаны змены}} з <strong>$3, $4</strong> (не больш за <strong>$1</strong>).",
        "rclistfrom": "Паказаць змены з $3 $2",
        "rcshowhideminor": "$1 дробныя праўкі",
        "fileduplicatesearch-noresults": "Не знойдзены файл з іменем «$1».",
        "specialpages": "Адмысловыя старонкі",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звычайныя адмысловыя старонкі.\n* <span class=\"mw-specialpagerestricted\">Адмысловыя старонкі з абмежаваным доступам.</span>\n* <span class=\"mw-specialpagecached\">Закэшаваныя адмысловыя старонкі (могуць быць састарэлымі).</span>",
        "specialpages-group-maintenance": "Звесткі аб працы",
        "specialpages-group-other": "Іншыя адмысловыя старонкі",
        "specialpages-group-login": "Прадстаўленне / рэгістрацыя",
index 2c49908..4f086f9 100644 (file)
        "mergehistory-fail-self-merge": "Изходната и целевата страница се еднакви.",
        "mergehistory-no-source": "Изходната страница $1 не съществува.",
        "mergehistory-no-destination": "Целевата страница $1 не съществува.",
-       "mergehistory-invalid-source": "Ð\98зÑ\85однаÑ\82а Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ð¸Ð¼Ð° ÐºÐ¾Ñ\80екÑ\82но Ð¸Ð¼е.",
-       "mergehistory-invalid-destination": "ЦелеваÑ\82а Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ð¸Ð¼Ð° ÐºÐ¾Ñ\80екÑ\82но Ð¸Ð¼е.",
+       "mergehistory-invalid-source": "Ð\98зÑ\85однаÑ\82а Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ð¸Ð¼Ð° Ð²Ð°Ð»Ð¸Ð´Ð½Ð¾ Ð·Ð°Ð³Ð»Ð°Ð²Ð¸е.",
+       "mergehistory-invalid-destination": "ЦелеваÑ\82а Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ð¸Ð¼Ð° Ð²Ð°Ð»Ð¸Ð´Ð½Ð¾ Ð·Ð°Ð³Ð»Ð°Ð²Ð¸е.",
        "mergehistory-autocomment": "Слята [[:$1]] в [[:$2]]",
        "mergehistory-comment": "Слята [[:$1]] в [[:$2]]: $3",
        "mergehistory-same-destination": "Изходната и целевата страница не могат да съвпадат",
        "rcfilters-highlightmenu-title": "Изберете цвят",
        "rcfilters-highlightmenu-help": "Изберете цвят за отбелязване на свойството",
        "rcfilters-filterlist-noresults": "Не са намерени филтри",
-       "rcfilters-filtergroup-registration": "Регистрация на потребители",
-       "rcfilters-filter-registered-label": "Регистрирани",
-       "rcfilters-filter-registered-description": "Влезли в системата редактори.",
-       "rcfilters-filter-unregistered-label": "Нерегистрирани",
-       "rcfilters-filter-unregistered-description": "Редактори, които не са влезли в системата.",
        "rcfilters-filtergroup-authorship": "Авторство на редакциите",
        "rcfilters-filter-editsbyself-label": "Ваши редакции",
        "rcfilters-filter-editsbyself-description": "Ваши редакции.",
        "rcfilters-filter-editsbyother-label": "Чужди редакции",
        "rcfilters-filter-editsbyother-description": "Всички редакции с изключение на вашите собствени.",
        "rcfilters-filtergroup-userExpLevel": "Ниво на опита (само за регистрирани потребители)",
+       "rcfilters-filter-user-experience-level-registered-label": "Регистрирани",
+       "rcfilters-filter-user-experience-level-registered-description": "Влезли в системата редактори.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Нерегистрирани",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Редактори, които не са влезли в системата.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новодошли",
        "rcfilters-filter-user-experience-level-newcomer-description": "По-малко от 10 редакции и 5 дни активност.",
        "rcfilters-filter-user-experience-level-learner-label": "Учещи се",
        "fileduplicatesearch-noresults": "Не беше открит файл с име „$1“.",
        "specialpages": "Специални страници",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Обикновени специални страници.\n* <strong class=\"mw-specialpagerestricted\">Специални страници с ограничения.</strong>",
        "specialpages-group-maintenance": "Доклади по поддръжката",
        "specialpages-group-other": "Други специални страници",
        "specialpages-group-login": "Влизане / създаване на сметка",
        "tags-create-reason": "Причина:",
        "tags-create-submit": "Създаване",
        "tags-create-no-name": "Трябва да посочите име на етикет.",
-       "tags-create-invalid-chars": "Имената на етикети не трябва да съдържат запетайки (<code>,</code>) или наклонени черти (<code>/</code>).",
+       "tags-create-invalid-chars": "Имената на етикети не трябва да съдържат запетайки (<code>,</code>), прави черти (<code>|</code>) или наклонени черти (<code>/</code>).",
        "tags-create-invalid-title-chars": "Имената на етикети не трябва да съдържат знаци, които не могат да бъдат използвани в заглавия на страници.",
        "tags-create-already-exists": "Етикет „$1“ вече съществува.",
        "tags-create-warnings-below": "Искате ли да продължите със създаването на етикета?",
index e93c826..074c99f 100644 (file)
        "editfont-monospace": "मोनोस्पेस्ड फोंट",
        "editfont-sansserif": "सैंस-सेरिफ फॉन्ट",
        "editfont-serif": "सेरिफ फॉन्ट",
-       "sunday": "à¤\87तवार",
-       "monday": "सà¥\8bमवार",
-       "tuesday": "मà¤\82à¤\97लवार",
-       "wednesday": "बुधवार",
-       "thursday": "à¤\97à¥\81रà¥\81वार",
-       "friday": "शुक्रवार",
-       "saturday": "शनिवार",
-       "sun": "à¤\87त",
+       "sunday": "à¤\85तवार",
+       "monday": "सोमार",
+       "tuesday": "मंगर",
+       "wednesday": "बुध",
+       "thursday": "बियफà¥\87",
+       "friday": "शुक",
+       "saturday": "शनिà¤\9aà¥\8dà¤\9aर",
+       "sun": "à¤\85त",
        "mon": "सोम",
-       "tue": "मà¤\82à¤\97ल",
+       "tue": "मà¤\82à¤\97र",
        "wed": "बुध",
-       "thu": "à¤\97à¥\81रà¥\81",
-       "fri": "शुक्र",
+       "thu": "बिफà¥\87",
+       "fri": "शुक",
        "sat": "शनि",
        "january": "जनवरी",
        "february": "फरवरी",
        "navigation-heading": "नेविगेशन मेनू",
        "errorpagetitle": "खराबी",
        "returnto": "$1 पर लवटीं।",
-       "tagline": "भोजपुरी {{SITENAME}} से",
+       "tagline": "{{SITENAME}} से",
        "help": "मदद",
-       "search": "खोज",
+       "search": "खोज करीं",
        "search-ignored-headings": " #<!-- एह लाइन के बिलकुल अइसहीं छोड़ दीं --> <pre>\n# हेडिंग जिनहन पर खोज करत समय धियान ना दिहल जाई।\n# एह हेडिंग वाला पन्ना जइसहीं सूचीबद्ध होखी, बदलाव प्रभावी हो जइहें।\n# आप एगो खाली संपादन (null edit) कइ के दुबारा सूचीकरण के लागू कर सकत बानी।\n# एकर सिंटेक्स अइसे बा कि:\n#   * Everything from a \"#\" character to the end of the line is a comment.\n#   * Every non-blank line is the exact title to ignore, case and everything.\nसंदर्भ\nबाहरी कड़ी\nइहो देखल जाय\n #</pre> <!-- एह लाइन के बिलकुल अइसहीं छोड़ दीं -->",
        "searchbutton": "खोजीं",
        "go": "जाईं",
        "history_short": "इतिहास",
        "history_small": "इतिहास",
        "updatedmarker": "हमरे अंतिम बेर देखले के बाद के बदलाव",
-       "printableversion": "à¤\9bापà¥\87 à¤²à¤¾à¤¯à¤\95 à¤¸à¤\82सà¥\8dà¤\95रण",
+       "printableversion": "à¤\9bापà¥\87 à¤²à¤¾à¤¯à¤\95 à¤µà¤°à¥\8dशन",
        "permalink": "स्थायी कड़ी",
        "print": "छापीं",
        "view": "देखीं",
        "view-foreign": "$1 पर देखीं",
-       "edit": "संपादन",
+       "edit": "संपादन करीं",
        "edit-local": "लोकल विवरण संपादन",
        "create": "बनाईं",
        "create-local": "लोकल विवरण जोड़ीं",
        "lastmodifiedat": "एह पन्ना पर आखिरी बेर संपादन $1 के $2 बजे भइल रहल।",
        "viewcount": "ई पन्ना {{PLURAL:$1|एक|$1}} बेर देखल गइल बा।",
        "protectedpage": "सुरक्षित पन्ना",
-       "jumpto": "इहाँ जाईं:",
+       "jumpto": "सà¥\80धà¥\87 à¤\87हाà¤\81 à¤\9cाà¤\88à¤\82:",
        "jumptonavigation": "नेविगेशन",
        "jumptosearch": "खोजीं",
        "view-pool-error": "माफ करीं, ए समय सर्वर पर बहुत ज्यादा लोड बढ़ गइल बा।\nए पन्ना के बहुते प्रयोगकर्ता लोग देखे के कोशिश कर रहल बा।\nए पन्ना के फिर से देखे से पहिले कृपया कुछ देर तक इंतजार करीं।\n\n$1",
        "aboutpage": "Project:बारे में",
        "copyright": "उपलब्ध सामग्री $1 के अधीन उपलब्ध बा जब तक की अलग से उल्लेख ना कइल गइल होखे।",
        "copyrightpage": "{{ns:project}}:कापीराइट सब",
-       "currentevents": "हाल के घटना सब",
-       "currentevents-url": "Project:हाल के घटना सब",
+       "currentevents": "हाल के घटना",
+       "currentevents-url": "Project:हाल के घटना",
        "disclaimers": "नामंजूरी",
        "disclaimerpage": "Project:सामान्य नामंजूरी",
        "edithelp": "संपादन में मदद",
        "mainpage-description": "मुख्य पन्ना",
        "policy-url": "Project:नीति",
        "portal": "समाज पन्ना",
-       "portal-url": "Project:सदसà¥\8dय-समाà¤\9c à¤®à¥\81à¤\96à¥\8dयपन्ना",
+       "portal-url": "Project:समाà¤\9c पन्ना",
        "privacy": "गोपनीयता नीति",
        "privacypage": "Project:गोपनीयता नीति",
        "badaccess": "परमीशन खराबी",
        "viewsourceold": "स्रोत देखीं",
        "editlink": "संपादन",
        "viewsourcelink": "स्रोत देखीं",
-       "editsectionhint": "खंड के संपादन: $1",
+       "editsectionhint": "$1 खंड के संपादित करीं",
        "toc": "बिसयसूची",
        "showtoc": "देखीं",
        "hidetoc": "छिपाईं",
        "nstab-main": "पन्ना",
        "nstab-user": "प्रयोगकर्ता पन्ना",
        "nstab-media": "मीडिया पन्ना",
-       "nstab-special": "विशà¥\87ष पन्ना",
+       "nstab-special": "à¤\96ास पन्ना",
        "nstab-project": "प्रोजेक्ट पन्ना",
        "nstab-image": "फाइल",
        "nstab-mediawiki": "सनेसा",
        "delete-hook-aborted": "हुक द्वारा हटावे के कार्रवाई बीचे में छोड़ल गइल।\nबिना कौनों कारण बतवले।",
        "no-null-revision": "पन्ना \"$1\" खातिर नया खाली संसोधन ना बन सकल",
        "badtitle": "खराब टाइटिल",
-       "badtitletext": "अनुरोधित टाइटिल अवैध, खाली या गलत जुड़ल अंतर-भाषीय या अंतर-विकि टाइटिल बा।\nबुझात बा कि एह में टाइटिल में ना इस्तेमाल हो सके लायक एक या एक से ढेर कॅरेक्टर बा।",
+       "badtitletext": "अनुरोधित टाइटिल अवैध, खाली या गलत जुड़ल अंतरभाषी या अंतरविकि टाइटिल बा।\nबुझात बा कि एह में टाइटिल में ना इस्तेमाल हो सके लायक एक ठो या एक से ढेर करेक्टर बा।",
        "title-invalid-empty": "माँगल जा रहल पन्ना टाइटिल या त खाली बा या फिर कौनों नाँवस्थान के नाँव भर दिहल गइल बाटे।",
        "title-invalid-utf8": "माँगल जा रहल पन्ना टाइटिल में अइसन UTF-8 सीक्वेंस बा जेवन मान्य नइखे।",
        "title-invalid-interwiki": "माँगल जा रहल पन्ना टाइटिल में इंटरविकि कड़ी बा जेवन टाइटिल में ना प्रयोग कइल जा सकत बा।",
        "createacct-another-submit": "खाता बनाईं",
        "createacct-continue-submit": "खाता बनावल जारी राखीं",
        "createacct-another-continue-submit": "खाता बनावल जारी राखीं",
-       "createacct-benefit-heading": "{{SITENAME}} à¤°à¤\89à¤\86 जइसन लोगन द्वारा बनावल गइल बा।",
+       "createacct-benefit-heading": "{{SITENAME}} à¤°à¤\89वà¥\87à¤\82 जइसन लोगन द्वारा बनावल गइल बा।",
        "createacct-benefit-body1": "{{PLURAL:$1|संपादन}}",
        "createacct-benefit-body2": "{{PLURAL:$1|पन्ना}}",
        "createacct-benefit-body3": "हाल के {{PLURAL:$1|योगदानकर्ता}}",
        "resettokens-watchlist-token": "[[Special:Watchlist|आपके धियानसूची के पन्नन में बदलावसभ]] के वेब फीड (एटम/आरऍसऍस) खातिर टोकन",
        "resettokens-done": "टोकन रीसेट हो चुकल बा।",
        "resettokens-resetbutton": "चुनल गइल टोकन रीसेट करीं",
-       "bold_sample": "मोट अच्छर में",
-       "bold_tip": "मोट अच्छर में",
+       "bold_sample": "मोट अच्छर",
+       "bold_tip": "मोट अच्छर",
        "italic_sample": "तिरछा पाठ",
        "italic_tip": "तिरछा पाठ",
        "link_sample": "कड़ी टाइटिल",
        "extlink_tip": "बाहरी कड़ी (शुरू में http:// जरुर लगाईं)",
        "headline_sample": "हेडिंग पाठ",
        "headline_tip": "दुसरा लेवल के हेडिंग",
-       "nowiki_sample": "बिना-फà¥\89रमेट कइल पाठ इहाँ डालीं",
-       "nowiki_tip": "विà¤\95ि à¤«à¥\89रमेटिंग के अनदेखी करीं",
+       "nowiki_sample": "बिना-फारमेट कइल पाठ इहाँ डालीं",
+       "nowiki_tip": "विà¤\95ि à¤«à¤¾रमेटिंग के अनदेखी करीं",
        "image_sample": "उदाहरण.jpg",
        "image_tip": "समाहित (एम्बेड कइल) फाइल",
        "media_sample": "उदाहरण.ogg",
        "hr_tip": "पड़ी लकीर (कम प्रयोग करीं)",
        "summary": "सारांश:",
        "subject": "बिसय:",
-       "minoredit": "à¤\9bà¥\8bà¤\9f à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन",
+       "minoredit": "à¤\88 à¤\8fà¤\97à¥\8b à¤\9bà¥\8bà¤\9f à¤¸à¤\82पादन à¤¬à¤¾",
        "watchthis": "धियानसूची में डालीं",
        "savearticle": "पन्ना सहेजीं",
        "savechanges": "बदलाव सहेजीं",
        "showpreview": "झलक देखीं",
        "showdiff": "बदलाव देखीं",
        "blankarticle": "<strong>चेतावनी:</strong> आप एगो खाली पन्ना बनावे जा रहल बानी।\nयदि आप \"$1\" के फेर से दबायेब त पन्ना बिना कउनो सामग्री के बन जाई।",
-       "anoneditwarning": "<strong>चेतावनी:</strong> रउआँ आपन खाता में प्रवेश नइखीं कइले। अगर रउआँ कौनों बदलाव करब त राउर आईपी पता सभके लउकी। अगर रउआ <strong>[$1 खाता में प्रवेश करब]</strong> या <strong>[$2 नया खाता बनाइब]</strong> त, अउरी सुबिधा सब की संघे राउर संपादन के श्रेय भी राउर प्रयोगकर्तानाँव के मिली!",
+       "anoneditwarning": "<strong>चेतावनी:</strong> रउआँ आपन खाता में प्रवेश नइखीं कइले। अगर रउआँ कौनों बदलाव करब त राउर आईपी पता सभके लउकी। अगर रउआ <strong>[$1 खाता में प्रवेश करब]</strong> या <strong>[$2 नया खाता बनाइब]</strong> त, अउरी सुबिधा सब की संघे राउर संपादन के श्रेय भी राउर प्रयोगकर्तानाँव के मिली!",
        "anonpreviewwarning": "''रउआँ खाता में प्रवेश नइखीं भइल। सहेजब तब ए पन्ना के संपादन इतिहास में राउर आईपी पता दर्ज हो जाई।\"",
        "missingsummary": "'''याद दियावल जात बा:'''रउआँ एगो संपादन सारांश नइखीं दिहले। अगर फिर \"$1\" पर क्लिक करब, त राउर संपादन एकरे बिना सहेजा जाई।",
        "selfredirect": "<strong>चेतावनी:</strong> आप ई पन्ना के खुदे एही पर अनुप्रेषण क रहल बानी।\nया त आप अनुप्रेषण खातिर गलत लक्ष्य देत बानी, या आप गलत पन्ना के संपादन करत बानी।\nएक बेर अउरी \"$1\" क्लिक करब त, ओइसहूँ ई अनुप्रेषण बना दिहल जाई।",
        "blockedtext": "'''राउर सदस्यनाम अथवा आइ॰पी पता अवरोधित कर दिहल गईल बा ।'''\n\nअवरोध $1 द्वारा करल गईल रहल।\nअवरोध के कारण बा ''$2''\n\n* अवरोध के आरंभ: $8\n* अवरोध के समाप्ति: $6\n* अवरोधित इकाई: $7\n\nइ अवरोध के बारे में चर्चा करे खातिर रउआ $1 या केहु अन्य [[{{MediaWiki:Grouppage-sysop}}|प्रबन्धक]] से संपर्क कर सकत बानी।\nअगर रउआ [[Special:Preferences|आपन वरीयता]] में वैद्य ई-मेल पता प्रविष्ट कइले होखब तबे 'इ प्रयोक्ता के ई-मेल भेजीं' वाला सुविधा के प्रयोग कर सकत बानी अउर रउआ एकर प्रयोग करे से ना रोकल गईल होखे।\nराउर हाल के आइ॰पी पता $3 ह अउर अवरोध क्रमांक #$5 ह।\nआपन कउनो भी प्रश्न में कृपया इ सब जानकारी भी शामिल करब।",
        "autoblockedtext": "राउर आइ॰पी पता अपने आप अवरुद्ध हो गईल बा काहे कि एकर प्रयोग केहु अन्य सदस्य द्वारा होत रहल,\nजे $1 द्वारा अवरोधित करल गईल रहलन। \nअवरोध करे के कारण बा:\n\n:''$2''\n\n* अवरोध प्रारंभ: $8\n* अवरोध समाप्ति: $6\n* अवरोधित सदस्य: $7\n\nअवरोध के चर्चा करे खातिर रउआ $1 या केहु अन्य [[{{MediaWiki:Grouppage-sysop}}|प्रबंधक]] से संपर्क कर सकत बानी।\n\nकृपया ध्यान रहे कि यदि रउआ \"इ सदस्य के ई-मेल भेजीं\" वाला सुविधा के प्रयोग करे के चाहत बानी त राउर [[Special:Preferences|वरीयता]] में वैद्य ई-मेल पता होखे के चाहीं अउर एकर प्रयोग रउआ खातिर अवरोधित ना भईल होखे।\n\nराउर हाल के आइ॰पी पता $3 ह अउर अवरोध क्रमांक #$5 ह।\nआपन कउनो भी प्रश्न में कृपया इ सब जानकारी शामिल करब।",
        "systemblockedtext": "राउर खाता या आइपी पता के मीडियाविकि द्वारा ऑटोमेटिक रूप से रोक दिहल गइल बा।\nएकरा खातिर कारण दिहल गइल बा:\n\n\n:<em>$2</em>\n\n* रोक के सुरुआत: $8\n* रोक समाप्त होखी: $6\n* रोक लगावे वाला: $7\n\nराउर वर्तमान आइपी पता $3 बा।\nअगर कौनों सवाल करीं तब ऊपर बतावल सगरी जानकारी देईं।",
-       "blockednoreason": "à¤\95à¤\89नà¥\8b à¤\95ारण à¤\89लà¥\8dलà¥\87à¤\96ित à¤¨à¤\88à¤\96à¥\87",
-       "whitelistedittext": "रà¤\89à¤\86 à¤ªà¤¨à¥\8dना à¤¸à¤®à¥\8dपादन à¤\95रà¥\87 à¤\96ातिर $1 à¤\95रà¥\87 à¤\95à¥\87 à¤ªà¤¡à¤¼à¥\80।",
-       "confirmedittext": "सà¤\82पादन à¤\95रà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤¿à¤²à¥\87 à¤\86पà¤\95à¥\87 à¤\85ापना à¤\88-मà¥\87ल à¤ªà¤¤à¤¾ à¤ªà¥\8dरमाणित à¤\95रावल à¤\9cरà¥\81रà¥\80 à¤¬à¤¾à¥¤\nà¤\95à¥\83पया à¤\86पन [[Special:Preferences|राà¤\89र à¤ªà¤¸à¤¨à¥\8dद]] à¤®à¥\87à¤\82 à¤\9cाà¤\95à¥\87 à¤\85ापन à¤\88-मà¥\87ल à¤ªà¤¤à¤¾ à¤¦à¤¿à¤¹à¥\80à¤\82 à¤\85à¤\89र à¤\89के प्रमाणित करीं।",
-       "nosuchsectiontitle": "à¤\96णà¥\8dड à¤¨à¤¾ à¤®à¤¿à¤² à¤¸à¤\95ल।",
-       "nosuchsectiontext": "à¤\86प à¤\8fà¤\97à¥\8b à¤\85à¤\87सन à¤\85नà¥\81भाà¤\97 à¤\95à¥\87 à¤¸à¤®à¥\8dपादन à¤\95रà¥\87 à¤\95à¥\87 à¤ªà¥\8dरयतà¥\8dन à¤\95र à¤°à¤¹à¤² à¤¬à¤¾à¤¨à¥\80 à¤\9cवन à¤\85सà¥\8dतितà¥\8dव à¤®à¥\87à¤\82 à¤¨à¤\87à¤\96à¥\87।\nसà¤\82भव à¤¬à¤¾ à¤\95ि à¤\9cब à¤\86प à¤ªà¤¨à¥\8dना à¤ªà¤¢à¤¤ à¤¯à¤¾ à¤¦à¥\87à¤\96त à¤°à¤¹à¤¨à¥\80 à¤¤à¤µà¤¨à¥\87 à¤\98ड़à¥\80 à¤\89 à¤\95à¥\87 à¤\85पनà¥\80 à¤\9cà¤\97ह à¤¸à¥\87 à¤¹à¤¿à¤²à¤¾à¤µà¤² à¤\97à¤\87ल à¤¹à¥\8bà¤\96à¥\87 à¤¯à¤¾ à¤¹à¤\9fा à¤¦à¤¿à¤¹à¤² à¤\97à¤\88ल à¤¹à¥\8bà¤\88।",
+       "blockednoreason": "à¤\95वनà¥\8b à¤\95ारण à¤¨à¤\87à¤\96à¥\87 à¤¬à¤¤à¤¾à¤µà¤² à¤\97à¤\87ल",
+       "whitelistedittext": "पनà¥\8dना à¤ªà¤° à¤¸à¤\82पादन à¤\95रà¥\87 à¤\96ातिर $1 à¤\95रà¥\80à¤\82।",
+       "confirmedittext": "सà¤\82पादन à¤\95रà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤¿à¤²à¥\87 à¤\86पà¤\95à¥\87 à¤\86पन à¤\88मà¥\87ल à¤ªà¤¤à¤¾ à¤ªà¥\8dरमाणित à¤\95रावल à¤\9cरà¥\81रà¥\80 à¤¬à¤¾à¥¤\nà¤\86पन [[Special:Preferences|पसà¤\82द à¤¸à¥\87à¤\9fिà¤\82à¤\97]] à¤®à¥\87à¤\82 à¤\9cाà¤\95à¥\87 à¤\85ापन à¤\88मà¥\87ल à¤ªà¤¤à¤¾ à¤¸à¥\87à¤\9f à¤\95रà¥\80à¤\82 à¤\86 à¤\93à¤\95रा के प्रमाणित करीं।",
+       "nosuchsectiontitle": "à¤\96à¤\82ड à¤¨à¤¾ à¤®à¤¿à¤²à¤²",
+       "nosuchsectiontext": "à¤\86प à¤\8fà¤\97à¥\8b à¤\85à¤\87सन à¤\96à¤\82ड à¤\95à¥\87 à¤¸à¤\82पादन à¤\95रà¥\87 à¤\95à¥\87 à¤\95à¥\8bसिस à¤\95 à¤°à¤¹à¤² à¤¬à¤¾à¤¨à¥\80 à¤\9cवन à¤®à¥\8cà¤\9cà¥\82द à¤¨à¤\87à¤\96à¥\87।\nहà¥\8b à¤¸à¤\95त à¤¬à¤¾ à¤\95ि à¤\9cवना à¤\9bन à¤\86प à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96त à¤°à¤¹à¤²à¥\80à¤\82 à¤\93हà¥\80 à¤¸à¤®à¤¯ à¤\88 à¤\96à¤\82ड à¤\98सà¤\95ा à¤­à¤¾ à¤¹à¤\9fा à¤¦à¤¿à¤¹à¤² à¤\97à¤\87ल à¤¹à¥\8bà¤\96à¥\87।",
        "loginreqtitle": "खाता में प्रवेश जरुरी बा",
        "loginreqlink": "लॉग इन",
        "loginreqpagetext": "रउआ अन्य पन्ना देखे खातिर $1 करे के पड़ी।",
-       "accmailtitle": "गुप्त-शब्द भेजा गईल",
+       "accmailtitle": "गुप्तशब्द भेजाइल",
        "accmailtext": "[[User talk:$1|$1]] खातिर एगो यंत्र जनित गुप्तशब्द $2 के भेज दिहल गइल बा। खाता में प्रवेश कइला के बाद इ '''[[Special:ChangePassword|गुप्तशब्द बदल लीं]]'' वाला पन्ना पर बदलल जा सकत बा।",
        "newarticle": "(नया)",
-       "newarticletext": "रउआ एगो अइसन कड़ी के पन्ना के अनुसरण कइले बानी जवन अभी तक उपलब्ध नइखे।\nपन्ना बनावे खातिर, नीचे के बाकस में टाइप करे के शुरु करीं (ज्यादा जानकारी खातिर देखीं [$1 मदद पन्ना])।\nयदि रउआ अहिजा गलती से आ गइल बानी त, आपन ब्राउजर के '''बैक''' (Back) बटन दबाईं!",
+       "newarticletext": "रउआँ एगो अइसन कड़ी पकड़ के आइल बानी जवना से जुड़ल पन्ना अबहिन मौजूद नइखे।\nपन्ना बनावे खातिर नीचे के बाकस में टाइप करे शुरु करीं (ढेर जानकारी खातिर देखीं [$1 मदद पन्ना])।\nअगर रउआँ गलती से इहाँ आ गइल बानी, आपन ब्राउजर के '''बैक''' बटन दबाईं!",
        "anontalkpagetext": "----\n<em>ई अइसन बेनाम प्रयोगकर्ता के बातचीत पन्ना बा जे या त अभी खाता नइखे बनवले या खाता के इस्तेमाल नइखे करत।</em>\nएही कारन ओह ब्यक्ति के पहिचान खातिर ई अंक वाला आइपी पता इस्तेमाल करे के परत बा।\nअइसन आइपी पता कई लोग के सझिया भी हो सकत बा।\nअगर आप बेनाम प्रयोगकर्ता बानी आ ई बुझाता कि कौनों बिना बात के कमेंट आपके ओर बा, [[Special:CreateAccount|खाता बनाईं]] या [[Special:UserLogin|लॉग इन]] करीं जेवना से भबिस्य में केहू रउवाँ पर केहू दूसर बेनाम प्रयोगकर्ता होखे के भरम न होखे।",
-       "noarticletext": "à¤\8f à¤ªà¤¨à¥\8dना à¤®à¥\87 à¤\85भà¥\80 à¤\95à¥\8cनà¥\8bà¤\82 à¤¸à¤¾à¤®à¤\97à¥\8dरà¥\80 à¤¨à¤\87à¤\96à¥\87।\nरà¤\89à¤\86à¤\81 à¤¦à¥\81सरा à¤ªà¤¨à¥\8dना à¤®à¥\87à¤\82 [[Special:Search/{{PAGENAME}}|à¤\8f à¤\9fाà¤\87à¤\9fिल à¤\95à¥\87 à¤\96à¥\8bà¤\9c]] à¤\95र à¤¸à¤\95त à¤¬à¤¾à¤¨à¥\80à¤\82,\nया <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} à¤¯à¤¾ à¤¸à¤\82बà¤\82धित à¤²à¥\89à¤\97 à¤\96à¥\8bà¤\9c à¤¸à¤\95त à¤¬à¤¾à¤¨à¥\80]</span>, à¤¬à¤¾à¤\95à¥\80 à¤°à¤\89à¤\86 के ई पन्ना बनावे के परमीशन नइखे।",
+       "noarticletext": "à¤\85बहिन à¤\8fह à¤ªà¤¨à¥\8dना à¤®à¥\87à¤\82 à¤\95à¥\8cनà¥\8bà¤\82 à¤¸à¤¾à¤®à¤\97à¥\8dरà¥\80 à¤¨à¤\87à¤\96à¥\87।\nरà¤\89à¤\86à¤\81 à¤¦à¥\81सरा à¤ªà¤¨à¥\8dना à¤®à¥\87à¤\82 [[Special:Search/{{PAGENAME}}|à¤\8f à¤\9fाà¤\87à¤\9fिल à¤\95à¥\87 à¤\96à¥\8bà¤\9c]] à¤\95र à¤¸à¤\95त à¤¬à¤¾à¤¨à¥\80à¤\82,\nया <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} à¤¸à¤\82बà¤\82धित à¤²à¥\89à¤\97 à¤\96à¥\8bà¤\9c à¤¸à¤\95त à¤¬à¤¾à¤¨à¥\80]</span>, à¤¬à¤¾à¤\95à¥\80 à¤°à¤\89à¤\86à¤\81 के ई पन्ना बनावे के परमीशन नइखे।",
        "noarticletext-nopermission": "ए पन्ना मे अभी कौनों सामग्री नइखे।\nरउआँ दुसरा पन्ना में [[Special:Search/{{PAGENAME}}|ए टाइटिल के खोज]] कर सकत बानीं,\nया <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} या संबंधित लॉग खोज सकत बानी]</span>, बाकी रउआ के ई पन्ना बनावे के परमीशन नइखे।",
        "missing-revision": "\"{{FULLPAGENAME}}\" पन्ना के संशोधन #$1 उपलब्ध नइखे।\n\nसाधारण रुप से इ एगो हटावल गइल पन्ना के पुरान लिंक पर क्लिक कइला से होखेला।\nअधिक जानकारी खातिर आप [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} हटावे के लॉग] देख सकत बानी।",
        "userpage-userdoesnotexist": "सदस्य खाता \"$1\" पंजीकृत नइखे।\nकृपया जाँच लीं कि आप इ पन्ना संपादित अथवा निर्मित करे के चाहत बानी कि ना।",
        "edit_form_incomplete": "<strong>संपादन के कुछ हिस्सा सर्वर ले ना पहुँच पावल ह; दोहरा के चेक करीं की राउर संपादन बदलल न होखे आ एक बेर फिर से सहेजे के कोसिस करीं।</strong>",
        "editing": "$1 संपादन",
        "creating": "$1 बनावे जा रहल बानी",
-       "editingsection": "$1 संपादन (खंड)",
+       "editingsection": "$1 (खंड) के संपादन कइल जात बा",
        "editingcomment": "$1 संपादन (नया खंड)",
        "editconflict": "संपादन अंतर्बिरोध: $1",
        "explainconflict": "आपके द्वारा संपादन सुरू कइला के बाद से केहू अउरी ए पन्ना में बदलाव क चुकल बाटे।\nऊपर के पाठ हिस्सा में लेख के पाठ के वर्तमान स्थिति देखावल जात बा।\nराउर संपादन नीचे के पाठ हिस्सा में देखावल गइल बा।\nरउआँ के आपन बदलाव एह पहिले से मौजूद पाठ में मिलावे के परी।\n<strong>खाली</strong> ऊपर की पाठ हिस्सा में लउकत पाठ सहेजल जाई अगर आप \"$1\" बटन दबाइब।",
        "undo-summary-username-hidden": "केहू छिपल सदस्य द्वारा कइल बदलाव $1 वापस कइल गइल",
        "cantcreateaccount-text": "एह आइपी पता (IP address)(<strong>$1</strong>) द्वारा नया खाता बनावे पर [[User:$3|$3]] द्वारा रोक लगावल गइल बा।\n\nएकरा खातिर $3 के दिहल कारण:<em>$2</em>",
        "cantcreateaccount-range-text": "आइपी पता बिस्तार (IP address range) <strong>$1</strong> पर, जेवना में आपके आइपी (<strong>$4</strong>) भी बा, नया खाता बनावे पर [[User:$3|$3]] द्वारा रोक लगावल गइल बा।\n\nएकरा खातिर $3 के दिहल कारण बा:<em>$2</em>",
-       "viewpagelogs": "à¤\8f à¤ªà¤¨à¥\8dना à¤\96ातिर à¤²à¥\89à¤\97 à¤\95à¥\81ल à¤¦à¥\87à¤\96à¥\80à¤\82",
+       "viewpagelogs": "ए पन्ना खातिर लॉग देखीं",
        "nohistory": "ए पन्ना के कौनों संपादन इतिहास नइखे",
        "currentrev": "सबसे नया बदलाव",
        "currentrev-asof": "$1 ले भइल नया बदलाव",
-       "revisionasof": "$1 à¤²à¥\87 à¤­à¤\87ल à¤¨à¤¯à¤¾ बदलाव",
+       "revisionasof": "$1 à¤¤à¤\95 à¤²à¥\87 à¤­à¤\87ल बदलाव",
        "revision-info": "{{GENDER:$6|$2}}$7 के द्वारा $1 के बदलाव",
        "previousrevision": "← पुरान बदलाव",
        "nextrevision": "नया बदलाव →",
        "revdelete-hide-text": "संशोधन पाठ्य",
        "revdelete-hide-image": "फाइल के सामग्री छुपाँईं",
        "revdelete-hide-name": "टारगेट आ पैरामीटर छिपाईं",
-       "revdelete-hide-comment": "साराà¤\82श à¤¸à¤®à¥\8dपादन",
+       "revdelete-hide-comment": "सà¤\82पादन à¤¸à¤¾à¤°à¤¾à¤\82श",
        "revdelete-hide-user": "सम्पादक के सदस्यनाम/आइ॰पी पता",
        "revdelete-hide-restricted": "डेटा के अउरी सदस्य सभ की साथै साथ प्रबंधक लोगन खातिर भी ढाँप दीं",
        "revdelete-radio-same": "(मत बदलीं)",
        "mergehistory-from": "स्रोत पन्ना:",
        "mergehistory-into": "लक्ष्य पन्ना:",
        "mergehistory-list": "विलय जोग्य संपादन इतिहास",
+       "mergehistory-go": "बिलय करे जोग संपादन देखावल जाव",
        "mergehistory-submit": "अवतरण विलय करीं",
        "mergehistory-empty": "कौनों अवतरण विलय नइखे कइल जा सकत।",
        "mergehistory-done": " $1 के $3 {{PLURAL:$3|अवतरण|अवतरण सभ}} सफलता से [[:$2]] में विलय भइल।",
        "mergehistory-fail": "इतिहास विलय करे में अक्षम, पन्ना आ एकर टाइम पैरामीटर चेक करीं।",
+       "mergehistory-fail-bad-timestamp": "समयमोहर अबैध बा।",
+       "mergehistory-fail-invalid-source": "स्रोत पन्ना अबैध बा।",
+       "mergehistory-fail-invalid-dest": "गंतब्य पन्ना अबैध बा।",
+       "mergehistory-fail-no-change": "इतिहास बिलय द्वारा कवनो रिवीजन के बिलय ना भइल। पन्ना आ टाइम पैरामीटर के दोबारा जाँच करीं।",
+       "mergehistory-fail-permission": "इतिहास बिलय खातिर पर्याप्त परमीशन नइखे।",
+       "mergehistory-fail-self-merge": "स्रोत आ गंतब्य पन्ना एकही बा।",
+       "mergehistory-fail-timestamps-overlap": "स्रोत रिवीजन या त गंतब्य रिवीजन के साथे ओभरलैप करत बा या बाद में आवत बा।",
+       "mergehistory-fail-toobig": "{{PLURAL:$1|रिवीजन|रिवीजन सभ}} के $1 के सीमा से ढेर रिवीजन घसकावे के पड़ी आ एही कारण इतिहास बिलय नइखे कइल जा सकत।",
+       "mergehistory-no-source": "स्रोत पन्ना $1 मौजूद नइखे।",
+       "mergehistory-no-destination": "गंतब्य पन्ना $1 मौजूद नइखे।",
+       "mergehistory-invalid-source": "स्रोत पन्ना एगो बैध टाइटिल होखे के चाहीं।",
+       "mergehistory-invalid-destination": "गंतब्य पन्ना एगो बैध टाइटिल होखे के चाहीं।",
        "mergehistory-autocomment": "[[:$1]] के [[:$2]] में विलय कइल गइल",
        "mergehistory-comment": "[[:$1]] के [[:$2]] में विलय कइल गइल: $3",
        "mergehistory-same-destination": "स्रोत आ लक्ष्य पन्ना एकही ना होखे सकत बा",
        "mergelogpagetext": "एक पन्ना इतिहास के दुसर पन्ना इतिहास में तुरंत विलय भइले के एगो सूची नीचे दिहल बा।",
        "history-title": "''$1'' के संशोधन इतिहास",
        "difference-title": "\"$1\" की अवतरण में अंतर",
+       "difference-title-multipage": "\"$1\" आ \"$2\" पन्ना सभ के बीच अंतर",
+       "difference-multipage": "(पन्नवन के बीच अंतर)",
        "lineno": "लाइन $1:",
        "compareselectedversions": "चुनल गइल संशोधन में अंतर देखीं",
        "showhideselectedversions": "चुनल गइल संशोधन के दृश्यता बदलीं",
        "editundo": "वापस लीं",
+       "diff-empty": "(कौनों अंतर नइखे)",
        "diff-multi-sameuser": "(एही सदस्य द्वारा कइल {{PLURAL:$1|बीच के एगो बदलाव|बीच के $1 बदलाव}} नइखे देखावल जात)",
        "searchresults": "खोज परिणाम",
        "searchresults-title": "$1 खातिर खोज परिणाम",
        "searchmenu-new": "<strong> ए विकि पर \"[[:$1]]\" नाँव के पन्ना बनाईं !</strong> {{PLURAL:$2|0=|अपनी खोज से मिलल पन्ना भी देखीं|खोज के परिणाम भी देखीं।}}",
        "searchprofile-articles": "सामग्री पन्ना",
        "searchprofile-images": "मल्टीमीडिया",
-       "searchprofile-everything": "सब कुछ",
-       "searchprofile-advanced": "नामस्थान अनुसार",
+       "searchprofile-everything": "सबकुछ",
+       "searchprofile-advanced": "नाà¤\81वस्थान अनुसार",
        "searchprofile-articles-tooltip": "$1 में खोजीं",
        "searchprofile-images-tooltip": "फाइल खातिर खोज",
        "searchprofile-everything-tooltip": "सब जगह खोजीं (बातचीत पन्ना सहित)",
-       "searchprofile-advanced-tooltip": "विशà¥\87ष à¤¨à¤¾à¤®स्थान में खोजीं",
+       "searchprofile-advanced-tooltip": "à¤\86पन à¤\9cरà¥\82रत à¤\95à¥\87 à¤¨à¤¾à¤\81वस्थान में खोजीं",
        "search-result-size": "$1 ({{PLURAL:$2|1 शब्द|$2 शब्द}})",
        "search-redirect": "($1 से अनुप्रेषित)",
        "search-section": "(खंड $1)",
        "search-category": "(श्रेणी $1)",
        "search-suggest": "का राउर मतलब बा: $1",
+       "search-rewritten": "$1 खातिर रिजल्ट। एकरे जगह $2 खातिर खोज करीं।",
        "search-interwiki-caption": "साथी प्रोजेक्ट सभ से रिजल्ट",
        "search-interwiki-default": "$1 से परिणाम:",
        "search-interwiki-more": "(अउर)",
        "powersearch-togglenone": "कउनो ना",
        "search-external": "बाहरी खोज",
        "preferences": "वरीयता",
-       "mypreferences": "हमार सेटिंग",
-       "prefs-edits": "सम्पादन संख्या",
-       "prefsnologintext2": "आपन वरीयता में बदलाव लावे खातिर प्रवेश करीं।",
-       "prefs-skin": "त्वचा",
-       "skin-preview": "पूर्वावलोकन",
-       "datedefault": "वरीयता नईखे",
-       "prefs-user-pages": "सदस्य पन्ना",
+       "mypreferences": "पसंदसेटिंग",
+       "prefs-edits": "संपादन संख्या",
+       "prefsnologintext2": "आपन पसंदसेटिंग बदले खातिर खाता में प्रवेश करीं।",
+       "prefs-skin": "स्किन",
+       "skin-preview": "झलक",
+       "datedefault": "कौनो खास पसंद नइखे",
+       "prefs-labs": "लैब्स के चीज",
+       "prefs-user-pages": "प्रयोगकर्ता पन्ना",
        "prefs-personal": "प्रयोगकर्ता प्रोफाइल",
        "prefs-rc": "हाल के बदलाव",
        "prefs-watchlist": "धियानसूची",
        "prefs-editwatchlist": "धियानसूची संपादन",
-       "prefs-editwatchlist-label": "à¤\85पनà¥\80 à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤\95à¥\87 à¤\9aà¥\80à¤\9c संपादित करीं:",
-       "prefs-editwatchlist-edit": "à¤\85पनà¥\80 à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤\95à¥\87 टाइटिल देखीं आ हटाईं",
+       "prefs-editwatchlist-label": "à¤\86पन à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤®à¥\87à¤\82 à¤¸à¤¾à¤®à¤¿à¤² à¤\8fà¤\82à¤\9fà¥\8dरà¥\80 संपादित करीं:",
+       "prefs-editwatchlist-edit": "à¤\86पन à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤®à¥\87à¤\82 à¤¸à¤¾à¤®à¤¿à¤² टाइटिल देखीं आ हटाईं",
        "prefs-editwatchlist-raw": "टटका धियानसूची संपादित करीं",
        "prefs-editwatchlist-clear": "आपन धियानसूची साफ करीं",
        "prefs-watchlist-days": "धियानसूची में देखावे खातिर दिन",
        "restoreprefs": "सगरी डिफाल्ट सेटिंग पहिले जइसन करीं (सगरी खंड में)",
        "prefs-editing": "संपादन",
        "searchresultshead": "खोज",
+       "stub-threshold": "आधार कड़ी फारमेटिंग($1) खातिर थ्रेशोल्ड:",
        "stub-threshold-sample-link": "नमूना",
        "stub-threshold-disabled": "अक्षम",
-       "recentchangesdays": "हाल à¤®à¥\87à¤\82 à¤­à¤\87ल à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन में देखावे खातिर दिन:",
+       "recentchangesdays": "हाल à¤\95à¥\87 à¤¬à¤¦à¤²à¤¾à¤µ में देखावे खातिर दिन:",
        "recentchangesdays-max": "अधिकतम $1{{PLURAL:$1|दिन}}",
        "recentchangescount": "डिफाल्ट में देखावे खातिर संपादन संख्या:",
-       "prefs-help-recentchangescount": "à¤\8fमà¥\8dमà¥\87à¤\82 à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन, à¤ªà¤¨à¥\8dना इतिहास, आ लॉग सब बाटे।",
+       "prefs-help-recentchangescount": "à¤\8fमà¥\8dमà¥\87à¤\82 à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ, à¤ªà¤¨à¥\8dना à¤\95à¥\87 इतिहास, आ लॉग सब बाटे।",
        "savedprefs": "राउर वरीयताएँ सुरक्षित कर दिहल गईल।",
        "timezonelegend": "समय जोन:",
        "localtime": "लोकल समय:",
        "userrights-user-editname": "प्रयोगकर्ता नाँव लिखीं:",
        "editusergroup": "प्रयोगकर्ता मंडली लोड करीं",
        "editinguser": "अधिकार बदलाव {{GENDER:$1|प्रयोगकर्ता}}<strong>[[User:$1|$1]]</strong> $2",
+       "viewinguserrights": "प्रयोगकर्ता अधिकार देखावल जात बा:{{GENDER:$1|प्रयोगकर्ता}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "{{GENDER:$1|प्रयोगकर्ता}} मंडली संपादित करीं",
+       "userrights-viewusergroup": "{{GENDER:$1|प्रयोगकर्ता}} मंडली देखीं",
+       "saveusergroups": "{{GENDER:$1|प्रयोगकर्ता}} मंडली सहेजीं",
+       "userrights-groupsmember": "सदस्यता हासिल बा:",
+       "userrights-groupsmember-auto": "निहित सदस्यता हासिल बा:",
+       "userrights-groups-help": "ई प्रयोगकर्ता कवना मंडली में रहिहें ई चीज आप बदल सकत बानी:\n* सही के निसान वाला बक्सा के मतलब बा एह मंडली में ई शामिल बाने।\n* बिना सही के निसान वाला बक्सा के मतलब बा एह मंडली में ई शामिल नइखें।\n* एक ठो * के चीन्हा अइसन अधिकार के सूचित करे ला जवना के आप एक बेर दे देइब त हटा नइखीं सकत, या एकरे ठीक उल्टा भी।\n* एक ठो # के चीन्हा सूचित करे ला कि एह मंडली के सदस्यता के खतम होखे के समय आप पाछे (बाद में) क सकत बानी लेकिन आगे नइखीं ले आ सकत।",
        "userrights-reason": "कारण:",
-       "group": "मंडली (ग्रुप):",
+       "userrights-no-interwiki": "अन्य विकि सभ पर सदस्य मंडली के संपादन करे खातिर रउवाँ लगे परमीशन नइखे।",
+       "userrights-changeable-col": "जवन मंडली आप बदल सकत बानी",
+       "userrights-unchangeable-col": "जवन मंडली आप नइखीं बदल सकत",
+       "userrights-expiry-current": "$1 के खतम होखी",
+       "userrights-expiry-none": "एक्सपायर ना होखी",
+       "userrights-expiry": "एक्सपायर हो जाई:",
+       "userrights-expiry-existing": "वर्तमान में एकरा एक्सपायर होखे के टाइम बा: $3, $2",
+       "userrights-expiry-othertime": "अन्य समय:",
+       "userrights-expiry-options": "1 दिन:1 दिन,1 हप्ता:1 हप्ता,1 महीना:1 महीना,3 महीना:3 महीना,6 महीना:6 महीना,1 बरिस:1 बरिस",
+       "userrights-invalid-expiry": "मंडली \"$1\" खातिर एक्सपायरी समय अबैध बा।",
+       "group": "मंडली:",
        "group-user": "सदस्य",
-       "group-autoconfirmed": "à¤\96à¥\81द à¤ªà¤°à¥\80à¤\95à¥\8dषित à¤¸à¤¦à¤¸à¥\8dय",
+       "group-autoconfirmed": "सà¥\8bतà¤\83परमाणित à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता",
        "group-bot": "बॉट",
        "group-sysop": "प्रबंधक",
        "group-bureaucrat": "ब्यूरोक्रेट",
        "group-suppress": "सप्रेसर",
        "group-all": "(सब)",
        "group-user-member": "{{GENDER:$1|सदस्य}}",
-       "group-autoconfirmed-member": "{{GENDER:$1|à¤\96à¥\81द à¤\85सà¥\8dथापित à¤¸à¤¦à¤¸à¥\8dय}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|सà¥\8bतà¤\83परमाणित à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता}}",
        "group-bot-member": "{{GENDER:$1|बॉट}}",
        "group-sysop-member": "{{GENDER:$1|प्रबंधक}}",
        "group-bureaucrat-member": "{{GENDER:$1|प्रशासक}}",
        "group-suppress-member": "{{GENDER:$1|सप्रेस}}",
        "grouppage-user": "{{ns:project}}:सदस्य सभ",
-       "grouppage-autoconfirmed": "{{ns:project}}:à¤\96à¥\81द à¤\85सà¥\8dथापित à¤¸à¤¦à¤¸à¥\8dय à¤¸à¤­",
+       "grouppage-autoconfirmed": "{{ns:project}}:सà¥\8bतà¤\83परमाणित à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता",
        "grouppage-bot": "{{ns:project}}:बॉट सभ",
        "grouppage-sysop": "{{ns:project}}:प्रबंधक सभ",
        "grouppage-bureaucrat": "{{ns:project}}:प्रशासक सभ",
        "right-createpage": "पन्ना बनाईं (बातचीत पन्ना की अलावा)",
        "right-createtalk": "बातचीत पन्ना बनाईं",
        "right-createaccount": "नया सदस्य खाता बनाईं",
-       "right-minoredit": "à¤\9bà¥\8bà¤\9f à¤¸à¤\82पादन चिह्नित करीं",
+       "right-minoredit": "सà¤\82पादन à¤\9bà¥\8bà¤\9f चिह्नित करीं",
        "right-move": "पन्ना स्थानांतरण करीं",
        "right-move-subpages": "पन्नवन के उनहन की उपपन्नवन की संघे स्थानांतरित करीं",
        "right-move-rootuserpages": "मूल (root) सदस्य पन्नवन के स्थानांतरित करीं",
        "right-movefile": "फाइल सब स्थानांतरित करीं",
        "right-suppressredirect": "स्थानांतरण करत घरी मूल पन्ना से पुनर्निदेश मत बनाईं",
        "right-upload": "फाइल अपलोड करीं",
-       "right-reupload": "पà¥\81रान à¤«à¤¾à¤\87ल à¤\95à¥\80 à¤\8aपर à¤¨à¤¯à¤¾ à¤²à¤¾à¤¦ीं",
-       "right-reupload-own": "खुदे लादल फाइल पर नया फाइल लादीं",
-       "right-reupload-shared": "लà¥\8bà¤\95ल à¤®à¥\87à¤\82 à¤¸à¤¾à¤\9dा à¤®à¥\80डिया à¤­à¤£à¥\8dडार à¤\95à¥\87 à¤«à¤¾à¤\87ल à¤¸à¤­ à¤\95à¥\87 ओवरराइड करीं",
+       "right-reupload": "मà¥\8cà¤\9cà¥\82द à¤«à¤¾à¤\87ल à¤ªà¤° à¤\93भरराà¤\87à¤\9f à¤\95रीं",
+       "right-reupload-own": "खुद के अपलोड कइल फाइल ओभरराइट करीं",
+       "right-reupload-shared": "साà¤\9dा à¤®à¥\80डिया à¤­à¤\82डार à¤\95à¥\87 à¤«à¤¾à¤\87ल à¤¸à¤­ à¤\95à¥\87 à¤²à¥\8bà¤\95ल ओवरराइड करीं",
        "right-upload_by_url": "यूआरयल से फाइल अपलोड करीं",
        "right-purge": "बिना पुछले कौनों पन्ना के साइट कैश के फिर लोड करीं",
-       "right-autoconfirmed": "आइ पी आधारित रेट के सीमा से प्रभावित ना होखे",
+       "right-autoconfirmed": "आइपी-आधारित रेट सीमा से ना परभावित",
        "right-bot": "ऑटोमेटेड प्रोसेस मानल जाय",
-       "right-writeapi": "API लेखन के इस्तेमाल",
+       "right-writeapi": "एपीआइ लेखन के इस्तेमाल",
        "right-delete": "पन्ना हटाईं",
        "right-bigdelete": "लंबा इतिहास वाला पन्ना हटाईं",
        "right-deletelogentry": "लॉग आइटम के हटाईं या वापस करीं",
        "action-movefile": "ई फाइल स्थानांतरित करीं",
        "action-upload": "इ फाइल अपलोड करीं",
        "action-reupload": "पहिले से मौजूद ए फाइल पर दूसर लादीं",
-       "action-delete": "ई पन्ना के मिटाईं",
+       "action-delete": "ए पन्ना के मिटाईं",
+       "action-deleterevision": "रिवीजन मेटाईं",
+       "action-deletelogentry": "लॉग के एंट्री मेटाईं",
        "action-unwatchedpages": "ध्यानसूची में जवन पन्ना नइखे ओकर सूची देखीं",
        "enhancedrc-history": "इतिहास",
        "recentchanges": "हाल के बदलाव",
        "rcshowhidecategorization": "$1 पन्ना श्रेणीकरण",
        "rcshowhidecategorization-show": "देखावल जाय",
        "rcshowhidecategorization-hide": "छिपावल जाय",
-       "rclinks": "पछिला $2 दिन में भइल $1 बदलाव देखावल जाय",
+       "rclinks": "पà¤\9bिला $2 à¤¦à¤¿à¤¨ à¤®à¥\87à¤\82 à¤­à¤\87ल $1 à¤¸à¤­à¤¸à¥\87 à¤¤à¤¾à¤\9cा à¤¬à¤¦à¤²à¤¾à¤µ à¤¦à¥\87à¤\96ावल à¤\9cाय",
        "diff": "अंतर",
        "hist": "इतिहास",
        "hide": "छिपावल जाय",
        "recentchangeslinked-feed": "संबंधित बदलाव",
        "recentchangeslinked-toolbox": "संबंधित बदलाव",
        "recentchangeslinked-title": "\"$1\" संबंधी बदलाव",
-       "recentchangeslinked-summary": "à¤\88 à¤\8fà¤\97à¥\8b à¤ªà¤¨à¥\8dना à¤¬à¤¿à¤¸à¥\87स à¤¸à¥\87 (या à¤\8fà¤\97à¥\8b à¤¶à¥\8dरà¥\87णà¥\80 à¤¬à¤¿à¤¸à¥\87स à¤\95à¥\80 à¤¸à¤¦à¤¸à¥\8dय à¤¸à¥\87) à¤\9cà¥\81ड़ल à¤ªà¤¨à¥\8dनवन à¤®à¥\87à¤\82 à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¥\87 à¤\8fà¤\97à¥\8b à¤²à¤¿à¤¸à¥\8dà¤\9f à¤¹à¤µà¥\87। \n[[Special:Watchlist|à¤\86पà¤\95à¥\80 à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80]] à¤\95à¥\87 à¤ªà¤¨à¥\8dना à¤¸à¤¬ <strong>मà¥\8bà¤\9f à¤\85à¤\9aà¥\8dà¤\9bर à¤®à¥\87à¤\82</strong> à¤²à¤\89à¤\95त à¤¬à¤¾à¤\9fे।",
+       "recentchangeslinked-summary": "à¤\88 à¤\8fà¤\97à¥\8b à¤ªà¤¨à¥\8dना à¤¬à¤¿à¤¸à¥\87स à¤¸à¥\87 (या à¤\8fà¤\97à¥\8b à¤¶à¥\8dरà¥\87णà¥\80 à¤¬à¤¿à¤¸à¥\87स à¤\95à¥\80 à¤¸à¤¦à¤¸à¥\8dय à¤¸à¥\87) à¤\9cà¥\81ड़ल à¤ªà¤¨à¥\8dनवन à¤®à¥\87à¤\82 à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¥\87 à¤\8fà¤\97à¥\8b à¤²à¤¿à¤¸à¥\8dà¤\9f à¤¹à¤µà¥\87। \n[[Special:Watchlist|à¤\86पà¤\95à¥\80 à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80]] à¤\95à¥\87 à¤ªà¤¨à¥\8dना à¤¸à¤¬ <strong>मà¥\8bà¤\9f à¤\85à¤\9aà¥\8dà¤\9bर à¤®à¥\87à¤\82</strong> à¤²à¤\89à¤\95त à¤¬à¤¾à¤¨े।",
        "recentchangeslinked-page": "पन्ना नाँव:",
        "recentchangeslinked-to": "एकरे बजाय ए पन्ना से जुड़ल पन्नवन मे भइल बदलाव देखावल जाय",
        "recentchanges-page-added-to-category": "[[:$1]] श्रेणी में जोड़ल गइल",
        "uploadlogpage": "लदनी (अपलोड) के लॉग",
        "uploadlogpagetext": "नीचे हाल में अपलोड करल गइल फाइलन के सूची बा।\nदृश्य अवलोकन खातिर [[Special:NewFiles|नया फाइलन के गैलरी]] देखीं।",
        "filename": "फाइलनाँव",
-       "filedesc": "à¤\9bà¥\8bà¤\9f à¤µà¤¿à¤µà¤°à¤£ (साराà¤\82श)",
+       "filedesc": "साराà¤\82श",
        "fileuploadsummary": "सारांश:",
        "filereuploadsummary": "फाइल बदलाव:",
        "filestatus": "कापीराइट स्टेटस",
        "listfiles_count": "संस्करण",
        "listfiles-latestversion": "वर्तमान संस्करण",
        "file-anchor-link": "फाइल",
-       "filehist": "पनà¥\8dना के इतिहास",
+       "filehist": "फाà¤\87ल के इतिहास",
        "filehist-help": "ओ समय ई फाइल कइसन लउके ई देखे खातिर कौनों तारीख/समय पर क्लिक करीं।",
        "filehist-deleteall": "सब मिटाईं",
        "filehist-deleteone": "मिटाईं",
        "filehist-thumb": "चिप्पी रूप",
        "filehist-thumbtext": "$1 ले के संस्करण के चिप्पी रूप।",
        "filehist-nothumb": "बिन थम्बनेल",
-       "filehist-user": "सदसà¥\8dय",
+       "filehist-user": "पà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता",
        "filehist-dimensions": "आयाम",
        "filehist-filesize": "फाईल के आकार",
        "filehist-comment": "टिप्पणी",
        "shared-repo-from": "$1 से",
        "shared-repo": "एगो आवटिंत भंडार गृह",
        "shared-repo-name-wikimediacommons": "विकिमीडिया कॉमन्स",
-       "upload-disallowed-here": "आप ए फाइल पर ओवरराइट ना कर सकत बानीं",
+       "upload-disallowed-here": "आप ए फाइल पर ओवरराइट ना कर सकत बानीं",
        "filerevert": "पुन: निर्देशित $1",
        "filerevert-legend": "पुन: निर्देशित फाईल",
        "filerevert-comment": "कारण:",
        "filedelete-success": "'''$1''' के मिटा दिहल गईल बा।",
        "filedelete-nofile": "'''$1''' उपलब्ध नईखे।",
        "mimesearch": "MIME खोज",
-       "unwatchedpages": "धà¥\8dयान à¤¨ à¤¦à¤¿à¤¹à¤² à¤\97à¤\87ल पन्ना",
-       "listredirects": "पà¥\81नरà¥\8dनिरà¥\8dदà¥\87शन à¤\95à¥\87 à¤¸à¥\82à¤\9aà¥\80",
-       "unusedtemplates": "बिना à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95à¥\87 à¤\96ाà¤\81à¤\9aा",
+       "unwatchedpages": "बिना à¤§à¤¿à¤¯à¤¾à¤¨à¤¸à¥\82à¤\9aà¥\80 à¤®à¥\87à¤\82 à¤\9cà¥\8bड़ल पन्ना",
+       "listredirects": "à¤\85नà¥\81पà¥\8dरà¥\87षण à¤ªà¤¨à¥\8dना à¤¸à¤­ à¤\95à¥\87 à¤²à¤¿à¤¸à¥\8dà¤\9f",
+       "unusedtemplates": "बिना à¤\87सà¥\8dतà¥\87माल à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f",
        "randompage": "अट्रेंडम पन्ना",
        "randomincategory": "श्रेणी में अनियमित पन्ना",
        "randomincategory-nopages": "[[:Category:$1|$1]] श्रेणी में कउनो पन्ना नइखे।",
        "nimagelinks": "Used on $1 {{PLURAL:$1|पन्ना|पन्ना सभ}}",
        "ntransclusions": "used on $1 {{PLURAL:$1|पन्ना|पन्ना सभ}}",
        "specialpage-empty": "एह रिपोर्ट खातिर कौनों रिजल्ट नइखे",
-       "lonelypages": "à¤\85नाथ पन्ना",
+       "lonelypages": "à¤\85à¤\95à¥\87ल à¤\9bà¥\82à¤\9fल पन्ना",
        "lonelypagestext": "ई पन्ना कुल कौनों दूसर पन्ना से नइखें जुड़ल न कौनों में ट्रांसक्लूड बाड़ें",
        "uncategorizedpages": "बिना श्रेणी के पन्ना",
        "uncategorizedcategories": "बिना श्रेणी के श्रेणी",
        "uncategorizedtemplates": "बिना श्रेणी के टेम्पलेट",
        "unusedcategories": "बिना इस्तेमाल श्रेणी",
        "unusedimages": "बिना इस्तेमाल फाइल",
-       "wantedcategories": "शà¥\8dरà¥\87णà¥\80 à¤\9aाहत à¤¬à¤¾",
-       "wantedpages": "पनà¥\8dना à¤\9aाहत à¤¬à¤¾",
-       "wantedfiles": "à¤\9cरà¥\81रत à¤\95à¥\87 à¤«à¤¾à¤\88लसभ",
+       "wantedcategories": "à¤\9cरà¥\82रत à¤\95à¥\87 à¤¶à¥\8dरà¥\87णà¥\80 à¤¸à¤­",
+       "wantedpages": "à¤\9cरà¥\82रत à¤\95à¥\87 à¤ªà¤¨à¥\8dना à¤¸à¤­",
+       "wantedfiles": "à¤\9cरà¥\82रत à¤\95à¥\87 à¤«à¤¾à¤\87ल सभ",
        "wantedfiletext-nocat-noforeign": "ई फाइल सभ इस्तेमाल में बाटे बाकी मौजूद नइखे।",
-       "wantedtemplates": "à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f à¤\9cिनहन à¤\95à¥\87 à¤\9cरà¥\82रत à¤¬à¤¾",
+       "wantedtemplates": "à¤\9cरà¥\82रत à¤\95à¥\87 à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f à¤¸à¤¬",
        "mostlinked": "पन्ना जहाँ सभसे ढेर कड़ी आ के जुड़त बा",
        "mostlinkedcategories": "सबसे अधिक से जुड़ल श्रेणी",
        "mostlinkedtemplates": "सभसे ढेर ट्रांसक्लूड पन्ना",
        "mostimages": "पन्ना जिनहन पर सभसे ढेर फाइल बा",
        "mostinterwikis": "पन्ना जिनहन में सभसे ढेर इंटरविकि कड़ी बा",
        "mostrevisions": "सभसे ढेर बदलाव इतिहास वाला पन्ना",
-       "prefixindex": "सà¤\97रà¥\80 à¤ªà¤¨à¥\8dना à¤\9cिनहन à¤®à¥\87à¤\82 à¤\89पसरà¥\8dà¤\97 à¤¬à¤¾",
+       "prefixindex": "सà¥\81रà¥\81à¤\86तà¥\80 à¤\85à¤\95à¥\8dषर à¤\85नà¥\81सार à¤ªà¤¨à¥\8dना à¤\96à¥\8bà¤\9cà¥\80à¤\82",
        "prefixindex-namespace": "सगरी पन्ना जिनहन में उपसर्ग ($1 नाँवस्थान) बा",
        "prefixindex-submit": "देखीं",
-       "shortpages": "à¤\9bà¥\8bà¤\9f à¤ªà¤¨à¥\8dना",
-       "longpages": "लमहर à¤ªà¤¨à¥\8dना",
+       "shortpages": "सभसà¥\87 à¤\9bà¥\8bà¤\9f à¤ªà¤¨à¥\8dना à¤¸à¤¬",
+       "longpages": "सभसà¥\87 à¤²à¤®à¤¹à¤° à¤ªà¤¨à¥\8dना à¤¸à¤¬",
        "deadendpages": "अंतिम छोर पर छूटल पन्ना",
        "protectedpages": "सुरक्षित पन्ना",
        "protectedpages-noredirect": "अनुप्रेषण छिपाईं",
        "usereditcount": "$1 {{PLURAL:$1|संपादन|संपादन सभ}}",
        "newpages": "नया पन्ना",
        "newpages-username": "प्रयोगकर्तानाँव:",
-       "ancientpages": "पà¥\81रान पन्ना",
+       "ancientpages": "सबसà¥\87 à¤ªà¥\81रान à¤¸à¤\82शà¥\8bधन à¤µà¤¾à¤²à¤¾ पन्ना",
        "move": "स्थानांतरण",
        "movethispage": "एह पन्ना के स्थानांतरण करीं",
-       "suppress": "ओवरसाइटर",
+       "pager-newer-n": "{{PLURAL:$1|नया 1|नया $1}}",
+       "pager-older-n": "{{PLURAL:$1|पुरान 1|पुरान $1}}",
+       "suppress": "सप्रेस",
        "apihelp": "एपीआइ (API) मदद",
        "apihelp-no-such-module": "मॉड्युल $1 ना मिलल।",
        "booksources": "किताबी स्रोत",
        "watch": "धियानसूची में डालीं",
        "watchthispage": "ई पन्ना ध्यानसूची में डालीं",
        "unwatch": "धियानसूची से हटाईं",
+       "wlshowlast": "पछिला $1 घंटा $2 दिन देखावल जाय",
        "watching": "ध्यानसूची में जाते हुए",
        "unwatching": "ध्यानसूची से हटते हुए",
        "created": "बनावल गईल",
        "rollbacklinkcount": "रोलबैक $1 {{PLURAL:$1|संपादन|संपादन सब}}",
        "protectlogpage": "सुरक्षा लॉग",
        "protectlogtext": "नीचे पन्ना सुरक्षा में भइल बदलावकुल के सूची बा।\nहाल में सुरक्षित पन्नन के सूची खातिर [[Special:ProtectedPages|सुरक्षित पन्नन के सूची]] देखीं।",
+       "protectedarticle": "\"[[$1]]\" सुरक्षित कइल गइल",
        "restriction-move": "स्थानांतरण",
        "restriction-create": "बनावे पर",
        "restriction-upload": "अपलोड",
        "undeletelink": "देखीं/बहाल करीं",
        "namespace": "नाँवस्थान:",
        "invert": "चुनाव पलटीं",
-       "tooltip-invert": "à¤\9aà¥\81नल à¤\97à¤\87ल à¤¨à¤¾à¤®à¤¸à¥\8dथान à¤\95à¥\80 à¤¸à¤\82à¤\98à¥\87 à¤¬à¤¦à¤²à¤¾à¤µ à¤\9bिपावà¥\87 à¤\96ातिर à¤\8f à¤¬à¤¾à¤\95स à¤\95à¥\87 à¤\9aिनà¥\8dहित à¤\95रà¥\80à¤\82 (समà¥\8dबनà¥\8dधित à¤¨à¤¾à¤®स्थान के भी, अगर चिन्हित होखे)",
-       "namespace_association": "सà¤\82बà¤\82धित à¤¨à¤¾à¤®स्थान",
-       "tooltip-namespace_association": "à¤\9aà¥\81नल à¤\97à¤\87ल à¤¨à¤¾à¤®à¤¸à¥\8dथान à¤¸à¥\87 à¤\9cà¥\81ड़ल à¤¬à¤¾à¤¤à¤\9aà¥\80त à¤¯à¤¾ à¤¬à¤¿à¤¸à¤¯ à¤¨à¤¾à¤®स्थान के शामिल करे खातिर ए बाकस के चिन्हित करीं",
+       "tooltip-invert": "à¤\9aà¥\81नल à¤\97à¤\87ल à¤¨à¤¾à¤\81वसà¥\8dथान à¤\95à¥\87 à¤¬à¤¦à¤²à¤¾à¤µ à¤\9bिपावà¥\87 à¤\96ातिर à¤\8f à¤¬à¤¾à¤\95स à¤\95à¥\87 à¤\9aिनà¥\8dहित à¤\95रà¥\80à¤\82 (सà¤\82बà¤\82धित à¤¨à¤¾à¤\81वस्थान के भी, अगर चिन्हित होखे)",
+       "namespace_association": "सà¤\82बà¤\82धित à¤¨à¤¾à¤\81वस्थान",
+       "tooltip-namespace_association": "à¤\9aà¥\81नल à¤\97à¤\87ल à¤¨à¤¾à¤\81वसà¥\8dथान à¤¸à¥\87 à¤\9cà¥\81ड़ल à¤¬à¤¾à¤¤à¤\9aà¥\80त à¤¯à¤¾ à¤¬à¤¿à¤¸à¤¯ à¤¨à¤¾à¤\81वस्थान के शामिल करे खातिर ए बाकस के चिन्हित करीं",
        "blanknamespace": "(मुख्य)",
-       "contributions": "{{GENDER:$1|सदसà¥\8dय}} योगदान",
+       "contributions": "{{GENDER:$1|पà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता}} à¤\95à¥\87 योगदान",
        "contributions-title": " $1 खातिर प्रयोगकर्ता योगदान",
-       "mycontris": "हमार à¤¯à¥\8bà¤\97दान",
+       "mycontris": "योगदान",
        "anoncontribs": "योगदान",
+       "contribsub2": "{{GENDER:$3|$1}} ($2) खातिर",
        "nocontribs": "ई मानदंड से मिलत जुलत कौनो बदलाव ना मिलल।",
        "uctop": "(वर्तमान)",
        "month": "महीना से (आ ओ से पहिले):",
        "sp-contributions-newbies-title": "नया खाता खातिर प्रयोगकर्ता के योगदान।",
        "sp-contributions-blocklog": "ब्लॉक लॉग",
        "sp-contributions-deleted": "नष्ट प्रयोगकर्ता के योगदान।",
+       "sp-contributions-uploads": "अपलोड",
        "sp-contributions-logs": "लॉग",
        "sp-contributions-talk": "बातचीत",
        "sp-contributions-userrights": "प्रयोगकर्ता अधिकार प्रबन्धन",
        "sp-contributions-blocked-notice": "ई प्रयोगकर्ता के ई समय निष्क्रीय करल गईल बा।\nनविनतम नष्ट लौग प्रविष्टी उद्धरण खातिर निचे दिहल बा:",
+       "sp-contributions-search": "योगदान खातिर खोज करीं",
+       "sp-contributions-username": "आइपी पता भा प्रयोगकर्तानाँव:",
+       "sp-contributions-newonly": "खाली उहे संपादन देखीं जेकरा से नया पन्ना बनल होखे",
+       "sp-contributions-submit": "खोजीं",
        "whatlinkshere": "इहाँ का जुड़ल बा",
        "whatlinkshere-title": "पन्ना जेवन \"$1\" से जुड़ल बा",
        "whatlinkshere-page": "पन्ना:",
-       "linkshere": "नीचे के सब पन्ना '''[[:$1]]''' से जुड़ल बा:",
+       "linkshere": "<strong>[[:$1]]</strong> से नीचे दिहल पन्ना जुड़ल बाने:",
        "nolinkshere": "'''[[:$1]]''' से कौनो पन्ना नईखे जुड़ल।",
        "nolinkshere-ns": "चुनल गईल सन्दर्भ में '''[[:$1]]''' से कौनो पन्ना ना जुड़ेला।",
        "isredirect": "अनुप्रेषित पन्ना",
-       "istemplate": "à¤\9fà¥\8dराà¤\82सà¥\8dà¤\95à¥\8dलà¥\8dयà¥\82à¤\9cन",
+       "istemplate": "ट्रांस्क्लूजन",
        "isimage": "फाइल कड़ी",
        "whatlinkshere-prev": "{{PLURAL:$1|पछिला|पछिला $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अगिला|अगिला $1}}",
        "whatlinkshere-hideredirs": "$1 अनुप्रेषण",
        "whatlinkshere-hidetrans": "$1 ट्रांसक्लूजन",
        "whatlinkshere-hidelinks": "$1 कड़ी",
-       "whatlinkshere-hideimages": "$1 à¤«à¤¼à¤¾à¤\87ल à¤²à¤¿à¤\82à¤\95",
+       "whatlinkshere-hideimages": "$1 à¤«à¤¾à¤\87ल à¤\95ड़à¥\80",
        "whatlinkshere-filters": "छननी",
        "blockip": "{{GENDER:$1|सदस्य}} अवरोधित करीं",
        "ipboptions": "२ घंटे:2 hours,१ दिन:1 day,३ दिन:3 days,१ हफ्ता:1 week,२ हफ्ते:2 weeks,१ महिना:1 month,३ महिने:3 months,६ महिने:6 months,१ साल:1 year,हमेशा खातिर:infinite",
        "blocklist": "अवरोधित प्रयोगकर्तासभ",
-       "blocklink": "रà¥\8bà¤\95 à¤¦à¥\80ं",
+       "blocklink": "रà¥\8bà¤\95 à¤²à¤\97ाà¤\88ं",
        "unblocklink": "ताला खोलीं",
        "change-blocklink": "ब्लॉक बदलीं",
        "contribslink": "योगदान",
        "movelogpage": "स्थानांतरण लॉग",
        "revertmove": "पिछलका स्थिति",
        "export": "पन्ना निर्यात करीं",
-       "thumbnail-more": "बढ़ाईं",
+       "thumbnail-more": "फà¤\87लाईं",
        "filemissing": "फाईल गायब",
        "import": "पन्ना आयात करीं",
        "importinterwiki": "दुसरा विकि से आयात करीं",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनसभ}} लावल गइल",
        "tooltip-pt-userpage": "{{GENDER:|राउर सदस्य}} पन्ना",
        "tooltip-pt-mytalk": "{{GENDER:|राउर}} बातचीत पन्ना",
-       "tooltip-pt-preferences": "{{GENDER:|राउर}} पसंद",
+       "tooltip-pt-preferences": "{{GENDER:|राउर}} पसंद के सेटिंग",
        "tooltip-pt-watchlist": "राउर धियानसूची में पन्ना सब के लिस्ट",
        "tooltip-pt-mycontris": "{{GENDER:|राउर}} योगदान के एगो लिस्ट",
-       "tooltip-pt-login": "रउआ के खाता मे प्रवेश (लॉग इन) खातिर उत्साहित कइल जात बा, बाकि ई जरूरी नइखे",
+       "tooltip-pt-login": "रउआँ से खाता मे प्रवेश करे के आगरह बा; हालाँकि, ई जरूरी नइखे",
        "tooltip-pt-logout": "लॉग आउट",
-       "tooltip-pt-createaccount": "हमनà¥\80 à¤\95à¥\87 à¤¸à¥\81à¤\9dाव à¤¬à¤¾ à¤\95à¥\80 à¤\86प à¤\96ाता à¤¬à¤¨à¤¾à¤\88à¤\82 à¤\86 à¤²à¥\89à¤\97 à¤\87न à¤\95रà¥\80à¤\82, à¤¬à¤¾à¤\95à¥\80 ई जरूरी नइखे",
-       "tooltip-ca-talk": "सामग्री पन्ना की बारे में बात-चीत",
+       "tooltip-pt-createaccount": "हमनà¥\80 à¤\95à¥\87 à¤¸à¤²à¤¾à¤¹ à¤¬à¤¾ à¤\95à¥\80 à¤\86प à¤\96ाता à¤¬à¤¨à¤¾à¤\88à¤\82 à¤\86 à¤\96ाता à¤®à¥\87à¤\82 à¤ªà¥\8dरवà¥\87श à¤\95रà¥\80à¤\82; à¤¹à¤¾à¤²à¤¾à¤\81à¤\95ि, ई जरूरी नइखे",
+       "tooltip-ca-talk": "सामग्री पन्ना की बारे में बातचीत",
        "tooltip-ca-edit": "ए पन्ना के संपादन करीं",
        "tooltip-ca-addsection": "एगो नया खंड शुरु करीं",
-       "tooltip-ca-viewsource": "à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤¸à¥\81रà¤\95à¥\8dषित à¤\95à¤\87 à¤¦à¤¿à¤¹ल गइल बा। आप एकर स्रोत देख सकत बानी।",
+       "tooltip-ca-viewsource": "à¤\88 à¤ªà¤¨à¥\8dना à¤¸à¥\81रà¤\95à¥\8dषित à¤\95à¤\87ल गइल बा। आप एकर स्रोत देख सकत बानी।",
        "tooltip-ca-history": "ए पन्ना के पछिला संशोधन",
        "tooltip-ca-protect": "इ पन्ना के संरक्षित करीं।",
        "tooltip-ca-unprotect": "ई पन्ना के सुरक्षा बदलीं।",
        "tooltip-ca-delete": "ई पन्ना मिटाईं",
-       "tooltip-ca-move": "ए पन्ना के स्थानांतरण करीं",
+       "tooltip-ca-move": "ए पन्ना के स्थानांतरण करीं",
        "tooltip-ca-watch": "ए पन्ना के अपनी धियानसूची में जोड़ीं",
-       "tooltip-search": "{{SITENAME}} खोजीं",
-       "tooltip-search-go": "अगर पन्ना मौजूद होखे त ठीक एही नाँव की पन्ना पर जाईं",
-       "tooltip-search-fulltext": "ए शब्द/वाक्य खातिर पन्ना खोजीं",
+       "tooltip-ca-unwatch": "ई पन्ना अपना धियानसूची से हटाईं",
+       "tooltip-search": "{{SITENAME}} में खोजीं",
+       "tooltip-search-go": "अगर ठीक एही नाँव के पन्ना मौजूद होखे तब ओहपर जाईं",
+       "tooltip-search-fulltext": "अइसन पन्ना खोजीं जिनहन में ई पाठ (शब्द भा वाक्य) बाटे",
        "tooltip-p-logo": "मुख्य पन्ना पर जाईं",
        "tooltip-n-mainpage": "मुख्य पन्ना पर जाईं",
        "tooltip-n-mainpage-description": "मुख्य पन्ना पर जाईं",
        "tooltip-n-portal": "प्रोजेक्ट की बारे में, रउआँ का कर सकत बानी, कौनों चीज कहाँ खोजब",
-       "tooltip-n-currentevents": "वरà¥\8dतमान à¤\95à¥\87 à¤\98à¤\9fना à¤ªà¤° à¤ªà¥\83षà¥\8dठभà¥\82मà¥\80 à¤\9cानà¤\95ारà¥\80 à¤\96à¥\8bà¤\9cà¥\80à¤\82",
-       "tooltip-n-recentchanges": "विà¤\95ि à¤ªà¤° à¤¤à¥\81रà¤\82त à¤­à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ के लिस्ट",
+       "tooltip-n-currentevents": "हाल à¤®à¥\87à¤\82 à¤\98à¤\9fित à¤\98à¤\9fना à¤¸à¤­ à¤¸à¥\87 à¤\9cà¥\81ड़ल à¤\9cानà¤\95ारà¥\80 à¤¦à¥\87à¤\96ल à¤\9cाय",
+       "tooltip-n-recentchanges": "à¤\8fह à¤µà¤¿à¤\95ि à¤ªà¤° à¤¹à¤¾à¤² à¤®à¥\87à¤\82 à¤­à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ à¤¸à¤­ के लिस्ट",
        "tooltip-n-randompage": "कौनों एगो पन्ना अट्रेंडम लोड करीं",
-       "tooltip-n-help": "à¤\9cà¤\97ह à¤ªà¤¤à¤¾ à¤²à¤\97ावà¥\87 à¤\96ातिर",
+       "tooltip-n-help": "à¤\9cानà¤\95ारà¥\80 à¤¹à¤¾à¤¸à¤¿à¤² à¤\95रà¥\87 à¤\96ातिर à¤\9cà¤\97ह",
        "tooltip-t-whatlinkshere": "इहाँ जुड़े वाला सब विकि पन्नवन के लिस्ट",
        "tooltip-t-recentchangeslinked": "ए पन्ना से जुड़ल पन्नवन पर तुरंत भइल बदलाव",
        "tooltip-feed-atom": "ए पन्ना खातिर एटम फीड",
        "tooltip-t-contributions": "{{GENDER:$1|एह सदस्य}} के योगदान के एक ठो लिस्ट",
        "tooltip-t-upload": "फाइल अपलोड करीं",
-       "tooltip-t-specialpages": "खास पन्नवन के लिस्ट",
-       "tooltip-t-print": "à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤\9bापà¥\87 à¤²à¤¾à¤¯à¤\95 à¤¸à¤\82सà¥\8dà¤\95रण",
+       "tooltip-t-specialpages": "सà¤\97रà¥\80 à¤\96ास à¤ªà¤¨à¥\8dनवन à¤\95à¥\87 à¤²à¤¿à¤¸à¥\8dà¤\9f",
+       "tooltip-t-print": "à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤\9bापà¥\87 à¤²à¤¾à¤¯à¤\95 à¤µà¤°à¥\8dशन",
        "tooltip-t-permalink": "ए पन्ना के संशोधन खातिर स्थायी कड़ी।",
        "tooltip-ca-nstab-main": "सामग्री पन्ना देखीं",
-       "tooltip-ca-nstab-user": "सदसà¥\8dय-पन्ना देखीं",
+       "tooltip-ca-nstab-user": "पà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता पन्ना देखीं",
        "tooltip-ca-nstab-special": "ई एगो खास पन्ना ह, एकर संपादन ना हो सकेला",
+       "tooltip-ca-nstab-project": "प्रोजेक्ट पन्ना देखीं",
        "tooltip-ca-nstab-image": "फाइल के पन्ना देखीं",
        "tooltip-ca-nstab-template": "टेम्पलेट देखीं",
        "tooltip-ca-nstab-category": "श्रेणी के पन्ना देखीं",
-       "tooltip-save": "बदलाव के सहेजीं",
-       "tooltip-preview": "à¤\86पन à¤¦à¥\8dवारा à¤\95ियल à¤\97à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¥\87 à¤¦à¥\87à¤\96à¥\80à¤\82, à¤¸à¤\82à¤\9cà¥\8bयà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤²à¥\87 à¤\88à¤\95ा à¤\87सà¥\8dतà¥\87माल à¤\95रà¥\80à¤\82!",
-       "tooltip-diff": "दà¥\87à¤\96ाà¤\88à¤\82 à¤\95ि à¤ªà¤¾à¤  à¤®à¥\87à¤\82 à¤\86प à¤\95वन à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¤\87लà¥\80à¤\82",
-       "tooltip-rollback": "\"वापिस à¤²à¥\80à¤\82\" à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤ªà¤\9bिला à¤¸à¤\82पादन à¤\95रà¥\87 à¤µà¤¾à¤²à¤¾ à¤\95à¥\87 à¤\95à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ à¤\8fà¤\95हà¥\80 à¤\95à¥\8dलिà¤\95 à¤®à¥\87à¤\82 à¤¹टा देला",
-       "tooltip-undo": "\"पलà¤\9fà¥\80à¤\82\" à¤\8f à¤¸à¤\82पादन à¤\95à¥\87 à¤ªà¤²à¤\9f à¤¦à¥\87ला à¤\86 à¤¸à¤\82पादन à¤«à¤¾à¤°à¥\8dम à¤\95à¥\87 à¤¨à¤®à¥\82ना (पà¥\8dरà¥\80वà¥\8dयà¥\82) à¤®à¥\8bड à¤®à¥\87à¤\82 à¤\96à¥\8bलà¥\87ला। à¤\88 à¤\9bà¥\8bà¤\9f à¤µà¤¿à¤µà¤°à¤£ (साराà¤\82श) à¤®à¥\87à¤\82 à¤\95ारण à¤\9cà¥\8bड़à¥\87 à¤\95à¥\87 à¤¸à¥\81बिधा देला।",
-       "tooltip-summary": "à¤\85पनà¥\80 à¤¸à¤\82पादन à¤\95à¥\87 à¤\9bà¥\8bà¤\9f à¤µà¤¿à¤µà¤°à¤£ (साराà¤\82श) लिखीं",
+       "tooltip-save": "à¤\9cवन à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¤\87लà¥\80à¤\82 à¤\93à¤\95रा के सहेजीं",
+       "tooltip-preview": "à¤\9cवन à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¤\87लà¥\80à¤\82 à¤\93à¤\95र à¤\9dलà¤\95 à¤¦à¥\87à¤\96à¥\80à¤\82। à¤¸à¤¹à¥\87à¤\9cà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤¿à¤²à¥\87 à¤\8fà¤\95र à¤\87सà¥\8dतà¥\87माल à¤\95रà¥\87 à¤\95à¥\87 à¤\86à¤\97रह à¤¬à¤¾à¥¤",
+       "tooltip-diff": "दà¥\87à¤\96à¥\80à¤\82 à¤\95ि à¤ªà¤¾à¤  à¤®à¥\87à¤\82 à¤\86प à¤\95ा à¤¬à¤¦à¤²à¤¾à¤µ à¤\95à¤\87लà¥\87 à¤¬à¤¾à¤¨à¥\80",
+       "tooltip-rollback": "\"रà¥\8bलबà¥\88à¤\95\" à¤\8fह à¤ªà¤¨à¥\8dना à¤ªà¤° à¤¸à¤­à¤¸à¥\87 à¤\85à¤\82तिम à¤¸à¤\82पादन à¤\95रà¥\87 à¤µà¤¾à¤²à¤¾ à¤¸à¤\82पादà¤\95 à¤\95à¥\87 à¤\95à¤\87ल à¤¬à¤¦à¤²à¤¾à¤µ(सभ) à¤\8fà¤\95हà¥\80 à¤\95à¥\8dलिà¤\95 à¤®à¥\87à¤\82 à¤µà¤¾à¤ªà¤¸ à¤²à¤µटा देला",
+       "tooltip-undo": "\"वापस à¤²à¥\80à¤\82\" à¤\8f à¤¸à¤\82पादन à¤\95à¥\87 à¤ªà¤²à¤\9f à¤¦à¥\87ला à¤\86 à¤¸à¤\82पादन à¤«à¤¾à¤°à¥\8dम à¤\95à¥\87 à¤\9dलà¤\95 à¤¦à¥\87à¤\96ावà¥\87 à¤µà¤¾à¤²à¤¾ à¤®à¥\8bड à¤®à¥\87à¤\82 à¤\96à¥\8bलà¥\87ला। à¤\88 à¤\9bà¥\8bà¤\9f à¤¸à¤¾à¤°à¤¾à¤\82श à¤®à¥\87à¤\82 à¤\95ारण à¤\9cà¥\8bड़à¥\87 à¤\95à¥\87 à¤®à¥\8bà¤\95ा देला।",
+       "tooltip-summary": "सà¤\82à¤\9bà¥\87प à¤®à¥\87à¤\82 à¤\8fà¤\97à¥\8b à¤¸à¤¾à¤°à¤¾à¤\82श लिखीं",
        "simpleantispam-label": "स्पैम-बिरोधी रोक (Anti-spam check)\nएके <strong>मत</strong> भरीं!",
-       "pageinfo-toolboxlink": "पन्ना के जानकारी",
+       "pageinfo-length": "पन्ना लंबाई (बाइट में)",
+       "pageinfo-toolboxlink": "पन्ना से जुड़ल जानकारी",
        "previousdiff": "← पुरान संपादन",
        "nextdiff": "नया संपादन →",
        "file-info-size": "$1 × $2 पिक्सेल, फाइल साइज: $3, MIME टाइप: $4",
        "file-nohires": "ए से उच्च गुणवत्ता उपलब्ध नइखे।",
-       "svg-long-desc": "à¤\8fसवà¥\80à¤\9cà¥\80 à¤«à¤¾à¤\87ल, à¤¨à¥\89मिनलà¥\8dलà¥\80 $1 x $2 à¤ªà¤¿à¤\95à¥\8dसलà¥\8dस, à¤«à¤¾à¤\87ल à¤\95à¥\87 à¤\86à¤\95ार $3",
+       "svg-long-desc": "à¤\8fसवà¥\80à¤\9cà¥\80 à¤«à¤¾à¤\87ल, à¤¨à¥\89मिनलà¥\80 $1 x $2 à¤ªà¤¿à¤\95à¥\8dसल, à¤«à¤¾à¤\87ल à¤\95à¥\87 à¤\86à¤\95ार: $3",
        "show-big-image": "ओरिजनल फाइल",
        "show-big-image-preview": "ए नमूना के साइज: $1.",
        "show-big-image-other": "अउरी {{PLURAL:$2|resolution|रिजोल्यूशन}}: $1.",
-       "show-big-image-size": "$1 × $2 पिक्सल",
+       "show-big-image-size": "$1 Ã\97 $2 à¤ªà¤¿à¤\95à¥\8dसà¥\87ल",
        "file-info-gif-looped": "लूप्ड",
        "file-info-gif-frames": "$1 {{PLURAL:$1|फ्रेम}}",
        "file-info-png-looped": "लूप्ड",
        "revdelete-unrestricted": "प्रबंधक पर से प्रतिबंध समाप्त",
        "logentry-move-move": "$1 पन्ना $3 के $4 पर {{GENDER:$2|स्थानांतरण कइलें}}",
        "logentry-newusers-create": "खाता $1 {{GENDER:$2|बनावल गइल}}",
-       "searchsuggest-search": "खोजीं",
+       "logentry-upload-upload": "$1 {{GENDER:$2|अपलोड कइलें}} $3",
+       "searchsuggest-search": "{{SITENAME}} में खोजीं",
        "expandtemplates": "टेम्पलेट बिस्तार",
        "mediastatistics": "मीडिया सांख्यिकी"
 }
index f86f88f..6015fa9 100644 (file)
        "rcfilters-legend-heading": "<strong>সংক্ষিপ্ত রূপের তালিকা:</strong>",
        "rcfilters-activefilters": "সক্রিয় ছাঁকনিসমূহ",
        "rcfilters-advancedfilters": "উন্নত ছাঁকনি",
+       "rcfilters-limit-title": "দেখানোর জন্য পরিবর্তনগুলি",
+       "rcfilters-limit-shownum": "শেষ $1টি পরিবর্তন দেখান",
+       "rcfilters-days-title": "সাম্প্রতিক দিন",
+       "rcfilters-hours-title": "সাম্প্রতিক ঘণ্টা",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|দিন}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ঘণ্টা}}",
        "rcfilters-quickfilters": "সংরক্ষিত ছাঁকনি",
        "rcfilters-quickfilters-placeholder-title": "এখনো কোন সংযোগ সংরক্ষণ হয়নি",
+       "rcfilters-quickfilters-placeholder-description": "আপনার ছাঁকনির সেটিং সংরক্ষণ করতে ও পরে পুনরায় ব্যবহার করতে, নীচে, সক্রিয় ছাঁকনি এলাকায় বুকমার্ক আইকনে ক্লিক করুন।",
        "rcfilters-savedqueries-defaultlabel": "ছাঁকনি সংরক্ষণ",
        "rcfilters-savedqueries-rename": "নামান্তর",
        "rcfilters-savedqueries-setdefault": "পূর্ব-নির্ধারিত হিসেবে নির্ধারন করুন",
        "rcfilters-invalid-filter": "অকার্যকর ছাঁকনি",
        "rcfilters-empty-filter": "কোনো সক্রিয় ফিল্টার নেই। সমস্ত অবদান দেখানো হয়েছে।",
        "rcfilters-filterlist-title": "ছাঁকনি",
-       "rcfilters-filterlist-whatsthis": "à¦\8fà¦\9fি à¦\95à§\80?",
+       "rcfilters-filterlist-whatsthis": "à¦\8fà¦\97à§\81লি à¦\95িভাবà§\87 à¦\95াà¦\9c à¦\95রà§\87?",
        "rcfilters-filterlist-feedbacklink": "নতুন (বিটা) ছাঁকনির উপর মতামত প্রদান করুন",
        "rcfilters-highlightbutton-title": "ফলাফলে আলোকপাত করুন",
        "rcfilters-highlightmenu-title": "একটি রং নির্বাচন করুন",
        "rcfilters-noresults-conflict": "কোনও ফলাফল পাওয়া যায়নি কারণ অনুসন্ধানের মাপকাঠিগুলির মধ্যে সংঘর্ষ আছে",
        "rcfilters-state-message-subset": "এই ছাঁকনিটির কোন প্রভাব নেই কারণ এর ফলাফলগুলি নিম্নোক্ত বৃহত্তর পরিধির {{PLURAL:$2|ছাঁকনিটির|ছাঁকনিগুলির}} মধ্যে অন্তর্ভুক্ত আছে (আলোকপাত করে এটিকে আলাদা করে দেখার চেষ্টা করুন): $1",
        "rcfilters-state-message-fullcoverage": "কোন দলের সমস্ত ছাঁকনি নির্বাচন করা এবং কোন ছাঁকনিই নির্বাচন না করা একই কথা, তাই এই ছাঁকনিটির কোন প্রভাব নেই। এই দলে অন্তর্ভুক্ত ছাঁকনিগুলি হল: $1",
-       "rcfilters-filtergroup-registration": "ব্যবহারকারী নিবন্ধন",
-       "rcfilters-filter-registered-label": "নিবন্ধিত",
-       "rcfilters-filter-registered-description": "প্রবেশকৃত সম্পাদকবৃন্দ।",
-       "rcfilters-filter-unregistered-label": "অনিবন্ধিত",
-       "rcfilters-filter-unregistered-description": "যেসব সম্পাদক অ্যাকাউন্টে প্রবেশ করেননি।",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "এই ছাঁকনিটির সাথে নিম্নোক্ত ব্যবহারকারী অভিজ্ঞতা {{PLURAL:$2|ছাঁকনিটির|ছাঁকনিগুলির}} সংঘর্ষ আছে; যা কেবলমাত্র নিবন্ধিত ব্যবহারকারীদের খুঁজে বের করে: $1",
        "rcfilters-filtergroup-authorship": "কার দ্বারা পরিবর্তিত",
        "rcfilters-filter-editsbyself-label": "আপনার দ্বারা পরিবর্তিত",
        "rcfilters-filter-editsbyself-description": "আপনার নিজস্ব অবদান।",
        "rcfilters-filter-editsbyother-label": "অন্যদের দ্বারা পরিবর্তিত",
        "rcfilters-filter-editsbyother-description": "আপনার নিজস্বগুলি ছাড়া সকল পরিবর্তন।",
-       "rcfilters-filtergroup-userExpLevel": "অভিজ্ঞতার স্তর (শুধু মাত্র নিবন্ধিত ব্যবহারকারীর জন্য)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "অভিজ্ঞতা ছাঁকনিগুলি কেবলমাত্র নিবন্ধিত ব্যবহারকারীদের খুঁজে বের করে, তাই এই ছাঁকনিটি \"অনিবন্ধিত\" ছাঁকনিটির সাথে সংঘর্ষে আছে।",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "\"অনিবন্ধিত\" ছাঁকনিটি এক বা তার অধিক অভিজ্ঞতা ছাঁকনির সাথে সংঘর্ষে আছে, যে ছাঁকনিগুলি কেবলমাত্র নিবন্ধিত ব্যবহারকারীদের খুঁজে বের করে। সংঘর্ষরত ছাঁকনিগুলিকে উপরের \"সক্রিয় ছাঁকনিসমূহ\" এলাকাতে চিহ্নিত করা হয়েছে।",
+       "rcfilters-filtergroup-userExpLevel": "ব্যবহারকারী নিবন্ধন ও অভিজ্ঞতা",
+       "rcfilters-filter-user-experience-level-registered-label": "নিবন্ধিত",
+       "rcfilters-filter-user-experience-level-registered-description": "প্রবেশকৃত সম্পাদকবৃন্দ।",
+       "rcfilters-filter-user-experience-level-unregistered-label": "অনিবন্ধিত",
+       "rcfilters-filter-user-experience-level-unregistered-description": "যেসব সম্পাদক অ্যাকাউন্টে প্রবেশ করেননি।",
        "rcfilters-filter-user-experience-level-newcomer-label": "নতুন আগত",
-       "rcfilters-filter-user-experience-level-newcomer-description": "১০টির কমসংখ্যক সম্পাদনা করেছেন ও ৪ দিনের কম সময় ধরে সক্রিয় আছেন।",
+       "rcfilters-filter-user-experience-level-newcomer-description": "১০টির কম সম্পাদনা ও ৪ দিনের কম সময় ধরে সক্রিয় থাকা নিবন্ধিত সম্পাদক।",
        "rcfilters-filter-user-experience-level-learner-label": "শিক্ষার্থী",
-       "rcfilters-filter-user-experience-level-learner-description": "\"নতুন আগতদের\" চেয়ে বেশি অভিজ্ঞ, কিন্তু \"অভিজ্ঞ ব্যবহারকারীদের\" চেয়ে কম।",
+       "rcfilters-filter-user-experience-level-learner-description": "নিবন্ধিত সম্পাদক যাদের অভিজ্ঞতা \"নতুন আগত\" ও \"অভিজ্ঞ ব্যবহারকারীদের\" মধ্যে পড়ে।",
        "rcfilters-filter-user-experience-level-experienced-label": "অভিজ্ঞ ব্যবহারকারী",
-       "rcfilters-filter-user-experience-level-experienced-description": "৩০ à¦¦à¦¿à¦¨à§\87র à¦¬à§\87শি à¦¸à¦\95à§\8dরিয় à¦\86à¦\9bà§\87ন à¦\93 à§«à§¦à§¦à¦\9fির à¦¬à§\87শি à¦¸à¦®à§\8dপাদনা à¦\95রà§\87à¦\9bà§\87ন।",
+       "rcfilters-filter-user-experience-level-experienced-description": "৩০ à¦¦à¦¿à¦¨à§\87র à¦¬à§\87শি à¦¸à¦\95à§\8dরিয় à¦¥à¦¾à¦\95া à¦\93 à§«à§¦à§¦à¦\9fির à¦¬à§\87শি à¦¸à¦®à§\8dপাদনা à¦\95রা à¦¨à¦¿à¦¬à¦¨à§\8dধিত à¦¸à¦®à§\8dপাদà¦\95।",
        "rcfilters-filtergroup-automated": "স্বয়ংক্রিয় অবদান",
        "rcfilters-filter-bots-label": "বট",
        "rcfilters-filter-bots-description": "স্বয়ংক্রিয় সরঞ্জাম দিয়ে করা সম্পাদনা।",
        "rcfilters-filter-major-description": "যেসব সম্পাদনাকে অনুল্লেখ্য হিসেবে চিহ্নিত করা হয়নি।",
        "rcfilters-filtergroup-watchlist": "নজরতালিকার পাতা",
        "rcfilters-filter-watchlist-watched-label": "নজরতালিকায়",
+       "rcfilters-filter-watchlist-watched-description": "আপনার নজরতালিকায় থাকা পাতাগুলিতে পরিবর্তন।",
        "rcfilters-filter-watchlist-watchednew-label": "নজরতালিকার নতুন পরিবর্তন",
+       "rcfilters-filter-watchlist-watchednew-description": "আপনার নজরতালিকায় থাকা পাতাগুলিতে পরিবর্তন যেগুলিতে আপনি সম্পাদনা করার পর আর যাননি।",
        "rcfilters-filter-watchlist-notwatched-label": "নজরতালিকাভুক্ত নয়",
+       "rcfilters-filter-watchlist-notwatched-description": "আপনার নজরতালিকায় থাকা পাতাগুলি ব্যতীয় সবকিছু।",
        "rcfilters-filtergroup-changetype": "পরিবর্তনের ধরন",
        "rcfilters-filter-pageedits-label": "পাতার সম্পাদনা",
        "rcfilters-filter-pageedits-description": "উইকি বিষয়বস্তু, আলোচনা, বিষয়শ্রেণীর বিবরণ... ইত্যাদিতে সম্পাদনা",
        "rcfilters-typeofchange-conflicts-hideminor": "এই \"পরিবর্তনের ধরন\"-সংক্রান্ত ছাঁকনিটির সাথে \"অনুল্লেখ্য সম্পাদনা\" ছাঁকনিটির সংঘর্ষ আছে। কিছু নির্দিষ্ট ধরনের সম্পাদনা \"অনুল্লেখ্য\" হিসেবে চিহ্নিত করা সম্ভব নয়।",
        "rcfilters-filtergroup-lastRevision": "সর্বশেষ সংস্করণ",
        "rcfilters-filter-lastrevision-label": "সর্বশেষ সংশোধন",
-       "rcfilters-filter-lastrevision-description": "একটি পাতার সর্বশেষ সাম্প্রতিক পরিবর্তন।",
-       "rcfilters-filter-previousrevision-label": "পà§\82রà§\8dববরà§\8dতà§\80 à¦¸à¦\82শà§\8bধন",
-       "rcfilters-filter-previousrevision-description": "সব পরিবর্তন যা একটি পাতার সর্বশেষ সাম্প্রতিক পরিবর্তন নয়।",
+       "rcfilters-filter-lastrevision-description": "শà§\81ধà§\81মাতà§\8dর à¦\8fà¦\95à¦\9fি à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à¦°à§\8dবশà§\87ষ à¦¸à¦¾à¦®à§\8dপà§\8dরতিà¦\95 à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন।",
+       "rcfilters-filter-previousrevision-label": "সরà§\8dবশà§\87ষ à¦¸à¦\82শà§\8bধন à¦¨à¦¯à¦¼",
+       "rcfilters-filter-previousrevision-description": "সব পরিবর্তন যা \"সর্বশেষ সংশোধন\" নয়।",
        "rcfilters-filter-excluded": "বর্জিত",
+       "rcfilters-tag-prefix-namespace-inverted": "$1 <strong>:নয়</strong>",
        "rcfilters-view-tags": "ট্যাগকৃত সম্পাদনা",
+       "rcfilters-view-tags-tooltip": "সম্পাদনা ট্যাগ ব্যবহার করে ফলাফল ছাঁকুন",
        "rcfilters-view-return-to-default-tooltip": "মূল ছাঁকনির মেনুতে ফিরুন",
        "rcfilters-liveupdates-button": "সরাসরি হালনাগাদ",
        "rcnotefrom": "<strong>$2</strong>টা থেকে সংঘটিত পরিবর্তনগুলি (সর্বোচ্চ <strong>$1টি</strong> দেখানো হয়েছে)।",
        "autochange-username": "মিডিয়াউইকি স্বয়ংক্রিয় পরিবর্তন",
        "upload": "আপলোড",
        "uploadbtn": "ফাইল আপলোড করুন",
-       "reuploaddesc": "à¦\86পলà§\8bড à¦¬à¦¾à¦¤à¦¿à¦² à¦\95রà§\8b à¦\8fবà¦\82 à¦\86পলà§\8bড à¦«à¦°à§\8dমà§\87 à¦«à§\87রত à¦¯à¦¾à¦\93।",
+       "reuploaddesc": "à¦\86পলà§\8bড à¦¬à¦¾à¦¤à¦¿à¦² à¦\95রà§\81ন à¦\8fবà¦\82 à¦\86পলà§\8bড à¦«à¦°à§\8dমà§\87 à¦«à§\87রত à¦¯à¦¾à¦¨",
        "upload-tryagain": "পরিবর্তিত ফাইল বর্ণনা জমা দিন",
        "uploadnologin": "আপনি প্রবেশ করেননি",
        "uploadnologintext": "ফাইল আপলোড করতে হলে আপনাকে অবশ্যই $1 করতে হবে।",
        "file-too-large": "আপনার জমাকৃত ফাইলটি অনেক বড়।",
        "filename-tooshort": "ফাইল নামটি খুব ছোট।",
        "filetype-banned": "এই ধরনের ফাইল নিষিদ্ধ।",
-       "verification-error": "à¦\8fà¦\87 à¦«à¦¾à¦\87লà§\87র à¦«à¦¾à¦\87ল à¦­à§\8dযালিডà§\87শন উত্তীর্ণ হয়নি।",
+       "verification-error": "à¦\8fà¦\87 à¦«à¦¾à¦\87লà¦\9fি à¦«à¦¾à¦\87ল à¦¯à¦¾à¦\9aাà¦\87à¦\95রণà§\87 উত্তীর্ণ হয়নি।",
        "hookaborted": "যে পরিবর্তনটি আপনি করার চেষ্টা করেছিলেন তা এক্সটেনশন দ্বারা বাতিল করা হয়েছে।",
        "illegal-filename": "ফাইলের এই নামটি গ্রহণযোগ্য নয়।",
        "overwrite": "বিদ্যমান কোনো ফাইল প্রতিস্থাপন গ্রহণযোগ্য নয়।",
        "delete-warning-toobig": "এই পাতাটির একটি বৃহৎ সম্পাদনা ইতিহাস রয়েছে, যা $1 {{PLURAL:$1|সংস্করণেরও|সংস্করণেরও}} বেশি।\nএই পাতাটি মুছে ফেললে তা {{SITENAME}} সাইটের ডেটাবেজ সমস্যার কারণ হতে পারে;\nসাবধানতার সাথে এগিয়ে যান।",
        "deleteprotected": "আপনি এই পাতাটি মুছে ফেলতে পারবেন না কারণ এটি সুরক্ষিত করা হয়েছে।",
        "deleting-backlinks-warning": "<strong>সতর্কীকরণ:</strong> আপনি যেটি মুছে ফেলতে যাচ্ছেন তা [[Special:WhatLinksHere/{{FULLPAGENAME}}|অন্যান্য পাতাসমূহে]] সংযুক্ত অথবা অন্তর্ভুক্ত রয়েছে।",
+       "deleting-subpages-warning": "<strong>সতর্কীকরণ:</strong> আপনি যে পাতাটি মুছে ফেলতে যাচ্ছেন তাঁর [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|একটি উপপাতা|$1টি উপপাতা|51=৫০টির বেশী}}]] রয়েছে।",
        "rollback": "সম্পাদনা ফিরিয়ে নিন",
        "rollbacklink": "পুনর্বহাল",
        "rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "undeletepage": "মুছে ফেলা পাতাগুলি দেখুন ও ফিরিয়ে আনুন",
        "undeletepagetitle": "'''[[:$1|$1]] এর অপসারিত সংস্করণগুলোর সমন্বয়ে দেখানো হচ্ছে'''।",
        "viewdeletedpage": "মুছে ফেলা হয়েছে, এমন পাতাগুলো দেখুন",
-       "undeletepagetext": "নিচের {{PLURAL:$1|পাতাটি মুছে ফেলা হয়েছে কিন্তু এটি|$1 পাতাগুলি মুছে ফেলা হয়েছে কিন্তু এগুলি}} এখনও আর্কাইভে আছে ও পুনরুদ্ধার করা সম্ভব। আর্কাইভ পর্যায়ক্রমিকভাবে পরিষ্কার করা হতে পারে।",
+       "undeletepagetext": "নিচের {{PLURAL:$1|পাতাটি মুছে ফেলা হয়েছে কিন্তু এটি|$1টি পাতা মুছে ফেলা হয়েছে কিন্তু এগুলি}} এখনও আর্কাইভে আছে ও পুনরুদ্ধার করা সম্ভব। আর্কাইভ পর্যায়ক্রমিকভাবে পরিষ্কার করা হতে পারে।",
        "undelete-fieldset-title": "সংশোধন পুনরুদ্ধার",
        "undeleteextrahelp": "সম্পূর্ণ পাতাটি পুনরুদ্ধার করার জন্য সবগুলি টিকবাক্স অনির্বাচিত করুন এবং '''''{{int:undeletebtn}}''''' বোতামে ক্লিক করুন।\nনির্বাচিত পুনরুদ্ধারের জন্য যেসব সংশোধন পুনরুদ্ধার করতে চান, তার পাশের বাক্সে টিক দিন এবং '''''{{int:undeletebtn}}''''' বোতামে ক্লিক করুন।",
        "undeleterevisions": "$1{{PLURAL:$1|টি সংশোধন}} অপসারিত",
        "fileduplicatesearch-noresults": "\"$1\" নামের কোনো ফাইল খুঁজে পাওয়া যায়নি।",
        "specialpages": "বিশেষ পাতাসমূহ",
        "specialpages-note-top": "ব্যাখ্যা",
-       "specialpages-note": "* সাধারণ বিশেষ পাতাসমূহ।\n* <span class=\"mw-specialpagerestricted\">সীমাবদ্ধ বিশেষ পাতা।</span>",
+       "specialpages-note-restricted": "* সাধারণ বিশেষ পাতাসমূহ।\n* <span class=\"mw-specialpagerestricted\">সীমাবদ্ধ বিশেষ পাতাসমূহ।</span>",
        "specialpages-group-maintenance": "রক্ষণাবেক্ষণের কার্যবিবরণীসমূহ",
        "specialpages-group-other": "অন্যান্য বিশেষ পাতাসমূহ",
        "specialpages-group-login": "প্রবেশ/নতুন অ্যাকাউন্ট",
index 0423c05..0926cc1 100644 (file)
        "search-suggest": "Jeste li mislili: $1",
        "search-rewritten": "Prikazujem rezultate za $1. Umjesto toga potraži $2.",
        "search-interwiki-caption": "Rezultati s bratskih projekata",
-       "search-interwiki-default": "$1 rezultati:",
+       "search-interwiki-default": "Rezultati sa $1:",
        "search-interwiki-more": "(više)",
        "search-interwiki-more-results": "više rezultata",
        "search-relatedarticle": "Povezano",
        "recentchanges-submit": "Prikaži",
        "rcfilters-activefilters": "Aktivni filteri",
        "rcfilters-advancedfilters": "Napredni filteri",
-       "rcfilters-quickfilters": "Sačuvane postavke filtera",
+       "rcfilters-quickfilters": "Sačuvani filteri",
        "rcfilters-quickfilters-placeholder-title": "Zasad nema sačuvanih linkova",
        "rcfilters-quickfilters-placeholder-description": "Da sačuvate postavke filtera da biste ih kasnije ponovo upotrijebili, kliknite na ikonu markera pod \"Aktivni filterima\" ispod.",
        "rcfilters-savedqueries-defaultlabel": "Sačuvani filteri",
        "rcfilters-filterlist-noresults": "Nema filtera",
        "rcfilters-noresults-conflict": "Nije pronađen nijedan rezultat jer su kriteriji pretrage sukobljeni",
        "rcfilters-state-message-fullcoverage": "Izabiranje svih filtera u grupi isto je kao da niste izabrali nijedan, tako da ovaj filter nema nikakvog efekta. Grupa sadrži: $1",
-       "rcfilters-filtergroup-registration": "Registracija korisnika",
-       "rcfilters-filter-registered-label": "Registrirani",
-       "rcfilters-filter-registered-description": "Prijavljeni korisnici.",
-       "rcfilters-filter-unregistered-label": "Anonimni",
-       "rcfilters-filter-unregistered-description": "Neprijavljeni korisnici.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Ovaj filter u sukobu je sa sljedećim {{PLURAL:$2|filterom|filterima}}, koji {{PLURAL:$2|pronalazi|pronalaze}} samo registrirane korisnike: $1",
        "rcfilters-filtergroup-authorship": "Autorstvo doprinosa",
        "rcfilters-filter-editsbyself-label": "Vaše izmjene",
        "rcfilters-filter-editsbyself-description": "Vaši vlastiti doprinosi.",
        "rcfilters-filter-editsbyother-label": "Tuđe izmjene",
        "rcfilters-filter-editsbyother-description": "Sve izmjene osim Vaših.",
        "rcfilters-filtergroup-userExpLevel": "Korisničko iskustvo (samo za registrirane)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filteri za iskustvo pronalaze samo registrirane korisnike, tako da je ovaj filter u sukobu s filterom \"Anonimni\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filter \"Anonimni\" u sukobu je s jednim ili više filtera za iskustvo budući da oni pronalaze samo registrirane korisnike. Sukobljeni filteri označeni su pod \"Aktivnim filterima\" iznad.",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrirani",
+       "rcfilters-filter-user-experience-level-registered-description": "Prijavljeni korisnici.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Anonimni",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Neprijavljeni korisnici.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novajlije",
        "rcfilters-filter-user-experience-level-newcomer-description": "Manje od 10 izmjena i 4 dana aktivnosti.",
        "rcfilters-filter-user-experience-level-learner-label": "Učenici",
        "move-page": "Premjesti $1",
        "move-page-legend": "Premjesti stranicu",
        "movepagetext": "Korištenjem ovog formulara možete preimenovati stranicu, premještajući cijelu historiju na novo ime.\nČlanak pod starim imenom postat će stranica koja preusmjerava na članak pod novim imenom. \nMožete automatski izmijeniti preusmjerenje do izvornog naslova.\nAko se ne odlučite na to, provjerite [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|neispravna preusmjeravanja]].\nDužni ste provjeriti da svi linkovi i dalje nastave voditi na prave stranice.\n\nImajte na umu da članak <strong>neće</strong> biti premješten ako već postoji članak pod imenom na koje ga namjeravate preusmjeriti osim u slučaju stranice za preusmjeravanje koja nema nikakvih starih izmjena.\nTo znači da možete vratiti stranicu na prethodno mjesto ako pogriješite, ali ne možete zamijeniti postojeću stranicu.\n\n<strong>Napomena:</strong>\nOvo može biti drastična i neočekivana promjena kad su u pitanju popularne stranice.\nMolimo da dobro razmislite prije no što premjestite stranicu.",
-       "movepagetext-noredirectfixer": "Koristeći donji obrazac, preimenovat ćete stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv postat će preusmjerenje na novi naziv.\nMolimo da provjerite postoje li [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti jesu li linkovi ispravni i vode li tamo kamo bi trebali voditi.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znači da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili, ali ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nImajte na umu da premještanje popularnog članka može biti\ndrastična i neočekivana promjena za korisnike; molimo da budete sigurni da ste shvatili posljedice prije no što nastavite.",
+       "movepagetext-noredirectfixer": "Koristeći donji obrazac, preimenovat ćete stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv postat će preusmjerenje na novi naziv.\nMolimo da provjerite postoje li [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti jesu li linkovi ispravni i vode li tamo kamo bi trebali voditi.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znači da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili, ali ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nOvo može biti drastična i neočekivana promjena za popularnu stranicu;\ndobro razmislite o posljedicama prije nego što nastavite.",
        "movepagetalktext": "Ako označite ovu kutijicu, odgovarajuća stranica za razgovor, ako postoji, automatski će biti premještena na novi naziv, osim ako već postoji sadržaj na odredišnoj stranici za razgovor.\n\nU tom slučaju, morat ćete ručno premjestiti ili prekopirati stranicu ako to želite.",
        "moveuserpage-warning": "<strong>Upozorenje:</strong> Premještate korisničku stranicu. Imajte u vidu da će samo stranica biti premještena, a sam korisnik <em>neće</em> biti preimenovan.",
        "movecategorypage-warning": "<strong>Upozorenje:</strong> Premještate stranicu kategorije. Imajte na umu da će samo stranica biti premještena i da sve stranice u staroj kategoriji <em>neće</em> biti ponovo kategorirane u novu kategoriju.",
        "pageinfo-visiting-watchers": "Broj korisnika koji prate ovu stranica i koji su je posjetili zbog najnovijih izmjena",
        "pageinfo-few-watchers": "Manje od $1 {{PLURAL:$1|pratioca|pratilaca}}",
        "pageinfo-few-visiting-watchers": "Moguće je da postoji korisnik koji prati i posjećuje nedavne izmjene",
-       "pageinfo-redirects-name": "Broj preusmjeravanja na ovu stranicu",
+       "pageinfo-redirects-name": "Broj preusmjerenja na ovu stranicu",
        "pageinfo-subpages-name": "Podstranice ove stranice",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|preusmjerenje|preusmjerenja}}; $3 {{PLURAL:$3|nepreusmjerenje|nepreusmjerenja}})",
        "pageinfo-firstuser": "Korisnik koji je napravio stranicu",
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice.\n* <strong class=\"mw-specialpagerestricted\">Zaštićene posebne stranice.</strong>",
        "specialpages-group-maintenance": "Izvještaji za održavanje",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava / otvaranje računa",
        "htmlform-user-not-exists": "<strong>$1</strong> ne postoji.",
        "htmlform-user-not-valid": "<strong>$1</strong> nije ispravno korisničko ime.",
        "logentry-delete-delete": "$1 {{GENDER:$2|obrisao|obrisala}} je stranicu $3",
-       "logentry-delete-delete_redir": "$1 {{GENDER:$2|obrisao|obrisala}} je preusmjerenje $3 prepisivanjem",
+       "logentry-delete-delete_redir": "$1 {{GENDER:$2|obrisao|obrisala}} je preusmjerenje $3 presnimavanjem",
        "logentry-delete-restore": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3 ($4)",
        "logentry-delete-restore-nocount": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3",
        "restore-count-revisions": "{{PLURAL:$1|1 izmjena|$1 izmjene|$1 izmjena}}",
index ce07b06..64845e3 100644 (file)
        "rcfilters-filterlist-noresults": "No s'ha trobat cap filtre",
        "rcfilters-noresults-conflict": "No s'ha trobat cap resultat perquè els criteris de cerca són en conflicte",
        "rcfilters-state-message-fullcoverage": "Seleccionant tots els filtres en un grup és el mateix que no seleccionar-ne cap, de manera que aquest filtre no té cap efecte. El grup inclou: $1",
-       "rcfilters-filtergroup-registration": "Registre d'usuari",
-       "rcfilters-filter-registered-label": "Registrats",
-       "rcfilters-filter-registered-description": "Editors que han iniciat sessió",
-       "rcfilters-filter-unregistered-label": "No registrats",
-       "rcfilters-filter-unregistered-description": "Editors que no han iniciat una sessió.",
        "rcfilters-filtergroup-authorship": "Autoria de les contribucions",
        "rcfilters-filter-editsbyself-label": "Els vostres canvis",
        "rcfilters-filter-editsbyself-description": "Les vostres pròpies contribucions.",
        "rcfilters-filter-editsbyother-label": "Canvis d'altres",
        "rcfilters-filter-editsbyother-description": "Tots els canvis excepte els vostres.",
        "rcfilters-filtergroup-userExpLevel": "Nivell d'experiència (només per a usuaris registrats)",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrats",
+       "rcfilters-filter-user-experience-level-registered-description": "Editors que han iniciat sessió",
+       "rcfilters-filter-user-experience-level-unregistered-label": "No registrats",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editors que no han iniciat una sessió.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novells",
        "rcfilters-filter-user-experience-level-newcomer-description": "Menys de 10 edicions i 4 dies d'activitat.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprenents",
index 8b8e77f..d150323 100644 (file)
        "anontalk": "Păng-gōng",
        "navigation": "Īng-dô̤:",
        "and": "&#32;gâe̤ng",
-       "qbfind": "討",
-       "qbbrowse": "覷蜀覷",
-       "qbedit": "修改",
-       "qbpageoptions": "茲蜀頁",
-       "qbmyoptions": "我其頁面",
        "faq": "真稠碰著其問題",
-       "faqpage": "Project:稠問其問題",
        "actions": "動作",
        "namespaces": "Miàng-kŭng-găng",
        "variants": "Biéng-tā̤",
        "edit-local": "編輯當地描述",
        "create": "創建",
        "create-local": "添加當地描述",
-       "editthispage": "修改茲頁",
-       "create-this-page": "創建茲蜀頁",
        "delete": "刪除",
-       "deletethispage": "刪除茲頁",
-       "undeletethispage": "恢復茲蜀頁",
        "undelete_short": "恢復$1回修改{{PLURAL:$1}}",
        "viewdeleted_short": "覷蜀覷$1回刪掉其修改{{PLURAL:$1}}",
        "protect": "保護",
        "protect_change": "改變",
-       "protectthispage": "保護茲蜀頁",
        "unprotect": "改變保護其狀態",
-       "unprotectthispage": "改變茲蜀頁其保護狀態",
        "newpage": "新頁",
-       "talkpage": "討論茲頁",
        "talkpagelinktext": "páng-gōng",
        "specialpage": "特殊頁",
        "personaltools": "Gó̤-ìng gì gă-sĭ-huă",
-       "articlepage": "覷蜀覷內容頁面",
        "talk": "Tō̤-lâung",
        "views": "Ché̤ṳ-siŏh-ché̤ṳ",
        "toolbox": "Gă-sĭ-huă",
-       "userpage": "覷蜀覷用戶頁面",
-       "projectpage": "看工程頁",
        "imagepage": "覷蜀覷文件頁面",
        "mediawikipage": "看消息頁",
        "templatepage": "看模板頁",
        "minoredit": "過幼修改",
        "watchthis": "監視茲頁",
        "savearticle": "Bō̤-còng ciā hiĕk",
+       "publishpage": "Huák-buó ùng-ciŏng",
+       "publishchanges": "Huák-buó siŭ-gāi",
        "preview": "預覽",
        "showpreview": "顯示預覽",
        "showdiff": "看改變其部分",
        "permissionserrorstext-withaction": "因為下底其{{PLURAL:$1|原因}},汝無能耐 $2 :",
        "recreate-moveddeleted-warn": "'''注意:汝敆𡅏重新創建舊底已經乞刪唻其頁面。'''\n\n汝應該考慮蜀下繼續去編輯茲蜀頁到底是伓是合適其。茲蜀頁其刪除記錄共移動記錄都敆嚽塊:",
        "edit-conflict": "編輯衝突",
+       "postedit-confirmation-saved": "Nṳ̄ gì siŭ-gāi ī-gĭng bō̤-còng.",
        "content-model-wikitext": "維基文本",
        "content-model-text": "純文本",
        "content-model-javascript": "JavaScript",
index 117b558..3044336 100644 (file)
        "parser-template-loop-warning": "ئەڵقەی داڕێژە دۆزرایەوە: [[$1]]",
        "parser-template-recursion-depth-warning": "سنووری قووڵی گەڕانەوەی داڕێژە تێپەڕیوە ($1)",
        "undo-success": "دەکرێ دەستکاریەکە پووچەڵبکرێتەوە.\nتکایە چاو لەو هەڵسەنگاندنەی خوارەوە بکە تا دڵنیا بیت ئەمە ئەوەیە کە‌ دەتویست بیکەی و دواتر گۆڕانکارییەکانی خوارەوە پاشەکەوت بکە بۆ تەواوکردنی پووچەڵکردنەوەکە.",
-       "undo-failure": "Ù\84Û\95بÛ\95ر Ú©Û\8eØ´Û\95Û\8c Ø¯Û\95ستâ\80\8cتÛ\8eâ\80\8cÙ\88Û\95رداÙ\86Ø\8c Ù\86اتÙ\88اÙ\86Û\8c Ø¯Û\95ستکارÛ\8cÛ\95Ú©Û\95 Ø¦Û\95Ù\86جاÙ\85â\80\8cÙ\86Û\95دراÙ\88 Ø¨Ú©Û\95Û\8cت.",
+       "undo-failure": "Ù\86Û\95تÙ\88اÙ\86درا Ø¯Û\95ستکارÛ\8cÛ\8cÛ\95Ú©Û\95 Ù¾Ù\88Ù\88Ú\86Û\95Úµ Ø¨Ú©Ø±Û\8eتÛ\95Ù\88Û\95 Ù\84Û\95بÛ\95ر Ú©Û\8eØ´Û\95Û\8c Ø¯Û\95ستتÛ\8eÙ\88Û\95رداÙ\86.",
        "undo-norev": "ناتوانی دەستکاریەکە ئەنجام‌نەدراو بکەی لەبەر ئەوەی بوونی نیە یا سڕدراوەتەوە.",
        "undo-nochange": "وا دیارە دەستکارییەکە پووچەڵ کراوەتەوە.",
        "undo-summary": "گەڕاندنەوەی پێداچوونەوەی $1 لە لایەن [[Special:Contributions/$2|$2]] ([[User talk:$2|لێدوان]])",
        "rcfilters-filterlist-title": "فیلتەرەکان",
        "rcfilters-filterlist-whatsthis": "ئەمە چییە؟",
        "rcfilters-highlightmenu-title": "ڕەنگێکی نوێ ھەڵبژێرە",
-       "rcfilters-filter-registered-label": "تۆمارکراو",
-       "rcfilters-filter-registered-description": "ئەو بەکارھێنەرانەی لە ژوورەوەن",
-       "rcfilters-filter-unregistered-label": "تۆمارنەکراوەکان",
-       "rcfilters-filter-unregistered-description": "ئەو بەکارھێنەرانەی لە ژوورەوە نین",
        "rcfilters-filter-editsbyself-label": "مافەکانی خۆت",
        "rcfilters-filter-editsbyself-description": "دەستکارییەکانی خۆت.",
        "rcfilters-filter-editsbyother-label": "دەستکارییەکانی کەسانی تر",
        "rcfilters-filter-editsbyother-description": "ھەموو گۆڕانکارییەکان بێجگە لەوەی خۆت",
+       "rcfilters-filter-user-experience-level-registered-label": "تۆمارکراو",
+       "rcfilters-filter-user-experience-level-registered-description": "ئەو بەکارھێنەرانەی لە ژوورەوەن",
+       "rcfilters-filter-user-experience-level-unregistered-label": "تۆمارنەکراوەکان",
+       "rcfilters-filter-user-experience-level-unregistered-description": "ئەو بەکارھێنەرانەی لە ژوورەوە نین",
        "rcfilters-filter-user-experience-level-newcomer-label": "تازەکاران",
        "rcfilters-filter-user-experience-level-newcomer-description": "کەمتر لە ١٠ دەستکاری و ٤ ڕۆژ لە چالاک بوون",
        "rcfilters-filter-user-experience-level-experienced-label": "بەکارھێنەرانی پێشکەوتوو",
        "fileduplicatesearch-result-n": "پەڕگەی «$1» {{PLURAL:$2|١ دووپاتکراوەی کوتوموتی|$2 دووپاتکراوەی کوتوموتی}} ھەیە.",
        "fileduplicatesearch-noresults": "پەڕگەیەک بە ناوی «$1» نەدۆزرایەوە.",
        "specialpages": "پەڕە تایبەتەکان",
-       "specialpages-note": "* پەڕە تایبەتە ئاساییەکان.\n* <span class=\"mw-specialpagerestricted\">پەڕە تایبەتە بەرگریلێکراوەکان.</span>",
        "specialpages-group-maintenance": "ڕاپۆرتەکانی چاکسازی",
        "specialpages-group-other": "پەڕە تایبەتەکانی دیکە",
        "specialpages-group-login": "چوونەژوورەوە / دروستکردنی ھەژمار",
index 64bba7a..bc4e002 100644 (file)
        "rcfilters-legend-heading": "<strong>Seznam zkratek:</strong>",
        "rcfilters-activefilters": "Aktivní filtry",
        "rcfilters-advancedfilters": "Pokročilé filtry",
+       "rcfilters-limit-shownum": "Zobrazit posledních $1 změn",
+       "rcfilters-days-title": "Poslední dny",
+       "rcfilters-hours-title": "Poslední hodiny",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|den|dny|dní}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hodina|hodiny|hodin}}",
        "rcfilters-quickfilters": "Uložené filtry",
        "rcfilters-quickfilters-placeholder-title": "Zatím neuloženy žádné odkazy",
        "rcfilters-quickfilters-placeholder-description": "Pokud chcete uložit svá nastavení filtrů a použít je později, klikněte na ikonku záložky v ploše aktivních filtrů níže.",
        "rcfilters-invalid-filter": "Neplatný filtr",
        "rcfilters-empty-filter": "Žádné aktivní filtry. Zobrazeny jsou všechny příspěvky.",
        "rcfilters-filterlist-title": "Filtry",
-       "rcfilters-filterlist-whatsthis": "Co to je?",
+       "rcfilters-filterlist-whatsthis": "Jak to funguje?",
        "rcfilters-filterlist-feedbacklink": "Poskytnout novým (beta) filtrům zpětnou vazbu",
        "rcfilters-highlightbutton-title": "Zvýraznit výsledky",
        "rcfilters-highlightmenu-title": "Vybrat barvu",
        "rcfilters-noresults-conflict": "Nenalezeny žádné výsledky, protože vyhledávací kritéria jsou v konfliktu",
        "rcfilters-state-message-subset": "Tento filtr nemá žádný vliv, protože jeho výsledky jsou již zahrnuty {{PLURAL:$2|následujícím, širším filtrem|následujícími, širšími filtry}} (pro odlišení zkuste zvýraznění): $1",
        "rcfilters-state-message-fullcoverage": "Výběr všech filtrů ve skupině odpovídá výběru žádného, proto tento filtr nemá žádný vliv. Skupina zahrnuje: $1",
-       "rcfilters-filtergroup-registration": "Registrace uživatele",
-       "rcfilters-filter-registered-label": "Registrovaní",
-       "rcfilters-filter-registered-description": "Přihlášení editoři.",
-       "rcfilters-filter-unregistered-label": "Neregistrovaní",
-       "rcfilters-filter-unregistered-description": "Editoři, kteří nejsou přihlášení",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Tento filtr je v konfliktu s {{PLURAL:$2|následujícím filtrem|následujícími filtry}} podle zkušeností, {{PLURAL:$2|vyhledávajícím|vyhledávajícími}} pouze registrované uživatele: $1",
        "rcfilters-filtergroup-authorship": "Autorství příspěvku",
        "rcfilters-filter-editsbyself-label": "Vaše změny",
        "rcfilters-filter-editsbyself-description": "Vaše vlastní příspěvky.",
        "rcfilters-filter-editsbyother-label": "Změny ostatních",
        "rcfilters-filter-editsbyother-description": "Všechny změny kromě vašich.",
-       "rcfilters-filtergroup-userExpLevel": "Úroveň zkušeností (pouze registrovaných uživatelů)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filtr podle zkušeností vyhledává pouze registrované uživatele, tento filtr je tedy v konfliktu s filtrem „Neregistrovaní“.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filtr „Neregistrovaní“ je v konfliktu s jedním nebo více filtry podle zkušeností, vyhledávajícími pouze registrované uživatele. Dotyčné filtry jsou označeny nahoře, v prostoru „Aktivní filtry“.",
+       "rcfilters-filtergroup-userExpLevel": "Registrace a zkušenost uživatelů",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrovaní",
+       "rcfilters-filter-user-experience-level-registered-description": "Přihlášení editoři.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neregistrovaní",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editoři, kteří nejsou přihlášení.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nováčci",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Méně než 10 editací a 4 dny činnosti.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registrovaní uživatelé s méně než 10 editacemi a 4 dny činnosti.",
        "rcfilters-filter-user-experience-level-learner-label": "Začátečníci",
-       "rcfilters-filter-user-experience-level-learner-description": "Více zkušeností než „Nováčci“, ale méně než „Zkušení uživatelé“.",
+       "rcfilters-filter-user-experience-level-learner-description": "Registrovaní uživatelé, jejichž zkušenosti spadají mezi „Nováčky“ a „Zkušené uživatele“.",
        "rcfilters-filter-user-experience-level-experienced-label": "Zkušení uživatelé",
-       "rcfilters-filter-user-experience-level-experienced-description": "Více než 30 dní činnosti a 500 editací.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registrovaní uživatelé s více než 500 editacemi a 30 dny činnosti.",
        "rcfilters-filtergroup-automated": "Automatizované příspěvky",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Editace provedené pomocí automatizovaných nástrojů.",
        "rcfilters-typeofchange-conflicts-hideminor": "Tento filtr podle typu změny je v konfliktu s filtrem „Malé editace“. Určité typy změn nelze označit jako malé.",
        "rcfilters-filtergroup-lastRevision": "Aktuální verze",
        "rcfilters-filter-lastrevision-label": "Aktuální verze",
-       "rcfilters-filter-lastrevision-description": "Poslední změna stránky.",
+       "rcfilters-filter-lastrevision-description": "Jen poslední změna stránky.",
        "rcfilters-filter-previousrevision-label": "Dřívější verze",
        "rcfilters-filter-previousrevision-description": "Všechny změny, které nejsou nejnovější úpravou stránky.",
        "rcfilters-view-tags": "Označené editace",
        "delete-warning-toobig": "Tato stránka má velkou historii editací, přes $1 {{PLURAL:$1|verzi|verze|verzí}}. Mazání takových stránek může narušit databázové operace {{grammar:2sg|{{SITENAME}}}}; postupujte opatrně.",
        "deleteprotected": "Tuto stránku nemůžete smazat, protože je zamčena.",
        "deleting-backlinks-warning": "<strong>Upozornění:</strong> Stránka, kterou se chystáte smazat, je [[Special:WhatLinksHere/{{FULLPAGENAME}}|na jiných stránkách]] odkazována nebo je do nich vložena.",
+       "deleting-subpages-warning": "<strong>Upozornění:</strong> Stránka, kterou se chystáte smazat, má [[Special:PrefixIndex/{{FULLPAGENAME}}|{{PLURAL:$1|podstránku|$1 podstránky|$1 podstránek|51=více než 50 podstránek}}]].",
        "rollback": "Vrátit zpět editace",
        "rollbacklink": "vrácení zpět",
        "rollbacklinkcount": "vrácení $1 {{PLURAL:$1|editace|editací}} zpět",
        "fileduplicatesearch-noresults": "Žádný soubor s názvem „$1“ nebyl nalezen.",
        "specialpages": "Speciální stránky",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normální speciální stránky\n* <span class=\"mw-specialpagerestricted\">Speciální stránky s&nbsp;vyhrazeným přístupem</span>",
        "specialpages-group-maintenance": "Údržba",
        "specialpages-group-other": "Ostatní",
        "specialpages-group-login": "Přihlášení / vytvoření účtu",
index fade353..6a78683 100644 (file)
        "viewsource-title": "Zdrojowi tekst starnë $1",
        "viewsourcetext": "Zdrojowi tekst starnë mòże przezérac ë kòpiérowac.",
        "editinginterface": "'''ÒSTRZÉGA:''' Editëjesz starnã, jakô zamëkô w se tekst interfejsu softwôrë. Wszëtczé zmianë tu zrobioné bãdze widzec na interfejse jinszëch brëkòwników.\nPrzemëszlë dolmaczënié na [https://translatewiki.net/wiki/Main_Page?setlang=csb translatewiki.net], ekstra ùdbie lokalizacëji softwôrë MediaWiki.",
-       "logouttext": "'''Jes wëlogòwóny.'''\nMòżesz robic dali na {{SITENAME}} jakno anonimòwi brëkòwnik abò sã <span class='plainlinks'>[$1 wlogòwac]</span> znowa jakno równy, a bò jinszi brëkòwnik.\nBôczë, że do czasu wëczëszczenia pòdrãczny pamiãcë przezérnika, niejedné starnë bãdą wëzdrzëc jakbë të bëł wlogòwóny.",
+       "exception-nologin": "Nie jes wlogòwôny/a",
+       "logouttext": "<strong>Jes terô wëlogòwóny.</strong>\n\nBôczë, że do czasu wëczëszczeniô pòdrãczny pamiãcë przezérnika, niejedné starnë bãdą wëzdrzëc jakbë të bëł wlogòwóny.",
+       "welcomeuser": "Witôj, $1!",
        "yourname": "Miono brëkòwnika",
        "userlogin-yourname": "Miono brëkòwnika",
        "userlogin-yourname-ph": "Wpiszë swòjé miono brëkòwnika",
+       "createacct-another-username-ph": "Wpiszë miono brëkòwnika",
        "yourpassword": "Twòja parola",
        "userlogin-yourpassword": "Parola",
        "userlogin-yourpassword-ph": "Wprowadzë swòją parolã",
        "createacct-yourpasswordagain": "Pòcwierdzë parolã",
        "createacct-yourpasswordagain-ph": "Wprowôdzë parolã przistãpù znowa",
        "userlogin-remembermypassword": "Nie wëlogùjë mie",
+       "cannotlogin-title": "Ni mòże sã wlogòwac",
+       "cannotlogin-text": "Logòwanié nie je mòżlëwé",
+       "cannotloginnow-title": "Ni mòże sã terô wlogòwac",
+       "cannotcreateaccount-title": "Ni mòże ùsôdzëc kòntów",
        "yourdomainname": "Twòjô domena",
        "login": "Wlogùjë mie",
        "nav-login-createaccount": "Logòwanié",
        "createaccount": "Założë nowé kònto",
        "userlogin-resetpassword-link": "Zabëtô parola?",
        "userlogin-helplink2": "Pòmòc przë logòwaniu",
+       "createacct-emailrequired": "E-mailowô adresa",
        "createacct-emailoptional": "Adresa e-mail (òptacjowò)",
        "createacct-email-ph": "Wpiszë swóją e-mailową adresã.",
+       "createacct-another-email-ph": "Wpiszë e-mailową adresã",
        "createaccountmail": "Ùżij timczasowégò hasła i wësli je na pòdóny adres e-mail.",
        "createacct-reason": "Przëczëna",
        "createacct-submit": "Ùsôdzë kònto",
+       "createacct-another-submit": "Ùsadzë kònto",
+       "createacct-continue-submit": "Biëj dali z ùsadzaniém kònta",
+       "createacct-another-continue-submit": "Biëj dali z ùsadzaniém kònta",
        "createacct-benefit-heading": "{{grammar:B.lp|{{SITENAME}}}} ùsôdzają lëdze taczi jak Të.",
        "createacct-benefit-body1": "{{PLURAL:$1|edicëjô|edicëje|edicëjów}}",
        "createacct-benefit-body2": "{{PLURAL:$1|starna|starnë|starnów}}",
        "badretype": "Wprowadzone parole jinaczą sã midze sobą.",
        "userexists": "To miono brëkòwnika je ju w ùżëcym. Proszã wëbrac jiné miono.",
        "loginerror": "Fela logòwaniô",
+       "createacct-error": "Fela ùsadzaniô kònta",
        "loginsuccesstitle": "Ùdałé logòwanié",
        "loginsuccess": "Të jes wlogòwóny do {{SITENAME}} jakno \"$1\".",
        "nosuchuser": "Nie mô brëkòwnika ò mionie \"$1\".\nW pòzwie brëkòwnika mô znaczenié wiôlgòsc lëtrów.\nSprawdzë pisënk abò [[Special:CreateAccount|ùsadzë nowé kònto]].",
        "passwordremindertitle": "Nowô doczasnô parola dlô {{SITENAME}}",
        "passwordremindertext": "Chtos (gwës Të, z adresë $1) pòprosëł ò wësłanié nowi\nparolë dlô {{SITENAME}} ($4). Aktualnô parola dlô brëkòwnika\n\"$2\" òsta ùsôdzonô ë nastôwionô jakno \"$3\". Jeżlë to bëło twòją\njintencëją, mùszisz sã terô wlogòwac ë zmienic swòją parolã.\nNowô parola je wôznô {{PLURAL:$5|dzéń|$5 dni}}.\nJeżlë chto jinszi wësłôł to zapëtanié, abò pamiãtôsz swòją parolã\në chcesz jã dali bez zmianë brëkòwac, zjignorëje to wiadło ë\nrobi dali ze starną parolą.",
        "noemail": "W baze ni ma email-adresë dlô brëkòwnika \"$1\".",
-       "acct_creation_throttle_hit": "Môsz ùsôdzoné ju {{PLURAL:$1|1 kònto|$1 kontów}}.\nNi mòżesz miec ju wicy.",
+       "acct_creation_throttle_hit": "Z adresë IP chtërny brëkùjesz òstało, w slédnych $2 ùsôdzonych {{PLURAL:$1|1 kònto|$1 kontów}}, co je maksymalną wielëną, \nBrëkòwnicy ti IP-adresë ni mògą terôczasno ùsôdzac wicy kòntów.",
        "emailauthenticated": "Twój adres e-mail òstôł pòcwierdzóny $2 ò $3.",
        "accountcreated": "Konto założone",
        "accountcreatedtext": "Kònto brëkòwnika dlô [[{{ns:User}}:$1|$1]], [[{{ns:User talk}}:$1|talk]] òstało ùsadzóné.",
        "headline_sample": "Tekst nadgłówka",
        "headline_tip": "Nadgłówk 2 lédżi",
        "nowiki_sample": "Wstawi tuwò niesfòrmatowóny tekst",
-       "nowiki_tip": "Ignorëjë wiki-fòrmatowanié",
+       "nowiki_tip": "Jignorëjë wiki-fòrmatowanié",
        "image_sample": "Przëmiôr.jpg",
        "image_tip": "Òbsôdzony lopk (n.p. òbrôzk)",
        "media_sample": "Przëmiôr.ogg",
        "showdiff": "Wëskrzëni zjinaczi",
        "anoneditwarning": "<strong>Bôczë:</strong> Të nie jes wlogòwóny. Jeżlë wëkònôsz jakąs zmianã, twòja adresa IP mdze widocznô dlô wszëtczich. Jeżlë <strong>[$1 wlogùjesz sã]</strong> abò <strong>[$2 ùsadzysz kònto]</strong>twòje zjinaczi òstóną przëpisóné do kònta, co wicy mającë kònto dobëjesz rozmajité ùdogòdnienia.",
        "anonpreviewwarning": "Të nie jes wlogòwóny. Jeżlë wprowadzysz jaczés zjinaczi, twòja adresa IP mdze ùmieszczónô w historie edicji starnë.",
-       "summary-preview": "Pòdzérk òpisënka:",
+       "summary-preview": "Pòdzérk òpisënka zjinaków:",
        "blockedtitle": "Brëkòwnik je zascëgóny",
        "blockedtext": "<strong>Twòje kònto abò ë IP-adresa òstałë zablokòwóné.</strong>\n\nZablokòwôł je $1.\nPòdónô przëczëna to:<em>$2</em>.\n\n * Zôczątk blokadë: $8\n * Kùńc blokadë: $6\n * Cél blokadë: $7\n\n\nBë zgwësnic sprawã zablokòwaniô mòżesz skòntaktowac sã z $1 abò jińszim [[{{MediaWiki:Grouppage-sysop}}|administratorã]].\nBoczë, że të ni mòżesz stądka sélac e-mailów, jeżlë nié môsz jesz zaregisterowóné e-mailowé adresë w [[Special:Preferences|nastôwach]].\nTwòjô aktualnô adresa IP to $3, a zablokòwónô adresa ID to #$5.\nProszëmë pòdac wëższé pòdôłczi przë wszëtczich pëtaniach.",
        "loginreqlink": "Wlogùjë",
        "yourdiff": "Zjinaczi",
        "copyrightwarning": "Bôczë, że wszëtczé edicëje w {{SITENAME}} są wprowadzané pòd zastrzégą $2 (òb. $1 dlô detalów). Jeżlë nie chcesz bë to co napiszesz bëło editowóné czë kòpijowóné, tedë nie zacwierdzôj nëch edicëjów.<br />Zacwierdzając zmianë dôwôsz parolã, że to co môsz napisóné je Twòjégò aùtorstwa, abò skòpijowóné z dostónków public domain abò jinëch wòlnëch licencëjów. '''NIE DODÔWÔJ CËZËCH TEKSTÓW BEZ ZEZWÒLENIÔ!'''",
        "copyrightwarning2": "Bôczë, że wszëtczé edicëje w {{SITENAME}} mògą bëc editowóné, zmienióné abò rëmniãté bez jinëch brëkòwników.\nJeżlë nie chcesz bë Twòja robòta bëła editowónô, tedë nie dodôwôj ji tuwò.<br />\nZacwierdzając zmianë dôwôsz zgòdã na to, że to co môsz napisóné je Twòjégò aùtorstwa, abò skòpijowóné z dostónków public domain abò jinëch wòlnëch licencëjów (zdrzë za detalama na $1).\n'''NIE DODÔWÔJ ROBÒTË CHRONIONY ÙSÔDZKÒWIMA PRAWAMA BEZ ZEZWÒLENIÔ!'''",
-       "readonlywarning": "'''BÔCZËNK: Pòdôwkòwô baza òsta sztërkòwô zablokòwónô dlô administracjowich célów. Ni mòże tej timczasã zapisac nowi wersëji artikla.\nBédëjemë przeniesc ji tekst do priwatnégò lopka (wëtnij/wstôw) ë ùchòwac na pózni.'''\n\nAdministrator, chtëren jã zablokòwôł, pòdôł przëczënã: $1",
+       "readonlywarning": "<strong>Bôczënk: Pòdôwkòwô baza òsta sztërkòwô zablokòwónô dlô administracjowich célów. Ni mòże tej timczasã zapisac nowi wersëji artikla.Jeżlë chcesz, mòżesz skòpirowac jã do lopka, abë móc jã pòzdze zapisac</strong>\nSprôwnik, chtëren jã zablokòwôł, pòdôł nôslédną ji przëczënã: $1",
        "titleprotectedwarning": "'''Czó! Starna ò ti pòzwie òsta zazychrowónô. Dlô ùsadzeniô ti starnë pòtrzébné są [[Special:ListGroupRights|apartné ùdowierzenia]].'''\nNiżi  je widzec slédny wpisënk z registru:",
        "templatesused": "{{PLURAL:$1|Ùżëtô szablona|Ùżëté szablónë}} w tim artiklu:",
        "templatesusedpreview": "{{PLURAL:$1|Szablóna ùżëtô|Szablónë użëté}} w tim pòdzérkù:",
        "template-protected": "(zazychrowónô)",
        "template-semiprotected": "(dzélowò zazychrowóné)",
        "hiddencategories": "Na starna przënôleżi do w {{PLURAL:$1|1 zatacony kategòrëji|$1 zataconych kategòrëjów}}:",
-       "permissionserrors": "Fela przistspù",
+       "permissionserrors": "Fela przistãpù",
        "permissionserrorstext-withaction": "Ni môsz przëstãpù do $2, z {{PLURAL:$1|nôslédny przëczënë|nôslédnych przëczënów}}:",
        "recreate-moveddeleted-warn": "<strong>Bôczënk! Chcesz usadzëc starnã, chtërna wczasni òsta rëmniãtô.</strong>\n\nÙgwësni sã, czë pònowné ùsôdzenié ti starnë je kònieczné. \nNiżi je widzec register rëmaniów i zmian pòzwë ti starnë:",
        "moveddeleted-notice": "Na starna òsta rëmniãtô.\nSpisënk rëmaniô ë zjinaków miona ti starnë je niżi.",
        "page_last": "kùńc",
        "histlegend": "Legenda: (aktualnô) = różnice w przërównanim do aktualny wersëje,\n(wczasniészô) = różnice w przërównanim do wczasniészi wersëje, D = drobné edicëje",
        "history-fieldset-title": "Szëkôj za wersëją",
-       "history-show-deleted": "Leno rëmniãté",
+       "history-show-deleted": "Blós rëmniãté edicëje",
        "histfirst": "òd nôstarszich",
        "histlast": "òd nônowszich",
        "history-feed-title": "Historëjô wersëji",
        "search-section": "(dzél $1)",
        "search-file-match": "(pasëje do zamkłoscë lopka)",
        "search-suggest": "Të mëszlôł ò: $1",
-       "search-interwiki-caption": "Sosterné ùdbë",
+       "search-interwiki-caption": "Skùtczi ze sostrnych ùdbów",
        "search-interwiki-default": "Wëniczi òd $1:",
        "search-interwiki-more": "(wicy)",
        "searchall": "wszëtczé",
        "saveprefs": "Zapiszë",
        "prefs-editing": "Edicëjô",
        "searchresultshead": "Szëkba",
-       "stub-threshold": "Greńca dlô fòrmatowaniô <a href=\"#\" class=\"stub\">lënków stubów</a>:",
+       "stub-threshold": "Fòrmat lënka dlô mniszich starnów ($1):",
        "recentchangesdays": "Kùli dni pòkazëwac w slédnëch edicëjach:",
        "recentchangescount": "Domëslnô wielëna wëskrzëniónych edicëjów",
        "savedprefs": "Twòjé nastôwë òstałë zapisóné.",
        "gender-female": "Białka",
        "email": "E-mail",
        "prefs-help-realname": "Prôwdzëwé miono je òptacjowé, a czej je dôsz, òstanié ùżëté do pòdpisaniô Twòjégò wkładu",
-       "prefs-help-email": "Adresa e-mail je òptacëjnô, zezwôlô równak sélac do ce nową parolã jak tã zabëjesz.\nMòżesz zezwòlëc jinszim brëkòwniką na łączbã z Tobą przez Twòją starnã abò starnã diskùsëji, bez mùszebnotë wëskrzënianiô swòjich pòdôwków.",
-       "editinguser": "Zmiana praw brëkòwnika '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]]{{int:pipe-separator}}[[Special:Contributions/$1|{{int:contribslink}}]])",
+       "prefs-help-email": "Adresa e-mail je òptacjowô, zezwôlô równak na zresetowanié zabëti przez ce parolë.",
+       "editinguser": "Zmiana prawa przistãpù {{GENDER:$1|brëkòwnika|brëkòwniczczi}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-reason": "Przëczëna:",
        "group": "Karno:",
        "group-user": "Brëkòwnicë",
        "group-bot-member": "{{GENDER:$1|bòt}}",
        "group-sysop-member": "{{GENDER:$1|sprôwnik}}",
        "group-bureaucrat-member": "{{GENDER:$1|biórokrata|biórokratka}}",
-       "group-suppress-member": "rewizora",
+       "group-suppress-member": "{{GENDER:$1|rewizora|rewizorka}}",
        "grouppage-user": "{{ns:project}}:Brëkòwnicë",
        "grouppage-autoconfirmed": "{{ns:project}}:Aùtomatno zacwierdzeni brëkòwnicë",
        "grouppage-bot": "{{ns:project}}:Bòtë",
        "right-reupload-shared": "Môlowé nadpisëwanié egzystëjącegò lopka, we wespóldzelnych dostónkach",
        "right-upload_by_url": "Wladënk lopka z adresë URL",
        "right-purge": "Czëszczenié pòdrãczny pamiãcë starnë bez pëtaniô ò pòcwierdzenié",
-       "right-autoconfirmed": "Edicëjô dzélowò zazychrowónych starnów",
+       "right-autoconfirmed": "Bez ògrańczeniów przez òpiartë na adresë IP limitë",
        "right-bot": "Nacéchòwanié edicëjó jakno aùtomatnych",
        "right-writeapi": "Zapisënk przez jinterfejs API",
        "newuserlogpage": "Nowi brëkòwnicë",
        "newpageletter": "N",
        "boteditletter": "b",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajtë|bajtów}} pò zjinace",
-       "rc-enhanced-expand": "Pòkażë detale (wëmôgô JavaScript)",
+       "rc-enhanced-expand": "Pòkażë detale",
        "rc-enhanced-hide": "Zatacë detale",
        "rc-old-title": "originalno ùsôdzoné jakno \"$1\"",
        "recentchangeslinked": "Zmianë w dolënkòwónëch",
        "uploadnologin": "Felënk logòwaniô",
        "uploadtext": "Brëkùjë negò fòrmùlara do wladënkù lopków.\nJeżlë chcesz przezdrzec abò szëkac w dotenczas wladowónëch lopkach, biéj do [[Special:FileList|lësta lopków]]. Kòżdi wladënk je registrowóny w [[Special:Log/upload|registrze wladënkù]], a rëmniãcé w [[Special:Log/delete|registrze rëmaniô]].\n\nAbë dodac lopk do starnë, ùżëjë ùniższegò lënka wedle nôslédnëch mùstrów:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Lopk.jpg]]</nowiki></code>''' wëskrzëni całi lopk\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Lopk.png|200px|thumb|left|pòdpisënk òbrôzka]]</nowiki></code>''' wëskrzëni z lewi starnë, przë ùbrzégù, miniaturkã w szérzë 200 pikslów w ramie, z nôdpisã 'pòdpisënk òbrôzka'\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Lopk.ogg]]</nowiki></code>''' òtemknie prosti lënk do lopka bez wëskrzënianiô sómegò lopka",
        "uploadlogpage": "Dołączoné",
-       "uploadlogpagetext": "Hewò je lësta slédno wladowónëch lopków.\nWszëtczé gòdzënë tikają conë ùniwersalnégò czasë.",
+       "uploadlogpagetext": "Lësta slédno wladowónëch lopków.\nBiéj do [[Special:NewFiles|galerëji nowich lopków]] abë òbôczëc jich miniaturczi.",
        "filename": "Miono lopka",
        "filedesc": "Òpisënk",
        "fileuploadsummary": "Pòdrechòwanié:",
        "statistics-edits-average": "Strzédnô lëczba edicji na starnã",
        "statistics-users": "Zaregistrowónëch [[Special:ListUsers|brëkòwników]]",
        "statistics-users-active": "Aktiwnëch brëkòwników",
-       "statistics-users-active-desc": "Brekòwnicë, jaczi bëlë aktiwni òb òstatné $1 dni",
+       "statistics-users-active-desc": "Brekòwnicë, chtërni bëlë aktiwny {{PLURAL:$1|slédnegò dnia|slédnych $1 dni}}",
        "doubleredirects": "Dëbeltné przeczérowania",
        "double-redirect-fixer": "Naprôwiôcz przeczérowaniów",
        "brokenredirects": "Zerwóné przeczerowania",
        "categories": "Kategòrëje",
        "deletedcontributions": "Rëmniãti wkłôd brëkòwnika",
        "deletedcontributions-title": "Rëmniãti wkłôd brëkòwnika",
-       "linksearch": "Bùtnowé lënczi",
+       "linksearch": "Szëkba bùtnowich lënków",
        "activeusers": "Lësta aktiwnëch brëkòwników",
        "listgrouprights-members": "(lësta nôlëżników karna)",
        "emailuser": "Wëslë e-maila do negò brëkòwnika",
-       "defemailsubject": "E-mail òd {{SITENAME}}",
+       "defemailsubject": "{{SITENAME}} – e‐mail òd brëkòwnika \"$1\"",
        "noemailtitle": "Felënk email-adresë",
        "emailusername": "Pòzwa brëkòwnika",
        "emailfrom": "Òd:",
        "mywatchlist": "Lësta ùzérónëch artiklów",
        "watchlistfor2": "Dlô $1 $2",
        "watchnologin": "Felënk logòwóniô",
-       "addedwatchtext": "Starna \"[[:$1]]\" òsta dodónô do twòji [[Special:Watchlist|lëstë ùzérónëch artiklów]].\nNa ti lësce są registre przińdnëch zjinak ti starne ë na ji starnie dyskùsëji, a samò miono starnë mdze '''wëtłëszczone''' na [[Special:RecentChanges|lësce slédnich edicëji]], bë të mògł to òbaczëc.\n\nCzej chcesz remôc starnã z lëste ùzéronëch artiklów, klikni ''Òprzestôj ùzérac''.",
-       "removedwatchtext": "Starna \"[[:$1]]\" òsta rëmniãtô z Twòji [[Special:Watchlist|lëstë ùzérónych]].",
+       "addedwatchtext": "Starna \"[[:$1]]\" òsta dodónô do twòji [[Special:Watchlist|lëstë ùzérónëch artiklów]].",
+       "removedwatchtext": "Starna \"[[:$1]]\" ze starną diskùsëji òsta rëmniãtô z Twòji [[Special:Watchlist|lëstë ùzérónych artiklów]].",
        "watch": "Ùzérôj",
        "watchthispage": "Ùzérôj ną starnã",
        "unwatch": "Òprzestôj ùzerac",
        "rollbackfailed": "Nie szło copnąc zmianë",
        "alreadyrolled": "Ni mòże copnąc slédny edicëji starnë [[:$1]], chtërny ùsôdzcą je [[User:$2|$2]] ([[User talk:$2|Diskùsëjô]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nchtos jiny ju zeditowôł starnã abò copnął zmianë.\n\nSlédnym ùsódzcą starnë bëł [[User:$3|$3]] ([[User talk:$3|Diskùsëjô]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "revertpage": "Edicje brëkòwnika [[Special:Contributions/$2|$2]] ([[User talk:$2|diskùsjô]]) òstałë òdrzucóné. Aùtorã przëwrócóny wersji je [[User:$1|$1]].",
-       "rollback-success": "Edicje brëkòwnika $1 òstałë òdrzucóné; \nòsta przëwrócónô òstatnô wersjô, aùtorã chtërny je $2.",
+       "rollback-success": "Copniãto edicëje{{GENDER:$3|brëkòwnmika|brëkòwniczczi}} $1;\ndoprowpdzono nazôd slédną wersëjã ùsôdzcë {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Edicje brëkòwnika $1 òstałë òdrzucóné; \nòsta przëwrócónô òstatnô wersjô, aùtorã chtërny je $2. [$3 Pòkażë zjinaczi]",
        "protectlogpage": "Zazychrowóné",
        "protectedarticle": "zazychrowónô [[$1]]",
        "modifiedarticleprotection": "zmienionô léga zazychrowaniô [[$1]]",
-       "unprotectedarticle": "òdzychrowóny [[$1]]",
+       "unprotectedarticle": "òdzychrowôÅ\82(wa) \"[[$1]]\"",
        "protectedarticle-comment": "{{GENDER:$2|Zazychrowôł|Zazychrowała}} „[[$1]]”",
        "prot_1movedto2": "$1 przeniesłé do $2",
        "protect-legend": "Pòcwierdzë zazychrowanié",
        "protect_expiry_old": "Czas wëgasniãcô leżi w przińdnocë.",
        "protect-text": "Mòżesz tuwò sprôwdzëc ë zjinaczëc légã zazychrowaniô starnë '''$1'''.",
        "protect-locked-access": "Ni môsz dosc prawa do zjinaczi lédżi zazychrowaniô starnë. Aktualny nastôw dlô starnë '''$1''':",
-       "protect-cascadeon": "Na starna je zazychrowónô przed edicëją, dlôte że je brëkòwónô przez {{PLURAL:$1|nôslédną starnã, chtërnô òsta zazychrowónô|nôslédné starnë, chtërné òstałe zazychrowóné}} z aktiwną kaskadową òpatcëją zazychrowëwaniô.\nMòżesz zmienic légã zazychrowaniô, nie bãdze to równak miało cëskù na kaskadowé zazychrowanié.",
+       "protect-cascadeon": "Na starna je zazychrowónô przed edicëją, dlôte że je brëkòwónô przez {{PLURAL:$1|nôslédną starnã, chtërnô òsta zazychrowónô|nôslédné starnë, chtërné òstałe zazychrowóné}} z aktiwną kaskadową òpatcëją zazychrowëwaniô.\nZmiana miarë zazychrowaniô ni mô cëskù na kaskadowé zazychrowanié.",
        "protect-default": "Zezwòlë wszëtczim brëkòwnikòm",
-       "protect-fallback": "Wëmôgô prawów \"$1\"",
-       "protect-level-autoconfirmed": "Blokùjë nowich ë nieregistrowónëch brëkòwników",
-       "protect-level-sysop": "blós sprôwnicë (sysopë)",
+       "protect-fallback": "Zezwòlë blós brëkòwnikòm z prawama \"$1\"",
+       "protect-level-autoconfirmed": "Zezwòlë blós aùtomatno zacwierdzonym brëkòwnikòm",
+       "protect-level-sysop": "Zezwòlë blós sprôwnikòm",
        "protect-summary-cascade": "kaskadowanié",
        "protect-expiring": "wëgasô $1 (UTC)",
        "protect-expiry-indefinite": "na wiedno",
        "sp-contributions-newbies": "Pòkażë edicëjã blós nowich brëkòwników",
        "sp-contributions-newbies-sub": "Dlô nowich brëkòwników",
        "sp-contributions-blocklog": "historëjô blokòwaniô",
-       "sp-contributions-deleted": "rëmniãti wkłôd brëkòwnika",
+       "sp-contributions-deleted": "rëmniãtô robòta {{GENDER:$1|brëkòwnika|brëkòwniczczi}}",
        "sp-contributions-uploads": "Wësłóné lopczi",
        "sp-contributions-logs": "Rejestr logòwaniô",
-       "sp-contributions-talk": "diskùsjô",
+       "sp-contributions-talk": "diskùsëjô",
        "sp-contributions-blocked-notice-anon": "Ta adresa IP je w tim sztërkù zablokòwónô.\nSlédny wpisënk z registru blokòwaniów je widzec niżi:",
        "sp-contributions-search": "Szëkba za edicëjama",
        "sp-contributions-username": "Adresa IP abò miono brëkòwnika:",
        "whatlinkshere-hidelinks": "$1 lënczi",
        "whatlinkshere-hideimages": "$1 lënk z lopków",
        "whatlinkshere-filters": "Filtrë",
-       "blockip": "Zascëgôj IP-adresã",
+       "blockip": "Blokùjë {{GENDER:$1|brëkòwnika|brëkòwniczkã}}",
        "blockiptext": "Brëkùje formùlarza niżi abë zascëgòwac prawò zapisënkù spòd gwësny adresë IP. To robi sã blós dlôte abë zascëgnąc wandalëznom, a bëc w zgòdze ze [[{{MediaWiki:Policy-url}}|wskôzama]]. Pòdôj przëczënã (np. dając miona starn, na chtërnëch dopùszczono sã wandalëzny).",
        "ipbreason": "Przëczëna:",
        "ipboptions": "2 gòdzënë:2 hours,1 dzéń:1 day,3 dni:3 days,1 tidzéń:1 week,2 tigòdnie:2 weeks,1 ksãżëc:1 month,3 ksãżëcë:3 months,6 ksãżëców:6 months,1 rok:1 year,na wiedno:infinite",
        "badipaddress": "IP-adresa nie je richtich pòdónô.",
        "blockipsuccesssub": "Zascëgónié dało sã",
        "blockipsuccesstext": "Brëkòwnik [[Special:Contributions/$1|$1]] òstał zascëgóny.<br />\nBiéj do [[Special:BlockList|lëstë zascëgónëch adresów IP]] abë òbaczëc zascëdżi.",
-       "ipblocklist": "Lësta zablokòwónëch adresów IP ë mionów brëkòwników",
+       "ipblocklist": "Zablokòwóni brëkòwnicë",
        "blocklist-timestamp": "Czasowô sygnatura",
        "blocklist-target": "Cél",
        "blocklist-expiry": "Ùpłiwô",
        "unblocklink": "òdblokùjë",
        "change-blocklink": "zmieni blokòwanié",
        "contribslink": "wkłôd",
-       "autoblocker": "Zablokòwóno ce aùtomatnie, ga brëkùjesz ti sami adresë IP co brëkòwnik \"[[User:$1|$1]]\". Przëczënô blokòwóniô $1 to: \"'''$2'''\".",
+       "autoblocker": "Zablokòwóno ce aùtomatno bò brëkùjesz ti sómy adresë IP co brëkòwnik \"[[User:$1|$1]]\". \nPrzëczënô blokòwaniô $1 to: \"$2\"",
        "blocklogpage": "Historëjô blokòwaniô",
        "blocklogentry": "zablokòwôł [[$1]], czas blokadë: $2 $3",
        "reblock-logentry": "{{GENDER:$2|zjinacził|zjinacziła}} unastôw blokadë dlô [[$1]], czas blokadë: $2 $3",
        "proxyblocker": "Blokòwanié proxy",
        "lockbtn": "Zascëgôj bazã pòdôwków",
        "move-page-legend": "Przeniesë starnã",
-       "movepagetext": "Z pòmòcą ùiższegò fòrmùlôra zjinaczisz miono starnë, przenosząc równoczasno ji historëjã.\nPòd stôrim titlã bãdze ùsôdzonô przeczérowùjącô starna.\nMòżesz aùtomatno zaktualniac przeczérowania wskazëwôjące titel przed zjinaką.\nJeżlë nie wëbiérzesz ti òptacëji, ùgwësni sã pò przenieseniu starnë, czë nie òstałé ùsôdzoné [[Special:DoubleRedirects|dëbeltné]] abò [[Special:BrokenRedirects|zerwóné przeczérowania]].\nJes òdpòwiedzalny za to, abë lënczi dali robiłë tam dze mają.\n\nStarna '''ni''' bãdze przeniosłô, jeżlë starna ò nowim mionie ju je, chòba że je òna pùstô abò je przeczérowaniém ë mô pùstą historëjã edicëji.\nTo òznôczô, że lëchą òperacëjã zjinaczi miona mòże doprowôdzëc bezpieczno nazôd, zjinaczając nowé miono starnë nawczasniészą, ë że ni mòże nadpisac stranë chtërną ju dô.\n\n'''BÔCZËNK!'''\nTo mòże bëc drasticznô abò nieprzewidëwólnô zjinaka w przëtrôfkù pòpùlarnych starnów.\nÙgwësni sã co do skùtków ti òperacëji, niglë to zrobisz.",
+       "movepagetext": "Z pòmòcą ùiższegò fòrmùlôra zjinaczisz miono starnë, przenosząc równoczasno ji historëjã.\nPòd stôrim titlã bãdze ùsôdzonô przeczérowùjącô starna.\nMòżesz aùtomatno zaktualniac przeczérowania wskazëwôjące titel przed zjinaką.\nJeżlë nie wëbiérzesz ti òptacëji, ùgwësni sã pò przenieseniu starnë, czë nie òstałé ùsôdzoné [[Special:DoubleRedirects|dëbeltné]] abò [[Special:BrokenRedirects|zerwóné przeczérowania]].\nJes òdpòwiedzalny za to, abë lënczi dali robiłë tam dze mają.\n\nStarna <strong>ni</strong> bãdze przeniosłô, jeżlë starna ò nowim mionie ju je, chòba że je òna pùstô abò je przeczérowaniém ë mô pùstą historëjã edicëji.\nTo òznôczô, że lëchą òperacëjã zjinaczi miona mòże doprowôdzëc bezpieczno nazôd, zjinaczając nowé miono starnë nawczasniészą, ë że ni mòże nadpisac stranë chtërną ju dô.\n\n<strong>BÔCZËNK!</strong>\nTo mòże bëc drasticznô abò nieprzewidëwólnô zjinaka w przëtrôfkù pòpùlarnych starnów.\nÙgwësni sã co do skùtków ti òperacëji, niglë to zrobisz.",
        "movepagetalktext": "Sparłãczonô starna diskùsëji, jeżlë ju je, to bãdze przeniosłô aùtomatno, chòba że:\n*niepùstô starna diskùsëji ju je z nowim mionã\n*rëmniész nacéchòwanié z niższegò pòla wëbiérkù\n\nW taczich przëtrôfkach zamkłosc diskùsëji mòże przeniesc blós rãczno.",
        "newtitle": "Nowi titel:",
        "move-watch": "Ùzérôj tã starnã",
        "allmessagesnotsupportedDB": "'''{{ns:special}}:Allmessages''' nie mòże bëc brëkòwónô, temù że '''$wgUseDatabaseMessages''' je wëłączony.",
        "thumbnail-more": "Zwikszi",
        "import": "Impòrtëjë starnë",
-       "importlogpage": "Log impòrtu",
+       "importlogpage": "Log jimpòrtu",
        "tooltip-pt-userpage": "{{GENDER:|Twòja}} starna brëkòwnika",
        "tooltip-pt-mytalk": "{{GENDER:|Mòjô}} starna diskùsëji",
        "tooltip-pt-anontalk": "Diskùsjô brëkòwnika dlô ti adresë IP",
        "tooltip-summary": "Wpiszë wãzłowati òpisënk",
        "anonymous": "Anonimòwi {{PLURAL:$1|brëkòwnik|brëkòwnicë}} na {{SITENAME}}",
        "siteuser": "Brëkòwnik {{SITENAME}} $1",
-       "lastmodifiedatby": "Na starna bëła slédno editowónô $2, $1 przez $3.",
+       "lastmodifiedatby": "Slédno edicëjô ti starnë: $2, $1, ùsôdzca: $3.",
        "othercontribs": "Òpiarté na prôcë $1.",
        "others": "jiné",
        "spamprotectiontitle": "Anti-spamòwi filter",
        "pageinfo-robot-policy": "Jindeksowanié przez robòtë",
        "pageinfo-robot-index": "Zezwòloné",
        "pageinfo-robot-noindex": "Niedozwóloné",
-       "pageinfo-watchers": "Wielëna ùżérających",
+       "pageinfo-watchers": "Wielëna ùzérających",
        "pageinfo-few-watchers": "Mni jak $1 {{PLURAL:$1|ùzyrający|ùzyrających}}",
        "pageinfo-redirects-name": "Wielëna przeczérowaniów do ti starnë",
        "pageinfo-subpages-name": "Wielëna pòdstarnów ti starnë",
        "patrol-log-page": "Log patrolowaniô",
        "previousdiff": "← Pòprzédnô edicëjô",
        "nextdiff": "Nôslédnô edicëjô →",
-       "imagemaxsize": "Ògrańczë na starnie òpisënkù òbrôzków jich miarã do:",
+       "imagemaxsize": "Ograńczenié wielgòscë òbrôzków:<br /><em>(na starnach òpisënkù lopków)</em>",
        "thumbsize": "Miara miniaturków:",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|starna|starnë|starnów}}",
        "file-info-size": "$1 × $2 pikslów, miara lopka: $3, ôrt MIME: $4",
        "imgmultigo": "Biéj!",
        "imgmultigoto": "Biéj do starnë $1",
        "autoredircomment": "Przeczérowanié do [[$1]]",
-       "autosumm-new": "Pòwsta nowô starna:",
+       "autosumm-new": "Ùsôdzonô nowô starna \"$1\"",
        "watchlisttools-clear": "Wëczësczë ùzérówną lëstã",
        "watchlisttools-view": "Òbaczë wôżnészé zmianë",
        "watchlisttools-edit": "Òbaczë a editëjë lëstã ùzérónëch artiklów",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskùsëjô]])",
        "version": "Wersëjô",
        "redirect": "Przeczérëjë z jidentyfikatora lopka, brëkòwnika, starnë, wersëji abò wpisënka loga",
-       "redirect-summary": "Na szpecjalnô starna przczerowùje do: lopka(ò pòdónym mionie), do sstarny (ò pòdónym numrze wersëji abò jidentyfikatorze starë), do starnë brëkòwnika (ò pòdónym numerowim jidentyfikatorze) abò do rejestru (ò pòdónym numrze akcëji). Òrt ùżëcô: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] abò [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-summary": "Na specjalnô starna przczerowùje do: lopka(ò pòdónym mionie), do starny (ò pòdónym numrze wersëji abò jidentyfikatorze starë), do starnë brëkòwnika (ò pòdónym numerowim jidentyfikatorze) abò do rejestru (ò pòdónym numrze akcëji). Òrt ùżëcô: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] abò [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Biéj",
        "redirect-lookup": "Szëkôj:",
        "redirect-value": "Wôrtnota:",
index 936e38c..d4ad69f 100644 (file)
        "anontalk": "Sgwrs",
        "navigation": "Panel llywio",
        "and": "&#32;a/ac",
-       "qbfind": "Canfod",
-       "qbbrowse": "Pori",
-       "qbedit": "Golygu",
-       "qbpageoptions": "Y dudalen hon",
-       "qbmyoptions": "Fy nhudalennau",
        "faq": "Cwestiynau cyffredin",
-       "faqpage": "Project:Cwestiynau cyffredin",
        "actions": "Gweithrediadau",
        "namespaces": "Parthau",
        "variants": "Amrywiolion",
        "edit-local": "Golygu'r disgrifiad ar y wici hwn",
        "create": "Dechrau",
        "create-local": "Ychwanegu disgrifiad ar y wici hwn",
-       "editthispage": "Golygwch y dudalen hon",
-       "create-this-page": "Creu'r dudalen",
        "delete": "Dileu",
-       "deletethispage": "Dilëir y dudalen hon",
-       "undeletethispage": "Adfer y dudalen hon",
        "undelete_short": "Adfer $1 {{PLURAL:$1|golygiad|golygiad|olygiad|golygiad}}",
        "viewdeleted_short": "Edrych ar y {{PLURAL:$1|golygiad sydd wedi'i ddileu|golygiad sydd wedi'i ddileu|$1 olygiad sydd wedi'u dileu|$1 golygiad sydd wedi'u dileu|$1 golygiad sydd wedi'u dileu|$1 golygiad sydd wedi'u dileu}}",
        "protect": "Diogelu",
        "protect_change": "newid",
-       "protectthispage": "Diogelu'r dudalen hon",
        "unprotect": "Newid y diogelwch",
-       "unprotectthispage": "Newid gosodiadau diogelwch y dudalen hon",
        "newpage": "Tudalen newydd",
-       "talkpage": "Sgwrsiwch am y dudalen hon",
        "talkpagelinktext": "Sgwrs",
        "specialpage": "Tudalen Arbennig",
        "personaltools": "Offer personol",
-       "articlepage": "Dangos tudalen bwnc",
        "talk": "Sgwrs",
        "views": "Golygon",
        "toolbox": "Offer",
        "tool-link-userrights": "Newid {{GENDER:$1|grwpiau defnyddwyr}}",
        "tool-link-userrights-readonly": "Cip ar {{GENDER:$1|grwpiau defnyddwyr}}",
        "tool-link-emailuser": "Ebostiwch y {{GENDER:$1|defnyddiwr}}",
-       "userpage": "Gweld tudalen y defnyddiwr",
-       "projectpage": "Gweld tudalen y wici",
        "imagepage": "Gweld tudalen y ffeil",
        "mediawikipage": "Gweld tudalen y neges",
        "templatepage": "Dangos y dudalen nodyn",
        "rcfilters-highlightmenu-title": "Dewisiwch liw",
        "rcfilters-highlightmenu-help": "Dewisiwch liw sy'n cyd-fynd gyda'r nodwedd hon",
        "rcfilters-filterlist-noresults": "Dim hidl ar gael",
-       "rcfilters-filtergroup-registration": "Defnyddiwch y gofrestr",
-       "rcfilters-filter-registered-label": "Cofrestr",
-       "rcfilters-filter-registered-description": "Golygyddion sydd wedi mewngofnodi.",
-       "rcfilters-filter-unregistered-label": "Heb gofrestru",
-       "rcfilters-filter-unregistered-description": "Golygyddion nad ydynt wedi cofrestru.",
        "rcfilters-filter-editsbyself-label": "Eich golygiadau chi",
        "rcfilters-filter-editsbyself-description": "Golygiadau gennych chi.",
        "rcfilters-filter-editsbyother-label": "Golygiadau gan eraill",
        "rcfilters-filter-editsbyother-description": "Golygiadau a grewyd gan eraill (nid gennych chi).",
        "rcfilters-filtergroup-userExpLevel": "Lefel profiad (defnyddwyr cofrestredig yn unig)",
+       "rcfilters-filter-user-experience-level-registered-label": "Cofrestr",
+       "rcfilters-filter-user-experience-level-registered-description": "Golygyddion sydd wedi mewngofnodi.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Heb gofrestru",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Golygyddion nad ydynt wedi cofrestru.",
        "rcfilters-filter-patrolled-label": "Wedi derbyn patrol",
        "rcfilters-filter-patrolled-description": "Marciwyd y golygiadau fel rhai sydd wedi derbyn patrol.",
        "rcfilters-filter-unpatrolled-description": "Ni farciwyd y golygiadau fel rhai sydd wedi derbyn patrol.",
        "unblocked-id": "Tynnwyd rhwystr $1",
        "unblocked-ip": "Mae [[Special:Contributions/$1|$1]] wedi ei atal.",
        "blocklist": "Defnyddwyr a rwystrwyd",
+       "autoblocklist-submit": "Chwilio",
        "ipblocklist": "Defnyddwyr a rwystrwyd",
        "ipblocklist-legend": "Dod o hyd i ddefnyddiwr a rwystrwyd",
        "blocklist-userblocks": "Cuddio rhwystrau cyfrifon",
        "export-download": "Cynnig rhoi ar gadw ar ffurf ffeil",
        "export-templates": "Cynnwys nodiadau",
        "export-pagelinks": "Cynhwyser tudalennau cysylltiedig hyd at ddyfnder o:",
+       "export-manual": "Ychwanegu dalennau gyda llaw",
        "allmessages": "Pob neges",
        "allmessagesname": "Enw",
        "allmessagesdefault": "Testun rhagosodedig",
        "tooltip-ca-nstab-category": "Dangos tudalen y categori",
        "tooltip-minoredit": "Marciwch hwn yn olygiad bychan.",
        "tooltip-save": "Cadwch eich newidiadau",
+       "tooltip-publish": "Cyhoeddi eich newidiadau",
        "tooltip-preview": "Dangos rhagolwg o'r newidiadau; defnyddiwch cyn cadw.",
        "tooltip-diff": "Dangos y newidiadau rydych chi wedi gwneud i'r testun.",
        "tooltip-compareselectedversions": "Cymharwch y fersiynau detholedig.",
        "confirmrecreate": "Mae'r defnyddiwr [[User:$1|$1]] ([[User talk:$1|Sgwrs]]) wedi dileu'r dudalen hon ers i chi ddechrau ei golygu. Y rheswm a roddwyd yw:\n: ''$2''\nCadarnhewch eich bod chi wir am ail-greu'r dudalen.",
        "confirmrecreate-noreason": "Dileodd y defnyddiwr [[User:$1|$1]] ([[User talk:$1|sgwrs]]) y dudalen hon wedi i chi ddechrau ei golygu. Cadarnhewch eich bod chi wir am ail-greu'r dudalen hon.",
        "recreate": "Ail-greu",
+       "confirm-purge-title": "Ailgyrchu'r ddalen",
        "confirm_purge_button": "Iawn",
        "confirm-purge-top": "Clirio'r dudalen o'r storfa?",
        "confirm-purge-bottom": "Mae carthu tudalen yn clirio'r celc ac yn gorfodi'r fersiwn diweddaraf i ymddangos.",
        "confirm-watch-top": "Ychwaneger y dudalen hon at eich rhestr wylio?",
        "confirm-unwatch-button": "Iawn",
        "confirm-unwatch-top": "Tynner y dudalen hon oddi ar eich rhestr wylio?",
+       "confirm-rollback-button": "Iawn",
        "confirm-rollback-top": "Dadwneud golygiadau'r ddalen hon?",
        "quotation-marks": "'$1'",
        "imgmultipageprev": "← i'r dudalen gynt",
        "fileduplicatesearch-noresults": "Ni ddaethpwyd o hyd i ffeil o'r enw \"$1\".",
        "specialpages": "Tudalennau arbennig",
        "specialpages-note-top": "Allwedd",
-       "specialpages-note": "* Tudalennau arbennig ar gael i bawb.\n* <span class=\"mw-specialpagerestricted\">Tudalennau arbennig cyfyngedig.</span>",
        "specialpages-group-maintenance": "Adroddiadau cynnal a chadw",
        "specialpages-group-other": "Eraill",
        "specialpages-group-login": "Mewngofnodi / creu cyfrif",
        "htmlform-cloner-create": "Ychwaneger rhes",
        "htmlform-cloner-delete": "Tynner i ffwrdd",
        "htmlform-cloner-required": "Mae angen o leiaf un peth!",
+       "htmlform-title-not-exists": "Nid yw $1  yn bodoli.",
        "logentry-delete-delete": "Dileodd $1 dudalen $3",
        "logentry-delete-restore": "Adferodd $1 y dudalen $3",
        "logentry-delete-event": "Newidiodd $1 ymddangosiad {{PLURAL:$5||cofnod lòg|$5 gofnod lòg|$5 chofnod lòg|$5 chofnod lòg|$5 cofnod lòg}} ar $3: $4",
        "feedback-subject": "Gwrthrych:",
        "feedback-submit": "Cyflwyner",
        "feedback-thanks": "Diolch! Gosodwyd eich adborth ar y dudalen \"[$2 $1]\".",
+       "feedback-thanks-title": "Diolch o galon!",
        "searchsuggest-search": "Chwilio",
        "searchsuggest-containing": "yn cynnwys...",
        "api-error-badtoken": "Gwall mewnol: tocyn gwael.",
        "special-characters-title-emdash": "heiffen em",
        "special-characters-title-minus": "arwydd minws",
        "mw-widgets-dateinput-placeholder-day": "BBBB-MM-DD",
-       "mw-widgets-dateinput-placeholder-month": "BBBB-MM"
+       "mw-widgets-dateinput-placeholder-month": "BBBB-MM",
+       "mw-widgets-categoryselector-add-category-placeholder": "Ychwanega gategori...",
+       "mw-widgets-usersmultiselect-placeholder": "Ychwanega ragor...",
+       "date-range-from": "O'r dyddiad:",
+       "date-range-to": "Hyd at y dydiad:",
+       "log-action-filter-block": "Math o floc:",
+       "log-action-filter-block-block": "Bloc",
+       "log-action-filter-block-reblock": "Newid y bloc",
+       "log-action-filter-block-unblock": "Dadflocio",
+       "log-action-filter-delete-delete": "Dileu dalennau",
+       "authmanager-email-label": "Ebost",
+       "authmanager-email-help": "Cyfeiriadau ebyst",
+       "authmanager-realname-label": "Enw cywir",
+       "authmanager-realname-help": "Enw cywir y defnyddiwr",
+       "credentialsform-account": "Enw'r cyfri:"
 }
index 68727dd..2c080f6 100644 (file)
@@ -62,7 +62,8 @@
                        "Jhertel",
                        "IBDJ",
                        "SimmeD",
-                       "BoBrandt"
+                       "BoBrandt",
+                       "R12ntech"
                ]
        },
        "tog-underline": "Understreg henvisninger:",
        "rcfilters-highlightmenu-help": "Vælg en farve for at fremhæve denne egenskab",
        "rcfilters-filterlist-noresults": "Ingen filtre fundet",
        "rcfilters-noresults-conflict": "Ingen resultater fundet fordi søgekriterierne er i konflikt",
-       "rcfilters-filtergroup-registration": "Brugerregistrering",
-       "rcfilters-filter-registered-label": "Registrerede",
-       "rcfilters-filter-registered-description": "Indloggede brugere",
-       "rcfilters-filter-unregistered-label": "Uregistrerede",
-       "rcfilters-filter-unregistered-description": "Redaktører, der ikke er logget ind.",
        "rcfilters-filtergroup-authorship": "Bidragets forfatter",
        "rcfilters-filter-editsbyself-label": "Ændringer af dig",
        "rcfilters-filter-editsbyself-description": "Dine egne bidrag.",
        "rcfilters-filter-editsbyother-label": "Ændringer af andre",
        "rcfilters-filter-editsbyother-description": "Alle ændringer undtagen din egen.",
        "rcfilters-filtergroup-userExpLevel": "Erfaringsniveau (kun for registrerede brugere)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Erfaringsfiltre finder kun registrerede brugere, så dette filter er i konflikt med filtret \"Uregistrerede\".",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrerede",
+       "rcfilters-filter-user-experience-level-registered-description": "Indloggede brugere",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Uregistrerede",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Redaktører, der ikke er logget ind.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nybegyndere",
        "rcfilters-filter-user-experience-level-newcomer-description": "Færre end 10 redigeringer og 4 dages aktivitet",
        "rcfilters-filter-user-experience-level-learner-label": "Let øvede",
        "version-libraries-license": "Licens",
        "version-libraries-description": "Beskrivelse",
        "version-libraries-authors": "Forfattere",
-       "redirect": "Omdirigering pga. fil, bruger-, side- eller udgave-ID",
-       "redirect-summary": "Denne specialside omdirigerer til en fil (hvis filnavnet er angivet), en side (hvis udgave ID'et eller side ID'et er angivet) eller en brugerside (hvis et numerisk brugernummer er angivet). Eksempler på brug: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],[[{{#Special:Redirect}}/revision/328429]] eller [[{{#Special:Redirect}}/user/101]].",
+       "redirect": "Omdiriger via filnavn, bruge-, side-, revision- eller log-ID.",
+       "redirect-summary": "Denne spesialsiden omdirigerer til en fil (hvis et filnavn angis), en lave (om revisions- eller side-ID angis), en brugerside (om bruge-ID angis), eller en loggoppføring (om log-ID angis). Bruk: [[{{#Special:Redirect}}/file/Eksempel.jpg]], [[{{#Special:Redirect}}/page/#64308]], [[{{#Special:Redirect}}/revision/#328429]], [[{{#Special:Redirect}}/user/#101]] eller [[{{#Special:Redirect}}/logid/#186]].",
        "redirect-submit": "Vis",
        "redirect-lookup": "Slå op:",
        "redirect-value": "Værdi:",
        "fileduplicatesearch-noresults": "Ingen fil med navnet \"$1\" blev fundet.",
        "specialpages": "Specialsider",
        "specialpages-note-top": "Forklaring",
-       "specialpages-note": "* Normale specialsider.\n* <span class=\"mw-specialpagerestricted\">Specialsider med begrænset adgang.</span>",
        "specialpages-group-maintenance": "Vedligeholdelsesside",
        "specialpages-group-other": "Andre specialsider",
        "specialpages-group-login": "Log på / opret bruger",
        "htmlform-title-not-exists": "$1 findes ikke.",
        "logentry-delete-delete": "$1 {{GENDER:$2|slettede}} siden $3",
        "logentry-delete-delete_redir": "$1 {{GENDER:$2|slettede}} omdirigering $3 ved overskrivning",
-       "logentry-delete-restore": "$1 {{GENDER:$2|gendannede}} siden $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|gendannede}} siden $3 ($4)",
        "logentry-delete-event": "$1 {{GENDER:$2|ændrede}} synligheden af {{PLURAL:$5|en loghændelse|$5 loghændelser}} for siden $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|ændrede}} synligheden af {{PLURAL:$5|en version|$5 versioner}} af siden $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|ændrede}} synligheden af loghændelser for siden $3",
index 934b635..0969376 100644 (file)
        "rcfilters-legend-heading": "<strong>Liste von Abkürzungen:</strong>",
        "rcfilters-activefilters": "Aktive Filter",
        "rcfilters-advancedfilters": "Erweiterte Filter",
+       "rcfilters-limit-title": "Anzuzeigende Änderungen",
+       "rcfilters-limit-shownum": "Die letzten $1 Änderungen anzeigen",
+       "rcfilters-days-title": "Letzte Tage",
+       "rcfilters-hours-title": "Letzte Stunden",
+       "rcfilters-days-show-days": "{{PLURAL:$1|Ein Tag|$1 Tage}}",
+       "rcfilters-days-show-hours": "{{PLURAL:$1|Eine Stunde|$1 Stunden}}",
        "rcfilters-quickfilters": "Gespeicherte Filter",
        "rcfilters-quickfilters-placeholder-title": "Noch keine Links gespeichert",
        "rcfilters-quickfilters-placeholder-description": "Um deine Filtereinstellungen zu speichern und später erneut zu verwenden, klicke unten auf das Lesezeichensymbol im Bereich der aktiven Filter.",
        "rcfilters-invalid-filter": "Ungültiger Filter",
        "rcfilters-empty-filter": "Keine aktiven Filter. Es werden alle Beiträge angezeigt.",
        "rcfilters-filterlist-title": "Filter",
-       "rcfilters-filterlist-whatsthis": "Was ist das?",
+       "rcfilters-filterlist-whatsthis": "Wie funktioniert das?",
        "rcfilters-filterlist-feedbacklink": "Rückmeldung zu den neuen (Beta-)Filtern hinterlassen",
        "rcfilters-highlightbutton-title": "Ergebnisse hervorheben",
        "rcfilters-highlightmenu-title": "Eine Farbe auswählen",
        "rcfilters-noresults-conflict": "Keine Ergebnisse gefunden, da sich die Suchkriterien in Konflikt befinden.",
        "rcfilters-state-message-subset": "Dieser Filter hat keinen Effekt, da seine Ergebnisse mit denen {{PLURAL:$2|des folgenden breiteren Filters|der folgenden breiteren Filter}} einbezogen sind (versuche die Hervorhebung, um sie zu unterscheiden): $1",
        "rcfilters-state-message-fullcoverage": "Das Auswählen aller Filter in einer Gruppe ist das Gleiche wie das Auswählen keines Filters. Deshalb hat dieser Filter keinen Effekt. Die Gruppe enthält: $1",
-       "rcfilters-filtergroup-registration": "Benutzerregistrierung",
-       "rcfilters-filter-registered-label": "Angemeldet",
-       "rcfilters-filter-registered-description": "Angemeldete Autoren.",
-       "rcfilters-filter-unregistered-label": "Unangemeldet",
-       "rcfilters-filter-unregistered-description": "Autoren, die nicht angemeldet sind.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Dieser Filter befindet sich in Konflikt mit {{PLURAL:$2|dem folgenden Erfahrungsfilter, der nur angemeldete Benutzer finden kann|den folgenden Erfahrungsfiltern, die nur angemeldete Benutzer finden können}}: $1",
        "rcfilters-filtergroup-authorship": "Beitrags-Autorenschaft",
        "rcfilters-filter-editsbyself-label": "Änderungen von dir",
        "rcfilters-filter-editsbyself-description": "Deine eigenen Beiträge.",
        "rcfilters-filter-editsbyother-label": "Änderungen von anderen",
        "rcfilters-filter-editsbyother-description": "Alle Änderungen außer deine eigenen.",
-       "rcfilters-filtergroup-userExpLevel": "Erfahrungsniveau (nur für angemeldete Benutzer)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Erfahrungsfilter finden nur angemeldete Benutzer. Deshalb befindet sich dieser Filter in Konflikt mit dem Filter „Unangemeldet“.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Der Filter „Unangemeldet“ befindet sich in Konflikt mit einem oder mehreren Erfahrungsfiltern, die nur angemeldete Benutzer finden können. Die kollidierenden Filter sind oben im Bereich der aktiven Filter markiert.",
+       "rcfilters-filtergroup-userExpLevel": "Benutzeranmeldung und -erfahrung",
+       "rcfilters-filter-user-experience-level-registered-label": "Angemeldet",
+       "rcfilters-filter-user-experience-level-registered-description": "Angemeldete Autoren.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Unangemeldet",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Autoren, die nicht angemeldet sind.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Neulinge",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Weniger als 10 Bearbeitungen und eine Aktivität von weniger als 4 Tagen.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Angemeldete Autoren mit weniger als 10 Bearbeitungen und einer Aktivität von weniger als 4 Tagen.",
        "rcfilters-filter-user-experience-level-learner-label": "Anfänger",
-       "rcfilters-filter-user-experience-level-learner-description": "Mehr Erfahrung als „Neulinge“, aber weniger als „Erfahrene Benutzer“.",
+       "rcfilters-filter-user-experience-level-learner-description": "Angemeldete Autoren, deren Erfahrung zwischen „Neulinge“ und „Erfahrene Benutzer“ fällt.",
        "rcfilters-filter-user-experience-level-experienced-label": "Erfahrene Benutzer",
-       "rcfilters-filter-user-experience-level-experienced-description": "Eine Aktivität von mehr als 30 Tagen und mehr als 500 Bearbeitungen.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Angemeldete Autoren mit mehr als 500 Bearbeitungen und einer Aktivität von mehr als 30 Tagen.",
        "rcfilters-filtergroup-automated": "Automatisierte Beiträge",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Bearbeitungen, die von automatisierten Werkzeugen durchgeführt wurden.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Der Filter „Kleine Bearbeitungen“ kollidiert mit einem oder mehreren Änderungstypfiltern, da bestimmte Änderungstypen nicht als „klein“ festgelegt werden können. Die kollidierenden Filter sind oben im Bereich der aktiven Filter markiert.",
        "rcfilters-hideminor-conflicts-typeofchange": "Bestimmte Änderungstypen können nicht als „klein“ festgelegt werden, so dass dieser Filter mit den folgenden Änderungstypfiltern kollidiert: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Dieser Änderungstypfilter kollidiert mit dem Filter „Kleine Bearbeitungen“. Bestimmte Änderungstypen können nicht als „klein“ festgelegt werden.",
-       "rcfilters-filtergroup-lastRevision": "Letzte Version",
-       "rcfilters-filter-lastrevision-label": "Letzte Version",
-       "rcfilters-filter-lastrevision-description": "Die aktuellste Änderung an einer Seite.",
-       "rcfilters-filter-previousrevision-label": "Frühere Versionen",
-       "rcfilters-filter-previousrevision-description": "Alle Änderungen, die nicht die aktuellste Änderung an einer Seite sind.",
+       "rcfilters-filtergroup-lastRevision": "Aktuellste Versionen",
+       "rcfilters-filter-lastrevision-label": "Aktuellste Version",
+       "rcfilters-filter-lastrevision-description": "Nur die aktuellste Änderung an einer Seite.",
+       "rcfilters-filter-previousrevision-label": "Nicht die aktuellste Version",
+       "rcfilters-filter-previousrevision-description": "Alle Änderungen, die nicht die „aktuellste Version“ sind.",
        "rcfilters-filter-excluded": "Ausgeschlossen",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:nicht</strong> $1",
+       "rcfilters-exclude-button-off": "Ausgewählte ausschließen",
+       "rcfilters-exclude-button-on": "Ausgewählte ausgeschlossen",
        "rcfilters-view-tags": "Markierte Bearbeitungen",
        "rcfilters-view-namespaces-tooltip": "Ergebnisse nach Namensraum filtern",
        "rcfilters-view-tags-tooltip": "Ergebnisse filtern, die Bearbeitungsmarkierungen verwenden",
        "delete-warning-toobig": "Diese Seite hat mit mehr als $1 {{PLURAL:$1|Version|Versionen}} eine sehr lange Versionsgeschichte. Das Löschen kann zu Störungen im Datenbankbetrieb führen.",
        "deleteprotected": "Du kannst diese Seite nicht löschen, da sie geschützt wurde.",
        "deleting-backlinks-warning": "<strong>Warnung:</strong> Es verweisen noch [[Special:WhatLinksHere/{{FULLPAGENAME}}|andere Seiten]] auf diese zu löschende Seite oder sie ist noch an anderer Stelle eingebunden.",
+       "deleting-subpages-warning": "<strong>Warnung:</strong> Die Seite, die du löschen möchtest, hat [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|eine Unterseite|$1 Unterseiten|51=über 50 Unterseiten}}]].",
        "rollback": "Zurücksetzen der Änderungen",
        "rollbacklink": "Zurücksetzen",
        "rollbacklinkcount": "{{PLURAL:$1|Eine Version|$1 Versionen}} zurücksetzen",
        "fileduplicatesearch-noresults": "Es wurde keine Datei namens „$1“ gefunden.",
        "specialpages": "Spezialseiten",
        "specialpages-note-top": "Legende",
-       "specialpages-note": "* Reguläre Spezialseiten\n* <span class=\"mw-specialpagerestricted\">Zugriffsbeschränkte Spezialseiten</span>",
+       "specialpages-note-restricted": "* Reguläre Spezialseiten\n* <span class=\"mw-specialpagerestricted\">Zugriffsbeschränkte Spezialseiten</span>",
        "specialpages-group-maintenance": "Wartungslisten",
        "specialpages-group-other": "Andere Spezialseiten",
        "specialpages-group-login": "Benutzerkonto",
index 043d179..eed494d 100644 (file)
        "oct": "Pthi",
        "nov": "Ptht",
        "dec": "Pthr",
-       "pagecategories": "{{PLURAL:$1|atëkthok|atëkthuɔk}}",
-       "category_header": "Apääm në atëkthok \"$1\"ic",
-       "subcategories": "Tëktëëkor",
-       "category-media-header": "Kuat në alëk $1 yic",
-       "hidden-categories": "{{PLURAL:$1|Atëkthok cï thiaan|Atëkthuɔk cï thiaan}}",
-       "category-subcat-count": "{{PLURAL:$2|Akuutkäŋë anɔŋ akutkäŋ-thiikɛ̈ kepɛ̈c.|Akuutkäŋë anɔŋ \n{{PLURAL:$1|akuutkäŋë|$1 akuutkäŋkɛ̈}}, në $2 yic̈;}}",
+       "pagecategories": "{{PLURAL:$1|Bekätakthook|Bekätakthuɔk}}",
+       "category_header": "Apääm në bekätakthook \"$1\"ic",
+       "subcategories": "Bekätakthuɔkkor",
+       "category-media-header": "Kuat në bekätakthook \"$1\" yic",
+       "hidden-categories": "{{PLURAL:$1|Bekätakthook cï thiaan|Bekätakthuɔk cï thiaan}}",
+       "category-subcat-count": "{{PLURAL:$2|Bekätakthookë anɔŋ bekätakthookkorkɛ̈ kepɛ̈c.|Bekätakthookë anɔŋ {{PLURAL:$1|bekätakthookkorë|$1 bekätakthuɔkkorkɛ̈}}, në $2 yic̈;}}",
        "category-article-count": "{{PLURAL:$2|Bekätakthookë anɔŋic yärë yetök.|{{PLURAL:$1|Yärë atɔ̈|$1 yɔ̈rkɛ̈ aatɔ̈}} bekätakthook thiöökë yic, në $2 yic.}}",
        "category-file-count": "{{PLURAL:$2|Bekätakthook kän anɔŋic wëtmät kän etök.|{{PLURAL:|Wëtmät de $1 thiöökë atɔ̈ |wëlmäät ke $1 thiookkɛ̈ aa tɔ̈}} në bekätakthook känic, në $2 yiic ëbɛ̈n.}}",
        "listingcontinuesabbrev": "ɣäthtueŋ",
        "editsection": "cokic",
        "editold": "cokic",
        "editlink": "cokic",
-       "viewsourcelink": "Ɣoi tënëyök",
+       "viewsourcelink": "ɣoi tënëyök",
        "editsectionhint": "Cokic akut: $1",
        "toc": "Kätɔ̈",
        "showtoc": "nyooth",
        "nstab-project": "Apam kälooi",
        "nstab-image": "Apamduööt",
        "nstab-template": "Macuëc",
-       "nstab-category": "Atëkthok",
+       "nstab-category": "Bekätakthook",
        "mainpage-nstab": "Apam këdït",
        "badtitle": "Rin awäc",
        "badtitletext": "Këjiɛmë ca thiëëcë acïï lɔcök, tëdɛ̈ ka cïn kë tɔ̈u thïn, tëdɛ̈ ka këjiɛmë de thuɔkmɛ̈t wälä de wikimɛ̈t aa këcëkɛ nyiɛc nuet apath. Tëkdɛ̈ kä nɔŋic cït cïï lëu bïke luööi në käjiɛmëkeyiic.",
        "bold_sample": "Gɔ̈t dïtnyin",
        "bold_tip": "Gɔ̈t dïtnyin",
        "italic_sample": "Rïugäät",
-       "italic_tip": "Gɔ̈t cïrïu",
+       "italic_tip": "Rïugäät",
        "link_sample": "Nuɛ̈të rinënyooth",
        "link_tip": "Nuɛ̈t akenythïn",
        "extlink_sample": "http://www.example.com ruk rin",
        "showpreview": "Nyooth këkoŋtiŋ",
        "showdiff": "Nyoothë käcëwaar",
        "anoneditwarning": "<strong>Nyïïn:</strong> Yin akɛ̈cë ŋaanythïn.  IP de tëcieŋdu abë tïc tënë jäŋ ëbɛ̈n të gɛ̈t yïn ke waar käŋ. Të <strong>[$1 bɛk yïn ɣöt]</strong> tëdɛ̈ <strong>[$2 të ciɛk yïn cinakuɛ̈n]</strong>, ka kä gɔ̈tke ku kä waarke (ku kä kɔ̈k nɔŋ kony ayɛ) aabï ke gät rinëduluuiku.",
-       "loginreqlink": "Å\8aaanythïn",
+       "loginreqlink": "Å\8baanythïn",
        "newarticletext": "Yïn acë anuët lɔ apam liu buɔɔthcök.\nTë wïc yin ba apam kënë cak, ka jɔk ba gɔ̈t në akeer  tɔ̈ piinyic (tïŋ [$1 apamëkuɔny] të wic yin ba wëlëŋïc yök).\nTë tɔ̈ yïn ëtɛ̈n ke kɛ̈ce ŋuun, kayï tuany gakyeethany de <strong>ciëën</strong> në akuenarekwëldu-yic.",
        "noarticletext": "Amɛn, acïn gɔ̈t tɔ̈ ee apam thiöökë-yic.\nYïn alëu ba [[Special:Search/{{PAGENAME}}|wɛ̈tnhom thiööke göör]] në apam kɔ̈kiic,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} göör cökthɛɛr nɔŋkaariic],\nnadëk [{{fullurl:{{FULLPAGENAME}}|action=edit}} cak apamë]</span>.",
        "noarticletext-nopermission": "Amɛn, acïn gär ee apam kënë yic.  Yïn alëu ba  [[Special:Search/{{PAGENAME}}|këjiɛɛmë de apam thööke göör]]  këjiɛɛmë de apam thööke göör në apɛmkɔ̈k yiic, tëdɛ̈ ka <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}}] yïn alëu ba athörtɔ̈ɔ̈uke nɔŋ kar ke apam thiöökë gööric ayɛ. Lakin, yïn acï  nɔŋ yic ba apam yam cak.",
        "templatesused": "{{PLURAL:$1|Macuëc|Mïcuëc}} ee luööi në apam känic",
        "template-protected": "(cïtiit)",
        "template-semiprotected": "(gëlamääth)",
-       "hiddencategories": "Apamkën ee rem ë {{PLURAL:$1|1 bekätakthook cï thiaan |$1 bekäŋtakthook cï  thiaan}}:",
-       "permissionserrorstext-withaction": "Yïn acïï nɔŋ nhomlääu ba $2, \n{{PLURAL:$1|wɛ̈t de kän|wët de käk}}:",
+       "hiddencategories": "Apamkën ee rem ë {{PLURAL:$1|1 bekätakthook cï thiaan |$1 bekätakthuɔk cï thiaan}}:",
+       "permissionserrorstext-withaction": "Yïn acïï nɔŋ nhomlääu ba $2, {{PLURAL:$1|wɛ̈t de kän|wët de käk}}:",
        "moveddeleted-notice": "Apam acï cuɔthwei. Athörtɔ̈ɔ̈u de cothëwei ku nyiɛɛi në apamë aa cïke gam piiny ëtɛ̈n tɔŋ raan wïc bë ke kueen",
        "content-model-javascript": "JavaScript",
        "viewpagelogs": "Ɣoi athörtɔ̈ɔ̈u në apamkën",
        "search-section": "(akut $1)",
        "search-suggest": "Ee $1 yen ca ŋuun?",
        "searchall": "ëbɛ̈n",
-       "search-showingresults": "{{PLURAL:$4|Käcïyök <riɛl>$1</riɛl> de <riɛl>$3</riɛl>|Këcïyök <riɛl>$1 - $2</riɛl> de <riɛl>$3</riɛl>}}",
+       "search-showingresults": "{{PLURAL:$4|Käcïyök <strong>$1</strong> de <strong>$3</strong>|Këcïyök <strong>$1 - $2</strong> de <strong>$3</strong>}}",
        "search-nonefound": "Acin këcïbenbei ë thɔ̈ŋ ke thiëc",
        "preferences": "Känyaar",
        "mypreferences": "Känyaar",
        "recentchangeslinked-toolbox": "Kaceyiicwar nɔŋ kar",
        "recentchangeslinked-title": "Weer thöŋ kekë \"$1\"",
        "recentchangeslinked-summary": "Kän areny de wɛ̈r cïloi wɛ̈ramɛn tënɔŋ apam nuɛtke apam nhic (nadëk ka nuɛtke kɔcakuut de bekätakthook nhic).\nApam tɔ̈ [[Special:Watchlist|abërtïtdu]] aa <strong>gɔ̈tdïtnyin</strong>.",
-       "recentchangeslinked-page": "Rin ë akap",
+       "recentchangeslinked-page": "Rin ë apam:",
        "recentchangeslinked-to": "Nyuɔɔthë kä cï ke waar në apɛ̈m cï nuɛ̈ɛ̈t ke apam tiöökë, ku acie kä cï ke waar në yen apam thiöökë yic",
        "upload": "Wälë apamduööt",
        "filedesc": "Cuutyic",
        "filehist-help": "Tuenyë në aköl/akɔ̈licic apamduööt ɣoi kecit rɔt nyuɔɔth në akölic.",
        "filehist-current": "ee mënë",
        "filehist-datetime": "Aköl/Akɔ̈lic",
-       "filehist-thumb": "riöpbundït",
+       "filehist-thumb": "Riöpbundït",
        "filehist-thumbtext": "Alelthook ë riöpcök, tɛ̈cït $1",
        "filehist-user": "Dului",
        "filehist-dimensions": "Thëmthëm",
        "logeventslist-submit": "Nyooth",
        "allarticles": "Abɛ̈ɛ̈k ëbɛ̈n",
        "allpagessubmit": "Lɔ",
-       "categories": "Atëkthuɔk",
+       "categories": "Bekätakthuɔk",
        "categories-submit": "Nyooth",
        "sp-deletedcontributions-contribs": "amöc",
        "linksearch-ns": "Rinɣɔnläu",
        "watchlist": "Abërtït",
        "mywatchlist": "Abërtït",
        "watch": "Ɣoi",
+       "watchthispage": "Watch this page",
        "watchlist-hide": "Thaan",
        "watchlist-submit": "Nyooth",
        "historyaction-submit": "Nyooth",
        "sp-contributions-talk": "jam",
        "sp-contributions-submit": "Yiɔ̈ɔ̈p",
        "whatlinkshere": "Këkerɛkthïn",
-       "whatlinkshere-title": "apääm aanyuët tënë \"$1\"",
+       "whatlinkshere-title": "Apääm aanyuët tënë \"$1\"",
        "whatlinkshere-page": "Apam:",
        "linkshere": "Apɛ̈m bɔ̈ anuët në <riɛl>[[:$1]]</riɛl>:",
        "isredirect": "bɛnwel apam",
        "istemplate": "tɛ̈wë anuët në athööric ku bë athöördɛ̈ nyooth",
-       "isimage": "Nuɛ̈t ë apamduööt",
+       "isimage": "nuɛ̈t ë apamduööt",
        "whatlinkshere-prev": "{{PLURAL:$1|ketueŋ|ketueŋ $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|la|la $1}}",
        "whatlinkshere-links": "← anyuët",
        "tooltip-ca-nstab-project": "Ɣoi apam kätɔ̈",
        "tooltip-ca-nstab-image": "Ɣoië apäm ë makec",
        "tooltip-ca-nstab-template": "Tïŋ macuëc",
-       "tooltip-ca-nstab-category": "Ɣoië apäm atëkthok",
+       "tooltip-ca-nstab-category": "Ɣoië apäm bekätakthook",
        "tooltip-save": "Tɔ̈ɔ̈uë weerdu",
        "tooltip-preview": "Tiɛ̈ŋë tueŋ weerdu. Yïn looië yen këcë guor tɔ̈ɔ̈u.",
        "tooltip-diff": "Nyoothë weer ɣo cä looi këcïgɔ̈tic",
        "exif-colorspace": "Tëlääu kiit",
        "exif-datetimeoriginal": "Akölnïn ku akölic ë cäk ë akutëyith",
        "exif-datetimedigitized": "Akölnïn ku akölic ë cɔkakuënnhialkupiny",
-       "exif-iimcategory": "Atëkthok",
+       "exif-iimcategory": "Bekätakthook",
        "exif-disclaimer": "Acëkakuɔ",
        "exif-orientation-1": "Epath",
        "namespacesall": "ëbɛ̈n",
index 1492aa4..3e4dd89 100644 (file)
        "summary": "Σύνοψη:",
        "subject": "Θέμα:",
        "minoredit": "Αυτή είναι μια μικροαλλαγή",
-       "watchthis": "Παρακολούθηση αυτής της σελίδας",
+       "watchthis": "Παρακολούθηση της σελίδας",
        "savearticle": "Αποθήκευση σελίδας",
        "savechanges": "Αποθήκευση αλλαγών",
        "publishpage": "Δημοσίευση σελίδας",
        "rcfilters-filterlist-noresults": "Δεν βρέθηκαν φίλτρα",
        "rcfilters-noresults-conflict": "Δε βρέθηκαν αποτελέσματα γιατί τα κριτήρια αναζήτησης είναι σε διένεξη.",
        "rcfilters-state-message-subset": "Αυτό το φίλτρο δεν έχει επιρροή επειδή τα αποτελέσματά του περιλαμβάνονται σε αυτά {{PLURAL:$2|ευρύτερου φίλτρου|ευρύτερων φίλτρων}} (δοκιμάστε την επισήμανση για να τα ξεχωρήσετε): $1",
-       "rcfilters-filtergroup-registration": "Επωνυμία χρηστών",
-       "rcfilters-filter-registered-label": "Εγγεγραμμένοι",
-       "rcfilters-filter-registered-description": "Συνδεδεμένοι συντάκτες.",
-       "rcfilters-filter-unregistered-label": "Μη εγγεγραμμένοι",
-       "rcfilters-filter-unregistered-description": "Συντάκτες που δεν είναι συνδεδεμένοι.",
        "rcfilters-filtergroup-authorship": "Πατρότητα επεξεργασιών",
        "rcfilters-filter-editsbyself-label": "Επεξεργασίες από εσάς",
        "rcfilters-filter-editsbyself-description": "Οι δικές σας συνεισφορές.",
        "rcfilters-filter-editsbyother-label": "Αλλαγές από άλλους",
        "rcfilters-filter-editsbyother-description": "Όλες οι αλλαγές εκτός από τις δικές σας.",
        "rcfilters-filtergroup-userExpLevel": "Επίπεδο εμπειρίας (για εγγεγραμμένους χρήστες μόνο)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Τα φίλτρα εμπειρίας βρίσκουν μόνο εγγεγραμμένους χρήστες, οπότε αυτό το φίλτρο αντικρούει το φίλτρο \"μη εγγεγραμμένοι\".",
+       "rcfilters-filter-user-experience-level-registered-label": "Εγγεγραμμένοι",
+       "rcfilters-filter-user-experience-level-registered-description": "Συνδεδεμένοι συντάκτες.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Μη εγγεγραμμένοι",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Συντάκτες που δεν είναι συνδεδεμένοι.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Νεοφερμένοι",
        "rcfilters-filter-user-experience-level-newcomer-description": "Λιγότερες από 10 επεξεργασίες και 4 ημέρες δραστηριότητας.",
        "rcfilters-filter-user-experience-level-learner-label": "Μαθητευόμενοι",
index 4d51c9e..85dd493 100644 (file)
        "rcfilters-legend-heading": "<strong>List of abbreviations:</strong>",
        "rcfilters-activefilters": "Active filters",
        "rcfilters-advancedfilters": "Advanced filters",
+       "rcfilters-limit-title": "Changes to show",
+       "rcfilters-limit-shownum": "Show last $1 changes",
+       "rcfilters-days-title": "Recent days",
+       "rcfilters-hours-title": "Recent hours",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|day|days}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hour|hours}}",
        "rcfilters-quickfilters": "Saved filters",
        "rcfilters-quickfilters-placeholder-title": "No links saved yet",
        "rcfilters-quickfilters-placeholder-description": "To save your filter settings and reuse them later, click the bookmark icon in the Active Filter area, below.",
        "rcfilters-invalid-filter": "Invalid filter",
        "rcfilters-empty-filter": "No active filters. All contributions are shown.",
        "rcfilters-filterlist-title": "Filters",
-       "rcfilters-filterlist-whatsthis": "What's this?",
+       "rcfilters-filterlist-whatsthis": "How do these work?",
        "rcfilters-filterlist-feedbacklink": "Provide feedback on the new (beta) filters",
        "rcfilters-highlightbutton-title": "Highlight results",
        "rcfilters-highlightmenu-title": "Select a color",
        "rcfilters-noresults-conflict": "No results found because the search criteria are in conflict",
        "rcfilters-state-message-subset": "This filter has no effect because its results are included with those of the following, broader {{PLURAL:$2|filter|filters}} (try highlighting to distinguish it): $1",
        "rcfilters-state-message-fullcoverage": "Selecting all filters in a group is the same as selecting none, so this filter has no effect. Group includes: $1",
-       "rcfilters-filtergroup-registration": "User registration",
-       "rcfilters-filter-registered-label": "Registered",
-       "rcfilters-filter-registered-description": "Logged-in editors.",
-       "rcfilters-filter-unregistered-label": "Unregistered",
-       "rcfilters-filter-unregistered-description": "Editors who aren’t logged in.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "This filter conflicts with the following Experience {{PLURAL:$2|filter|filters}}, which {{PLURAL:$2|finds|find}} only registered users: $1",
        "rcfilters-filtergroup-authorship": "Contribution authorship",
        "rcfilters-filter-editsbyself-label": "Changes by you",
        "rcfilters-filter-editsbyself-description": "Your own contributions.",
        "rcfilters-filter-editsbyother-label": "Changes by others",
        "rcfilters-filter-editsbyother-description": "All changes except your own.",
-       "rcfilters-filtergroup-userExpLevel": "Experience level (for registered users only)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Experience filters find only registered users, so this filter conflicts with the “Unregistered” filter.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "The \"Unregistered\" filter conflicts with one or more Experience filters, which find registered users only. The conflicting filters are marked in the Active Filters area, above.",
+       "rcfilters-filtergroup-userExpLevel": "User registration and experience",
+       "rcfilters-filter-user-experience-level-registered-label": "Registered",
+       "rcfilters-filter-user-experience-level-registered-description": "Logged-in editors.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Unregistered",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editors who aren't logged-in.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Newcomers",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Fewer than 10 edits and 4 days of activity.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registered editors with fewer than 10 edits and 4 days of activity.",
        "rcfilters-filter-user-experience-level-learner-label": "Learners",
-       "rcfilters-filter-user-experience-level-learner-description": "More experience than \"Newcomers\" but less than \"Experienced users\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Registered editors whose experience falls between \"Newcomers\" and \"Experienced users.\"",
        "rcfilters-filter-user-experience-level-experienced-label": "Experienced users",
-       "rcfilters-filter-user-experience-level-experienced-description": "More than 30 days of activity and 500 edits.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registered editors with more than 500 edits and 30 days of activity.",
        "rcfilters-filtergroup-automated": "Automated contributions",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Edits made by automated tools.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "The \"Minor edits\" filter conflicts with one or more Type of change filters, because certain types of change cannot be designated as \"minor\". The conflicting filters are marked in the Active filters area, above.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certain types of change cannot be designated as \"minor\", so this filter conflicts with the following Type of Change filters: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "This Type of change filter conflicts with the \"Minor edits\" filter. Certain types of change cannot be designated as \"minor\".",
-       "rcfilters-filtergroup-lastRevision": "Last revision",
-       "rcfilters-filter-lastrevision-label": "Last revision",
-       "rcfilters-filter-lastrevision-description": "The most recent change to a page.",
-       "rcfilters-filter-previousrevision-label": "Earlier revisions",
-       "rcfilters-filter-previousrevision-description": "All changes that are not the most recent change to a page.",
+       "rcfilters-filtergroup-lastRevision": "Latest revisions",
+       "rcfilters-filter-lastrevision-label": "Latest revision",
+       "rcfilters-filter-lastrevision-description": "Only the most recent change to a page.",
+       "rcfilters-filter-previousrevision-label": "Not the latest revision",
+       "rcfilters-filter-previousrevision-description": "All changes that are not the \"latest revision\".",
        "rcfilters-filter-excluded": "Excluded",
        "rcfilters-tag-prefix-namespace": ":$1",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
        "rcfilters-tag-prefix-tags": "#$1",
+       "rcfilters-exclude-button-off": "Exclude selected",
+       "rcfilters-exclude-button-on": "Excluding selected",
        "rcfilters-view-tags": "Tagged edits",
        "rcfilters-view-namespaces-tooltip": "Filter results by namespace",
        "rcfilters-view-tags-tooltip": "Filter results using edit tags",
        "delete-warning-toobig": "This page has a large edit history, over $1 {{PLURAL:$1|revision|revisions}}.\nDeleting it may disrupt database operations of {{SITENAME}};\nproceed with caution.",
        "deleteprotected": "You cannot delete this page because it has been protected.",
        "deleting-backlinks-warning": "<strong>Warning:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Other pages]] link to or transclude the page you are about to delete.",
+       "deleting-subpages-warning": "<strong>Warning:</strong> The page you are about to delete has [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|a subpage|$1 subpages|51=over 50 subpages}}]].",
        "rollback": "Roll back edits",
        "rollbacklink": "rollback",
        "rollbacklinkcount": "rollback $1 {{PLURAL:$1|edit|edits}}",
        "specialpages": "Special pages",
        "specialpages-summary": "",
        "specialpages-note-top": "Legend",
-       "specialpages-note": "* Normal special pages.\n* <span class=\"mw-specialpagerestricted\">Restricted special pages.</span>",
+       "specialpages-note-restricted": "* Normal special pages.\n* <span class=\"mw-specialpagerestricted\">Restricted special pages.</span>",
+       "specialpages-note-cached": "-",
        "specialpages-group-maintenance": "Maintenance reports",
        "specialpages-group-other": "Other special pages",
        "specialpages-group-login": "Login / create account",
index c151f52..965bc54 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista de abreviaturas:</strong>",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzados",
+       "rcfilters-limit-title": "Cambios para mostrar",
+       "rcfilters-limit-shownum": "Mostrar los últimos $1 cambios",
+       "rcfilters-days-title": "Días recientes",
+       "rcfilters-hours-title": "Horas recientes",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|días}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros guardados",
        "rcfilters-quickfilters-placeholder-title": "Ningún enlace guardado aún",
        "rcfilters-quickfilters-placeholder-description": "Para guardar tus ajustes de filtro y reutilizarlos más tarde, pulsa en el icono del marcador en el área de Filtro activo que se encuentra a continuación.",
        "rcfilters-invalid-filter": "Filtro no válido",
        "rcfilters-empty-filter": "No hay filtros activos. Se muestran todas las contribuciones.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "¿Qué es esto?",
+       "rcfilters-filterlist-whatsthis": "¿Cómo funcionan?",
        "rcfilters-filterlist-feedbacklink": "Comparte tus comentarios sobre los filtros (beta) nuevos",
        "rcfilters-highlightbutton-title": "Resaltar los resultados",
        "rcfilters-highlightmenu-title": "Selecciona un color",
        "rcfilters-noresults-conflict": "No se encontraron resultados porque los criterios de búsqueda están en conflicto.",
        "rcfilters-state-message-subset": "Este filtro no tiene ningún efecto debido a que sus resultados se incluyen con los de los siguientes: {{PLURAL:$2|filtro|filtros}} (intente destacarlo para distinguirlo): $1",
        "rcfilters-state-message-fullcoverage": "Seleccionar todos los filtros de un grupo es lo mismo que seleccionar ninguno, por lo que este filtro no tiene efecto. El grupo incluye: $1",
-       "rcfilters-filtergroup-registration": "Registro de usuario",
-       "rcfilters-filter-registered-label": "Registrados",
-       "rcfilters-filter-registered-description": "Editores conectados.",
-       "rcfilters-filter-unregistered-label": "No registrados",
-       "rcfilters-filter-unregistered-description": "Editores no conectados.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Este filtro está en conflicto con {{PLURAL:$2|el siguiente filtro|los siguientes filtros}} de nivel de experiencia, que solo {{PLURAL:$2|encuentra|encuentran}} usuarios registrados: $1",
        "rcfilters-filtergroup-authorship": "Autoría de la contribución",
        "rcfilters-filter-editsbyself-label": "Cambios tuyos",
        "rcfilters-filter-editsbyself-description": "Tus propias contribuciones",
        "rcfilters-filter-editsbyother-label": "Cambios de otros",
        "rcfilters-filter-editsbyother-description": "Todos los cambios, excepto los tuyos.",
        "rcfilters-filtergroup-userExpLevel": "Nivel de experiencia (solo para usuarios registrados)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Los filtros de nivel de experiencia sólo encuentran usuarios registrados, por lo que este filtro entra en conflicto con el filtro \"No registrado\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "El filtro \"No registrado\" está en conflicto con uno o más filtros de nivel de Experiencia, que solo encuentran usuarios registrados. Los filtros en conflicto están marcados en el área Filtros Activos, anterior.",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrados",
+       "rcfilters-filter-user-experience-level-registered-description": "Editores conectados.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "No registrados",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores no conectados.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Recién llegados",
        "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 ediciones y 4 días de actividad.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
        "rcfilters-hideminor-conflicts-typeofchange-global": "El filtro \"Ediciones menores\" está en conflicto con uno o más Tipos de filtros de Cambio, ya que ciertos tipos de cambio no pueden ser designados como \"menores\". Los filtros en conflicto están marcados en el área Filtros activos, anterior.",
        "rcfilters-hideminor-conflicts-typeofchange": "Ciertos tipos de cambio no pueden ser designados como \"menores\", por lo que este filtro entra en conflicto con los siguientes  Tipos de filtros de Cambio: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro «Tipo de cambio» entra en conflicto con el filtro «Ediciones menores». Hay ciertos tipos de cambios que no pueden denominarse «menores».",
-       "rcfilters-filtergroup-lastRevision": "Revisión actual",
-       "rcfilters-filter-lastrevision-label": "Revisión actual",
-       "rcfilters-filter-lastrevision-description": "El cambio más reciente a una página.",
-       "rcfilters-filter-previousrevision-label": "Revisiones anteriores",
-       "rcfilters-filter-previousrevision-description": "Todos los cambios que no son los más recientes cambian a una página.",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisiones",
+       "rcfilters-filter-lastrevision-label": "Última revisión",
+       "rcfilters-filter-lastrevision-description": "Solo el cambio más reciente a una página.",
+       "rcfilters-filter-previousrevision-label": "No la última revisión",
+       "rcfilters-filter-previousrevision-description": "Todos los cambios que no son la \"última revisión\".",
        "rcfilters-filter-excluded": "Excluido",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>Estado:</strong> $1",
+       "rcfilters-exclude-button-off": "Excluir los seleccionados",
+       "rcfilters-exclude-button-on": "Excluyendo los seleccionados",
        "rcfilters-view-tags": "Ediciones etiquetadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por espacio de nombres",
        "rcfilters-view-tags-tooltip": "filtrado de resultados usando etiquetas de edición",
        "fileduplicatesearch-noresults": "Ningún archivo con el nombre «$1» encontrado.",
        "specialpages": "Páginas especiales",
        "specialpages-note-top": "Leyenda",
-       "specialpages-note": "* Páginas especiales normales.\n* <span class=\"mw-specialpagerestricted\">Páginas especiales restringidas.</span>",
        "specialpages-group-maintenance": "Informes de mantenimiento",
        "specialpages-group-other": "Otras páginas especiales",
        "specialpages-group-login": "Acceder/crear cuenta",
index b3bed3d..a8d1e96 100644 (file)
        "recentchanges-legend-heading": "<strong>Seletus:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (vaata ka [[Special:NewPages|uute lehekülgede loendit]])",
        "recentchanges-submit": "Näita",
+       "rcfilters-legend-heading": "<strong>Lühendite loetelu:</strong>",
        "rcfilters-activefilters": "Aktiivsed filtrid",
        "rcfilters-advancedfilters": "Täpsemad filtrid",
+       "rcfilters-limit-title": "Näita nii mitut muudatust",
+       "rcfilters-limit-shownum": "Näita viimast $1 muudatust",
+       "rcfilters-days-title": "Viimased päevad",
+       "rcfilters-hours-title": "Viimased tunnid",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|päev|päeva}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|tund|tundi}}",
        "rcfilters-quickfilters": "Salvestatud filtrid",
        "rcfilters-quickfilters-placeholder-title": "Linke pole veel salvestatud",
        "rcfilters-quickfilters-placeholder-description": "Et filtri sätted salvestada ja et neid hiljem uuesti kasutada, klõpsa alloleva aktiivsete filtrite loendi juures järjehoidjaikooni.",
        "rcfilters-invalid-filter": "Vigane filter",
        "rcfilters-empty-filter": "Aktiivsed filtrid puuduvad. Näidatakse kogu kaastööd.",
        "rcfilters-filterlist-title": "Filtrid",
-       "rcfilters-filterlist-whatsthis": "Mis see on?",
+       "rcfilters-filterlist-whatsthis": "Kuidas see töötab?",
        "rcfilters-filterlist-feedbacklink": "Anna uute filtrite beetaversiooni kohta tagasisidet",
        "rcfilters-highlightbutton-title": "Tulemuste esiletõst",
        "rcfilters-highlightmenu-title": "Vali värvus",
        "rcfilters-noresults-conflict": "Tulemusi ei leitud, sest otsikriteeriumid on konfliktsed.",
        "rcfilters-state-message-subset": "See filter ei tee midagi, sest selle tulemused on kaasatud {{PLURAL:$2|järgmise laiema filtri|järgmiste laiemate filtrite}} tulemustes (tulemuste eristamiseks proovi esiletõstu): $1",
        "rcfilters-state-message-fullcoverage": "Ühe rühma kõigi filtrite valimine on samaväärne mitte ühegi filtri valimisega, mistõttu ei tee see filter midagi. Rühmas on: $1",
-       "rcfilters-filtergroup-registration": "Registreerumine",
-       "rcfilters-filter-registered-label": "Registreerunud",
-       "rcfilters-filter-registered-description": "Sisse logitud kasutajad.",
-       "rcfilters-filter-unregistered-label": "Registreerumata",
-       "rcfilters-filter-unregistered-description": "Kasutajad, kes pole sisse logitud.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "See filter on konfliktis {{PLURAL:$2|järgmise kogemustaseme filtriga|järgmiste kogemustasemete filtritega}}, mis {{PLURAL:$2|leiab|leiavad}} ainult registreerunud kasutajaid: $1",
        "rcfilters-filtergroup-authorship": "Kaastöö autorsus",
        "rcfilters-filter-editsbyself-label": "Enda muudatused",
        "rcfilters-filter-editsbyself-description": "Sinu enda muudatused.",
        "rcfilters-filter-editsbyother-label": "Teiste muudatused",
        "rcfilters-filter-editsbyother-description": "Kõik muudatused peale sinu enda omade.",
-       "rcfilters-filtergroup-userExpLevel": "Kogemustase (ainult registreerunud kasutajate puhul)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Kogemustaseme filtrid leiavad ainult registreerunud kasutajaid, mistõttu on see filter konfliktis filtriga \"{{int:rcfilters-filter-unregistered-label}}\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filter \"{{int:rcfilters-filter-unregistered-label}}\" on konfliktis vähemalt ühe kogemustaseme filtriga, mis leiab ainult registreerunud kasutajaid. Konfliktsed filtrid on ära märgitud ülal aktiivsete filtrite loendis.",
+       "rcfilters-filtergroup-userExpLevel": "Registreerumine ja kasutaja kogemus",
+       "rcfilters-filter-user-experience-level-registered-label": "Registreerunud",
+       "rcfilters-filter-user-experience-level-registered-description": "Sisse logitud kasutajad.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Registreerumata",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Kasutajad, kes pole sisse logitud.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Äsjaalustanud",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Alla 10 muudatuse või tegutsenud alla 4 päeva.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registreerunud toimetajad, teinud alla 10 muudatuse või tegutsenud alla 4 päeva.",
        "rcfilters-filter-user-experience-level-learner-label": "Tutvujad",
-       "rcfilters-filter-user-experience-level-learner-description": "Rohkem kogemust kui äsjaalustanutel, aga vähem kui kogenud kasutajatel.",
+       "rcfilters-filter-user-experience-level-learner-description": "Registreerunud toimetajad, kellel on rohkem kogemust kui äsjaalustanutel, aga vähem kui kogenud kasutajatel.",
        "rcfilters-filter-user-experience-level-experienced-label": "Kogenud kasutajad",
-       "rcfilters-filter-user-experience-level-experienced-description": "Üle 500 muudatuse ja tegutsenud üle 30 päeva.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registreerunud toimetajad, teinud üle 500 muudatuse ja tegutsenud üle 30 päeva.",
        "rcfilters-filtergroup-automated": "Automaatne kaastöö",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Automaattööriistade tehtud muudatused.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filter \"{{int:rcfilters-filter-minor-label}}\" on konfliktis vähemalt ühe muudatuste tüübifiltriga, sest teatud tüüpi muudatusi ei saa märkida pisimuudatusteks. Konfliktsed filtrid on ära märgitud ülal aktiivsete filtrite loendis.",
        "rcfilters-hideminor-conflicts-typeofchange": "Teatud tüüpi muudatusi ei saa märkida pisimuudatusteks. Seetõttu on see filter konfliktis järgmiste tüübifiltritega: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "See muudatuste tüübifilter on konfliktis filtriga \"{{int:rcfilters-filter-minor-label}}\". Teatud tüüpi muudatusi ei saa märkida pisimuudatusteks.",
-       "rcfilters-filtergroup-lastRevision": "Viimane redaktsioon",
+       "rcfilters-filtergroup-lastRevision": "Viimased redaktsioonid",
        "rcfilters-filter-lastrevision-label": "Viimane redaktsioon",
-       "rcfilters-filter-lastrevision-description": "Muudatus, mis on leheküljel kõige viimane.",
-       "rcfilters-filter-previousrevision-label": "Varasemad redaktsioonid",
-       "rcfilters-filter-previousrevision-description": "Kõik muudatused, mis pole leheküljel kõige viimased.",
+       "rcfilters-filter-lastrevision-description": "Ainult muudatus, mis on leheküljel kõige viimane.",
+       "rcfilters-filter-previousrevision-label": "Pole viimane redaktsioon",
+       "rcfilters-filter-previousrevision-description": "Kõik muudatused, mis pole kõige viimased.",
        "rcfilters-filter-excluded": "Välja arvatud",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:mitte</strong> $1",
+       "rcfilters-exclude-button-off": "Jäta valitud välja",
+       "rcfilters-exclude-button-on": "Valitud välja jäetud",
        "rcfilters-view-tags": "Märgistatud muudatused",
+       "rcfilters-view-namespaces-tooltip": "Filtri tulemusi nimeruumide lõikes",
+       "rcfilters-view-tags-tooltip": "Filtri tulemusi muudatusmärgiste lõikes",
+       "rcfilters-view-return-to-default-tooltip": "Naase filtri peamenüüsse",
+       "rcfilters-liveupdates-button": "Uuendused reaalajas",
        "rcnotefrom": "Allpool on toodud {{PLURAL:$5|muudatus|muudatused}} alates: <strong>$3, kell $4</strong> (näidatakse kuni <strong>$1</strong> muudatust)",
        "rclistfromreset": "Lähtesta kuupäeva valik",
        "rclistfrom": "Näita muudatusi alates: $3, kell $2",
        "delete-warning-toobig": "See lehekülg on pika redigeerimislooga – üle {{PLURAL:$1|ühe muudatuse|$1 muudatuse}}.\nEttevaatust, selle kustutamine võib esile kutsuda häireid {{GRAMMAR:genitive|{{SITENAME}}}} andmebaasi töös.",
        "deleteprotected": "Seda lehekülge ei saa kustutada, sest see on kaitstud.",
        "deleting-backlinks-warning": "<strong>Hoiatus:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Teised leheküljed]] viitavad leheküljele, mida oled kustutamas, või see lehekülg on kasutuses mallina.",
+       "deleting-subpages-warning": "<strong>Hoiatus:</strong> Oled kustutamas lehekülge, millel on [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|alamlehekülg|$1 alamlehekülge|51=üle 50 alamlehekülje}}]].",
        "rollback": "Tühista muudatused",
        "rollbacklink": "tühista",
        "rollbacklinkcount": "tühista {{PLURAL:$1|üks muudatus|$1 muudatust}}",
        "undelete-search-title": "Kustutatud lehekülgede otsimine",
        "undelete-search-box": "Kustutatud lehekülgede otsimine",
        "undelete-search-prefix": "Näita lehekülgi, mille pealkiri algab nii:",
+       "undelete-search-full": "Näita leheküljepealkirju, milles sisaldub:",
        "undelete-search-submit": "Otsi",
        "undelete-no-results": "Kustutatud lehekülgede arhiivist sellist lehekülge ei leidunud.",
        "undelete-filename-mismatch": "Failiversiooni ajatempliga $1 ei saa taastada, sest failinimed ei klapi.",
        "fileduplicatesearch-noresults": "Faili nimega \"$1\" ei leidu.",
        "specialpages": "Erileheküljed",
        "specialpages-note-top": "Seletus",
-       "specialpages-note": "* Harilikud erileheküljed.\n* <span class=\"mw-specialpagerestricted\">Piiranguga erileheküljed.</span>",
        "specialpages-group-maintenance": "Hooldusaruanded",
        "specialpages-group-other": "Teised erileheküljed",
        "specialpages-group-login": "Sisselogimine ja konto loomine",
index 9dd6be4..19e3f20 100644 (file)
        "botpasswords-created-title": "Botaren pasahitza sortu da",
        "botpasswords-updated-title": "Botaren pasahitza eguneratu da",
        "botpasswords-deleted-title": "Botaren pasahitza ezabatu da",
+       "botpasswords-deleted-body": "$2 erabiltzailearen $1 errobot izenak duen errobot pasahitza ezabatua izan da.",
        "botpasswords-no-provider": "BotPasswordsSessionProvider ez dago eskuragarri.",
        "botpasswords-restriction-failed": "Errobot pasahitza murrizketek logina saihesten dute.",
        "resetpass_forbidden": "Ezin dira pasahitzak aldatu",
        "content-not-allowed-here": "\"$1\" edukia ez dago baimendua [[$2]] orrialdean",
        "editwarning-warning": "Orri honetatik irteten bazara, egindako aldaketak galdu egingo dira, beharbada.\nSaioa hasi baduzu, mezu hau kendu dezakezu zure hobespenen orriko «{{int:prefs-editing}}» atalean.",
        "editpage-invalidcontentmodel-title": "Eduki eredua ez da onartzen.",
+       "editpage-invalidcontentmodel-text": "$1 eduki eredua ezin da erabili.",
        "editpage-notsupportedcontentformat-title": "Eduki formatu hori ez da onartzen",
+       "editpage-notsupportedcontentformat-text": "$2 eduki ereduak ezin da erabili $1 eduki formatuarekin.",
        "content-model-wikitext": "wikitestua",
        "content-model-text": "testu laua",
        "content-model-javascript": "JavaScript",
        "node-count-exceeded-category": "Nodo-zenbaketa gainditu den orrialdeak",
        "node-count-exceeded-warning": "Orriak nodo-kopuruaren muga gainditu du",
        "expansion-depth-exceeded-category": "Orrialdearen espantsio sakonera gainditu da",
+       "expansion-depth-exceeded-category-desc": "Orriak zabalkunde sakonera maximoa gainditzen du.",
        "expansion-depth-exceeded-warning": "Espantsio sakonera gainditu duten orrialdeak",
        "parser-unstrip-loop-warning": "Loop unstrip bat aurkitu da",
        "parser-unstrip-recursion-limit": "Unstrip errekurtsio limitea gainditu da ($1)",
        "right-protect": "Orrialde babestuak aldatu eta babes maila aldatu",
        "right-editprotected": "Babestutako orrialdeak aldatu (babes jauzirik gabe)",
        "right-editsemiprotected": "Aldatu \"{{int:protect-level-autoconfirmed}}\" babesa duten orrialdeak",
+       "right-editcontentmodel": "Aldatu orri bateko eduki eredua",
        "right-editinterface": "Erabiltzailearen interfazea aldatu",
        "right-editusercss": "Beste lankideen CSS fitxategiak aldatu",
        "right-edituserjs": "Beste lankideen JS fitxategiak aldatu",
        "recentchanges-legend-heading": "<strong>Azalpenak:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ikus, gainera, [[Special:NewPages|orri berrien zerrenda]])",
        "recentchanges-submit": "Erakutsi",
+       "rcfilters-legend-heading": "<indartsu>Laburpenen zerrenda:</indartsu>",
        "rcfilters-activefilters": "Iragazki aktiboak",
        "rcfilters-advancedfilters": "Iragazki aurreratuak",
+       "rcfilters-limit-title": "Aldaketak erakutsi",
+       "rcfilters-limit-shownum": "Azken $1 aldaketak erakutsi",
+       "rcfilters-days-title": "Azken egunak",
+       "rcfilters-hours-title": "Azken orduak",
+       "rcfilters-days-show-days": "{{PLURAL:$1|Egun $1|$1 egun}}",
+       "rcfilters-days-show-hours": "{{PLURAL:$1|Ordu $1|$1 ordu}}",
        "rcfilters-quickfilters": "Gordetako iragazkiak",
        "rcfilters-quickfilters-placeholder-title": "Ez dira oraindik Link-ak gorde",
        "rcfilters-savedqueries-defaultlabel": "Gordetako iragazkiak",
        "rcfilters-clear-all-filters": "Iragazki guztiak garbitu",
        "rcfilters-search-placeholder": "Aldaketa berriak iragazi (nabigatu ala idatzi)",
        "rcfilters-invalid-filter": "Balio ez duen iragazkia",
+       "rcfilters-empty-filter": "Filtro aktiborik ez dago. Ekarpen guztiak erakusten.",
        "rcfilters-filterlist-title": "Iragazkiak",
-       "rcfilters-filterlist-whatsthis": "Zer da hau?",
+       "rcfilters-filterlist-whatsthis": "Nola erabiltzen da?",
        "rcfilters-highlightbutton-title": "Nabarmendu emaitzak",
        "rcfilters-highlightmenu-title": "Hautatu kolore bat",
        "rcfilters-filterlist-noresults": "Ez da iragazkirik aurkitu",
-       "rcfilters-filtergroup-registration": "Erabiltzaile erregistroa",
-       "rcfilters-filter-registered-label": "Erregistratuak",
-       "rcfilters-filter-unregistered-label": "Ez erregistratuak",
        "rcfilters-filtergroup-authorship": "Ekarpenaren egiletza",
        "rcfilters-filter-editsbyself-label": "Zuk egindako aldaketak",
        "rcfilters-filter-editsbyself-description": "Zure ekarpenak",
        "rcfilters-filter-editsbyother-label": "Besteek egindako aldaketak",
        "rcfilters-filter-editsbyother-description": "Aldaketa guztiak norberarenak izan ezik.",
+       "rcfilters-filtergroup-userExpLevel": "Erregistro esperientzia eta esperientzia",
+       "rcfilters-filter-user-experience-level-registered-label": "Erregistratuak",
+       "rcfilters-filter-user-experience-level-registered-description": "Sesioa hasi duten editoreak.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Ez erregistratuak",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Saioa hasi ez duten editoreak.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Iritsiberriak",
+       "rcfilters-filter-user-experience-level-newcomer-description": "10 aldaketa eta 4 ekintza-egun baino gutxiagoko erregistratutako editoreak.",
        "rcfilters-filter-user-experience-level-learner-label": "Ikasleak",
+       "rcfilters-filter-user-experience-level-learner-description": "Erregistratutako editoreak, zeinen esperientzia \"Etorri berriak\" eta \"Erabiltzaile aurreratuen\" artean dagoena.",
        "rcfilters-filter-user-experience-level-experienced-label": "Erabiltzaile trebatuak",
-       "rcfilters-filter-user-experience-level-experienced-description": "30 eguneko jarduera eta 500 aldaketa baino gehiago.",
+       "rcfilters-filter-user-experience-level-experienced-description": "30 eguneko jarduera eta 500 aldaketa baino gehiago dituzten erregistratutako editoreak.",
        "rcfilters-filtergroup-automated": "Automatizatutako ekarpenak",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Tresna automatizatuekin egindako aldaketak.",
        "rcfilters-filter-newpages-description": "Orri berriak egiten dituzten aldaketak",
        "rcfilters-filter-categorization-label": "Kategoria aldaketak",
        "rcfilters-filter-logactions-label": "Erregistratutako ekintzak",
-       "rcfilters-filtergroup-lastRevision": "Azken berrikuspena",
+       "rcfilters-filtergroup-lastRevision": "Azken berrikuspenak",
        "rcfilters-filter-lastrevision-label": "Azken berrikuspena",
        "rcfilters-filter-lastrevision-description": "Orrialde bati eginiko aldaketarik berriena.",
-       "rcfilters-filter-previousrevision-label": "Aurreko berrikuspenak",
+       "rcfilters-filter-previousrevision-label": "Ez da azken berrikuspena",
+       "rcfilters-filter-previousrevision-description": "\"Azken berrikuspena\" ez diren aldaketa guztiak.",
        "rcfilters-filter-excluded": "Baztertua",
+       "rcfilters-exclude-button-off": "Baztertzea aukeratuta",
+       "rcfilters-exclude-button-on": "Baztertzea aukeratuta",
        "rcfilters-view-tags": "Etiketa aldaketak",
+       "rcfilters-view-namespaces-tooltip": "Emaitzak aurrikiaren arabera hautatu",
+       "rcfilters-view-tags-tooltip": "Iragazi emaitzak aldaketa etiketak erabiliz",
+       "rcfilters-view-return-to-default-tooltip": "Oinarrizko iragazketa menura bueltatu",
        "rcfilters-liveupdates-button": "Zuzenean egindako eguneraketak",
        "rcnotefrom": "Jarraian azaltzen diren {{PLURAL:$5|aldaketak}} data honetatik aurrerakoak dira: <strong>$3,$4</strong> (gehienez <b>$1</b> erakusten dira).",
        "rclistfrom": "Erakutsi $3 $2 ondorengo aldaketa berriak",
        "download": "jaitsi",
        "unwatchedpages": "Jarraitu gabeko orrialdeak",
        "listredirects": "Birzuzenketen zerrenda",
+       "listduplicatedfiles": "Kopiak dituzten artxiboen zerrenda",
        "unusedtemplates": "Erabili gabeko txantiloiak",
        "unusedtemplatestext": "Orrialde honetan beste edozein orrialdetan erabiltzen ez diren {{ns:template}} izen-tarteko orrialdeak zerrendatzen dira. Ez ahaztu txantiloietara egon daitezkeen loturak egiaztatzeaz ezabatu aurretik.",
        "unusedtemplateswlh": "beste loturak",
        "wantedpages-summary": "Lotura gehien dituzten eta existitzen ez diren orrialdeen zerrenda da hau, eurengana birzuzenketak baino ez dituzten orrialdeak kenduta. Eurenganako birzuzenketak dituzten baina existitzen ez diren orrialdeen zerrenda bat ikusteko, [[{{#special:BrokenRedirects}}|apurtutako birzuzenketen zerrendara]] jo dezakezu.",
        "wantedpages-badtitle": "Izenburu okerra hautatutako emaitzetan: $1",
        "wantedfiles": "Eskatutako fitxategiak",
+       "wantedfiletext-nocat-noforeign": "Hurrengo artxiboak erabiltzen ari dira baina ez dira existitzen.",
        "wantedtemplates": "Eskatutako txantiloiak",
        "mostlinked": "Gehien lotutako orrialdeak",
        "mostlinkedcategories": "Gehien lotutako kategoriak",
        "apihelp": "API laguntza",
        "apihelp-no-such-module": "Ez da \"$1\" modulua aurkitu.",
        "apisandbox": "API proba orria",
+       "apisandbox-jsonly": "API sandbox-a erabiltzeko JavaScript eskatzen da.",
        "apisandbox-fullscreen": "Zabaldu panela",
        "apisandbox-unfullscreen": "Erakutsi orria",
        "apisandbox-submit": "Egin eskaera",
        "apisandbox-dynamic-parameters-add-label": "Gehitu parametroa:",
        "apisandbox-dynamic-parameters-add-placeholder": "Parametroaren izena",
        "apisandbox-dynamic-error-exists": "$1 parametro izena dagoeneko existitzen da",
+       "apisandbox-submit-invalid-fields-title": "Zelai batzuk ez dute balio.",
+       "apisandbox-submit-invalid-fields-message": "Mesedez, zuzendu markatutako zelaiak eta saiatu berrio.",
        "apisandbox-results": "Emaitzak",
        "apisandbox-sending-request": "APIari eskaera bidaltzen...",
        "apisandbox-loading-results": "APIaren emaitzak jasotzen...",
+       "apisandbox-request-url-label": "Eskatutako URLa:",
+       "apisandbox-request-json-label": "Eskatutako JSONa:",
+       "apisandbox-request-time": "Eskakisun denbora: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-alert-page": "Orri honetako zelaiek ez dute balio.",
+       "apisandbox-alert-field": "Zelai honetako balioak ez du balio.",
        "apisandbox-continue": "Jarraitu",
        "apisandbox-continue-clear": "Garbitu",
        "apisandbox-multivalue-all-values": "$1 (balio guztiak)",
        "rollbacklinkcount": "desegin {{PLURAL:$1|edizio bat|$1 edizio}}",
        "rollbacklinkcount-morethan": "desegin {{PLURAL:$1|edizio bat|$1 edizio}} baino gehiago",
        "rollbackfailed": "Desegiteak huts egin dud",
+       "rollback-missingrevision": "Berrikusketa data ezin izan da kargatu.",
        "cantrollback": "Ezin da aldaketa desegin; erabiltzaile bakarrak hartu du parte.",
        "alreadyrolled": "Ezin da [[User:$2|$2]] ([[User talk:$2|eztabaida]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) wikilariak «[[:$1]]» orrian egindako azken aldaketa desegin;\nbeste norbaitek editatu edo desegin du jadanik.\n\nAzken aldaketa [[User:$3|$3]] ([[User talk:$3|eztabaida]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) wikilariak egin du.",
        "editcomment": "Aldaketaren laburpena: <em>$1</em>.",
        "rollback-success": "{{GENDER:$3|$1}}; wikilariaren aldaketak deseginda,\nedukia {{GENDER:$4|$2}} wikilariaren azken bertsiora itzuli da.",
        "sessionfailure-title": "Saio-akatsa",
        "sessionfailure": "Badirudi saioarekin arazoren bat dagoela; bandalismoak saihesteko ekintza hau ezeztatu egin da. Mesedez, nabigatzaileko \"atzera\" botoian klik egin, hona ekarri zaituen orrialde hori berriz kargatu, eta saiatu berriz.",
+       "changecontentmodel": "Aldatu orri bateko eduki eredua",
+       "changecontentmodel-legend": "Aldatu eduki eredua",
        "changecontentmodel-title-label": "Orriaren izenburua",
+       "changecontentmodel-model-label": "Eduki eredu berria",
        "changecontentmodel-reason-label": "Arrazoia:",
        "changecontentmodel-submit": "Aldatu",
+       "changecontentmodel-success-title": "Eduki eredua aldatu egin da",
+       "changecontentmodel-emptymodels-title": "Eduki eredurik ez dago eskuragarri",
        "logentry-contentmodel-change-revertlink": "desegin",
        "logentry-contentmodel-change-revert": "desegin",
        "protectlogpage": "Babes erregistroa",
        "undelete-search-title": "Ezabatutako orrialdeak bilatu",
        "undelete-search-box": "Ezabatutako orrialdeak bilatu",
        "undelete-search-prefix": "Honela hasten diren orrialdeak erakutsi:",
+       "undelete-search-full": "Orrialde izenburuak erakutsi:",
        "undelete-search-submit": "Bilatu",
        "undelete-no-results": "Ez da bat datorren orrialderik aurkitu ezabaketen artxiboan.",
        "undelete-filename-mismatch": "Ezin da $1 denbora-marka duten fitxategi aldaketa ezabatua berrezarri: fitxategi-izena ez dator bat",
        "block-log-flags-hiddenname": "lankide-izen ezkutua",
        "range_block_disabled": "Administratzaileak IP eremuak blokeatzeko gaitasuna ezgaituta dago.",
        "ipb_expiry_invalid": "Aldiaren bukaerako data ez da baliozkoa.",
+       "ipb_expiry_old": "Epemuga iraganean dago.",
        "ipb_expiry_temp": "Izkutuan dauden lankide izenen blokeoa betierekikoa izan behar du.",
        "ipb_hide_invalid": "Ezin izan da kontu hau ezabatu; aldaketa asko izan baitezake.",
        "ipb_already_blocked": "\"$1\" badago blokeatuta",
        "lockdbsuccesstext": "Datu-basea blokeatu egin da. <br />Ez ahaztu mantenu lanak burutu ondoren [[Special:UnlockDB|blokeoa kentzeaz]].",
        "unlockdbsuccesstext": "Datu-basea desblokeatu egin da.",
        "lockfilenotwritable": "Ezin da datu-baseko blokeo fitxategian idatzi. Datu-basea blokeatu edo desblokeatzeko, zerbitzariak idazteko aukera izan beharra dauka.",
+       "databaselocked": "Datu-basea blokeatuta dago jada.",
        "databasenotlocked": "Datu-basea ez dago blokeatuta.",
        "lockedbyandtime": "({{GENDER:$1|$1}}k egina $2ko $3(e)tan)",
        "move-page": "Mugitu «$1»",
        "pageinfo-category-pages": "Orrialde kopurua",
        "pageinfo-category-subcats": "Azpikategorien zenbakia",
        "pageinfo-category-files": "Fitxategi kopurua",
+       "pageinfo-user-id": "Erabiltzaile IDa",
        "markaspatrolleddiff": "Patruilatutzat markatu",
        "markaspatrolledtext": "Artikulu hau patruilatutzat markatu",
        "markaspatrolledtext-file": "Fitxategi honen bertsioa patruilatutzat markatu",
        "patrol-log-header": "Hau patruliatutako aldaketen log bat da.",
        "log-show-hide-patrol": "$1 patruilatze loga",
        "log-show-hide-tag": "$1 etiketa erregistroa",
+       "confirm-markpatrolled-button": "Ados",
        "deletedrevision": "$1 berrikuspen zaharra ezabatu da",
        "filedeleteerror-short": "Errorea fitxategia ezabatzerakoan: $1",
        "filedeleteerror-long": "Erroreak gertatu dira fitxategia ezabatzerakoan:\n\n$1",
        "newimages-legend": "Iragazkia",
        "newimages-label": "Fitxategia (edo bere zati bat):",
        "newimages-user": "IP helbidea edo erabiltzaile-izena",
+       "newimages-newbies": "Soilik kontu berrien ekarpenak erakutsi",
        "newimages-showbots": "Erakutsi botek igotako fitxategiak",
+       "newimages-hidepatrolled": "Izkutatu patruilatutako igoerak",
+       "newimages-mediatype": "Media mota:",
        "noimages": "Ez dago ezer ikusteko.",
        "ilsubmit": "Bilatu",
        "bydate": "dataren arabera",
        "deletedwhileediting": "'''Oharra''': Zu aldaketak egiten hasi ondoren orrialdea ezabatua izan da!",
        "confirmrecreate": "[[User:$1|$1]] erabiltzaileak ([[User talk:$1|eztabaida]]) orrialde hau ezabatu zu aldatzen hasi eta gero. Hona arrazoia: : ''$2'' Mesedez, baieztatu orrialde hau berriz sortu nahi duzula.",
        "recreate": "Birsortu",
+       "confirm-purge-title": "Orri hau purgatu",
        "confirm_purge_button": "Ados",
        "confirm-purge-top": "Orrialde honen katxea ezabatu?",
        "confirm-purge-bottom": "Orrialdea purgatzean katxea ezabatzen du eta orrialdearen bertsiorik eguneratuena erakustera behartzen du.",
        "redirect-page": "Orriaren IDa",
        "redirect-revision": "Orrialdearen berrikuspena",
        "redirect-file": "Fitxategiaren izena",
+       "redirect-logid": "Log IDa",
        "redirect-not-exists": "Ez da baliorik aurkitu",
        "fileduplicatesearch": "Artxibo bikoiztuen bilaketa",
        "fileduplicatesearch-summary": "Bikoiztutako fitxategiak bilatu bere hash balioaren arabera.",
        "fileduplicatesearch-noresults": "Ez da aurkitu \"$1\" izeneko fitxategirik.",
        "specialpages": "Orri bereziak",
        "specialpages-note-top": "Azalpenak",
-       "specialpages-note": "* Orri berezi arruntak.\n* <strong class=\"mw-specialpagerestricted\">Mugatutako orri bereziak.</strong>",
        "specialpages-group-maintenance": "Mantentze-oharrak",
        "specialpages-group-other": "Beste orri berezi batzuk",
        "specialpages-group-login": "Hasi saioa / sortu kontua",
        "tag-filter": "[[Special:Tags|Etiketa]] iragazkia:",
        "tag-filter-submit": "Iragazkia",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiketa|Etiketak}}]]: $2)",
+       "tag-mw-contentmodelchange": "Eduki eredu aldaketa",
        "tags-title": "Etiketak",
        "tags-intro": "Orri honek softwareak aldatzeko bezala marka ditzazkeen etiketak zerrendatzen ditu, eta berauen esanahia.",
        "tags-tag": "Etiketaren izena",
        "tags-delete-title": "Etiketa ezabatu",
        "tags-delete-explanation-initial": "Datu-basetik \"$1\" etiketa ezabatzera zoaz",
        "tags-delete-reason": "Arrazoia:",
+       "tags-delete-submit": "Betirako ezabatu etiketa hau",
        "tags-delete-not-found": "\"$1\" etiketa  ez da existitzen.",
+       "tags-delete-no-permission": "Ez daukazu baimenik etiketa aldaketak ezabatzeko.",
        "tags-activate-title": "Etiketa aktibatu",
        "tags-activate-question": "\"$1\" etiketa aktibatzear zaude.",
        "tags-activate-reason": "Arrazoia:",
        "tags-edit-remove": "Kendu etiketa hauek:",
        "tags-edit-remove-all-tags": "(kendu etiketa guztiak)",
        "tags-edit-chosen-placeholder": "Hautatu etiketa batzuk",
+       "tags-edit-chosen-no-results": "Balio duen etiketarik ez da aurkitu",
        "tags-edit-reason": "Arrazoia:",
        "tags-edit-success": "Aldaketak ezarri dira.",
        "tags-edit-failure": "Ezin izan dira aldaketak ezarri:\n$1",
+       "tags-edit-nooldid-title": "Baliogabeko berrikusketa helburua",
        "comparepages": "Orrialdeak alderatu",
        "compare-page1": "1. orrialdea",
        "compare-page2": "2. orrialdea",
        "htmlform-chosen-placeholder": "Aukeratu",
        "htmlform-cloner-create": "Gehitu gehiago",
        "htmlform-cloner-delete": "Kendu",
+       "htmlform-cloner-required": "Gutxienez balio bat behar da.",
        "htmlform-date-placeholder": "UUUU-HH-EE",
        "htmlform-time-placeholder": "OO:MM:SS",
        "htmlform-datetime-placeholder": "UUUU-HH-EE OO:MM:SS",
        "pagelang-language": "Hizkuntza",
        "pagelang-use-default": "Hizkuntza lehenetsia erabili",
        "pagelang-select-lang": "Hizkuntza aukeratu",
+       "pagelang-reason": "Arrazoia",
        "pagelang-submit": "Bidali",
+       "pagelang-nonexistent-page": "$1 orria ez da existitzen.",
        "pagelang-unchanged-language": "$1 orrialdea jada $2 hizkuntzan dago.",
        "right-pagelang": "Aldatu orrialdearen hizkuntza",
        "action-pagelang": "orrialdearen hizkuntza aldatu",
        "special-characters-group-ipa": "NAF",
        "special-characters-group-symbols": "Ikurrak",
        "special-characters-group-greek": "Greziera",
+       "special-characters-group-greekextended": "Luzatutako Greziera",
        "special-characters-group-cyrillic": "Zirilikoa",
        "special-characters-group-arabic": "Arabiera",
        "special-characters-group-arabicextended": "Arabiera hedatua",
        "special-characters-group-thai": "Thaiera",
        "special-characters-group-lao": "Laosera",
        "special-characters-group-khmer": "Khmerrera",
+       "special-characters-group-canadianaboriginal": "Kanadiar aborigenoa",
        "special-characters-title-endash": "en lerroa",
        "special-characters-title-emdash": "em lerroa",
        "special-characters-title-minus": "minus zeinua",
        "mw-widgets-dateinput-no-date": "Ez duzu datarik aukeratu",
+       "mw-widgets-mediasearch-input-placeholder": "Multimedia bilatu",
        "mw-widgets-mediasearch-noresults": "Ez da emaitzarik aurkitu.",
        "mw-widgets-titleinput-description-new-page": "orri hori oraindik ez da existitzen",
        "mw-widgets-titleinput-description-redirect": "$1ra birzuzendu",
        "sessionprovider-generic": "$1 sesio",
        "randomrootpage": "Ausazko sustrai orria",
        "log-action-filter-block": "Blokeatze mota:",
+       "log-action-filter-contentmodel": "Eduki eredu motaren aldaketa:",
        "log-action-filter-delete": "Ezabatze mota:",
        "log-action-filter-import": "Inportazio mota:",
        "log-action-filter-move": "Mugimendu mota:",
        "log-action-filter-block-block": "Blokeatu",
        "log-action-filter-block-reblock": "Blokeoa aldatu",
        "log-action-filter-block-unblock": "blokeoa kendu",
+       "log-action-filter-contentmodel-change": "Aldatu eduki eredua",
+       "log-action-filter-delete-delete": "Orri ezabaketa",
+       "log-action-filter-delete-restore": "Ezabatutako orria berreskuratu",
        "log-action-filter-delete-revision": "Berrikuspen ezabaketa",
        "log-action-filter-import-interwiki": "Transwiki inportazioa",
        "log-action-filter-import-upload": "Inportatu XML igoera bidez",
        "log-action-filter-managetags-delete": "Etiketa ezabaketa",
        "log-action-filter-managetags-activate": "Etiketa aktibazioa",
        "log-action-filter-managetags-deactivate": "Etiketa desaktibazioa",
+       "log-action-filter-newusers-create": "Erabiltzaile anonimo batek sortua",
+       "log-action-filter-newusers-create2": "Izena emandako erabiltzaile batek idatzia",
        "log-action-filter-newusers-autocreate": "Sorrera automatikoa",
+       "log-action-filter-patrol-patrol": "Patruilaketa manuala",
+       "log-action-filter-patrol-autopatrol": "Patruilaketa automatikoa",
        "log-action-filter-protect-protect": "Babesa",
+       "log-action-filter-protect-modify": "Babespen aldaketa",
+       "log-action-filter-protect-unprotect": "Babesgabea",
+       "log-action-filter-protect-move_prot": "Mugitutako babesa",
        "log-action-filter-rights-rights": "Eskuzko aldaketa",
        "log-action-filter-rights-autopromote": "Aldaketa automatikoa",
        "log-action-filter-upload-upload": "Igoera berria",
+       "log-action-filter-upload-overwrite": "Birkargatu",
+       "authmanager-authplugin-setpass-failed-title": "Ezin izan da pasahitza aldatu",
+       "authmanager-authplugin-setpass-bad-domain": "Balio ez duen domeinua.",
+       "authmanager-autocreate-noperm": "Ezin da automatikoki erabiltzaile bat sortu.",
        "authmanager-userdoesnotexist": "\"$1\" erabiltzaile kontua ez dago erregistratua.",
+       "authmanager-retype-help": "Pasahitza berriro baieztatzeko.",
        "authmanager-email-label": "Emaila",
        "authmanager-email-help": "Helbide elektronikoa",
        "authmanager-realname-label": "Benetako izena",
        "authmanager-realname-help": "Erabiltzailearen benetako izena",
+       "authmanager-provider-password": "Pasahitzan oinarritutako baieztapena",
+       "authmanager-provider-password-domain": "Pasahitza eta domeinuan oinarritutako baieztapena",
        "authmanager-provider-temporarypassword": "Behin-behineko pasahitza",
        "authprovider-resetpass-skip-label": "Utzi",
        "authform-wrongtoken": "Token okerra",
+       "specialpage-securitylevel-not-allowed-title": "Baimenik gabe",
+       "cannotauth-not-allowed-title": "Ez da baimendu",
+       "cannotauth-not-allowed": "Ez duzu baimenik orri hau erabiltzeko",
+       "changecredentials": "Kredentzialak aldatu",
        "credentialsform-account": "Kontuaren izena:",
        "gotointerwiki": "{{SITENAME}}(e)tik irteten"
 }
index 47eed7a..99046c3 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (همچنین به [[Special:NewPages|فهرست صفحات تازه]] نگاه کنید)",
        "recentchanges-legend-plusminus": "(<em>±۱۲۳</em>)",
        "recentchanges-submit": "نمایش",
+       "rcfilters-legend-heading": "<strong>فهرست کوته‌نوشت‌ها:</strong>",
        "rcfilters-activefilters": "پالایه‌های فعال",
        "rcfilters-advancedfilters": "پالایه‌‌های پیشرفته",
        "rcfilters-quickfilters": "پالایه‌های ذخیره‌شده",
        "rcfilters-noresults-conflict": "نتیجه‌ای یافت نشد چون محدودهٔ جستجو دارای تعارض است",
        "rcfilters-state-message-subset": "این پالایه تاثیری ندارد چون نتایج آن توسط {{PLURAL:$2|پالایهٔ|پالایه‌های}} گسترده‌تری که در ادامه {{PLURAL:$2|آمده‌است|آمده‌اند}} شامل می‌شود (از پررنگ‌کننده برای تمایز استفاده کنید): $1",
        "rcfilters-state-message-fullcoverage": "انتخاب تمام پالایه‌ها در یک گروه مثل این است که هیچ‌کدام را انتخاب نکنید، بنابراین پالایه‌ها اثری نداشتند. گروه شامل این موارد است: $1",
-       "rcfilters-filtergroup-registration": "ثبت نام کاربر",
-       "rcfilters-filter-registered-label": "ثبت شده",
-       "rcfilters-filter-registered-description": "ویرایشگران وارد شده به سامانه.",
-       "rcfilters-filter-unregistered-label": "ثبت‌نام نکرده",
-       "rcfilters-filter-unregistered-description": "ویرایشگرانی که به سامانه وارد نشده‌اند.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "این پالایه با {{PLURAL:$2|پالایه|پالایه‌های}} تجربهٔ زیر که فقط کاربران ثبت‌نام شده را {{PLURAL:$2|می‌یابد|می‌یابند}} تعارض دارد: $1",
        "rcfilters-filtergroup-authorship": "ویرایشگر",
        "rcfilters-filter-editsbyself-label": "تغییریافته توسط شما",
        "rcfilters-filter-editsbyself-description": "ویرایش‌های شما.",
        "rcfilters-filter-editsbyother-label": "تغییریافته توسط دیگران",
        "rcfilters-filter-editsbyother-description": "همهٔ تغییرات به جز تغییرات شما.",
        "rcfilters-filtergroup-userExpLevel": "درجهٔ تجربه (فقط برای کاربران ثبت‌نام کرده)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "پالایه‌های تجربه فقط کاربران ثبت‌نام کرده را پیدا می‌کنند، در نتیجه با پالایهٔ «کاربران ثبت‌نام نکرده» تعارض دارند.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "پالایه «کاربران ثبت‌نام نکرده» با یک یا چند مورد از پالایه‌های تجربه در تعارض است. پالایه‌های دیگر در قسمت «پالایه‌های فعال» در بالا نشان داده شده‌اند.",
+       "rcfilters-filter-user-experience-level-registered-label": "ثبت شده",
+       "rcfilters-filter-user-experience-level-registered-description": "ویرایشگران وارد شده به سامانه.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "ثبت‌نام نکرده",
+       "rcfilters-filter-user-experience-level-unregistered-description": "ویرایشگرانی که به سامانه وارد نشده‌اند.",
        "rcfilters-filter-user-experience-level-newcomer-label": "تازه‌واردها",
        "rcfilters-filter-user-experience-level-newcomer-description": "کمتر از ۱۰ ویرایش یا ۴ روز فعالیت.",
        "rcfilters-filter-user-experience-level-learner-label": "آموزندگان",
index f818043..900871b 100644 (file)
        },
        "tog-underline": "Soulignement des liens :",
        "tog-hideminor": "Masquer les modifications mineures dans les modifications récentes",
-       "tog-hidepatrolled": "Masquer les modifications vérifiées dans les modifications récentes",
+       "tog-hidepatrolled": "Masquer les modifications relues parmi les modifications récentes",
        "tog-newpageshidepatrolled": "Masquer les pages relues dans la liste des nouvelles pages",
        "tog-hidecategorization": "Masquer la catégorisation des pages",
        "tog-extendwatchlist": "Étendre la liste de suivi pour afficher toutes les modifications et pas uniquement les plus récentes",
        "passwordtoolong": "Les mots de passe ne peuvent pas dépasser $1 caractère{{PLURAL:$1||s}}.",
        "passwordtoopopular": "Les mots de passe trop courants ne peuvent pas être utilisés. Veuillez choisir un mot de passe plus original.",
        "password-name-match": "Votre mot de passe doit être différent de votre nom d’utilisateur.",
-       "password-login-forbidden": "L’utilisation de ce nom d’utilisateur et/ou de ce mot de passe a été interdite.",
+       "password-login-forbidden": "L’utilisation de ce nom d’utilisateur ou de ce mot de passe a été interdite.",
        "mailmypassword": "Réinitialiser le mot de passe",
        "passwordremindertitle": "Nouveau mot de passe temporaire pour {{SITENAME}}",
        "passwordremindertext": "Quelqu’un (probablement vous, ayant l’adresse IP $1) a demandé un nouveau mot de\npasse pour {{SITENAME}} ($4). Un mot de passe temporaire pour l’utilisateur\n« $2 » a été créé et est « $3 ». Si cela était votre intention,\nvous devrez vous connecter et choisir un nouveau mot de passe.\nVotre mot de passe temporaire expirera dans $5 jour{{PLURAL:$5||s}}.\n\nSi vous n’êtes pas l’auteur de cette demande, ou si vous vous souvenez à présent de\nvotre mot de passe et ne souhaitez plus en changer, vous pouvez ignorer ce message\net continuer à utiliser votre ancien mot de passe.",
        "rcfilters-legend-heading": "<strong>Liste des abréviations :</strong>",
        "rcfilters-activefilters": "Filtres actifs",
        "rcfilters-advancedfilters": "Filtres avancés",
+       "rcfilters-limit-title": "Modifications à afficher",
+       "rcfilters-limit-shownum": "Afficher les $1 dernières modifications",
+       "rcfilters-days-title": "Derniers jours",
+       "rcfilters-hours-title": "Dernières heures",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|jour|jours}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|heure|heures}}",
        "rcfilters-quickfilters": "Filtres sauvegardés",
        "rcfilters-quickfilters-placeholder-title": "Aucun lien n’a encore été sauvegardé",
        "rcfilters-quickfilters-placeholder-description": "Pour sauvegarder la configuration de vos filtres pour la réutiliser ultérieurement, cliquez sur l’icône des raccourcis dans la zone des filtres actifs, ci-dessous.",
        "rcfilters-invalid-filter": "Filtre non valide",
        "rcfilters-empty-filter": "Aucun filtre actif. Toutes les contributions sont affichées.",
        "rcfilters-filterlist-title": "Filtres",
-       "rcfilters-filterlist-whatsthis": "Qu’est-ce que c'est ?",
+       "rcfilters-filterlist-whatsthis": "Comment ça marche ?",
        "rcfilters-filterlist-feedbacklink": "Fournir un commentaire sur les nouveaux filtres (en bêta)",
        "rcfilters-highlightbutton-title": "Mettre en valeur les résultats",
        "rcfilters-highlightmenu-title": "Choisir une couleur",
        "rcfilters-noresults-conflict": "Aucun résultat trouvé car les critères de recherche sont en conflit",
        "rcfilters-state-message-subset": "Ce filtre n'a aucun effet, parce que ses résultats sont inclus dans ceux de la recherche suivante, {{PLURAL:$2|filtre plus étendu|filtres plus étendus}} (essayez la mise en forme pour le différencier): $1",
        "rcfilters-state-message-fullcoverage": "Sélectionner tous les filtres dans un groupe est la même chose que d'en sélectionner aucun, aussi ce filtre n'a aucun effet. Le groupe comprend: $1",
-       "rcfilters-filtergroup-registration": "Inscription de l’utilisateur",
-       "rcfilters-filter-registered-label": "Connectés",
-       "rcfilters-filter-registered-description": "Éditeurs connectés.",
-       "rcfilters-filter-unregistered-label": "Non connectés",
-       "rcfilters-filter-unregistered-description": "Éditeurs qui ne sont pas connectés.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Ce filtre est en conflit avec le{{PLURAL:$2| filtre|s filtres}}      Experience suivant, qui ne {{PLURAL:$2|trouve|trouvent}} que des utilisateurs inscrits : $1",
        "rcfilters-filtergroup-authorship": "Paternité des contributions",
        "rcfilters-filter-editsbyself-label": "Modifications faites par vous",
        "rcfilters-filter-editsbyself-description": "Vos propres contributions.",
        "rcfilters-filter-editsbyother-label": "Modifications faites par les autres.",
        "rcfilters-filter-editsbyother-description": "Toutes les modifications sauf les votres.",
-       "rcfilters-filtergroup-userExpLevel": "Niveau d’expérience (uniquement pour les utilisateurs enregistrés)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Les filtres Experience ne trouvent que des utilisateurs enregistrés, et ce filtre est en conflit avec le filtre \"non enregistré\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Le filtre \"non enregistrés\" est en conflit avec un ou plusieurs  filtres Expérience, qui ne trouvent que les utilisateurs inscrits seulement. Les filtres en conflit sont marqués dans le champ des filtres actifs, ci-dessus.",
+       "rcfilters-filtergroup-userExpLevel": "Enregistrement des utilisateurs et expérience",
+       "rcfilters-filter-user-experience-level-registered-label": "Connecté",
+       "rcfilters-filter-user-experience-level-registered-description": "Éditeurs connectés.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Non connecté",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Éditeurs qui ne sont pas connectés.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nouveaux arrivants",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Moins de 10 modifications et 4 jours d’activité.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editeurs connectés avec moins de 10 modifications et 4 jours d’activité.",
        "rcfilters-filter-user-experience-level-learner-label": "Apprentis",
-       "rcfilters-filter-user-experience-level-learner-description": "Plus expérimenté que les « Nouveaux arrivants » mais moins que les « Utilisateurs expérimentés ».",
+       "rcfilters-filter-user-experience-level-learner-description": "Editeurs connectés dont l'expérience se situe entre  « Nouveaux arrivants » et  « Utilisateurs expérimentés ».",
        "rcfilters-filter-user-experience-level-experienced-label": "Utilisateurs expérimentés",
-       "rcfilters-filter-user-experience-level-experienced-description": "Plus de 30 jours d’activité et 500 modifications",
+       "rcfilters-filter-user-experience-level-experienced-description": "Editeurs connectés avec plus de 500 modifications et 30 jours d'activité.",
        "rcfilters-filtergroup-automated": "Contributions automatisées",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Modifications faites par des outils automatisés.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Le filtre « Modifications mineures » est en conflit avec au moins un filtre de Type de modification, parce que certains types de modification ne peuvent être marqués comme « mineurs ». Les filtres en conflit sont marqués dans la zone Filtres actifs ci-dessus.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certains types de modification ne peuvent pas être qualifiés de « mineurs », donc ce filtre est en conflit avec les filtres de Type de modification suivants : $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Ce filtre de Type de modification est en conflit avec le filtre « Modifications mineures ». Certains type sde modification ne peuvent pas être indiqués comme « mineurs ».",
-       "rcfilters-filtergroup-lastRevision": "Version actuelle",
-       "rcfilters-filter-lastrevision-label": "Version actuelle",
-       "rcfilters-filter-lastrevision-description": "Dernière modification apportée à une page.",
-       "rcfilters-filter-previousrevision-label": "Versions précédentes",
-       "rcfilters-filter-previousrevision-description": "Toutes les modifications apportées à une page et qui ne sont pas la dernière.",
+       "rcfilters-filtergroup-lastRevision": "Dernières révisions",
+       "rcfilters-filter-lastrevision-label": "Dernière révision",
+       "rcfilters-filter-lastrevision-description": "Uniquement la dernière modification apportée à une page.",
+       "rcfilters-filter-previousrevision-label": "Pas la dernière version",
+       "rcfilters-filter-previousrevision-description": "Toutes les modifications apportées à une page et qui ne concernent pas la « dernière version ».",
        "rcfilters-filter-excluded": "Exclu",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Exclure les sélectionnés",
+       "rcfilters-exclude-button-on": "Sélectionnés exclus",
        "rcfilters-view-tags": "Modifications marquées",
        "rcfilters-view-namespaces-tooltip": "Résultats du filtrage par espace de noms",
        "rcfilters-view-tags-tooltip": "Résultats du filtrage par balise d'édition",
        "delete-warning-toobig": "Cette page possède un historique important de modifications, dépassant $1 version{{PLURAL:$1||s}}.\nLa supprimer peut perturber le fonctionnement de la base de données de {{SITENAME}} ;\nveuillez procéder avec prudence.",
        "deleteprotected": "Vous ne pouvez pas supprimer cette page car elle a été protégée.",
        "deleting-backlinks-warning": "<strong>Attention :</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|D’autres pages]] ont un lien vers ou incorporent la page que vous allez supprimer.",
+       "deleting-subpages-warning": "<strong>Attention :</strong> la page que vous essayez de supprimer possède  [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|une sous-page|$1 sous-pages|51=plus de 50 sous-pages}}]].",
        "rollback": "Révoquer les modifications",
        "rollbacklink": "révoquer",
        "rollbacklinkcount": "révoquer $1 {{PLURAL:$1|modification|modifications}}",
        "ip_range_invalid": "Plage d’adresses IP incorrecte.",
        "ip_range_toolarge": "Les plages de blocage plus grandes que /$1 ne sont pas autorisées.",
        "proxyblocker": "Bloqueur de mandataires",
-       "proxyblockreason": "Votre adresse IP a été bloquée car il s'agit d'un mandataire ouvert.\nVeuillez contacter votre fournisseur d'accès Internet ou votre support technique et l'informer de ce sérieux problème de sécurité.",
+       "proxyblockreason": "Votre adresse IP a été bloquée car il s’agit d’un mandataire ouvert.\nVeuillez contacter votre fournisseur d’accès Internet ou votre soutien technique et l’informer de ce sérieux problème de sécurité.",
        "sorbsreason": "Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.",
        "sorbs_create_account_reason": "Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.\nVous ne pouvez pas créer un compte.",
        "softblockrangesreason": "Les contributions anonymes ne sont pas autorisées à partir de votre adresse IP ($1). Veuillez vous connecter.",
        "fileduplicatesearch-noresults": "Aucun fichier nommé « $1 » n'a été trouvé.",
        "specialpages": "Pages spéciales",
        "specialpages-note-top": "Légende",
-       "specialpages-note": "* Pages spéciales normales.\n* <span class=\"mw-specialpagerestricted\">Pages spéciales restreintes.</span>",
+       "specialpages-note-restricted": "* Pages spéciales normales.\n* <span class=\"mw-specialpagerestricted\">Pas spéciales restreintes.</span>",
        "specialpages-group-maintenance": "Rapports de maintenance",
        "specialpages-group-other": "Autres pages spéciales",
        "specialpages-group-login": "S'identifier / s'inscrire",
        "tags-create-warnings-below": "Voulez-vous continuer la création de la balise?",
        "tags-delete-title": "Supprimer la balise",
        "tags-delete-explanation-initial": "Vous êtes sur le point de supprimer la balise « $1 » de la base de données.",
-       "tags-delete-explanation-in-use": "Elle sera supprimée de {{PLURAL:$2|$2 révision ou entrée de journal à laquelle|toutes les $2 révisions et/ou entrées de journal auxquelles}} elle est actuellement appliquée.",
+       "tags-delete-explanation-in-use": "Elle sera supprimée de {{PLURAL:$2|$2 révision ou entrée de journal à laquelle|toutes les $2 révisions ou entrées de journal auxquelles}} elle est actuellement appliquée.",
        "tags-delete-explanation-warning": "Cette action est <strong>irréversible</strong> et <strong>ne peut pas être annulée</strong>, même pas par les administrateurs de base de données. Soyez certain que c'est cette balise que vous voulez supprimer.",
        "tags-delete-explanation-active": "<strong>La balise « $1 » est toujours active, et continuera à être appliquée dans le futur. </strong> Pour arrêter cela, allez à l'endroit (ou aux endroits) où la balise est appliquée, et désactivez la.",
        "tags-delete-reason": "Motif :",
        "log-name-pagelang": "Tracer les changements de langue",
        "log-description-pagelang": "Ceci est un journal des changements dans les langues des pages.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|a changé}} la langue de la page $3 de $4 à $5.",
-       "default-skin-not-found": "Oups ! L’habillage par défaut pour votre wiki, défini par <code dir=\"ltr\">$wgDefaultSkin</code> comme <code>$1</code>, n’est pas disponible.\n\nVotre installation semble inclure {{PLURAL:$4|l’habillage suivant|les habillages suivants}}. Voyez [https://www.mediawiki.org/wiki/Manual:Skin_configuration le manuel de configuration des habillages] pour savoir comment {{PLURAL:$4|l’|les }}activer et choisir celui par défaut.\n\n$2\n\n; Si vous venez juste d’installer MediaWiki :\n: Vous l’avez probablement installé depuis git, ou directement depuis le code source avec une autre méthode. C’est normal. Essayez d’installer des habillages depuis [https://www.mediawiki.org/wiki/Category:All_skins le répertoire des habillages de mediawiki.org], en:\n:* Téléchargeant le [https://www.mediawiki.org/wiki/Download fichier tar de l’installeur], qui comprend plusieurs habillages et extensions. Vous pouvez copier et coller le répertoire <code>skins/</code> depuis là.\n:* Téléchargeant les fichiers tar d’habillage individuel depuis [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Téléchargeant des habillages avec Git].\n: Faire ainsi ne devrait pas interférer avec votre dépôt git, si vous êtes un développeur de MediaWiki.\n\n; Si vous venez juste de mettre à jour MediaWiki :\n: MediaWiki 1.24 et au-delà n’active plus automatiquement les habillages installés (voyez [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery le manuel sur la découverte automatique des habillages]). Vous pouvez coller {{PLURAL:$5|la ligne suivante|les lignes suivantes}} dans <code>LocalSettings.php</code> pour activer {{PLURAL:$5|l’habillage actuellement installé|tous les habillages actuellement installés}} :\n\n<pre dir=\"ltr\">$3</pre>\n\n; Si vous venez de modifier <code>LocalSettings.php</code> :\n: Vérifiez deux fois le nom des habillages pour éviter les erreurs de frappe.",
-       "default-skin-not-found-no-skins": "Oups ! L’habillage par défaut pour votre wiki , défini par <code>$wgDefaultSkin</code> comme <code>$1</code>, n’est pas disponible.\n\nVous n’avez aucun habillage d’installé.\n\n; Si vous venez juste d’installer ou de mettre à jour MediaWiki :\n: Vous l’avez sans doute fait depuis git, ou directement depuis le code source avec une autre méthode. C’est normal. MediaWiki 1.24 et au-delà n’inclut aucun habillage dans le dépôt principal. Essayez d’installer des habillages depuis [https://www.mediawiki.org/wiki/Category:All_skins le répertoire des habillages de mediawiki.org], en :\n:* Téléchargeant [https://www.mediawiki.org/wiki/Download le fichier tar de l’installeur], qui comprend différents habillages et extensions. Vous pouvez copier et coller le répertoire <code>skins/</code> depuis là.\n:* Téléchargeant les fichiers tar d’habillage individuel depuis [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Utiliser Git pour télécharger des habillages].\n: Faire ainsi ne devrait pas interférer avec votre dépôt git si vous êtes un développeur de MediaWiki. Voyez [https://www.mediawiki.org/wiki/Manual:Skin_configuration le manuel de la configuration des habillages] pour des instructions sur la manière d’activer les habillages et choisir celui par défaut.",
+       "default-skin-not-found": "Oups ! L’habillage par défaut pour votre wiki, défini par <code dir=\"ltr\">$wgDefaultSkin</code> comme <code>$1</code>, n’est pas disponible.\n\nVotre installation semble inclure {{PLURAL:$4|l’habillage suivant|les habillages suivants}}. Voyez [https://www.mediawiki.org/wiki/Manual:Skin_configuration le manuel de configuration des habillages] pour savoir comment {{PLURAL:$4|l’|les }}activer et choisir celui par défaut.\n\n$2\n\n; Si vous venez juste d’installer MediaWiki :\n: Vous l’avez probablement installé depuis git, ou directement depuis le code source avec une autre méthode. C’est normal. Essayez d’installer des habillages depuis [https://www.mediawiki.org/wiki/Category:All_skins le répertoire des habillages de mediawiki.org], en:\n:* Téléchargeant le [https://www.mediawiki.org/wiki/Download fichier tar du programme d’installation], qui comprend plusieurs habillages et extensions. Vous pouvez copier et coller le répertoire <code>skins/</code> depuis là.\n:* Téléchargeant les fichiers tar d’habillage individuel depuis [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Téléchargeant des habillages avec Git].\n: Faire ainsi ne devrait pas interférer avec votre dépôt git, si vous êtes un développeur de MediaWiki.\n\n; Si vous venez juste de mettre à jour MediaWiki :\n: MediaWiki 1.24 et au-delà n’active plus automatiquement les habillages installés (voyez [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery le manuel sur la découverte automatique des habillages]). Vous pouvez coller {{PLURAL:$5|la ligne suivante|les lignes suivantes}} dans <code>LocalSettings.php</code> pour activer {{PLURAL:$5|l’habillage actuellement installé|tous les habillages actuellement installés}} :\n\n<pre dir=\"ltr\">$3</pre>\n\n; Si vous venez de modifier <code>LocalSettings.php</code> :\n: Vérifiez deux fois le nom des habillages pour éviter les erreurs de frappe.",
+       "default-skin-not-found-no-skins": "Oups ! L’habillage par défaut pour votre wiki , défini par <code>$wgDefaultSkin</code> comme <code>$1</code>, n’est pas disponible.\n\nVous n’avez aucun habillage d’installé.\n\n; Si vous venez juste d’installer ou de mettre à jour MediaWiki :\n: Vous l’avez sans doute fait depuis git, ou directement depuis le code source avec une autre méthode. C’est normal. MediaWiki 1.24 et au-delà n’inclut aucun habillage dans le dépôt principal. Essayez d’installer des habillages depuis [https://www.mediawiki.org/wiki/Category:All_skins le répertoire des habillages de mediawiki.org], en :\n:* Téléchargeant [https://www.mediawiki.org/wiki/Download le fichier tar du programme d’installation], qui comprend différents habillages et extensions. Vous pouvez copier et coller le répertoire <code>skins/</code> depuis là.\n:* Téléchargeant les fichiers tar d’habillage individuel depuis [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Utiliser Git pour télécharger des habillages].\n: Faire ainsi ne devrait pas interférer avec votre dépôt git si vous êtes un développeur de MediaWiki. Voyez [https://www.mediawiki.org/wiki/Manual:Skin_configuration le manuel de la configuration des habillages] pour des instructions sur la manière d’activer les habillages et choisir celui par défaut.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (activé)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>désactivé</strong>)",
        "mediastatistics": "Statistiques sur les médias",
-       "mediastatistics-summary": "Statistiques sur les types de fichier téléversés. Elles ne prennent en compte que la version la plus récente d’un fichier. Les versions anciennes ou supprimées des fichiers sont exclues.",
+       "mediastatistics-summary": "Statistiques sur les types de fichiers téléversés. Elles ne prennent en compte que la version la plus récente des fichiers. Les versions anciennes ou supprimées sont exclues.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 octet|$1 octets}} ($2 ; $3%)",
        "mediastatistics-bytespertype": "Taille totale de fichiers pour cette section : {{PLURAL:$1|$1 octet|$1 octets}} ($2 ; $3%).",
        "mediastatistics-allbytes": "Taille totale pour tous les fichiers : {{PLURAL:$1|$1 octet|$1 octets}} ($2).",
        "undelete-cantcreate": "Vous ne pouvez pas annuler la suppression de cette page car il n’existe pas de page avec ce nom, et vous n’êtes pas autorisé à la créer.",
        "pagedata-title": "Données de page",
        "pagedata-text": "Cette page fournit une interface de données aux pages. Veuillez fournir le titre de la page dans l’URL en utilisant la syntaxe de sous-page.\n* La négociation de contenu s’applique d’après l’entête Accept de votre client. Cela veut dire que les données de la page seront fournies dans le format préféré par votre client.",
-       "pagedata-not-acceptable": "Aucun format correspondant trouvé. Types MIME supportés : $1",
+       "pagedata-not-acceptable": "Aucun format correspondant trouvé. Types MIME pris en charge : $1",
        "pagedata-bad-title": "Titre non valide : $1."
 }
index 817b636..f2fe856 100644 (file)
        "errorpagetitle": "Diar as wat skiaf gingen",
        "returnto": "Turag tu sidj $1.",
        "tagline": "Faan {{SITENAME}}",
-       "help": "Halep",
+       "help": "MediaWiki Halep",
        "search": "Schük",
        "search-ignored-headings": " #<!-- Detdiar rä ei feranre --> <pre>\n# Auerskraften, diar bi't schüken ei beaachtet wurd.\n# Jodiar feranrangen wurd seekert, wan det sidj mä det auerskraft indeksiaret wurden as.\n# Dü könst det sidjenindeksiarang föörtji, wan dü en nul-edit maagest.\n# Syntax:\n#   * Ales, wat bääft en dobelkrüs („#“) stäänt, as en komentaar.\n#   * Arke rä, wat ei leesag as, as di akeroot tiitel, diar ei beaachtet woort.\nFutnuuten\nFerwisangen\nLuke uk diar\n #</pre> <!-- Detdiar rä ei feranre -->",
        "searchbutton": "Schük",
        "listgrouprights-namespaceprotection-header": "Nöömrüm mögelkhaiden",
        "listgrouprights-namespaceprotection-namespace": "Nöömrüm",
        "listgrouprights-namespaceprotection-restrictedto": "Rochten faan di brüker tu bewerkin",
+       "listgrants": "Brükerrochten (list)",
        "trackingcategories": "Kategoriin ferfulge",
        "trackingcategories-summary": "Üüb detdiar sidj stun ferfulagt kategoriin, diar faan't MediaWiki-software iindraanj wurden san. Hör nöömer kön feranert wurd, wan dü a süsteem-noorachten uun a {{ns:8}}-nöömrüm feranerst.",
        "trackingcategories-msg": "Kategorii ferfulge",
        "namespace_association": "Ferbünjen nöömrüm",
        "tooltip-namespace_association": "Saat diar en tiaken, am di ferbünjen nöömrüm of diskusjuunsnöömrüm mä iintubetjin.",
        "blanknamespace": "(Sidjen)",
-       "contributions": "{{GENDER:$1|Brüker}} bidracher",
+       "contributions": "{{GENDER:$1|Brüker}}bidracher",
        "contributions-title": "Brükerbidracher för \"$1\"",
        "mycontris": "Bidracher",
        "anoncontribs": "Bidracher",
        "unblocked-id": "Sperang $1 as apheewen",
        "unblocked-ip": "[[Special:Contributions/$1|$1]] as freiden wurden.",
        "blocklist": "Speret brükern",
+       "autoblocklist": "Automaatisk speren",
        "ipblocklist": "Speret brükern",
        "ipblocklist-legend": "Speret brüker finj",
        "blocklist-userblocks": "Brükersperen ei uunwise",
        "fileduplicatesearch-noresults": "Nian datei mä di nööm „$1“ fünjen.",
        "specialpages": "Spezial-sidjen",
        "specialpages-note-top": "Legend",
-       "specialpages-note": "* Normool spezial-sidjen\n* <span class=\"mw-specialpagerestricted\">Spezial-sidjen mä tugripsrochten</span>",
        "specialpages-group-maintenance": "Werksteedsidjen",
        "specialpages-group-other": "Ööder spezial-sidjen",
        "specialpages-group-login": "Melde di uun of skriiw di iin",
        "specialpages-group-changes": "Leetst feranrangen an logbuken",
-       "specialpages-group-media": "Medien",
+       "specialpages-group-media": "Meedien",
        "specialpages-group-users": "Brükern an rochten",
        "specialpages-group-highuse": "Flooksis brükt sidjen",
        "specialpages-group-pages": "Sidjen",
        "mw-widgets-dateinput-no-date": "Nian dootem ütjsoocht",
        "mw-widgets-titleinput-description-new-page": "sidj jaft at noch ei",
        "mw-widgets-titleinput-description-redirect": "widjerfeerang tu $1",
-       "randomrootpage": "Tufelag stamsidj"
+       "randomrootpage": "Tufelag stamsidj",
+       "changecredentials": "Uunmeldeinformatjuunen feranre",
+       "changecredentials-submit": "Uunmeldeinformatjuunen feranre",
+       "removecredentials": "Uunmeldeinformatjuunen wechnem",
+       "removecredentials-submit": "Uunmeldeinformatjuunen wechnem"
 }
index 9de6680..03ca4f2 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista de abreviaturas:</strong>",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzados",
+       "rcfilters-limit-title": "Modificacións a amosar",
+       "rcfilters-limit-shownum": "Amosar as últimas $1 modificacións",
+       "rcfilters-days-title": "Últimos días",
+       "rcfilters-hours-title": "Últimas horas",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|días}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros gardados",
        "rcfilters-quickfilters-placeholder-title": "Aínda non se gardou ningunha ligazón",
        "rcfilters-quickfilters-placeholder-description": "Para gardar a configuración dos seus filtros e reutilizala máis tarde, prema na icona do marcador na área de Filtro activo que se atopa a abaixo.",
        "rcfilters-invalid-filter": "Filtro no válido",
        "rcfilters-empty-filter": "Non hai filtros activos. Móstranse tódalas contribucións.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "Que é isto?",
+       "rcfilters-filterlist-whatsthis": "Como funciona isto?",
        "rcfilters-filterlist-feedbacklink": "Deixar comentarios sobre os novos filtros (en fase beta)",
        "rcfilters-highlightbutton-title": "Resaltar resultados",
        "rcfilters-highlightmenu-title": "Seleccione unha cor",
        "rcfilters-noresults-conflict": "Non se atoparon resultados porque os criterios de busca están en conflito.",
        "rcfilters-state-message-subset": "Este filtro non ten efecto porque os seus resultados están incluídos dentro da seguinte procura, {{PLURAL:$2|filtro máis amplo|filtros máis amplos}} (probe a destacalo para distinguilo): $1",
        "rcfilters-state-message-fullcoverage": "Seleccionar tódolos filtros dun grupo é o mesmo que non seleccionar ningún, polo que este filtro non ten efecto. O grupo inclúeː $1",
-       "rcfilters-filtergroup-registration": "Rexistro de usuario",
-       "rcfilters-filter-registered-label": "Rexistrado",
-       "rcfilters-filter-registered-description": "Editores autenticados.",
-       "rcfilters-filter-unregistered-label": "Non rexistrado",
-       "rcfilters-filter-unregistered-description": "Editores que non están autenticados.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Este filtro está en conflito {{PLURAL:$2|co seguinte filtro|cos seguintes filtros}} Experience, que {{PLURAL:$2|atopa|atopan}} só usuarios rexistradosː $1",
        "rcfilters-filtergroup-authorship": "Autoría da contribución",
        "rcfilters-filter-editsbyself-label": "Cambios seus",
        "rcfilters-filter-editsbyself-description": "As súas contribucións",
        "rcfilters-filter-editsbyother-label": "Modificacións doutros.",
        "rcfilters-filter-editsbyother-description": "Tódolos cambios, excepto os seus.",
-       "rcfilters-filtergroup-userExpLevel": "Nivel de experiencia (só para usuarios rexistrados)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Os filtros Experience só atopan usuarios rexistrados, polo que este filtro está en conflito co filtro \"non rexistrado\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "O filtro \"non rexistrados\" está en conflito cun ou máis filtros Experience, que só atopan usuarios rexistrados. Os filtros en conflito están marcados na área dos filtros activos, arriba.",
+       "rcfilters-filtergroup-userExpLevel": "Rexistro de usuarios e experiencia",
+       "rcfilters-filter-user-experience-level-registered-label": "Rexistrados",
+       "rcfilters-filter-user-experience-level-registered-description": "Editores autenticados.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Non rexistrados",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que non están autenticados.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Chegados recentemente",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 edicións e 4 días de actividade.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editores rexistrados con menos de 10 edicións e 4 días de actividade.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
-       "rcfilters-filter-user-experience-level-learner-description": "Máis experimentado que os \"usuarios novatos\" pero menos que os \"usuarios experimentados\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Editores rexistrados cuxa experiencia está entre os \"usuarios novatos\" e os \"usuarios experimentados\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Usuarios experimentados",
-       "rcfilters-filter-user-experience-level-experienced-description": "Máis de 30 días de actividade e 500 edicións.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Editores rexistrados con máis de 500 edicións e 30 días de actividade.",
        "rcfilters-filtergroup-automated": "Contribucións automatizadas",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Edicións realizadas por ferramentas automatizadas.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"edicións menores\" está en conflito con un ou máis filtros Tipo de modificación, porque certos tipos de modificación non poden designarse como \"menores\". Os filtros en conflito están marcados na zona Filtros activos, arriba.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certos tipos de modificación non poden designarse como \"menores\", polo que este filtro entra en conflito cos seguintes filtros Tipo de modificaciónː $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro Tipo de modificación entra en conflito co filtro \"Modificacións menores\". Certos tipos de modificación non poden designarse como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Versión actual",
-       "rcfilters-filter-lastrevision-label": "Versión actual",
-       "rcfilters-filter-lastrevision-description": "A última modificación a unha páxina.",
-       "rcfilters-filter-previousrevision-label": "Versións anteriores",
-       "rcfilters-filter-previousrevision-description": "Tódolos cambios realizados nunha páxina e que non son os máis recentes.",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisións",
+       "rcfilters-filter-lastrevision-label": "Últimas revisións",
+       "rcfilters-filter-lastrevision-description": "Só a última modificación a unha páxina.",
+       "rcfilters-filter-previousrevision-label": "Non a última edición",
+       "rcfilters-filter-previousrevision-description": "Tódolos cambios realizados nunha páxina e que non son a \"última modificación\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
+       "rcfilters-exclude-button-off": "Excluír os seleccionados",
+       "rcfilters-exclude-button-on": "Excluíndo os seleccionados",
        "rcfilters-view-tags": "Edicións marcadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por espazo de nomes",
        "rcfilters-view-tags-tooltip": "Filtrar resultados usando etiquetas de edición",
        "delete-warning-toobig": "Esta páxina conta cun historial de edicións longo, de máis {{PLURAL:$1|dunha revisión|de $1 revisións}}.\nAo eliminala pódense provocar problemas de funcionamento nas operacións da base de datos de {{SITENAME}};\nproceda con coidado.",
        "deleteprotected": "Non pode borrar esta páxina porque está protexida.",
        "deleting-backlinks-warning": "<strong>Atención:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Outras páxinas]] conteñen unha ligazón ou unha transclusión da páxina que está a piques de borrar.",
+       "deleting-subpages-warning": "<strong>Aviso:</strong> A páxina que quere eliminar ten [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|unha subpáxina|$1 subpáxinas|51=máis de 50 subpáxinas}}]].",
        "rollback": "Reverter as edicións",
        "rollbacklink": "reverter",
        "rollbacklinkcount": "reverter $1 {{PLURAL:$1|edición|edicións}}",
        "fileduplicatesearch-noresults": "Non se atopou ningún ficheiro chamado \"$1\".",
        "specialpages": "Páxinas especiais",
        "specialpages-note-top": "Lenda",
-       "specialpages-note": "* Páxinas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páxinas especiais restrinxidas.</span>",
        "specialpages-group-maintenance": "Informes de mantemento",
        "specialpages-group-other": "Outras páxinas especiais",
        "specialpages-group-login": "Rexistro",
index bafdb74..b1c3880 100644 (file)
        "newarticletext": "Yi'o lodudu'a wumbuta ode halaman diya'a. \nWonu mohutu halaman botiye, ketik tuwango halaman to kotak to tibawa botiye (bilohi [$1 halaman wubodu] ode habari wumbutiyo). \nWonu Yi'o ja sangaja tilumuwota ode halaman botiye, kutiya tombol <strong>mohuwalingo</strong>.",
        "noarticletext": "Sa'ati botiye diya'a teks to halaman botiye.\nYi'o mowali [[Special:Search/{{PAGENAME}}|mololohu  judul halaman botiye]] to halaman-halaman uweewo, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mololohu log a'ayita], meyalo [{{fullurl:{{FULLPAGENAME}}|action=edit}} mohutu halaman botiye]</span>.",
        "noarticletext-nopermission": "!Sa'ati botiye diya'a teks to halaman boptiye.\nYi'o mowali [[Special:Search/{{PAGENAME}}|mololohu judul halaman botiye]] to halaman-halaman uweewo, meyalo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mololohu log a'ayita]</span>, dabo Yi'o ja o ijin mohutu halaman botiye.",
+       "userpage-userdoesnotexist-view": "Ta ohu'uwo \"$1\" diyaalu to daputari.",
        "editing": "Momoli'o $1",
        "creating": "Mohutu $1",
        "editingsection": "Momoli'o $1 (tayadu)",
        "moveddeleted-notice": "Halaman botiye ma yiluluto.\nSebagai referensi, botiya log piloluluta wawu piloheyiya halaman botiye.",
        "postedit-confirmation-saved": "Biloli'umu ma tilahu.",
        "edit-already-exists": "Ja mowali mohutu halaman bohu. Ma woluwo.",
+       "content-model-wikitext": "tuladu wiki",
        "viewpagelogs": "Bilohi log lo halaman botiye",
        "currentrev-asof": "Biloli'o pulitiyo to $1",
        "revisionasof": "Biloli'o to $1",
        "history-fieldset-title": "Lolohe u biloli'o",
        "histfirst": "mohihewo da'a",
        "histlast": "bohu da'a",
+       "history-feed-title": "Riwayati lo'u biloli'o",
+       "history-feed-description": "Riwayati bilolio to halaman wiki botiye",
+       "history-feed-item-nocomment": "$1 to $2",
        "rev-delundel": "popobilohe/wanto'a",
        "history-title": "Riwayati lo'u loboli'a lonto \"$1\"",
        "difference-title": "$1 hihede revisi",
        "lineno": "Baarisi $1:",
+       "compareselectedversions": "Popotadenga u tilulawoto",
        "editundo": "pohuwalinga",
        "diff-multi-sameuser": "({{PLURAL:$1|$1 revisi wolota}} pilohutu lo tawu ngota ja pilopobilohu)",
        "searchresults": "U yilotapu",
        "search-result-size": "$1 ({{PLURAL:$2|1 tahe|$2 tahe}})",
        "search-redirect": "(pilobale lonto $1)",
        "search-section": "(tayadu) $1",
+       "search-file-match": "(sama lo tuwango berkas)",
        "search-suggest": "Patujumu yito:$1",
        "searchall": "nga'amila",
        "search-showingresults": "{{PLURAL:$4|hASIL <strong>$1</strong> of <strong>$3</strong>|Hasil <strong>$1 - $2</strong> lonto <strong>$3</strong>}}",
        "search-nonefound": "Diya'a hasili mohumayawa lo kriteria",
        "powersearch-toggleall": "Nga'amila",
        "powersearch-togglenone": "Diya'a",
-       "powersearch-remember": "Eelayi u tilulawoto wonu mololohe pe'eentamayi",
+       "powersearch-remember": "Toloma u tilulawoto wonu mololohe pe'eenta mayi",
        "mypreferences": "Preperensi",
        "prefs-skin": "Alipo",
        "searchresultshead": "Lolohe",
        "prefs-searchoptions": "Lolohe",
        "prefs-namespaces": "Huwali lo tanggulo",
-       "default": "Kakali",
+       "default": "kakali",
        "yourrealname": "Tanggula banari",
        "yourlanguage": "Bahasa",
        "grouppage-bot": "{{ns:project}}:Bot",
        "recentchangeslinked-page": "Tanggulo halaman:",
        "recentchangeslinked-to": "Poppobilohe loboli'a to halaman wayitiyo wolo halaman hepoposadiyalo",
        "upload": "Detohe berkas",
+       "uploadlogpage": "Detohu log",
        "filedesc": "Limbu'o",
+       "license": "Lisensi",
        "license-header": "Tayadu lisensi",
        "imgfile": "berkas",
        "file-anchor-link": "Berkas",
        "linkstoimage": "{{PLURAL:$1|halaman lapatiyoma'o}} o wumbuta ode berkas botiye:",
        "nolinkstoimage": "Diya'a halaman u owumbuta ode berkas botiye",
        "sharedupload-desc-here": "Berkas botiye lonto $1 wawu hepohunaliyo to poroyek uweewo.\nDeskripsi lonto [$2 halaman deskripsiliyo] woluwo to tibawa botiya.",
+       "filepage-nofile": "Diya'a berkas lo tanggula botiye",
        "upload-disallowed-here": "Yi'o diila mowali modeehe berkas botiye",
        "randompage": "Halaman totonula",
        "statistics": "Statistik",
        "nmembers": "$1 {{PLURAL:$1|tuwango}}",
        "listusers": "Daputari ta ohu'uwo",
        "newpages": "Halaman bohu",
+       "move": "Heyiya",
        "pager-newer-n": "{{PLURAL:$1|bohu da'a|$1bohu da'a}}",
        "pager-older-n": "{{PLURAL:$1|$1 mohihewo}}",
        "booksources": "Bungo buku",
        "booksources-search-legend": "Lolohe to bungo lo buku",
        "booksources-search": "Lolohe",
        "log": "Log",
+       "all-logs-page": "Nga'amila log publik",
+       "allpages": "Nga'amila halaman",
        "allarticles": "Nga'amila halaman",
        "allpagessubmit": "Ntali",
        "categories": "Kategori",
+       "usermessage-editor": "Sistem lo tahuli",
+       "watchlist": "U he'awasiyalo",
        "mywatchlist": "Daputari he'awasiyalo",
        "watch": "Dahayi",
+       "unwatch": "Batali mongawasi",
        "wlshowlast": "Popobilohe $1 jam $2 dulahe pulitiyo",
        "dellogpage": "Log loluluto",
        "rollbacklink": "wuwalinga",
        "protectlogpage": "Log mopo'aamani",
        "protectedarticle": "modaha \"[[$1]]\"",
        "protect-default": "Poluliya nga'amila ta ohu'uwo",
+       "restriction-edit": "Boli'a",
+       "restriction-move": "Heyiya",
        "namespace": "Huwali lo tanggulo",
        "invert": "Pohuwalinga tilulawoto",
        "tooltip-invert": "Centang kotak botiye u mopowanto'o halaman yiloboli'a to delomo huwali lo tanggulo tilulawoto (wawu huwali lo tanggulo a'ayita wanu dicentang)",
        "contributions": "Kontribusi {{GENDER:$1|Ta ohu'uwo}}",
        "mycontris": "Kontribusi",
        "anoncontribs": "Kontribusi",
+       "contribsub2": "Ode {{GENDER:$3|$1}} ($2)",
        "uctop": "(masatiya)",
        "month": "Lonto hulalo (wawu to'udiipo)",
        "year": "Lonto taawunu (wawu to'udiipo)",
+       "sp-contributions-newbies": "Popobilohe bo lonto ta ohu'uwo bohu",
+       "sp-contributions-blocklog": "bubuli log",
+       "sp-contributions-uploads": "u diletohu",
        "sp-contributions-logs": "log",
        "sp-contributions-talk": "lo'iya",
        "sp-contributions-search": "Lolohe kontribusi",
+       "sp-contributions-username": "Alamat IP meyalo tanggulo ta ohu'uwo",
+       "sp-contributions-toponly": "Popobiloho bo biloli'a to yitaato",
+       "sp-contributions-newonly": "Popobilohe biloli'o bo u lohutu halaman",
        "sp-contributions-submit": "Lolohe",
        "whatlinkshere": "Wumbuta",
        "whatlinkshere-title": "Halaman botiye o wumbuta ode \"$1\"",
        "whatlinkshere-hidelinks": "$1 wumbuta",
        "whatlinkshere-hideimages": "$1 berkas wumbuta",
        "whatlinkshere-filters": "U'ayahu",
+       "ipboptions": "2 jam:2 hours,1 huyi:1 day,3 huyi:3 days,1 diminggu:1 week,2 diminggu:2 weeks,1 hula:1 month,3 hula:3 months,6 hula:6 months,1 taawunu:1 year,layito:infinite",
        "blocklink": "tangguwalo",
        "contribslink": "kontrib",
+       "blocklogpage": "Bubuli log",
+       "blocklogentry": "momubulo [[$1]] wolo pulito wakutu $2 $3",
+       "proxyblocker": "Bubulo proxi",
        "movelogpage": "Log piloheyiya",
        "export": "Ekspor halaman",
        "thumbnail-more": "Po'odamanga",
        "tooltip-ca-nstab-special": "Utiye halaman istimewa, wawu ja mowali boli'olo",
        "tooltip-ca-nstab-project": "Bilohi halaman poroyek",
        "tooltip-ca-nstab-image": "Bilohi berkas lo halaman",
+       "tooltip-ca-nstab-mediawiki": "Bilohi tahuli lo sistem",
        "tooltip-ca-nstab-template": "Bilohi template",
        "tooltip-ca-nstab-category": "Bilohi kategori halaman",
        "tooltip-save": "Tahuwa u biloli'umu",
        "tooltip-preview": "Bilohipo u biloli'umu. Popopasiya utiye to'u diipo molahu.",
        "tooltip-diff": "Bilohi u loboli'o pilohutumu",
        "tooltip-compareselectedversions": "Bilohi hihede lohalaman duluwo u tilulawoto.",
+       "tooltip-watch": "Poduhengama'o halaman botiye to daputari he'awasiyalo",
        "tooltip-rollback": "\"Wuwalingo\" lopobatali u pilo'opiyohu to halaman botiye ode kontributor pulitiyo pe'enta lo klik.",
        "tooltip-undo": "\"wuwalingo\" lopobatali u biloli'a botiye wawu lomu'o kotak momoli'o wolo mode pratayang. Alasani mowali duhengalo to kotak limbu-limbu'o.",
        "tooltip-summary": "Tuwota tulade limbu-limbu'o",
        "simpleantispam-label": "Momarakisa anti-spam.\n<strong>kekeya</strong> tuwangalo!",
+       "pageinfo-title": "Informasi untuk \"$1\"",
+       "pageinfo-header-basic": "Bungo lo habari",
+       "pageinfo-header-restrictions": "Dudaha halaman",
+       "pageinfo-display-title": "Judul bibilohu",
+       "pageinfo-length": "Haya'o halaman (to delomo bita)",
+       "pageinfo-article-id": "ID Halaman",
+       "pageinfo-language": "Bahasa tuwango halaman",
+       "pageinfo-robot-policy": "Pengindeksan monto robot",
+       "pageinfo-watchers": "Jumula lo ta hemongawasi halaman",
+       "pageinfo-redirects-name": "Jumula u pilobale ode halaman botiya",
+       "pageinfo-firsttime": "Tanggal pilohutuwa halaman",
+       "pageinfo-recent-edits": "Jumula boheli biloli'a mola (to delomo $1 pulitiyo)",
        "pageinfo-toolboxlink": "Halaman habari",
+       "pageinfo-contentpage-yes": "Jo",
        "previousdiff": "← Biloli'o to'udiipo",
        "nextdiff": "Biloli'o lapatiyoma'o →",
        "file-info-size": "$1 x $2 piksel, tu'udu berkas:$3, MIME tipe: $4",
        "namespacesall": "nga'amila",
        "monthsall": "nga'amila",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|bisala]])",
+       "redirect-submit": "Ntali",
+       "redirect-lookup": "Yilolohu",
+       "redirect-page": "ID Halaman",
+       "redirect-revision": "Halaman biloli'o",
+       "redirect-file": "Tanggulo berkas",
        "specialpages": "Halaman Spesial",
        "tag-filter": "[[Special:Tags|Tag]]filter:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag}}]]: $2)",
        "tags-active-yes": "Jo",
        "logentry-delete-delete": "$1 {{GENDER:$2|moluluto}}halaman $3",
+       "revdelete-content-hid": "tuwango yilanto'o",
        "logentry-move-move": "$1 {{GENDER:$2|moheyi}} halaman $3 ode $4",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|loheyi}} halaman $3 ode $4 ja lohutu pengalihan",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|loheyi}} halaman $3 ode $4 lodeehu pengalihan",
        "logentry-newusers-create": "Ta ohu'uwo akun $1 {{GENDER:$2|mohutu}}",
+       "logentry-newusers-autocreate": "Akun $1 {{GENDER:$2|pilohutu}} otomatis",
        "logentry-upload-upload": "$1 {{GENDER:$2|mengunggah}} $3",
-       "searchsuggest-search": "Lolohe {{SITENAME}}"
+       "searchsuggest-search": "Lolohe {{SITENAME}}",
+       "duration-days": "$1 {{PLURAL:$1|huyi}}",
+       "randomrootpage": "Halaman totonulalo"
 }
index a81f2df..9306bff 100644 (file)
        "accmailtext": "E zuefällig generiert Passwort fir [[User talk:$1|$1]] isch an $2 gschickt wore.\n\nS Passwort fir des nej Benutzerkonto cha uf dr Spezialsyte „[[Special:ChangePassword|Passwort ändere]]“ gänderet wäre.",
        "newarticle": "(Nej)",
        "newarticletext": "Du bisch eme Link nogange zuen ere Syte, wu s nid git.\nZum die Syte aalege, chasch do in däm Chaschte unte aafange schrybe (lueg [$1 Hilfe] fir meh Informatione).\nWänn do nid hesch welle aane goh, no druck in Dyynem Browser uf '''Zruck'''.",
-       "anontalkpagetext": "----''Des isch e Diskussionssyte vun eme anonyme Benutzer, wu kei Zuegang aagleit het oder wu ne nit bruucht. Sälleweg mien mir di numerisch IP-Adräss bruuche zum ihn oder si z identifiziere. So ne IP-Adräss cha au vu mehrere Benutzer teilt wäre. Wenn Du ne anonyme Benutzer bisch un s Gfiel hesch, ass do irrelevanti Kommentar an di grichtet wäre, derno [[Special:CreateAccount|leg e Konto aa]] oder [[Special:UserLogin|mäld di aa]] zum in Zuekumft Verwirrige mit andere anonyme Benutzer z vermyyde.''",
+       "anontalkpagetext": "----\n<em>Des isch e Diskussionssyte vun eme anonyme Benutzer, wu kei Zuegang aagleit het oder wu ne nit bruucht.</em>\nSälleweg mien mir di numerisch IP-Adräss bruuche zum ihn oder si z identifiziere. So ne IP-Adräss cha au vu mehrere Benutzer teilt wäre. Wenn Du ne anonyme Benutzer bisch un s Gfiel hesch, ass do irrelevanti Kommentar an di grichtet wäre, derno [[Special:CreateAccount|leg e Konto aa]] oder [[Special:UserLogin|mäld di aa]] zum in Zuekumft Verwirrige mit andere anonyme Benutzer z vermyyde.",
        "noarticletext": "Uf däre Syte het s no kei Täxt. \nDu chasch uf andere Syte [[Special:Search/{{PAGENAME}}|dä Yytrag sueche]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} dr Logbuechyytrag sueche, wo dezue ghert],\noder [{{fullurl:{{FULLPAGENAME}}|action=edit}} die Syte erstelle]</span>.",
        "noarticletext-nopermission": "In däre Syte het s zur Zyt no kei Text.\nDu chasch dää Titel uf andre Syte [[Special:Search/{{PAGENAME}}|sueche]]\noder <span class=\"plainlinks\">in dr zuegherige [{{fullurl:{{#special:Log}}|page={{FULLPAGENAMEE}}}} Logbiecher sueche].</span> Du derfsch aber die Syte nit aalege.",
        "missing-revision": "D Version $1 vu dr Syte mit Name „{{FULLPAGENAME}}“ git s nit.\n\nDää Fähler chunnt normalerwyys dur e veraltete Link zue dr Versionsgschicht vun ere Syte, wu in dr Zwischezyt glescht woren isch.\nEinzelheite chasch im [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lesch-Logbuech] bschaue.",
        "userpage-userdoesnotexist": "S Benutzerkonto „<nowiki>$1</nowiki>“ git s nit. Bitte prief, eb Du die Syte wirkli wit aalege/bearbeite.",
        "userpage-userdoesnotexist-view": "S Benutzerkonto „$1“ isch nit registriert.",
        "blocked-notice-logextract": "Dää Benutzer isch zur Zyt gsperrt.\nAs Information chunnt do ne aktuälle Uuszug us em Benutzersperr-Logbuech:",
-       "clearyourcache": "'''Hiiwys:''' Noch em Spycheremuesch no dr Browser-Zwischespycher lääre go d Änderige sää.\n* '''Firefox/ Safari:''' ''Umschaltig'' drucken un glychzytig ''Aktualisiere'' aaklicken oder entwäder ''Strg+F5'' oder ''Strg+R'' (''Befehlstaste-R'' uf em Mac) drucke\n* '''Google Chrome:''' ''Umschaltig+Strg+R'' (''Befählstaschte-R'' uf em Mac) drucke\n* '''Internet Explorer:''' ''Strg+F5'' drucken oder ''Strg'' drucken un glychzytig ''Aktualisiere'' aaklicke\n* '''Opera:''' ''Extra → Internetspure lesche … → Individuäll Uuswahl → Dr komplett Cache lesche''",
+       "clearyourcache": "<strong>Hiiwys:</strong> Noch em Spycheremuesch no dr Browser-Zwischespycher lääre go d Änderige sää.\n* <strong>Firefox/ Safari:</strong> <em>Umschaltig</em> drucken un glychzytig <em>Aktualisiere</em> aaklicken oder entwäder <em>Strg+F5</em> oder <em>Strg+R</em> (<em>Befehlstaste-R</em> uf em Mac) drucke\n* <strong>Google Chrome:</strong> <em>Umschaltig+Strg+R</em> (<em>Befählstaschte-R</em> uf em Mac) drucke\n* <strong>Internet Explorer:</strong> <em>Strg+F5</em> drucken oder <em>Strg</em> drucken un glychzytig <em>Aktualisiere</em> aaklicke\n* <strong>Opera:</strong> Gang uff <em>Menü → Yystellige</em> (<em>Opera → Yystellige</em> uff eme Mac) un deno uff <em>Dateschutz & Sicherheit → Browserdate lösche → Gspyycherti Bilder un Dateie</em>.",
        "usercssyoucanpreview": "'''Tipp:''' Nimm dr „{{int:showpreview}}”-Chnopf, zum Dyy nej CSS vor em Spichere z teschte.",
        "userjsyoucanpreview": "'''Tipp:''' „Nimm dr {{int:showpreview}}”-Chnopf, zum Dyy nej JS vor em Spichere z teschte.",
        "usercsspreview": "== Vorschau vu Dyynem Benutzer-CSS. ==\n'''Wichtig:''' Noch em Spichere muesch Dyynem Browser sage, ass er die nej Version ladet:\n\n'''Mozilla:''' ''Strg-Shift-R'', '''IE:''' ''Strg-F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
        "tooltip-feed-rss": "RSS-Feed für selli Syte",
        "tooltip-feed-atom": "Atom-Feed für selli Syte",
        "tooltip-t-contributions": "E Lischt vo de Byträg vo {{GENDER:$1|däm Benutzer}}",
-       "tooltip-t-emailuser": "Schick däm Benutzer e E-Bost",
+       "tooltip-t-emailuser": "Schigg e E-Mail aa {{GENDER:$1|de Benutzer|die Benutzeri}}",
        "tooltip-t-info": "Meh Informationen über die Syte",
        "tooltip-t-upload": "Dateien ufelade",
        "tooltip-t-specialpages": "Lischte vo allne Spezialsyte",
        "version-libraries-license": "Lizänz",
        "version-libraries-description": "Beschrybig",
        "version-libraries-authors": "Autor/inne",
-       "redirect": "Wyterleitig uf Benutzersyte, Syte, Syteversion oder Datei",
-       "redirect-summary": "Die Spezialsyte leitet wyter uf e Benutzersyte (numerischi Benutzerkännig aagee), Syte (Sytekännig aagee), Syteversion (Versionskännig aagee) oder Datei (Dateiname aagee). Benutzig: [[{{#Special:Redirect}}/user/101]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] oder [[{{#Special:Redirect}}/file/Example.jpg]].",
+       "redirect": "Wyterleitig uf Datei, Benutzersyte, Syte, Syteversion oder Logbuechyytraag.",
+       "redirect-summary": "Die Spezialsyte leitet wyter uf e Benutzersyte (numerischi Benutzerkännig aagee), Syte (Sytekännig aagee), Syteversion (Versionskännig aagee), e Datei (Dateiname aagee) oder en Logbeuchyytrag (Logbuechkennig aagee). Benutzig: Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], oder [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Gang",
        "redirect-lookup": "Sueche:",
        "redirect-value": "Wärt:",
        "fileduplicatesearch-noresults": "S isch kei Datei mit em Name „$1“ gfunde wore.",
        "specialpages": "Spezialsytene",
        "specialpages-note-top": "Zeichenerklärig:",
-       "specialpages-note": "* Normali Spezialsyte.\n* <span class=\"mw-specialpagerestricted\">Spezialsyte mit bschränktem Zuegang.</span>",
        "specialpages-group-maintenance": "Wartigslischte",
        "specialpages-group-other": "Andri Spezialsyte",
        "specialpages-group-login": "Aamälde/Konto aalege",
        "htmlform-user-not-exists": "<strong>$1</strong> git’s nid.",
        "htmlform-user-not-valid": "<strong>$1</strong> isch ke gültige Name.",
        "logentry-delete-delete": "{{GENDER:$2|Dr|D|}} $1 het d Syte $3 glöscht",
-       "logentry-delete-restore": "{{GENDER:$2|Der $1|D $1|$1}} het d Syte $3 wider härgstellt",
+       "logentry-delete-restore": "{{GENDER:$2|Der $1|D $1|$1}} het d Syte $3 wider härgstellt ($4)",
        "logentry-delete-event": "{{GENDER:$2|Der $1|D $1|$1}} het d Sichtbarkeit {{PLURAL:$5|vumene Logbuechyytrag|vo $5 Logbuechyyträg}} gänderet uff $3: $4",
        "logentry-delete-revision": "{{GENDER:$2|Der $1|D $1|$1}} het d Sichtbarkeit {{PLURAL:$5|vunere Version|vo $5 Versione}} gänderet uff $3: $4",
        "logentry-delete-event-legacy": "{{GENDER:$2|Der $1|D $1|$1}} het d Sichtbarkeit vo Logbuechyyträg uff $3 gänderet",
index 44fe822..dec017d 100644 (file)
        "printableversion": "Sufar bugawa",
        "permalink": "Dawwamammen mahaɗi",
        "print": "Buga",
+       "view": "Duba",
        "edit": "Gyarawa",
        "create": "Ƙirƙira",
        "delete": "Soke",
        "showdiff": "Nuna sauye-sauye",
        "anoneditwarning": "'''Hattara:''' Ba ku yi logi ba.\nZa a rubuta adireshinku na IP a cikin tarihin sauye-sauyen wannan shafi.",
        "summary-preview": "Rigya-gani na taƙaici:",
+       "loginreqlink": "Shiga",
        "newarticle": "(Sabo)",
        "newarticletext": "Kun latsa mahaɗi zuwa shafin da babu shi tukuna.\nDomin ƙirƙiro wannan shafin, ku fara rubutu a cikin fage na ƙasa (duba [$1 shafin taimako] don ƙarin bayani).\nIdan kun ɓata ne cikin shawaginku, to ku latsa maɓallin '''baya''' na safuwayan shawaginku.",
        "noarticletext": "A halin yanzu babu matani a kan wannan shafi.\nKuna iya [[Special:Search/{{PAGENAME}}|nemo kan wannan shafi]] cikin wasu shafuna,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} bincika rajistan ayyukan],\nko [{{fullurl:{{FULLPAGENAME}}|action=edit}} gyara wannan shafi]</span>.",
        "prevn": "baya {{PLURAL:$1|$1}}",
        "nextn": "gaba {{PLURAL:$1|$1}}",
        "viewprevnext": "Duba ($1 {{int:pipe-separator}} $2) ($3)",
+       "searchprofile-everything": "Duk abin da",
        "search-result-size": "$1 ({{PLURAL:$2|1 kalma|$2 kalmomi}})",
        "search-redirect": "(turawa daga $1)",
        "search-section": "(sashe $1)",
        "rightslog": "Rajistan bayar da izini ga ma'aikata",
        "action-edit": "gyara wannan shafi",
        "nchanges": "{{PLURAL:$1|sauyi|sauye-sauye}} $1",
+       "enhancedrc-history": "Tarihi",
        "recentchanges": "Sauye-sauyen baya-bayan nan",
        "recentchanges-legend": "Zaɓi na sauye-sauyen baya-bayan nan",
        "recentchanges-feed-description": "Bi sawun sauye-sauyen ƙarshe na wikin da ke cikin wannan kwarare",
+       "recentchanges-label-minor": "Karamin gyara ne",
        "rclistfrom": "Nuna sabbin sauye-sauye tun daga $3 $2",
        "rcshowhideminor": "$1 ƙananen sauye-sauye",
        "rcshowhidebots": "Rabuwat $1",
+       "rcshowhidebots-show": "Nuna",
        "rcshowhideliu": "$1 Ma'aikata logaggi",
        "rcshowhideanons": "$1 ma'aikata masu ɓoyayye suna",
        "rcshowhidemine": "$1 sauye-sauyena",
        "tooltip-search": "Binciko {{SITENAME}}",
        "tooltip-search-go": "A je ga shafi mai wannan suna idan akwai shi",
        "tooltip-search-fulltext": "Binciki shafuna masu wannan matani",
+       "tooltip-p-logo": "Duba babban shafin",
        "tooltip-n-mainpage": "Duba shafin Marhabin",
        "tooltip-n-mainpage-description": "Duba shafin marhabin",
        "tooltip-n-portal": "A game da wannan shiri, abinda za a iya yi, ina za a samu abubuwa",
index 03f044c..a46eebc 100644 (file)
        "anontalk": "Kâu-liù",
        "navigation": "Thô-hòng",
        "and": "&#32;lâu",
-       "qbfind": "Cháu-chhìm",
-       "qbbrowse": "Liù-lám",
-       "qbedit": "Phiên-siá",
-       "qbpageoptions": "Ya̍p-mien sién-hong",
-       "qbmyoptions": "Ngài-ke ya̍p-mien",
        "faq": "Sòng-kien mun-thì kié-tap",
-       "faqpage": "Project:Sòng-kien mun-thì kié-tap",
        "actions": "Thûng-chok",
        "namespaces": "Miàng-sṳ khûng-kiên",
        "variants": "Pien-von",
        "edit": "Phiên-siá",
        "create": "Kien-li̍p",
        "create-local": "Sîn-chen pún-thi sot-mìn",
-       "editthispage": "Phiên-siá liá ya̍p",
-       "create-this-page": "Kien-li̍p pún-ya̍p",
        "delete": "San-chhù",
-       "deletethispage": "San-chhù pún-ya̍p",
-       "undeletethispage": "Chhí-sêu san-chhù liá-ya̍p.",
        "undelete_short": "恢復$1隻分删除个编寫",
        "viewdeleted_short": "查看$1項已刪除个修訂",
        "protect": "Pó-fu",
        "protect_change": "Kiên-kói",
-       "protectthispage": "Pó-fu pún-ya̍p",
        "unprotect": "更改保護",
-       "unprotectthispage": "更改本頁保護",
        "newpage": "Sîn ya̍p-mien",
-       "talkpage": "Thó-lun pún-ya̍p",
        "talkpagelinktext": "kâu-liù",
        "specialpage": "Thi̍t-sû ya̍p-mien",
        "personaltools": "Sṳ̂-ngìn kûng-khí",
-       "articlepage": "Khon nui-yùng ya̍p",
        "talk": "Thó-lun",
        "views": "Chhà-khon-sú",
        "toolbox": "Kûng-khí-siông",
-       "userpage": "查看用戶頁面",
-       "projectpage": "查看項目頁面",
        "imagepage": "Chhà-khon vùn-khien ya̍p-mien",
        "mediawikipage": "Chhà-khon sêu-sit ya̍p-mien",
        "templatepage": "Chhà-khon mù-pán ya̍p-mien",
        "minoredit": "Liá-he yit-chak se-mì siû-kói",
        "watchthis": "Kâm-sṳ pún-ya̍p",
        "savearticle": "Pó-chhùn pún-ya̍p",
+       "publishpage": "Fat-péu vùn-chông",
+       "publishchanges": "Fat-péu siû-kói",
        "preview": "預覽",
        "showpreview": "Chán-sṳ yi-lám",
        "showdiff": "Chán-sṳ chhâ-phe̍t",
        "edit-gone-missing": "毋做得更新頁面。\n其可能正正分刪除。",
        "edit-conflict": "Phiên-siá chhûng-thu̍t.",
        "edit-no-change": "汝嘅編寫已經略過,因為文字無任何改動。",
+       "postedit-confirmation-saved": "Ngì ke siû-kói yí-kîn pó-chhùn.",
        "edit-already-exists": "毋做得建立一隻新頁面。\n其已經存在。",
        "defaultmessagetext": "Me̍t-ngin sêu-sit vùn-sṳ",
        "invalid-content-data": "無效嘅數據內容",
index 064a627..2422012 100644 (file)
        "rcfilters-legend-heading": "<strong>רשימת קיצורים:</strong>",
        "rcfilters-activefilters": "מסננים פעילים",
        "rcfilters-advancedfilters": "מסננים מתקדמים",
+       "rcfilters-limit-title": "אילו שינויים להראות",
+       "rcfilters-limit-shownum": "להציג {{PLURAL:$1|שינוי אחרון אחד|$1 שינויים אחרונים}}",
+       "rcfilters-days-title": "ימים אחרונים",
+       "rcfilters-hours-title": "שעות אחרונות",
+       "rcfilters-days-show-days": "{{PLURAL:$1|יום אחד|יומיים|$1 ימים}}",
+       "rcfilters-days-show-hours": "{{PLURAL:$1|שעה אחת|שעתיים|$1 שעות}}",
        "rcfilters-quickfilters": "מסננים שמורים",
        "rcfilters-quickfilters-placeholder-title": "טרם נשמרו קישורים",
        "rcfilters-quickfilters-placeholder-description": "כדי לשמור את הגדרות המסננים שלך ולהשתמש בהן מאוחר יותר, יש ללחוץ על סמל הסימנייה באזור המסנן הפעיל להלן.",
        "rcfilters-invalid-filter": "מסנן בלתי־תקין",
        "rcfilters-empty-filter": "אין מסננים פעילים. כל התרומות מוצגות.",
        "rcfilters-filterlist-title": "מסננים",
-       "rcfilters-filterlist-whatsthis": "×\9e×\94 ×\96×\94?",
+       "rcfilters-filterlist-whatsthis": "×\90×\99×\9a ×\94×\9d ×¢×\95×\91×\93×\99×\9d?",
        "rcfilters-filterlist-feedbacklink": "שליחת משוב על המסננים החדשים (בטא)",
        "rcfilters-highlightbutton-title": "הבלטת התוצאות",
        "rcfilters-highlightmenu-title": "בחירת צבע",
        "rcfilters-noresults-conflict": "לא נמצאו תוצאות משום שקריטריוני החיפוש מתנגשים",
        "rcfilters-state-message-subset": "המסנן הזה אינו משפיע כי התוצאות שלו כלולות {{PLURAL:$2|במסנן הרחב יותר הבא|במסננים הרחבים יותר הבאים}} (נסו להדגיש כדי להבדיל ביניהם): $1",
        "rcfilters-state-message-fullcoverage": "בחירת כל המסננים בקבוצה זהה לביטול כל הבחירות, אז למסנן הזה אין השפעה. הקבוצה כוללת: $1",
-       "rcfilters-filtergroup-registration": "רישום העורכים",
-       "rcfilters-filter-registered-label": "רשומים",
-       "rcfilters-filter-registered-description": "עורכים שנכנסו לחשבון.",
-       "rcfilters-filter-unregistered-label": "לא רשומים",
-       "rcfilters-filter-unregistered-description": "עורכים שלא נכנסו לחשבון.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "המסנן הזה פעיל מתנגש עם {{PLURAL:$2|מסנן רמת הניסיון הבא, שמוצא|מסנני רמת הניסיון הבאים, שמוצאים}} רק משתמשים רשומים: $1",
        "rcfilters-filtergroup-authorship": "מבצעי התרומה",
        "rcfilters-filter-editsbyself-label": "שינויים שלך",
        "rcfilters-filter-editsbyself-description": "תרומות שביצעת בעצמך.",
        "rcfilters-filter-editsbyother-label": "שינויים של אחרים",
        "rcfilters-filter-editsbyother-description": "כל השינויים מלבד אלה שלך.",
-       "rcfilters-filtergroup-userExpLevel": "רמת ניסיון (למשתמשים רשומים בלבד)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "מסנני הניסיון מוצאים רק משתמשים רשומים, כך שמסנן זה מתנגש עם המסנן \"לא רשומים\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "המסנן \"לא רשומים\" מתנגש עם אחד או יותר ממסנני רמת הניסיון, שמוצאים משתמשים רשומים בלבד. המסננים המתנגשים מסומנים באזור המסננים הפעילים לעיל.",
+       "rcfilters-filtergroup-userExpLevel": "הרשמת משתמשים ורמת ניסיון",
+       "rcfilters-filter-user-experience-level-registered-label": "רשומים",
+       "rcfilters-filter-user-experience-level-registered-description": "עורכים שנכנסו לחשבון.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "לא רשומים",
+       "rcfilters-filter-user-experience-level-unregistered-description": "עורכים שלא נכנסו לחשבון.",
        "rcfilters-filter-user-experience-level-newcomer-label": "חדשים",
-       "rcfilters-filter-user-experience-level-newcomer-description": "פחות מ־10 עריכות ומ־4 ימים של פעילות.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "×¢×\95ר×\9b×\99×\9d ×¨×©×\95×\9e×\99×\9d ×¢×\9d ×¤×\97×\95ת ×\9eÖ¾10 ×¢×¨×\99×\9b×\95ת ×\95×\9eÖ¾4 ×\99×\9e×\99×\9d ×©×\9c ×¤×¢×\99×\9c×\95ת.",
        "rcfilters-filter-user-experience-level-learner-label": "לומדים",
-       "rcfilters-filter-user-experience-level-learner-description": "×\99×\95תר × ×\99ס×\99×\95×\9f ×\9e\"×\97×\93ש×\99×\9d\", ×\90×\91×\9c ×¤×\97×\95ת ×\9e\"×\9eשת×\9eש×\99×\9d מנוסים\".",
+       "rcfilters-filter-user-experience-level-learner-description": "×¢×\95ר×\9b×\99×\9d ×¨×©×\95×\9e×\99×\9d ×©×¨×\9eת ×\94× ×\99ס×\99×\95×\9f ×©×\9c×\94×\9d ×\94×\99×\90 ×\91×\99×\9f \"×\97×\93ש×\99×\9d\" ×\9c×\91×\99×\9f \"מנוסים\".",
        "rcfilters-filter-user-experience-level-experienced-label": "משתמשים מנוסים",
-       "rcfilters-filter-user-experience-level-experienced-description": "×\99×\95תר ×\9eÖ¾30 ×\99×\9e×\99×\9d ×©×\9c ×¤×¢×\99×\9c×\95ת ×\95Ö¾500 ×¢×¨×\99×\9bות.",
+       "rcfilters-filter-user-experience-level-experienced-description": "×¢×\95ר×\9b×\99×\9d ×¨×©×\95×\9e×\99×\9d ×¢×\9d ×\99×\95תר ×\9eÖ¾500 ×¢×¨×\99×\9b×\95ת ×\95Ö¾30 ×\99×\9e×\99×\9d ×©×\9c ×¤×¢×\99×\9cות.",
        "rcfilters-filtergroup-automated": "תרומות אוטומטיות",
        "rcfilters-filter-bots-label": "בוטים",
        "rcfilters-filter-bots-description": "עריכות שבוצעו על־ידי כלים אוטומטיים.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "מסנן \"עריכות משניות\" מתנגש עם מסנן סוג השינויים אחד או יותר, כי סוגים מסוימים של שינויים אינם יכולים להיות מסווגים בתור \"משניים\". המסננים המתנגשים מסומנים באזור המסננים הפעילים לעיל.",
        "rcfilters-hideminor-conflicts-typeofchange": "סוגים מסוימים של שינויים אינם יכולים להיות מסווגים כ\"משניים\", כך שמסנן זה מתנגש עם מסנן סוג השינויים הבא: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "מסנן סוג השינויים הזה מתנגש עם מסנן \"עריכות משניות\". סוגים מסוימים של שינויים אינם יכולים מסווגים כ\"משניים\".",
-       "rcfilters-filtergroup-lastRevision": "×\92רס×\94 ×\90×\97ר×\95× ×\94",
+       "rcfilters-filtergroup-lastRevision": "×\92רס×\90×\95ת ×\90×\97ר×\95× ×\95ת",
        "rcfilters-filter-lastrevision-label": "הגרסה האחרונה",
-       "rcfilters-filter-lastrevision-description": "השינוי האחרון בדף.",
-       "rcfilters-filter-previousrevision-label": "×\92רס×\90×\95ת ×§×\95×\93×\9e×\95ת",
-       "rcfilters-filter-previousrevision-description": "כל השינויים שאינם השינוי האחרון בדף.",
+       "rcfilters-filter-lastrevision-description": "רק ×\94ש×\99× ×\95×\99 ×\94×\90×\97ר×\95×\9f ×\91×\93×£.",
+       "rcfilters-filter-previousrevision-label": "×\9c×\90 ×\94×\92רס×\94 ×\94×\90×\97ר×\95× ×\94",
+       "rcfilters-filter-previousrevision-description": "כל השינויים שאינם \"הגרסה האחרונה\".",
        "rcfilters-filter-excluded": "מוחרג",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:לא</strong> $1",
+       "rcfilters-exclude-button-off": "להחריג את המסומנים",
+       "rcfilters-exclude-button-on": "ללא המסומנים",
        "rcfilters-view-tags": "עריכות מתויגות",
        "rcfilters-view-namespaces-tooltip": "סינון התוצאות לפי מרחב שם",
        "rcfilters-view-tags-tooltip": "סינון התוצאות לפי תגיות עריכה",
        "delete-warning-toobig": "דף זה כולל מעל {{PLURAL:$1|גרסה אחת|$1 גרסאות}} בהיסטוריית העריכות שלו. מחיקה שלו עלולה להפריע לפעולות בבסיס הנתונים; אנא שקלו שנית את המחיקה.",
        "deleteprotected": "אין {{GENDER:|באפשרותך|באפשרותך|באפשרותכם}} למחוק את הדף כי הוא מוגן.",
        "deleting-backlinks-warning": "<strong>אזהרה:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|דפים אחרים]] מקשרים לדף ש{{GENDER:|אתה עומד|את עומדת|אתם עומדים}} למחוק או מכלילים אותו.",
+       "deleting-subpages-warning": "<strong>אזהרה:</strong> לדף ש{{GENDER:|אתה עומד|את עומדת|אתם עומדים}} למחוק יש [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|דף משנה|$1 דפי משנה|51=יותר מ־50 דפי משנה}}]].",
        "rollback": "שחזור עריכות",
        "rollbacklink": "שחזור",
        "rollbacklinkcount": "שחזור {{PLURAL:$1|עריכה אחת|$1 עריכות}}",
        "fileduplicatesearch-noresults": "לא נמצא קובץ בשם \"$1\".",
        "specialpages": "דפים מיוחדים",
        "specialpages-note-top": "מקרא",
-       "specialpages-note": "* דפים מיוחדים רגילים.\n* <span class=\"mw-specialpagerestricted\">דפים מיוחדים מוגבלים.</span>",
        "specialpages-group-maintenance": "דיווחי תחזוקה",
        "specialpages-group-other": "דפים מיוחדים אחרים",
        "specialpages-group-login": "כניסה לחשבון / הרשמה",
index 965326b..5f2ba29 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|नए पन्नों की सूची]] को भी देखें)",
        "recentchanges-submit": "दिखाएँ",
        "rcfilters-activefilters": "सक्रिय फिल्टर",
+       "rcfilters-limit-shownum": "पिछले $1 बदलाव दिखायें",
+       "rcfilters-days-title": "कुछ दिनों के",
+       "rcfilters-hours-title": "कुछ घंटों के",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|दिन}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|घंटा|घंटे}}",
        "rcfilters-quickfilters": "सुरक्षित फ़िल्टर",
        "rcfilters-quickfilters-placeholder-title": "कोई कड़ी अभी तक सहेजा नहीं गया",
        "rcfilters-quickfilters-placeholder-description": "अपने फ़िल्टर सेटिंग को सहेजने और बाद में उपयोग करने के लिए नीचे दिये बूकमार्क छवि पर क्लिक करें।",
        "rcfilters-savedqueries-unsetdefault": "मूल के रूप से हटाएँ",
        "rcfilters-savedqueries-remove": "निकालें",
        "rcfilters-savedqueries-new-name-label": "नाम",
-       "rcfilters-savedqueries-apply-label": "सेटिंग संजोयें",
+       "rcfilters-savedqueries-new-name-placeholder": "फ़िल्टर का उद्देश्य समझाएँ",
+       "rcfilters-savedqueries-apply-label": "फ़िल्टर बनायें",
        "rcfilters-savedqueries-cancel-label": "रद्द करें",
        "rcfilters-savedqueries-add-new-title": "वर्तमान फ़िल्टर सेटिंग को सहेजें",
        "rcfilters-restore-default-filters": "मूलभूत फिल्टर पुनर्स्थापित करे",
        "rcfilters-invalid-filter": "अमान्य फ़िल्टर",
        "rcfilters-empty-filter": "कोई सक्रिय फिल्टर नहीं। सभी योगदान दिखाए गए है।",
        "rcfilters-filterlist-title": "फिल्टर",
-       "rcfilters-filterlist-whatsthis": "यह à¤\95à¥\8dया है?",
+       "rcfilters-filterlist-whatsthis": "यह à¤\95à¥\88सà¥\87 à¤\95ारà¥\8dय à¤\95रता है?",
        "rcfilters-filterlist-feedbacklink": "नए (बीटा) फिल्टर पर प्रतिक्रिया दें",
        "rcfilters-highlightbutton-title": "Highlight results",
        "rcfilters-highlightmenu-title": "रंग चुनें",
        "rcfilters-noresults-conflict": "कोई भी परिणाम नहीं मिला क्योंकि खोज मापदंड संघर्ष में है",
        "rcfilters-state-message-subset": "इस फिल्टर का कोई प्रभाव नहीं है क्योंकि इसका परिणाम निम्न, व्यापक {{PLURAL: $2 |फ़िल्टर|फिल्टर}} के साथ शामिल है (इसे भेद करने के लिए हाइलाइट करने की कोशिश करें): $1",
        "rcfilters-state-message-fullcoverage": "किसी समूह में सभी फ़िल्टर चुनना कोई भी नहीं चुनने के समान है इसलिए इस फ़िल्टर का कोई प्रभाव नहीं है समूह में शामिल हैं: $1",
-       "rcfilters-filtergroup-registration": "उपयोगकर्ता पंजीकरण",
-       "rcfilters-filter-registered-label": "पंजीकृत:",
-       "rcfilters-filter-registered-description": "लॉग-इन संपादक।",
-       "rcfilters-filter-unregistered-label": "अपंजीकृत",
-       "rcfilters-filter-unregistered-description": "संपादक जो लॉग इन नहीं हैं।",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "यह फ़िल्टर निम्न {{PLURAL:$2|फ़िल्टर}} के साथ संघर्ष करता है, जो केवल पंजीकृत सदस्यों को ही {{PLURAL:$2|ढूंढ}} रहा है: $1",
        "rcfilters-filtergroup-authorship": "लेखक योगदान",
        "rcfilters-filter-editsbyself-label": "आपके द्वारा बदलाव",
        "rcfilters-filter-editsbyself-description": "आपके अपने योगदान।",
        "rcfilters-filter-editsbyother-label": "दूसरों के द्वारा बदलाव",
        "rcfilters-filter-editsbyother-description": "आपके बदलावों को छोड़ कर सभी के बदलाव।",
        "rcfilters-filtergroup-userExpLevel": "अनुभव स्तर (केवल पंजीकृत सदस्यों के लिए)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "अनुभव फ़िल्टर केवल पंजीकृत उपयोगकर्ता पाते हैं इसलिए यह फ़िल्टर \"अपंजीकृत\" फ़िल्टर के साथ संघर्ष करता है।",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "\"अपंजीकृत\" फ़िल्टर एक या अधिक अनुभव फिल्टर के साथ संघर्ष करता है, जो केवल पंजीकृत उपयोगकर्ता ढूंढते हैं परस्पर विरोधी फिल्टर ऊपर सक्रिय फिल्टर क्षेत्र में चिह्नित हैं।",
+       "rcfilters-filter-user-experience-level-registered-label": "पंजीकृत",
+       "rcfilters-filter-user-experience-level-registered-description": "लॉग-इन संपादक।",
+       "rcfilters-filter-user-experience-level-unregistered-label": "अपंजीकृत",
+       "rcfilters-filter-user-experience-level-unregistered-description": "संपादक जो लॉग इन नहीं हैं।",
        "rcfilters-filter-user-experience-level-newcomer-label": "अपरिचित",
        "rcfilters-filter-user-experience-level-newcomer-description": "4 दिनों की गतिविधि और 10 सम्पादन से कम।",
        "rcfilters-filter-user-experience-level-learner-label": "शिक्षार्थियों",
        "rcfilters-hideminor-conflicts-typeofchange-global": "\"लघु संपादन\" फ़िल्टर एक या एक से अधिक प्रकार के परिवर्तन फ़िल्टर के साथ संघर्ष करता है, क्योंकि कुछ प्रकार के परिवर्तन को \"लघु\" के रूप में निर्दिष्ट नहीं किया जा सकता है। परस्पर विरोधी फिल्टर ऊपर सक्रिय फिल्टर क्षेत्र में चिह्नित हैं।",
        "rcfilters-hideminor-conflicts-typeofchange": "कुछ प्रकार के परिवर्तन को \"लघु\" के रूप में निर्दिष्ट नहीं किया जा सकता है\", इसलिए यह फ़िल्टर निम्न प्रकार के परिवर्तन फिल्टर के साथ संघर्ष करता है: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "इस प्रकार का परिवर्तन फ़िल्टर \"लघु संपादन\" फ़िल्टर के साथ संघर्ष करता है। कुछ प्रकार के परिवर्तन को \"लघु\" के रूप में निर्दिष्ट नहीं किया जा सकता है।",
-       "rcfilters-filtergroup-lastRevision": "सदà¥\8dय अवतरण",
-       "rcfilters-filter-lastrevision-label": "à¤\85à¤\82तिम अवतरण",
+       "rcfilters-filtergroup-lastRevision": "नया अवतरण",
+       "rcfilters-filter-lastrevision-label": "नया अवतरण",
        "rcfilters-filter-lastrevision-description": "पृष्ठ का सबसे हाल में हुआ बदलाव",
        "rcfilters-filter-previousrevision-label": "पहले के अवतरण",
        "rcfilters-filter-previousrevision-description": "सभी परिवर्तन जो एक पृष्ठ में सबसे हाल के परिवर्तन नहीं हैं।",
        "rcfilters-filter-excluded": "अपवर्जित",
        "rcfilters-tag-prefix-namespace-inverted": " $1 <strong>:नहीं</strong>",
-       "rcfilters-view-tags": "à¤\9aिपà¥\8dपियाà¤\81",
+       "rcfilters-view-tags": "à¤\9fà¥\88à¤\97 à¤µà¤¾à¤²à¥\87 à¤¸à¤®à¥\8dपादन",
        "rcnotefrom": "नीचे <strong>$2</strong> के बाद से (<strong>$1</strong> तक) {{PLURAL:$5|हुआ बदलाव दर्शाया गया है|हुए बदलाव दर्शाए गये हैं}}।",
        "rclistfromreset": "चुने दिनांक पहले जैसा करें",
        "rclistfrom": "$3 $2 से नये बदलाव दिखाएँ",
        "fileduplicatesearch-noresults": "कोई फ़ाइल नाम \"$1\" मिला नहीं ।",
        "specialpages": "विशेष पृष्ठ",
        "specialpages-note-top": "कुंजी",
-       "specialpages-note": "* साधारण विशेष पृष्ठ।\n* <span class=\"mw-specialpagerestricted\">प्रतिबंधित विशेष पृष्ठ।</span>",
        "specialpages-group-maintenance": "अनुरक्षण रिपोर्ट",
        "specialpages-group-other": "अन्य विशेष पृष्ठ",
        "specialpages-group-login": "सत्र आरम्भ / खाता खोलें",
index 3fb2671..0362e77 100644 (file)
        "rcfilters-filterlist-noresults": "Nema filtera",
        "rcfilters-noresults-conflict": "Rezultati pretrage nisu pronađeni zbog sukoba kriterija pretrage",
        "rcfilters-state-message-fullcoverage": "Označavanje svih filtera u grupi je isto kao da nije označen niti jedan, tako da filter nema učinka. Grupa uključuje: $1",
-       "rcfilters-filtergroup-registration": "Registracija suradnika",
-       "rcfilters-filter-registered-label": "Prijavljeni",
-       "rcfilters-filter-registered-description": "Prijavljeni suradnici.",
-       "rcfilters-filter-unregistered-label": "Neprijavljeni",
-       "rcfilters-filter-unregistered-description": "Suradnici koji nisu prijavljeni.",
        "rcfilters-filtergroup-authorship": "Doprinosi prema autorima",
        "rcfilters-filter-editsbyself-label": "Uređivanja koja ste Vi napravili",
        "rcfilters-filter-editsbyself-description": "Vaša uređivanja.",
        "rcfilters-filter-editsbyother-label": "Promjene drugih suradnika",
        "rcfilters-filter-editsbyother-description": "Sve promjene osim Vaših.",
        "rcfilters-filtergroup-userExpLevel": "Napredna razina (samo za registrirane suradnike)",
+       "rcfilters-filter-user-experience-level-registered-label": "Prijavljeni",
+       "rcfilters-filter-user-experience-level-registered-description": "Prijavljeni suradnici.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neprijavljeni",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Suradnici koji nisu prijavljeni.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novopridošli",
        "rcfilters-filter-user-experience-level-newcomer-description": "Manje od 10 uređivanja i 4 dana aktivnosti.",
        "rcfilters-filter-user-experience-level-learner-label": "Početnici",
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Posebne stranice s ograničenim pristupom.</span>",
        "specialpages-group-maintenance": "Izvješća održavanja",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava/otvaranje računa",
index 629bb70..225abb8 100644 (file)
        "recentchanges-legend-heading": "<strong>Jelmagyarázat:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (lásd még: [[Special:NewPages|új lapok listája]])",
        "recentchanges-submit": "Megjelenítés",
+       "rcfilters-legend-heading": "<strong>Rövidítések listája:</strong>",
        "rcfilters-activefilters": "Aktív szűrők",
        "rcfilters-advancedfilters": "Haladó szűrők",
        "rcfilters-quickfilters": "Mentett szűrők",
        "rcfilters-noresults-conflict": "Nincs találat, mert a keresési kritériumok konfliktusban vannak",
        "rcfilters-state-message-subset": "Ennek a szűrőnek nincs hatása, mert az eredményét {{PLURAL:$2|tartalmazza a következő, nagyobb szűrő|tartalmazzák a következő, nagyobb szűrők}} (próbáld meg kiemelni a megkülönböztetéshez): $1",
        "rcfilters-state-message-fullcoverage": "Az összes szűrő kijelölése egy csoportban ugyanaz, mint ha semmit nem választanál ki, így ennek a szűrőnek nincs hatása. A csoport tartalmazza: $1",
-       "rcfilters-filtergroup-registration": "Regisztráció",
-       "rcfilters-filter-registered-label": "Regisztrált",
-       "rcfilters-filter-registered-description": "Bejelentkezett szerkesztők.",
-       "rcfilters-filter-unregistered-label": "Nem regisztrált",
-       "rcfilters-filter-unregistered-description": "Nem bejelentkezett szerkesztők.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Ez a szűrő ütközik a következő tapasztalati {{PLURAL:$2|szűrővel, ami|szűrőkkel, amik}} csak regisztrált felhasználókat {{PLURAL:$2|talál|találnak}} meg: $1",
        "rcfilters-filtergroup-authorship": "Hozzájárulás szerzője",
        "rcfilters-filter-editsbyself-label": "Saját változtatásaid",
        "rcfilters-filter-editsbyself-description": "A te hozzájárulásaid.",
        "rcfilters-filter-editsbyother-label": "Mások módosításai",
        "rcfilters-filter-editsbyother-description": "Minden változtatás a tieidet kivéve.",
        "rcfilters-filtergroup-userExpLevel": "Tapasztalati szint (csak regisztrált felhasználók)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "A tapasztalat szűrő csak regisztrált felhasználókat keres, tehát ez a szűrő ellentétben van a \"Regisztrálatlan\" szűrővel.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "A \"Regisztrálatlan\" szűrő ellentétben van egy vagy több Tapasztalat szűrővel, ami csak regisztrált felhasználókat keres. A konfliktusban lévő szűrők meg vannak jelölve fent az Aktív Szűrők résznél.",
+       "rcfilters-filter-user-experience-level-registered-label": "Regisztrált",
+       "rcfilters-filter-user-experience-level-registered-description": "Bejelentkezett szerkesztők.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Nem regisztrált",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Nem bejelentkezett szerkesztők.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Újoncok",
        "rcfilters-filter-user-experience-level-newcomer-description": "Kevesebb mint 10 szerkesztés és 4 nap aktivitás.",
        "rcfilters-filter-user-experience-level-learner-label": "Tanulók",
        "booksources-search": "Keresés",
        "booksources-text": "Alább látható a másik webhelyekre mutató hivatkozások listája, ahol új és használt könyveket árulnak, és\ntovábbi információkat lelhetsz ott az általad keresett könyvekről:",
        "booksources-invalid-isbn": "A megadott ISBN hibásnak tűnik; ellenőrizd, hogy jól másoltad-e át az eredeti forrásból.",
+       "magiclink-tracking-rfc": "Mágikus RFC-linkeket használó lapok",
+       "magiclink-tracking-rfc-desc": "Ez az oldal mágikus RFC-linkeket használ. Javításukhoz a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org]-on találsz segítséget.",
+       "magiclink-tracking-pmid": "Mágikus PMID-linkeket használó lapok",
+       "magiclink-tracking-pmid-desc": "Ez az oldal mágikus PMID-linkeket használ. Javításukhoz a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org]-on találsz segítséget.",
+       "magiclink-tracking-isbn": "Mágikus ISBN-linkeket használó lapok",
+       "magiclink-tracking-isbn-desc": "Ez az oldal mágikus ISBN-linkeket használ. Javításukhoz a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org]-on találsz segítséget.",
        "specialloguserlabel": "Szerkesztő:",
        "speciallogtitlelabel": "Cél (cím vagy felhasználóhoz {{ns:user}}:felhasználói név):",
        "log": "Rendszernaplók",
        "autoblocklist-submit": "Keresés",
        "autoblocklist-legend": "Autoblokkok listája",
        "autoblocklist-localblocks": "Helyi {{PLURAL:$1|autoblokk|autoblokkok}}",
+       "autoblocklist-total-autoblocks": "Autoblokkok száma: $1",
        "autoblocklist-empty": "Az autoblokklista üres.",
        "autoblocklist-otherblocks": "Egyéb {{PLURAL:$1|autoblokk|autoblokkok}}",
        "ipblocklist": "Blokkolt felhasználók",
        "newimages-legend": "Fájlnév",
        "newimages-label": "Fájlnév (vagy annak részlete):",
        "newimages-user": "IP-cím vagy felhasználónév",
+       "newimages-newbies": "Csak az újonnan regisztrált szerkesztők közreműködéseinek mutatása",
        "newimages-showbots": "Botos feltöltések mutatása",
        "newimages-hidepatrolled": "Ellenőrzött szerkesztések elrejtése",
        "newimages-mediatype": "Médiatípus:",
        "fileduplicatesearch-noresults": "Nincs „$1” nevű fájl.",
        "specialpages": "Speciális lapok",
        "specialpages-note-top": "Jelmagyarázat",
-       "specialpages-note": "* Mindenki számára elérhető speciális lapok.\n* <span class=\"mw-specialpagerestricted\">Korlátozott hozzáférésű speciális lapok.</span>",
        "specialpages-group-maintenance": "Állapotjelentések",
        "specialpages-group-other": "További speciális lapok",
        "specialpages-group-login": "Bejelentkezés / fiók létrehozása",
        "tags-create-reason": "Indoklás:",
        "tags-create-submit": "Létrehozás",
        "tags-create-no-name": "Kötelező megadnod egy címkenevet.",
-       "tags-create-invalid-chars": "Címkenevek nem tartalmazhatnak vesszőt (<code>,</code>) és normál perjelet (<code>/</code>).",
+       "tags-create-invalid-chars": "Címkenevek nem tartalmazhatnak vesszőt (<code>,</code>), függőleges vonalat (<code>|</code>) és normál perjelet (<code>/</code>).",
        "tags-create-invalid-title-chars": "Címkenevek nem tartalmazhatnak lapcímben nem használható karaktereket.",
        "tags-create-already-exists": "A(z) „$1” címke már létezik.",
        "tags-create-warnings-above": "A következő {{PLURAL:$2|figyelmeztetést|figyelmeztetéseket}} találtam a(z) „$1” címke létrehozási kísérlete közben:",
        "gotointerwiki-external": "A(z) {{SITENAME}} elhagyására és a(z) [[$2]] meglátogatására készülsz, ami egy másik webhelyen található.\n\n[$1 Kattints ide a(z) $1 oldalra való továbblépéshez.]",
        "undelete-cantedit": "Nem állíthatod helyre ezt a lapot, mert nincs jogosultságod a szerkesztéséhez.",
        "undelete-cantcreate": "Nem állíthatod helyre ezt a lapot, mert nem létezik ilyen című lap, és nincs jogosultságod létrehozni azt.",
+       "pagedata-title": "Az oldal adatai",
+       "pagedata-not-acceptable": "Nem található megfelelő formátum. Támogatott MIME-típusok: $1",
        "pagedata-bad-title": "Érvénytelen cím: $1."
 }
index 826489b..bd41893 100644 (file)
        "watchthis": "Հսկել այս էջը",
        "savearticle": "Հիշել էջը",
        "savechanges": "Պահպանել փոփոխությունները",
-       "publishpage": "Õ\80Õ«Õ·Õ¥Õ¬ Ö\83Õ¸Ö\83Õ¸Õ­Õ¸Ö\82Õ©ÕµÕ¸Ö\82Õ¶Õ¶Õ¥Ö\80ը",
+       "publishpage": "Õ\8dÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ§Õ»ը",
        "publishchanges": "Հիշել փոփոխությունները",
        "preview": "Նախադիտում",
        "showpreview": "Նախադիտել",
        "recentchanges-legend-heading": "<strong>Լեգենդ՝</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (տես նաև՝  [[Special:NewPages|նոր էջերի ցանկ]])",
        "recentchanges-submit": "Ցույց տալ",
+       "rcfilters-limit-shownum": "Ցուցադրել վերջին $1 փոփոխությունները",
        "rcfilters-savedqueries-rename": "Վերանվանել",
        "rcfilters-savedqueries-remove": "Ջնջել",
        "rcfilters-savedqueries-new-name-label": "Անուն",
        "rcfilters-savedqueries-apply-label": "Ստեղծել արագ հղում",
        "rcfilters-savedqueries-cancel-label": "Չեղարկել",
        "rcfilters-filterlist-title": "Զտիչներ",
+       "rcfilters-filterlist-whatsthis": "Ինչպե՞ս է սա աշխատում:",
        "rcfilters-filter-editsbyself-label": "Ձեր խմբագրումներ",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Գրանցված խմբագիրներ՝ ոչ պակաս քան 10 խմբագրումով և 4 օր ակտիվությամբ:",
        "rcfilters-filtergroup-lastRevision": "Ընթացիկ տարբերակ",
        "rcnotefrom": "Ստորև բերված են փոփոխությունները սկսած՝ '''$2''' (մինչև՝ '''$1''')։",
        "rclistfrom": "Ցույց տալ նոր փոփոխությունները սկսած $3 $2",
        "fileduplicatesearch-result-n": "$1 նիշքն ունի {{PLURAL:$2|1 նույնական կրկնօրինակ|$2 նույնական կրկնօրինակ}}.",
        "fileduplicatesearch-noresults": "$1 անունով նիշք չի գտնվել",
        "specialpages": "Սպասարկող էջեր",
-       "specialpages-note": "* Հասարակ հատուկ էջեր։\n* <span class=\"mw-specialpagerestricted\">Սահմանափակված հատուկ էջեր։</span>",
        "specialpages-group-maintenance": "Տեխնիկական սպասարկման տեղեկատուներ",
        "specialpages-group-other": "Այլ հատուկ էջեր",
        "specialpages-group-login": "Մտնել / Գրանցվել",
index 53bacc4..af746fb 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (vide etiam le [[Special:NewPages|lista de nove paginas]])",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Monstrar",
+       "rcfilters-legend-heading": "<strong>Lista de abbreviationes:</strong>",
        "rcfilters-activefilters": "Filtros active",
        "rcfilters-advancedfilters": "Filtros avantiate",
        "rcfilters-quickfilters": "Filtros salveguardate",
        "rcfilters-noresults-conflict": "Nulle resultato trovate perque le criterios de recerca es in conflicto",
        "rcfilters-state-message-subset": "Iste filtro non ha effecto perque su resultatos es includite in illos del sequente {{PLURAL:$2|filtro|filtros}} plus comprehensive (essaya colorar pro poter distinguer lo): $1",
        "rcfilters-state-message-fullcoverage": "Seliger tote le filtros in un gruppo equivale seliger nulle, dunque iste filtro non ha effecto. Le gruppo include: $1",
-       "rcfilters-filtergroup-registration": "Registration del usator",
-       "rcfilters-filter-registered-label": "Registrate",
-       "rcfilters-filter-registered-description": "Redactores que ha aperite session.",
-       "rcfilters-filter-unregistered-label": "Non registrate",
-       "rcfilters-filter-unregistered-description": "Redactores que non ha aperite session.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Iste filtro es in conflicto con le sequente {{PLURAL:$2|filtro|filtros}} de Experientia, le {{PLURAL:$2|qual|quales}} trova solmente usatores registrate: $1",
        "rcfilters-filtergroup-authorship": "Autor del contributiones",
        "rcfilters-filter-editsbyself-label": "Modificationes facite per te",
        "rcfilters-filter-editsbyself-description": "Tu proprie contributiones.",
        "rcfilters-filter-editsbyother-label": "Modificationes facite per alteres",
        "rcfilters-filter-editsbyother-description": "Tote le modificationes excepte le tues.",
        "rcfilters-filtergroup-userExpLevel": "Nivello de experientia (solmente pro usatores registrate)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filtros de Experientia trova solmente usatores registrate. Iste filtro es dunque in conflicto con le filtro \"Non registrate\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Le filtro \"Non registrate\" es in conflicto con un o plure filtros \"Experientia\", le quales trova solmente usatores registrate. Le filtros in conflicto es marcate in le area \"Filtros active\" supra.",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrate",
+       "rcfilters-filter-user-experience-level-registered-description": "Redactores que ha aperite session.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Non registrate",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Redactores que non ha aperite session.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novicios",
        "rcfilters-filter-user-experience-level-newcomer-description": "Minus de 10 modificationes e 4 dies de activitate.",
        "rcfilters-filter-user-experience-level-learner-label": "Apprentisses",
        "undelete-search-title": "Cercar paginas delite",
        "undelete-search-box": "Cercar paginas delite",
        "undelete-search-prefix": "Monstrar paginas que comencia con:",
+       "undelete-search-full": "Monstrar paginas con titulos que contine:",
        "undelete-search-submit": "Cercar",
        "undelete-no-results": "Nulle paginas correspondente trovate in le archivo de deletiones.",
        "undelete-filename-mismatch": "Non pote restaurar le version del file con data e hora $1: le nomine del file non corresponde",
index 4feb7b8..37b4749 100644 (file)
        "anontalk": "Дувца оттадар",
        "navigation": "Навигаци",
        "and": "&#32;а",
-       "qbfind": "Лахар",
-       "qbbrowse": "БIаргтохар",
-       "qbedit": "Нийсде",
-       "qbpageoptions": "ОагIон оттамаш",
-       "qbmyoptions": "Хьа гIирсаш тоаяраш",
        "faq": "КТХ",
-       "faqpage": "Project:КТХ",
        "actions": "Ардамаш",
        "namespaces": "ЦIерий аренаш",
        "variants": "Варианташ",
        "edit-local": "Хувца локальни йоазонца сурт оттадар",
        "create": "Хьакхолла",
        "create-local": "ТIатоха локальни йоазонца сурт оттадар",
-       "editthispage": "Нийсъе ер оагIув",
-       "create-this-page": "Хьакхолла ер оагӀув",
        "delete": "ДӀаяккха",
-       "deletethispage": "ДӀаяккха ер оагӀув",
-       "undeletethispage": "Юхаметтаоттае ер оагӀув",
        "undelete_short": "Юхаметтаоттде {{PLURAL:$1|$1 нийсдар|$1 нийсдараш}}",
        "viewdeleted_short": "{{PLURAL:$1|$1 дIадаьккха нийсдарга|дIадаьккха нийсдарга|$1 дIадаьккха нийсдарашга}} хьажар",
        "protect": "ГIо де",
        "protect_change": "хувца",
-       "protectthispage": "ГIо (лорадар) де укх оагIон",
        "unprotect": "ГIо хувца",
-       "unprotectthispage": "Укх оагIон гIо (лорадар) хувца",
        "newpage": "Керда оагӀув",
-       "talkpage": "Ер оагIув ювца",
        "talkpagelinktext": "дувца оттадар",
        "specialpage": "ГIулакха оагӀув",
        "personaltools": "Доакъашхочун гӀирсаш",
-       "articlepage": "БIаргтоха оагIонга",
        "talk": "Дувца оттадар",
        "views": "Хьажараш",
        "toolbox": "ГӀирсаш",
-       "userpage": "Доакъашхочун оагIон бIаргтоха",
-       "projectpage": "Проекта оагIон бIаргтоха",
        "imagepage": "Файла оагIон бIаргтоха",
        "mediawikipage": "Хьахьокха хоам бара оагIув",
        "templatepage": "Лера оагIон бIаргтоха",
        "redirectedfrom": "($1 дIа-сахьожаяьй укхаз)",
        "redirectpagesub": "ОагIув-дIа-сахьожадар",
        "redirectto": "ДIа-сахьожадар укхаза:",
-       "lastmodifiedat": "УкÑ\85 Ð¾Ð°Ð³Ioн Ñ\82IеÑ\85Ñ\85Ñ\8cаÑ\80а Ñ\85Ñ\83вÑ\86ам: $2, $1.",
+       "lastmodifiedat": "Ð\95Ñ\80 Ð¾Ð°Ð³IÑ\83в Ñ\82IеÑ\85Ñ\85Ñ\8cаÑ\80а Ñ\85ийÑ\86а Ñ\85иннай $2 $1 Ñ\8fÑ\8cннаÑ\87а Ñ\85аÌ\81на.",
        "viewcount": "Укх оагIонга хьежа хиннаб $1{{PLURAL:$1|-зза}}.",
        "protectedpage": "ГIо оттадаь лораяь оагIув",
        "jumpto": "Дехьавала укхаза:",
        "noarticletext-nopermission": "ХIанз укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи белгалъяр хьалаха]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарай дIаяздаьраш].</span> Ер оагӀув хьакхолла Хьа бокъо яц.",
        "note": "'''Белгалдоахар:'''",
        "previewnote": "'''Теркам бе, ер хьалххе бIаргтохар мара бац.'''\nХьа хувцамаш хIанза а дIаяздаь дац!",
+       "continue-editing": "Хувцар кхы дIахо де",
        "editing": "Хувцам: $1",
        "creating": "«$1» оагIув хьакхоллар",
        "editingsection": "Хувцам: $1 (оагӀон дáкъа)",
        "page_first": "цхьоаллагIа",
        "page_last": "тӀехьара",
        "histlegend": "Версий хоржам: белгалъе шун вIаши йиста безам бола оагIон версеш, тIаккха тоIае '''{{int:compare-submit}}'''.<br />\nКхетавар: '''({{int:cur}})''' — карара версеца дола башхалонаш; '''({{int:last}})''' — хьалха йоагIаш версеца дола башхалонаш; '''{{int:minoreditletter}}''' — зIамига хувцамаш.",
-       "history-fieldset-title": "Ð\98Ñ\81Ñ\82оÑ\80ена Ð±IаÑ\80гÑ\82оÑ\85а",
+       "history-fieldset-title": "Ð\94аÑ\8c Ñ\85инна Ñ\85Ñ\83вÑ\86амаÑ\88 Ð»Ð°Ñ\85аÑ\80",
        "history-show-deleted": "Алхха дӀадаьккхараш",
        "histfirst": "эггара къаьнагIа",
        "histlast": "эггара кердагIа",
        "lineno": "МугI $1:",
        "compareselectedversions": "ВIаши йиста хержа версеш",
        "editundo": "юхадаккха",
+       "diff-empty": "(башхалонаш яц)",
        "diff-multi-sameuser": "({{PLURAL:$1|цхьа юкъ хулаш йола верси|$1 юкъ хулаш йола версеш}} гуш яц цу доакъашхочун)",
        "searchresults": "Лахар чакхдоалаш корадаьр",
        "searchresults-title": "«$1» лахар",
        "nextn-title": "{{PLURAL:$1|ТIадоагIа $1 яздар|ТIадоагIа $1 яздараш}}",
        "shown-title": "Гойта $1 {{PLURAL:$1|яздаьр|яздаьраш}} укх оáгIон тIа",
        "viewprevnext": "ДIахьажа ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "<strong>Укх вики чу йолаш я оагӀув «[[:$1]]».</strong>",
+       "searchmenu-exists": "'''Укх вики чу йолаш я оагӀув «[[:$1]]».'''",
        "searchmenu-new": "<strong>Хьакхолла оагIув «[[:$1]]» укх вики-проекте!</strong>\n{{PLURAL:$2|0=|Иштта хьажа Iайха лийха оагIонга.|Иштта хьажа хьай лахара хьахиннарашка.}}",
        "searchprofile-articles": "Кертера оагIонаш",
        "searchprofile-images": "Мультимедиа",
        "nolinkstoimage": "Укх файла тIахьожавеш йола оагIонаш яц.",
        "sharedupload": "Ер файл $1 чура я, из пайда эцаш лелае мегаш я кхыйола проекташ чу.",
        "sharedupload-desc-here": "Ер файл $1 чура я, иштта кхыйола проекташ чу пайда эца аьттув болаш я.\nЦун [$2 сурт оттадара оагIон] хоам кIалхахь хьабоалабаьб.",
+       "filepage-nofile": "Ишта цӀи йола файл йоацаш я.",
        "uploadnewversion-linktext": "Чуяккха укх файла керда верси",
        "upload-disallowed-here": "Хьа бокъо яц ер файл юха дӀаязъе.",
        "filerevert-comment": "Бахьан:",
        "booksources-search-legend": "Джейнах лаьца хоам лахар",
        "booksources-search": "Хьалáха",
        "log": "Тептараш",
+       "logempty": "Укх оагӀон дӀаяздаьраш тептара чу дац.",
        "allpages": "Еррига оагIонаш",
        "prevpage": "Хьалха йоагIа оагIув ($1)",
        "allpagesfrom": "Гучаяккха оагIонаш йолалуш йола укхох:",
        "version-specialpages": "ГIулакха оагӀонаш",
        "version-version": "($1)",
        "version-software-version": "Верси",
+       "redirect-submit": "Дехьавала",
+       "redirect-lookup": "Лахар:",
+       "redirect-value": "Боарам:",
+       "redirect-user": "Доакъашхочун ID",
+       "redirect-page": "ОагӀон ID",
+       "redirect-revision": "ОагӀон верси",
+       "redirect-file": "Файла цӀи",
        "fileduplicatesearch-filename": "Файла цӀи:",
        "fileduplicatesearch-submit": "Хьалáха",
        "specialpages": "ЛаьрххIа йола оагIонаш",
index 4ee4bd4..de9f286 100644 (file)
        "rcfilters-legend-heading": "<strong>Elenco di abbreviazioni:</strong>",
        "rcfilters-activefilters": "Filtri attivi",
        "rcfilters-advancedfilters": "Filtri avanzati",
+       "rcfilters-limit-title": "Modifiche da mostrare",
+       "rcfilters-limit-shownum": "Mostra le ultime $1 modifiche",
+       "rcfilters-days-title": "Giorni recenti",
+       "rcfilters-hours-title": "Ore recenti",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|giorno|giorni}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ora|ore}}",
        "rcfilters-quickfilters": "Filtri salvati",
        "rcfilters-quickfilters-placeholder-title": "Nessun collegamento salvato ancora",
        "rcfilters-quickfilters-placeholder-description": "Per salvare le impostazioni del tuo filtro e riutilizzarle dopo, clicca l'icona segnalibro nell'area \"Filtri attivi\" qui sotto",
        "rcfilters-invalid-filter": "Filtro non valido",
        "rcfilters-empty-filter": "Nessun filtro attivo. Sono mostrati tutti i contributi.",
        "rcfilters-filterlist-title": "Filtri",
-       "rcfilters-filterlist-whatsthis": "Cos'è questo?",
+       "rcfilters-filterlist-whatsthis": "Come funzionano?",
        "rcfilters-filterlist-feedbacklink": "Lascia un commento sulla nuova funzionalità sperimentale",
        "rcfilters-highlightbutton-title": "Evidenzia risultati",
        "rcfilters-highlightmenu-title": "Seleziona un colore",
        "rcfilters-noresults-conflict": "Nessun risultato trovato, perché i criteri di ricerca sono in conflitto",
        "rcfilters-state-message-subset": "Questo filtro non ha effetto perché i suoi risultati sono inclusi in quelli {{PLURAL:$2|del seguente filtro, più ampio|dei seguenti filtri, più ampi}} (prova ad evidenziarli per distinguerli): $1",
        "rcfilters-state-message-fullcoverage": "La selezione di tutti i filtri in un gruppo è come non selezionarne alcuno, in questo modo questo filtro non ha effetto. Il gruppo include: $1",
-       "rcfilters-filtergroup-registration": "Registrazione utente",
-       "rcfilters-filter-registered-label": "Registrato",
-       "rcfilters-filter-registered-description": "Contributori che hanno effettuato l'accesso.",
-       "rcfilters-filter-unregistered-label": "Non registrato",
-       "rcfilters-filter-unregistered-description": "Contributori che non hanno effettuato l'accesso.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Questo filtro è in conflitto con {{PLURAL:$2|il seguente filtro|i seguenti filtri}} esperienza, che {{PLURAL:$2|trova|trovano}} soltanto utenti registrati: $1",
        "rcfilters-filtergroup-authorship": "Autore del contributo",
        "rcfilters-filter-editsbyself-label": "Mie modifiche",
        "rcfilters-filter-editsbyself-description": "I tuoi contributi.",
        "rcfilters-filter-editsbyother-label": "Modifiche di altri",
        "rcfilters-filter-editsbyother-description": "Tutte le modifiche eccetto le tue.",
-       "rcfilters-filtergroup-userExpLevel": "Livello d'esperienza (solo per utenti registrati)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "I filtri esperienza trovano solo utenti registrati, quindi questo filtro è in conflitto con il filtro \"Non registrato\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Il filtro \"Non registrato\" è in conflitto con uno o più filtri esperienza, che trovano solo utenti registrati. I filtri in conflitto sono evidenziati nell'area \"Filtri attivi\" qui sopra.",
+       "rcfilters-filtergroup-userExpLevel": "Registrazione utente ed esperienza",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrato",
+       "rcfilters-filter-user-experience-level-registered-description": "Contributori che hanno effettuato l'accesso.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Non registrato",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Contributori che non hanno effettuato l'accesso.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nuovi utenti",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Meno di 10 modifiche e 4 giorni di attività.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Utenti registrati con meno di 10 modifiche e 4 giorni di attività.",
        "rcfilters-filter-user-experience-level-learner-label": "Principianti",
-       "rcfilters-filter-user-experience-level-learner-description": "Con più esperienza dei \"Nuovi arrivati\" ma meno degli \"Utenti esperti\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Utenti registrati il cui livello di esperienza è compreso tra \"Nuovi arrivati\" e \"Utenti esperti\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Utenti con esperienza",
-       "rcfilters-filter-user-experience-level-experienced-description": "Più di 30 giorni di attività e 500 modifiche.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Utenti registrati con più di 500 modifiche e 30 giorni di attività.",
        "rcfilters-filtergroup-automated": "Contributi automatici",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Modifiche effettuate da strumenti automatici.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Il filtro \"Modifiche minori\" è in confitto con uno o più dei filtri \"Tipo di modifica\", perché certe modifiche non possono essere indicate come \"minori\". I filtri in conflitto sono indicati nell'area \"Filtri attivi\" qui sopra.",
        "rcfilters-hideminor-conflicts-typeofchange": "Alcuni tipi di modifiche non possono essere indicate come \"minori\", quindi questo filtro è in conflitto con i seguenti filtri \"Tipo di modifica\": $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Questo filtro \"Tipo di modifica\" è in conflitto con il filtro \"Modifiche minori\". Alcuni tipi di modifiche non possono essere indicati come \"minori\".",
-       "rcfilters-filtergroup-lastRevision": "Ultima versione",
-       "rcfilters-filter-lastrevision-label": "Ultima versione",
-       "rcfilters-filter-lastrevision-description": "Le ultime modifiche ad una pagina.",
-       "rcfilters-filter-previousrevision-label": "Versioni precedenti",
-       "rcfilters-filter-previousrevision-description": "Tutte le modifiche che non sono l'ultima modifica effettuata sulla voce.",
+       "rcfilters-filtergroup-lastRevision": "Ultime versioni",
+       "rcfilters-filter-lastrevision-label": "Versione attuale",
+       "rcfilters-filter-lastrevision-description": "Solo l'ultima modifica ad una pagina.",
+       "rcfilters-filter-previousrevision-label": "Non l'ultima versione",
+       "rcfilters-filter-previousrevision-description": "Tutte le modifiche che non sono la \"versione attuale\".",
        "rcfilters-filter-excluded": "Escluso",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
+       "rcfilters-exclude-button-off": "Escludi selezionato",
+       "rcfilters-exclude-button-on": "Escludendo selezionato",
        "rcfilters-view-tags": "Modifiche etichettate",
        "rcnotefrom": "Di seguito {{PLURAL:$5|è elencata la modifica apportata|sono elencate le modifiche apportate}} a partire da <strong>$3, $4</strong> (mostrate fino a <strong>$1</strong>).",
        "rclistfromreset": "Reimposta la selezione della data",
        "delete-warning-toobig": "La cronologia di questa pagina è molto lunga (oltre $1 {{PLURAL:$1|versione|versioni}}). La sua cancellazione può creare dei problemi di funzionamento al database di {{SITENAME}}; procedere con cautela.",
        "deleteprotected": "Non puoi cancellare questa pagina perché è stata protetta.",
        "deleting-backlinks-warning": "<strong>Attenzione:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|altre pagine]] contengono collegamenti o inclusioni alla pagina che stai per cancellare.",
+       "deleting-subpages-warning": "<strong>Attenzione:</strong> la pagina che stai per cancellare ha [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|una sotto-pagina|$1 sotto-pagine|51=più di 50 sotto-pagine}}]].",
        "rollback": "Annulla le modifiche",
        "rollbacklink": "rollback",
        "rollbacklinkcount": "rollback di {{PLURAL:$1|una modifica|$1 modifiche}}",
        "fileduplicatesearch-noresults": "Nessun file di nome \"$1\" trovato.",
        "specialpages": "Pagine speciali",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Pagine speciali non riservate.\n* <span class=\"mw-specialpagerestricted\">Pagine speciali riservate ad alcune categorie di utenti.</span>",
        "specialpages-group-maintenance": "Resoconti di manutenzione",
        "specialpages-group-other": "Altre pagine speciali",
        "specialpages-group-login": "Accesso / creazione utenze",
index 50d13ae..88f9e85 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|新しいページ一覧]]も参照)",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "recentchanges-submit": "表示",
+       "rcfilters-legend-heading": "<strong>略語のリスト:</strong>",
        "rcfilters-activefilters": "絞り込み",
+       "rcfilters-advancedfilters": "詳細フィルター",
        "rcfilters-quickfilters": "フィルターを保存",
        "rcfilters-quickfilters-placeholder-title": "リンクはまだ保存されていません",
        "rcfilters-quickfilters-placeholder-description": "フィルターの設定を保存し、後で再び使用するためには、下のアクティブフィルター内のブックマークアイコンをクリックしてください。",
        "rcfilters-noresults-conflict": "検索条件が競合しているため、絞り込みできません",
        "rcfilters-state-message-subset": "この項目による絞り込みは全て以下の{{PLURAL:$2|項目}}による絞り込みの結果に含まれています(強調表示を使うことで該当項目を抽出できます) : $1",
        "rcfilters-state-message-fullcoverage": "同じグループの全ての項目が選択されています。絞り込みできません。同じグループに含まれる項目 :$1",
-       "rcfilters-filtergroup-registration": "利用者登録",
-       "rcfilters-filter-registered-label": "登録済み",
-       "rcfilters-filter-registered-description": "ログイン済みの編集者。",
-       "rcfilters-filter-unregistered-label": "未登録",
-       "rcfilters-filter-unregistered-description": "ログインしていない利用者。",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "この項目は、登録済み利用者を編集回数別で絞り込む以下の{{PLURAL:$2|項目}}と競合しています :$1",
        "rcfilters-filtergroup-authorship": "自分の編集か他者の編集か",
        "rcfilters-filter-editsbyself-label": "自分の編集",
        "rcfilters-filter-editsbyself-description": "自分の投稿記録を絞り込む",
        "rcfilters-filter-editsbyother-label": "自分以外の編集",
        "rcfilters-filter-editsbyother-description": "自分以外の利用者による編集",
        "rcfilters-filtergroup-userExpLevel": "編集回数別(登録利用者のみが対象)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "編集回数別絞り込みは登録利用者のみが対象です。「未登録」の絞り込み項目と競合しています。",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "「未登録」の項目が登録済み利用者を絞り込む項目と競合しています。競合している項目は項目選択欄で強調表示されています。",
+       "rcfilters-filter-user-experience-level-registered-label": "登録済み",
+       "rcfilters-filter-user-experience-level-registered-description": "ログイン済みの編集者。",
+       "rcfilters-filter-user-experience-level-unregistered-label": "未登録",
+       "rcfilters-filter-user-experience-level-unregistered-description": "ログインしていない利用者。",
        "rcfilters-filter-user-experience-level-newcomer-label": "新規利用者",
        "rcfilters-filter-user-experience-level-newcomer-description": "登録から4日以内、かつ10編集以下の利用者",
        "rcfilters-filter-user-experience-level-learner-label": "初学者",
        "rcfilters-filter-lastrevision-description": "ページの最新の変更",
        "rcfilters-filter-previousrevision-label": "古い版",
        "rcfilters-filter-previousrevision-description": "ページの最新の変更ではない全ての変更。",
+       "rcfilters-view-namespaces-tooltip": "名前空間別のフィルター結果",
+       "rcfilters-view-tags-tooltip": "編集タグを利用した絞込み",
        "rcnotefrom": "以下は<strong>$3 $4</strong>以降の{{PLURAL:$5|更新です}} (最大 <strong>$1</strong> 件)。",
        "rclistfromreset": "日時指定をリセット",
        "rclistfrom": "$3の$2以降の更新を表示する",
        "newimages-legend": "絞り込み",
        "newimages-label": "ファイル名 (またはその一部):",
        "newimages-user": "IPアドレスまたは利用者名:",
+       "newimages-newbies": "新規利用者の投稿のみ表示",
        "newimages-showbots": "ボットによるアップロードを表示",
        "newimages-hidepatrolled": "巡回済みのアップロードを隠す",
        "noimages": "表示できるものがありません。",
        "gotointerwiki-external": "{{SITENAME}}を離れ、別のウェブサイトである[[$2]]を訪れようとしています。\n\n'''[$1 $1に行く]'''",
        "undelete-cantedit": "このページを編集する許可がないため復元できません。",
        "undelete-cantcreate": "同名のページが存在せず、このページを作成する許可がないため復元できません。",
-       "pagedata-not-acceptable": "該当する形式が見つかりません。対応している MIME タイプ: $1"
+       "pagedata-title": "ページ・データ",
+       "pagedata-not-acceptable": "該当する形式が見つかりません。対応している MIME タイプ: $1",
+       "pagedata-bad-title": "「$1」は無効なページ名です。"
 }
index 069c712..023ca90 100644 (file)
        "searchbutton": "Golèk",
        "go": "Menyang",
        "searcharticle": "Menyang",
-       "history": "Sujarah kaca",
-       "history_short": "Sujarah",
-       "history_small": "sujarah",
+       "history": "Sajarah kaca",
+       "history_short": "Sajarah",
+       "history_small": "sajarah",
        "updatedmarker": "wis dianyari kawit tekaku mréné pungkasan",
        "printableversion": "Vèrsi cap-capan",
        "permalink": "Pranala permanèn",
        "showpreview": "Deleng pratuduh",
        "showdiff": "Tuduhaké owahan",
        "anoneditwarning": "<strong>Pènget:</strong> Panjenengan durung mlebu log. Alamat IP-né panjenengan bakal katon marang wong akèh manawa panjenengan mbesut. Manawa panjenengan <strong>[$1 mlebu log]</strong> utawa <strong>[$2 nggawé akun]</strong>, besutané panjenengan bakal dadi darbéné naragunané panjenengan lan uga ana kauntungan liya.",
-       "anonpreviewwarning": "<em>Panjenengan durung mlebu log. Yèn disimpen, alamat IP panjenengan bakal kacathet ing sujarah besutan kaca iki.</em>",
+       "anonpreviewwarning": "<em>Panjenengan durung mlebu log. Yèn disimpen, alamat IP panjenengan bakal kacathet ing sajarah besutan kaca iki.</em>",
        "missingsummary": "<strong>Pangéling-éling:</strong> Panjenengan ora ngisèni ringkesané besutan.\nManawa panjenengan mencèt \"$1\" manèh, besutané panjengan bakal kasimpen tanpa katerangan.",
        "selfredirect": "<strong>Pélik:</strong> Sampéyan ngalih kaca iki iya nyang kaca iki dhéwé.\nSampéyan mungkin salah wènèh tujuan kanggo alihan utawa salah mbesut kaca.\nYèn sampéyan ngeklik \"$1\" manèh, kaca alihan bakal digawé.",
        "missingcommenttext": "Mangga isi tanggepan ing ngisor iki.",
        "anontalkpagetext": "----\n<em>Iki kaca parembugané panganggo anonim sing durung gawé akun, utawa sing ora nganggo akuné.</em>\nMula, awak dhéwé kudu nganggo alamat IP awujud angka kanggo nglacak dhèwèké.\nAlamat IP mangkono bisa dianggo déning sawenèh panganggo.\nManawa panjenengan panganggo anonim lan rumasa yèn ana tanggepan sing ora ilok dieneraké marang panjenengan, mangga [[Special:CreateAccount|gawéa akun]] utawa [[Special:UserLogin|mlebua log]] kanggo ngéndhani salah pangira karo panganggo anonim liyané ing tembé buri.",
        "noarticletext": "Kala saiki kaca iki durung ana tulisané.\nSampéyan bisa [[Special:Search/{{PAGENAME}}|nggolèki sesirahing kaca iki]] sajeroning kaca liya,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} nggolèki log sing magepokan],\nutawa [{{fullurl:{{FULLPAGENAME}}|action=edit}} nggawé kaca iki]</span>.",
        "noarticletext-nopermission": "Saiki lagi ora ana tèks ing kaca iki. \nPanjenengan bisa [[Special:Search/{{PAGENAME}}|nggolèk sesirah kaca iki]] ing kaca liyané, \nutawa <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{urlencode:{{FULLPAGENAME}}}}}} nggolèk ing log sing gegayutan]</span>, nanging panjenengan ora kawogan nggawé kaca iki.",
-       "missing-revision": "Révisi #$1 saka kaca ajeneng \"{{FULLPAGENAME}}\" ora ana.\n\nIki biyasané kasababaké awit nututi pranala sujarah sing wis lawas saka sawijiné kaca sing wis dibusak.\nRerincèné bisa digolèki ing [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
+       "missing-revision": "Révisi #$1 saka kaca ajeneng \"{{FULLPAGENAME}}\" ora ana.\n\nIki biyasané kasababaké awit nututi pranala sajarah sing wis lawas saka sawijiné kaca sing wis dibusak.\nRerincèné bisa digolèki ing [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
        "userpage-userdoesnotexist": "Akun panganggo \"$1\" ora kadhaftar.\nMangga pesthèkaké dhisik yèn panjenengan péngin nggawé/mbesut kaca iki.",
        "userpage-userdoesnotexist-view": "Akun panganggo \"$1\" ora kadhaftar.",
        "blocked-notice-logextract": "Panganggo iki saiki lagi diblokir.\nLog pamblokiran pungkasan dituduhaké ing ngisor iki minangka bahan rujukan:",
        "histlast": "anyar dhéwé",
        "historysize": "($1 {{PLURAL:$1|bét|bét}})",
        "historyempty": "(suwung)",
-       "history-feed-title": "Sujarah owahan",
-       "history-feed-description": "Sujarah owahaning kaca iki ing wiki",
+       "history-feed-title": "Sajarah owahan",
+       "history-feed-description": "Sajarah owahaning kaca iki ing wiki",
        "history-feed-item-nocomment": "$1 ing $2",
        "history-feed-empty": "Kaca sing disuwun ora ditemokaké. Mbokmenawa wis dibusak saka wiki, utawa diwènèhi jeneng anyar. Coba [[Special:Search|golèka ing wiki]] kanggo kaca anyar sing rélevan.",
        "rev-deleted-comment": "(tingkesaning besutan dibusak)",
        "logdelete-failure": "'''Aturan pandhelikan ora bisa disèt:'''\n$1",
        "revdel-restore": "Ngowahi visiblitas (pangatonan)",
        "pagehist": "Babading kaca",
-       "deletedhist": "Sujarah kabusak",
+       "deletedhist": "Sajarah kabusak",
        "revdelete-hide-current": "Gagal ndhelikaké révisi tanggal $2, $1: iki arupa révisi paling anyar.\nRévisi iki ora bisa didhelikaké.",
        "revdelete-show-no-access": "Gagal nampilaké révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-modify-no-access": "Gagal ngowahi révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-offender": "Juru pangriptaning owahan:",
        "suppressionlog": "Log barang-barang sing didelikaké (''oversight'')",
        "suppressionlogtext": "Ngisor iki daptar apa-apa waé sing wis dibusak lan diblokir kalebu kontèn sing didhelikaké saka para pangurus.\nDelok [[Special:BlockList|daptar blokiran]] sing isiné daptar apa-apa waé sing lagi dilarang lan diblokir.",
-       "mergehistory": "Gabung sujarah kaca",
+       "mergehistory": "Gabung sajarah kaca",
        "mergehistory-header": "Ing kaca iki panjenengan bisa nggabung révisi-révisi sajarah saka sawijining kaca sumber menyang kaca anyar.\nPastèkna yèn owah-owahan iki bakal netepaké kasinambungan sajarah kaca.",
        "mergehistory-box": "Gabungna revisi-revisi saka rong kaca:",
        "mergehistory-from": "Kaca sumber:",
        "mergehistory-into": "Kaca paran:",
-       "mergehistory-list": "Sujarah besutan sing bisa digabung",
+       "mergehistory-list": "Sajarah besutan sing bisa digabung",
        "mergehistory-merge": "Révisi-révisi sing kapacak ing ngisor iki saka [[:$1]] bisa digabungaké menyang [[:$2]].\nGunakna tombol radio kanggo nggabungaké révisi-révisi sing digawé sadurungé wektu tartamtu. Gatèkna, menawa nganggo pranala navigasi bakal ngesèt ulang kolom iki.",
        "mergehistory-go": "Tuduhaké besutan sing bisa digabung",
        "mergehistory-submit": "Gabung owahan",
        "mergehistory-fail": "Ora bisa nggabung sajarah, coba dipriksa manèh kacané lan paramèter wektuné.",
        "mergehistory-fail-invalid-source": "Kaca sumber ora trep.",
        "mergehistory-fail-invalid-dest": "Kaca paran ora trep.",
-       "mergehistory-fail-no-change": "Panggabung sujarah ora nggabungaké rèvisi. Mangga priksanen kaca lan paramèter wektuné.",
+       "mergehistory-fail-no-change": "Panggabung sajarah ora nggabungaké rèvisi. Mangga priksanen kaca lan paramèter wektuné.",
        "mergehistory-fail-self-merge": "Kaca asal lan kaca paran padha.",
        "mergehistory-fail-timestamps-overlap": "Rèvisi asal tumpuk-undhung utawa njedhul sawisé révisi paran.",
-       "mergehistory-fail-toobig": "Ora bisa nggabungaké sujarah amarga {{PLURAL:$1|révisi}} sing arep dilih munjuli $1.",
+       "mergehistory-fail-toobig": "Ora bisa nggabungaké sajarah amarga {{PLURAL:$1|révisi}} sing arep dilih munjuli $1.",
        "mergehistory-no-source": "Kaca sumber $1 ora ana.",
        "mergehistory-no-destination": "Kaca paran $1 ora ana.",
        "mergehistory-invalid-source": "Kaca sumber kudu asesirah sing sah.",
        "mergelog": "Gabung log",
        "revertmerge": "Wurung gabung",
        "mergelogpagetext": "Ing ngisor iki kapacak daftar panggabungan sajarah kaca ing kaca liyané.",
-       "history-title": "Sujarah owahaning \"$1\"",
+       "history-title": "Sajarah owahaning \"$1\"",
        "difference-title": "Prabéda antara owahan \"$1\"",
        "difference-title-multipage": "Béda antarané kaca \"$1\" lan \"$2\"",
        "difference-multipage": "(Prabédhan antar kaca)",
        "right-autopatrol": "Gawé supaya suntingan-suntingan ditandhani minangka wis dipatroli",
        "right-patrolmarks": "Ndeleng tandha-tandha patroli owah-owahan anyar",
        "right-unwatchedpages": "Tuduhna daftar kaca-kaca sing ora diawasi",
-       "right-mergehistory": "Gabung sujarah kaca",
+       "right-mergehistory": "Gabung sajarah kaca",
        "right-userrights": "Besut kabèh hak panganggo",
        "right-userrights-interwiki": "Besut hak-haking panganggo asal wiki jaba",
        "right-siteadmin": "Kunci lan buka kunci basis data",
        "action-delete": "busak kaca iki",
        "action-deleterevision": "busak révisi",
        "action-deletelogentry": "busak isian log",
-       "action-deletedhistory": "deleng sujarah sing dibusak sawijiné kaca",
+       "action-deletedhistory": "deleng sajarah sing dibusak sawijiné kaca",
        "action-deletedtext": "deleng tèks révisi sing dibusak",
        "action-browsearchive": "nggolèki kaca-kaca sing wis dibusak",
        "action-undelete": "wurung busak kaca",
        "action-purge": "buwang kaca iki",
        "nchanges": "$1 {{PLURAL:$1|pangowahan|owah-owahan}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|saka keri dhewe mrene}}",
-       "enhancedrc-history": "sujarah",
+       "enhancedrc-history": "sajarah",
        "recentchanges": "Owahan pungkasan",
        "recentchanges-legend": "Pilihan owah-owahan pungkasan",
        "recentchanges-summary": "Runutna owah-owahan pungkasan ing wiki iki ing kaca iki.",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (uga delengen [[Special:NewPages|pratélané kaca-kaca anyar]])",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Tuduhaké",
+       "rcfilters-legend-heading": "<strong>Pratélané cekakan:</strong>",
        "rcfilters-activefilters": "Saringan murub",
        "rcfilters-quickfilters": "Pranala rikat",
        "rcfilters-quickfilters-placeholder-title": "Durung ana pranala sing disimpen",
        "rcfilters-highlightmenu-help": "Pilih werna kanggo nyentrong properti iki",
        "rcfilters-filterlist-noresults": "Saringan ora katemu",
        "rcfilters-noresults-conflict": "Ora ana kasil amarga wewatoné kanggo nggolèk ana masalah",
-       "rcfilters-filtergroup-registration": "Pandhaftaran panganggo",
-       "rcfilters-filter-registered-label": "Kadhaftar",
-       "rcfilters-filter-registered-description": "Pambesut sing mlebu log.",
-       "rcfilters-filter-unregistered-label": "Ora kadhaftar",
-       "rcfilters-filter-unregistered-description": "Pambesut sing ora mlebu log.",
        "rcfilters-filtergroup-authorship": "Pangripta besutan",
        "rcfilters-filter-editsbyself-label": "Owah-owahané panjenengan",
        "rcfilters-filter-editsbyself-description": "Sumbangané panjenengan dhéwé.",
        "rcfilters-filter-editsbyother-label": "Owah-owahané liyan",
        "rcfilters-filter-editsbyother-description": "Kabèh owahan kajaba duwèké panjenengan.",
        "rcfilters-filtergroup-userExpLevel": "Tataran pangalaman (mung kanggo panganggo kadhaftar)",
+       "rcfilters-filter-user-experience-level-registered-label": "Kadhaftar",
+       "rcfilters-filter-user-experience-level-registered-description": "Pambesut sing mlebu log.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Ora kadhaftar",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Pambesut sing ora mlebu log.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Wong neneka anyar",
        "rcfilters-filter-user-experience-level-newcomer-description": "Kurang saka 10 besutan lan 4 dina kagiyatan.",
        "rcfilters-filter-user-experience-level-learner-label": "Wong sinau",
        "listfiles-latestversion-yes": "Iya",
        "listfiles-latestversion-no": "Ora",
        "file-anchor-link": "Barkas",
-       "filehist": "Sujarah barkas",
+       "filehist": "Sajarah barkas",
        "filehist-help": "Klik ing tanggal/wektuné saprelu ndeleng rupané barkasé nalika tanggal iku.",
        "filehist-deleteall": "busaken kabèh",
        "filehist-deleteone": "busaken iki",
        "deletereasonotherlist": "Alesan liya",
        "deletereason-dropdown": "*Alesan pambusakan\n** Spam\n** Vandalisme\n** Nglanggar hak cipta\n** Disuwun sing nulis\n** Pangalihan rusak",
        "delete-edit-reasonlist": "Besut alesané pambusak",
-       "delete-toobig": "Kaca iki darbé sujarah besutan sing dawa, punjul $1 {{PLURAL:$1|owahan}}.\nPambusak tumrap kaca sing kaya mangkono wis ora diidinaké nedya njagani murih ora ana karusakan ing {{SITENAME}}.",
-       "delete-warning-toobig": "Kaca iki duwé sujarah besut sing dawa, punjul $1 {{PLURAL:$1|révisi}}.\nMbusak kaca iki bisa ngrusak lakuné basis dhata ing {{SITENAME}};\nkudu diayahi kanthi ngati-ati.",
+       "delete-toobig": "Kaca iki darbé sajarah besutan sing dawa, punjul $1 {{PLURAL:$1|owahan}}.\nPambusak tumrap kaca sing kaya mangkono wis ora diidinaké nedya njagani murih ora ana karusakan ing {{SITENAME}}.",
+       "delete-warning-toobig": "Kaca iki duwé sajarah besut sing dawa, punjul $1 {{PLURAL:$1|révisi}}.\nMbusak kaca iki bisa ngrusak lakuné basis dhata ing {{SITENAME}};\nkudu diayahi kanthi ngati-ati.",
        "deleteprotected": "Panjenengan ora bisa mbusak kaca iki amarga direksa.",
        "deleting-backlinks-warning": "'''Awas:''' Kaca liyane mungkin ana sing nautake ing kaca sing arep sampeyan busak.",
        "rollback": "Pulihaké besutan",
        "lockedbyandtime": "(déning {{GENDER:$1|$1}} tanggal $2 wanci $3)",
        "move-page": "Ngalih $1",
        "move-page-legend": "Mindhah kaca",
-       "movepagetext": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sujarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan bisa ndandani kaca alihan sing otomatis nggayut nyang kaca asliné.\nYèn ora, pesthèkaké yèn panjenengan wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sujarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
-       "movepagetext-noredirectfixer": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sujarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan kudu yakin yèn wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sujarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
+       "movepagetext": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sajarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan bisa ndandani kaca alihan sing otomatis nggayut nyang kaca asliné.\nYèn ora, pesthèkaké yèn panjenengan wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sajarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
+       "movepagetext-noredirectfixer": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sajarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan kudu yakin yèn wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sajarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
        "movepagetalktext": "Menawa sampéyan nyénthang kothak iki, kaca parembugan sing magepokan bakal otomatis dilih nyang sesirah anyar, kajaba kaca parembugané sing dituju wis ana isiné.\n\nYèn mangkéné, sampéyan kudu ngalih utawa nggabung kaca-kaca iku kanthi manual.",
        "moveuserpage-warning": "<strong>Pènget:</strong> Panjenengan iki arep ngalih kaca panganggo. Mangga èlingana yèn mung kacané waé sing bakal dilih, déné panganggoné <em>ora</em> bakal ganti jeneng.",
        "movecategorypage-warning": "<strong>Pélik:</strong> Panjenengan arep ngalih kaca kategori. Tulung gatèkaké yèn mung kacané thok sing bakal dilih déné samubarang kaca sing ana ing kategori lawas <em>ora</em> bakal mèlu dilih nyang kaca kategori anyar.",
        "pageinfo-title": "Inpormasi kanggo \"$1\"",
        "pageinfo-not-current": "Maaf, tidak mungkin memberikan informasi ini ke revisi lama.",
        "pageinfo-header-basic": "Informasi dhasar",
-       "pageinfo-header-edits": "Sujarah besutan",
+       "pageinfo-header-edits": "Sajarah besutan",
        "pageinfo-header-restrictions": "Perlindungan halaman",
        "pageinfo-header-properties": "Properti kaca",
        "pageinfo-display-title": "Sesirah pajangan",
        "fileduplicatesearch-noresults": "Ora tinemu barkas kanthi jeneng \"$1\".",
        "specialpages": "Kaca mirunggan",
        "specialpages-note-top": "Katrangan",
-       "specialpages-note": "* Kaca mirunggan sedhengan.\n* <span class=\"mw-specialpagerestricted\">Kaca mirunggan winatesan.</span>",
        "specialpages-group-maintenance": "Lapuran pangopèn",
        "specialpages-group-other": "Kaca mirunggan liyané",
        "specialpages-group-login": "Mlebu log / nggawé akun",
index 9522e03..e8ffe2d 100644 (file)
        "redirectedfrom": "(ត្រូវបានបញ្ជូនបន្តពី $1)",
        "redirectpagesub": "ទំព័របញ្ជូនបន្ត",
        "redirectto": "បញ្ជូនបន្តទៅ៖",
-       "lastmodifiedat": "ទំព័រនេះត្រូវបានកែចុងក្រោយនៅ$2 $1",
+       "lastmodifiedat": "ទំព័រនេះត្រូវបានកែចុងក្រោយនៅ$2 $1",
        "viewcount": "ទំព័រនេះ​ត្រូវបានចូលមើល​ចំនួន'''{{PLURAL:$1|ម្ដង|$1ដង}}'''",
        "protectedpage": "ទំព័រដែលត្រូវបានការពារ",
        "jumpto": "លោតទៅ៖",
        "newarticle": "(ថ្មី)",
        "newarticletext": "អ្នកបានតាម​តំណភ្ជាប់​ទៅ​ទំព័រដែលមិនទាន់មាននៅឡើយ។\nដើម្បីបង្កើតទំព័រនេះ សូមចាប់ផ្ដើមវាយ​ក្នុងប្រអប់ខាងក្រោម (សូមមើល [$1 ទំព័រ​ជំនួយ] សម្រាប់​ព័ត៌មានបន្ថែម)។\nបើ​អ្នក​មក​ទីនេះដោយអចេតនា សូមចុចប៊ូតុង '''ត្រឡប់ក្រោយ''' របស់ឧបករណ៍រាវរករបស់អ្នក។",
        "anontalkpagetext": "----''ទំព័រពិភាក្សានេះគឺសម្រាប់តែអ្នកប្រើប្រាស់អនាមិកដែលមិនទាន់បានបង្កើតគណនីតែប៉ុណ្ណោះ។ ដូច្នេះអាសយដ្ឋានលេខIPរបស់កុំព្យូទ័ររបស់លោកអ្នក​នឹងត្រូវបានបង្ហាញ ដើមី្បសម្គាល់លោកអ្នក។\n\nអាសយដ្ឋានIPទាំងនោះអាចនឹងត្រូវប្រើដោយមនុស្សច្រើននាក់។\n\nប្រសិនបើអ្នកជាអ្នកប្រើប្រាស់អនាមិក​ហើយ​ប្រសិនបើអ្នកឃើញមានការបញ្ចេញយោបល់មកអ្នកពីអ្វី​ដែល​មិន​ទាក់ទងទៅនឹងអ្វីដែល​អ្នកបាន​ធ្វើ​ សូម[[Special:CreateAccount|បង្កើតគណនី]] ឬ [[Special:UserLogin|កត់ឈ្មោះចូល]] ដើម្បីចៀសវាង​ការភ័ន្តច្រឡំ​ណាមួយជាយថាហេតុជាមួយនិងអ្នកប្រើប្រាស់អនាមិកដទៃទៀត។''",
-       "noarticletext": "បច្ចុប្បន្នគ្មានអត្ថបទក្នុងទំព័រនេះទេ។\n\nអ្នកអាច [[Special:Search/{{PAGENAME}}|ស្វែងរក​ចំណងជើង​នៃទំព័រនេះ]]ក្នុងទំព័រដទៃទៀត​​ ឬ [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ស្វែង​រក​កំណត់​ហេតុ​ដែល​ពាក់ព័ន្ធ] ឬ [{{fullurl:{{FULLPAGENAME}}|action=edit}} កែប្រែ​ទំព័រនេះ]។",
+       "noarticletext": "បច្ចុប្បន្នគ្មានអត្ថបទក្នុងទំព័រនេះទេ។\n\nអ្នកអាច [[Special:Search/{{PAGENAME}}|ស្វែងរក​ចំណងជើង​នៃទំព័រនេះ]]ក្នុងទំព័រដទៃទៀត​​ ឬ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ស្វែង​រក​កំណត់​ហេតុ​ដែល​ពាក់ព័ន្ធ] ឬ [{{fullurl:{{FULLPAGENAME}}|action=edit}} បង្កើត​ទំព័រនេះ]</span>។",
        "noarticletext-nopermission": "បច្ចុប្បន្ន គ្មានអត្ថបទណាមួយក្នុងទំព័រនេះទេ។\n\nអ្នកអាច [[Special:Search/{{PAGENAME}}|ស្វែងរក​ចំណងជើង​នៃទំព័រនេះ]] ក្នុងទំព័រ​ផ្សេងៗ ឬ<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ស្វែង​រក​កំណត់​ហេតុ​ដែល​ពាក់ព័ន្ធ]</span>។ ប៉ុន្តែអ្នកគ្មានសិទ្ធិក្នុងការបង្កើតទំព័រនេះទេ។",
        "userpage-userdoesnotexist": "គណនីអ្នកប្រើឈ្មោះ\"<nowiki>$1</nowiki>\" មិនទាន់បានចុះបញ្ជី។\n\nចូរគិតម្ដងទៀតថាអ្នកចង់ បង្កើត / កែប្រែ ទំព័រនេះឬទេ។",
        "userpage-userdoesnotexist-view": "គណនីអ្នកប្រើប្រាស់ដែលមានឈ្មោះ \"$1\"មិនទាន់បានចុះឈ្មោះទេ។",
        "searchrelated": "ទាក់ទិន",
        "searchall": "ទាំងអស់",
        "showingresults": "ខាងក្រោមកំពុងបង្ហាញរហូតដល់ {{PLURAL:$1|'''១''' លទ្ឋផល|'''$1''' លទ្ឋផល}} ចាប់ផ្ដើមពីលេខ #'''$2'''។",
+       "search-showingresults": "{{PLURAL:$4|លទ្ធផល <strong>$1</strong> របស់ <strong>$3</strong>|លទ្ធផល <strong>$1 - $2</strong> របស់ <strong>$3</strong>}}",
        "search-nonefound": "មិនមានលទ្ធផលណាមួយ​ត្រូវគ្នានឹងសំណើសុំនេះទេ",
        "powersearch-legend": "ស្វែងរកថ្នាក់ខ្ពស់",
        "powersearch-ns": "ស្វែងរកក្នុងលំហឈ្មោះ៖",
        "rcshowhidemine-hide": "លាក់",
        "rcshowhidecategorization-show": "បង្ហាញ​",
        "rcshowhidecategorization-hide": "លាក់",
-       "rclinks": "បង្ហាញបន្លាស់ប្ដូរ$1ចុងក្រោយធ្វើឡើងក្នុងរយៈពេល$2ថ្ងៃចុងក្រោយ",
+       "rclinks": "បង្ហាញបន្លាស់ប្ដូរ$1ចុងក្រោយ ដែលធ្វើឡើងក្នុងរយៈពេល$2ថ្ងៃចុងក្រោយ",
        "diff": "ប្រៀបធៀប",
        "hist": "ប្រវត្តិ",
        "hide": "លាក់",
        "contributions": "ការរួមចំណែក​របស់{{GENDER:$1|អ្នកប្រើប្រាស់}}",
        "contributions-title": "ការរួមចំណែករបស់អ្នកប្រើប្រាស់ $1",
        "mycontris": "ការរួមចំណែក",
+       "anoncontribs": "ការរួមចំណែក",
        "contribsub2": "សម្រាប់{{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "គណនីអ្នកប្រើប្រាស់ដែលមានឈ្មោះ \"$1\"មិនទាន់បានចុះឈ្មោះទេ។",
        "nocontribs": "គ្មានការផ្លាស់ប្តូរត្រូវបានឃើញដូចនឹងលក្ខណៈវិនិច្ឆ័យទាំងនេះ។",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|កំណែ}}",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|កំណែ}} ពី $2",
        "tooltip-pt-userpage": "ទំព័រ​{{GENDER:|អ្នកប្រើប្រាស់​របស់អ្នក}}",
-       "tooltip-pt-mytalk": "ទំព័រពិភាក្សា​របស់អ្នក​",
+       "tooltip-pt-mytalk": "ទំព័រពិភាក្សា​​{{GENDER:|របស់អ្នក}}",
        "tooltip-pt-anontalk": "ការពិភាក្សាអំពីកំណែប្រែដែល​ធ្វើ​ឡើង​ចេញ​ពីអាសយដ្ឋាន IP នេះ",
-       "tooltip-pt-preferences": "ចំណង់ចំណូលចិត្ត",
+       "tooltip-pt-preferences": "ចំណង់ចំណូលចិត្ត{{GENDER:|របស់អ្នក}}",
        "tooltip-pt-watchlist": "បញ្ជី​នៃ​ទំព័រ​ដែលអ្នកកំពុង​ត្រួតពិនិត្យ​រក​បន្លាស់ប្ដូរ",
-       "tooltip-pt-mycontris": "បញ្ជី​នៃ​ការរួមចំណែក​របស់​អ្នក",
+       "tooltip-pt-mycontris": "បញ្ជី​នៃ​ការរួមចំណែក​{{GENDER:|របស់​អ្នក}}",
        "tooltip-pt-login": "អ្នកត្រូវបានលើកទឹកចិត្តឱ្យកត់ឈ្មោះចូល។ ប៉ុន្តែនេះមិនមែនជាការបង្ខំទេ។",
        "tooltip-pt-logout": "កត់ឈ្មោះចេញ",
        "tooltip-pt-createaccount": "អ្នកត្រូវបានលើកទឹកចិត្តបង្កើតគណនីនិងកត់ឈ្មោះចូល។ ប៉ុន្តែនេះមិនមែនជាការបង្ខំទេ។",
        "tooltip-t-recentchangeslinked": "បន្លាស់ប្ដូរថ្មីៗ ក្នុងទំព័រដែលត្រូវបានភ្ជាប់មក ទំព័រនេះ",
        "tooltip-feed-rss": "បម្រែបម្រួល RSS ចំពោះទំព័រនេះ",
        "tooltip-feed-atom": "បម្រែបម្រួល Atom ចំពោះទំព័រនេះ",
-       "tooltip-t-contributions": "á\9e\94á\9e\84á\9f\92á\9e á\9e¶á\9e\89á\9e\94á\9e\89á\9f\92á\9e\87á\9e¸á\9e\9aá\9e½á\9e\98á\9e\85á\9f\86á\9e\8eá\9f\82á\9e\80á\9e\9aá\9e\94á\9e\9fá\9f\8bá\9e¢á\9f\92á\9e\93á\9e\80á\9e\94á\9f\92á\9e\9aá\9e¾á\9e\94á\9f\92á\9e\9aá\9e¶á\9e\9fá\9f\8bá\9e\93á\9f\81á\9f\87",
-       "tooltip-t-emailuser": "ផ្ញើអ៊ីមែលទៅកាន់អ្នកប្រើប្រាស់នេះ",
+       "tooltip-t-contributions": "á\9e\94á\9e\89á\9f\92á\9e\87á\9e¸á\9e\93á\9f\83á\9e\80á\9e¶á\9e\9aá\9e\9aá\9e½á\9e\98á\9e\85á\9f\86á\9e\8eá\9f\82á\9e\80á\9e\9aá\9e\94á\9e\9fá\9f\8b{{GENDER:$1|á\9e¢á\9f\92á\9e\93á\9e\80á\9e\94á\9f\92á\9e\9aá\9e¾á\9e\94á\9f\92á\9e\9aá\9e¶á\9e\9fá\9f\8bá\9e\93á\9f\81á\9f\87}}",
+       "tooltip-t-emailuser": "ផ្ញើអ៊ីមែលទៅកាន់{{GENDER:$1|អ្នកប្រើប្រាស់នេះ}}",
        "tooltip-t-info": "ព័ត៌មានបន្ថែមអំពីទំព័រនេះ",
        "tooltip-t-upload": "ឯកសារផ្ទុកឡើង",
        "tooltip-t-specialpages": "បញ្ជីទំព័រពិសេសៗទាំងមូល",
        "version-software-version": "កំណែ",
        "version-entrypoints-header-url": "URL",
        "redirect-submit": "ទៅ",
+       "redirect-lookup": "បើកមើល៖",
        "redirect-value": "តម្លៃ៖",
        "redirect-user": "អត្តលេខអ្នកប្រើប្រាស់",
        "redirect-page": "អត្តលេខទំព័រ",
index 74b9d41..18b1110 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|새 문서 목록]]도 보세요)",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "recentchanges-submit": "보기",
+       "rcfilters-legend-heading": "<strong>약어 목록:</strong>",
        "rcfilters-activefilters": "사용 중인 필터",
        "rcfilters-advancedfilters": "고급 필터",
+       "rcfilters-limit-shownum": "최근 $1개의 변경사항 표시",
+       "rcfilters-days-show-days": "$1{{PLURAL:$1|일}}",
+       "rcfilters-days-show-hours": "$1{{PLURAL:$1|시간}}",
        "rcfilters-quickfilters": "저장된 필터",
        "rcfilters-quickfilters-placeholder-title": "저장된 링크가 아직 없습니다",
        "rcfilters-quickfilters-placeholder-description": "필터 설정을 저장하고 나중에 다시 사용하려면 아래의 사용 중인 필터 영역의 북마크 아이콘을 클릭하십시오.",
        "rcfilters-invalid-filter": "유효하지 않은 필터",
        "rcfilters-empty-filter": "활성화된 필터가 없습니다. 모든 기여가 표시됩니다.",
        "rcfilters-filterlist-title": "필터",
-       "rcfilters-filterlist-whatsthis": "ì\9d´ê²\8c ë­\90죠?",
+       "rcfilters-filterlist-whatsthis": "ì\9d´ê²\83ë\93¤ì\9d´ ì\96´ë\96»ê²\8c ë\8f\99ì\9e\91í\95©ë\8b\88ê¹\8c?",
        "rcfilters-filterlist-feedbacklink": "새로운 (베타) 필터에 대한 의견을 주세요",
        "rcfilters-highlightbutton-title": "결과 강조",
        "rcfilters-highlightmenu-title": "색 선택",
        "rcfilters-noresults-conflict": "검색 조건이 충돌하기 때문에 결과를 찾을 수 없습니다",
        "rcfilters-state-message-subset": "필터의 결과가 다음의 범위가 더 넓은 {{PLURAL:$2|필터}}의 결과에 포함되기 때문에 이 필터는 효력이 없습니다 (구별을 위해 강조해 보십시오): $1",
        "rcfilters-state-message-fullcoverage": "한 그룹의 모든 필터를 선택하는 것은 아무 것도 선택하지 않는 것과 동일하므로 이 필터는 효력이 없습니다. 그룹은 다음을 포함합니다: $1",
-       "rcfilters-filtergroup-registration": "사용자 등록",
-       "rcfilters-filter-registered-label": "등록됨",
-       "rcfilters-filter-registered-description": "로그인된 편집자.",
-       "rcfilters-filter-unregistered-label": "등록 안 됨",
-       "rcfilters-filter-unregistered-description": "로그인하지 않은 편집자.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "이 필터는 등록된 사용자만을 {{PLURAL:$2|찾는}} 다음의 경험 {{PLURAL:$2|필터}}와 충돌합니다: $1",
        "rcfilters-filtergroup-authorship": "원작자 기여",
        "rcfilters-filter-editsbyself-label": "당신의 변경사항",
        "rcfilters-filter-editsbyself-description": "당신의 기여.",
        "rcfilters-filter-editsbyother-label": "다른 사용자의 변경사항",
        "rcfilters-filter-editsbyother-description": "당신을 제외한 모든 변경사항.",
-       "rcfilters-filtergroup-userExpLevel": "경험 수준 (등록된 사용자 전용)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "경험 필터는 등록된 사용자만을 검색하기 때문에 이 필터는 \"등록 안 됨\" 필터와 충돌합니다.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "\"등록 안 됨\" 필터는 하나 이상의 경험 필터와 충돌합니다. 경험 필터는 등록된 사용자만 찾습니다. 충돌되는 필터는 위의 활성화된 필터 목록에 표시됩니다.",
+       "rcfilters-filtergroup-userExpLevel": "사용자 등록 및 경험",
+       "rcfilters-filter-user-experience-level-registered-label": "등록됨",
+       "rcfilters-filter-user-experience-level-registered-description": "로그인된 편집자.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "등록 안 됨",
+       "rcfilters-filter-user-experience-level-unregistered-description": "로그인하지 않은 편집자.",
        "rcfilters-filter-user-experience-level-newcomer-label": "신규 사용자",
        "rcfilters-filter-user-experience-level-newcomer-description": "10회 미만의 편집 및 4일 미만의 활동.",
        "rcfilters-filter-user-experience-level-learner-label": "학습자",
        "rcfilters-hideminor-conflicts-typeofchange-global": "특정한 유형의 변경사항을 \"사소한 편집\"으로 지정할 수 없기 때문에 \"사소한 편집\" 필터는 하나 이상의 변경사항 유형 필터와 충돌합니다. 충돌되는 필터들은 위의 사용 중인 필터 영역에 표시됩니다.",
        "rcfilters-hideminor-conflicts-typeofchange": "특정한 종류의 변경사항은 \"사소한 편집\"으로 지정할 수 없으므로 이 필터는 다음 유형의 변경사항 필터와 충돌합니다: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "이 유형의 변경사항 필터는 \"사소한 편집\" 필터와 충돌합니다. 특정한 종류의 변경사항은 \"사소한 편집\"으로 지정할 수 없습니다.",
-       "rcfilters-filtergroup-lastRevision": "마지막 판",
-       "rcfilters-filter-lastrevision-label": "마지막 판",
-       "rcfilters-filter-lastrevision-description": "문서의 최근 변경사항입니다.",
-       "rcfilters-filter-previousrevision-label": "ì\9d´ì \84 í\8c\90",
-       "rcfilters-filter-previousrevision-description": "문서에 대한 최근 변경사항이 아닌 모든 변경사항입니다.",
+       "rcfilters-filtergroup-lastRevision": "최신판",
+       "rcfilters-filter-lastrevision-label": "최신판",
+       "rcfilters-filter-lastrevision-description": "문서의 최근 변경사항입니다.",
+       "rcfilters-filter-previousrevision-label": "ìµ\9cì\8b í\8c\90ì\9d´ ì\95\84ë\8b\98",
+       "rcfilters-filter-previousrevision-description": "\"최신판\"이 아닌 모든 변경사항입니다.",
        "rcfilters-filter-excluded": "제외됨",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:아님</strong> $1",
        "rcfilters-view-tags": "태그된 편집",
        "delete-warning-toobig": "이 문서에는 {{PLURAL:$1|편집 역사}}가 $1개 있습니다.\n편집 역사가 긴 문서를 삭제하면 {{SITENAME}} 데이터베이스 동작에 큰 영향을 줄 수 있습니다.\n주의해 주세요.",
        "deleteprotected": "이 문서가 보호되어 있기 때문에 삭제할 수 없습니다.",
        "deleting-backlinks-warning": "<strong>경고:</strong> 삭제하려는 문서가 [[Special:WhatLinksHere/{{FULLPAGENAME}}|다른 문서]]에 링크되어 있거나 끼워져 있습니다.",
+       "deleting-subpages-warning": "<strong>경고:</strong> 삭제하려는 문서에 [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|하나의 하위 문서|$1개의 하위 문서|51=50개 이상의 하위 문서}}]]가 있습니다.",
        "rollback": "편집 되돌리기",
        "rollbacklink": "되돌리기",
        "rollbacklinkcount": "{{PLURAL:$1|편집}} $1회 되돌리기",
        "fileduplicatesearch-noresults": "\"$1\"이라는 이름을 가진 파일이 없습니다.",
        "specialpages": "특수 문서 목록",
        "specialpages-note-top": "범례",
-       "specialpages-note": "* 일반 특수 문서입니다.\n* <span class=\"mw-specialpagerestricted\">제한된 특수 문서입니다.</span>",
        "specialpages-group-maintenance": "관리용 목록",
        "specialpages-group-other": "다른 특수 문서",
        "specialpages-group-login": "로그인 / 계정 만들기",
index 0c550ee..874f2b5 100644 (file)
        "rcfilters-legend-heading": "<strong>Lëscht vun Ofkierzungen:</strong>",
        "rcfilters-activefilters": "Aktiv Filteren",
        "rcfilters-advancedfilters": "Erweidert Filteren",
+       "rcfilters-limit-title": "Ännerunge fir ze weisen",
+       "rcfilters-limit-shownum": "Lescht $1 Ännerunge weisen",
+       "rcfilters-days-title": "Rezent Deeg",
+       "rcfilters-hours-title": "Rezent Stonnen",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|Dag|Deeg}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|Stonn|Stonnen}}",
        "rcfilters-quickfilters": "Gespäichert Filteren",
        "rcfilters-quickfilters-placeholder-title": "Nach keng Linke gespäichert",
        "rcfilters-quickfilters-placeholder-description": "Fir Är Filterastellungen z'änneren a méi spéit nees ze benotzen, klickt op d'Zeeche  fir Lieszeechen (bookmark) am Beräich vun den Aktive Filteren hei drënner.",
        "rcfilters-invalid-filter": "Net valabele Filter",
        "rcfilters-empty-filter": "Keen aktive Filter. All Kontributioune gi gewisen.",
        "rcfilters-filterlist-title": "Filteren",
-       "rcfilters-filterlist-whatsthis": "Wat ass dat?",
+       "rcfilters-filterlist-whatsthis": "Wéi geet dat?",
        "rcfilters-highlightbutton-title": "Resultater ervirhiewen",
        "rcfilters-highlightmenu-title": "Eng Faarf eraussichen",
        "rcfilters-filterlist-noresults": "Keng Filtere fonnt",
        "rcfilters-noresults-conflict": "Näischt fonnt well d'Sichcritère sech widderspriechen",
-       "rcfilters-filter-registered-label": "Ugemellt",
-       "rcfilters-filter-unregistered-label": "Net-ugemellt",
-       "rcfilters-filter-unregistered-description": "Auteuren déi net ageloggt sinn.",
        "rcfilters-filter-editsbyself-label": "Ännerunge vun Iech",
        "rcfilters-filter-editsbyself-description": "Är eegen Ännerungen.",
        "rcfilters-filter-editsbyother-label": "Ännerunge vun Aneren",
        "rcfilters-filter-editsbyother-description": "All Ännerunge ausser Ären eegenen.",
-       "rcfilters-filtergroup-userExpLevel": "Niveau vun der Erfahrung (just fir registréiert Benotzer)",
+       "rcfilters-filtergroup-userExpLevel": "Umeldung an Erfarung vu Benotzer",
+       "rcfilters-filter-user-experience-level-registered-label": "Ugemellt",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Net-ugemellt",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Auteuren déi net ageloggt sinn.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Neier",
        "rcfilters-filter-user-experience-level-newcomer-description": "Manner wéi 10 Ännerungen a manner wéi 4 Deeg Aktivitéit.",
        "rcfilters-filter-user-experience-level-learner-label": "Ufänger",
        "rcfilters-filter-logactions-label": "Protokolléiert Aktiounen",
        "rcfilters-filter-logactions-description": "Administrativ Aktiounen, Uleeë vu Benotzerkonten, Läsche vu Säiten, Eropgeluede Fichieren, ...",
        "rcfilters-hideminor-conflicts-typeofchange": "Verschidden Type vu Ännerunge kënnen net als \"kleng\" markéiert ginn, dofir ass dëse Filter a Konflikt mat dësem Typ vun Ännerungsfilteren: $1",
-       "rcfilters-filtergroup-lastRevision": "Lescht Versioun",
+       "rcfilters-filtergroup-lastRevision": "Lescht Versiounen",
        "rcfilters-filter-lastrevision-label": "Lescht Versioun",
-       "rcfilters-filter-lastrevision-description": "Déi lescht Ännerung op enger Säit",
-       "rcfilters-filter-previousrevision-label": "Méi fréi Versiounen",
+       "rcfilters-filter-lastrevision-description": "Nëmmen déi lescht Ännerung op enger Säit.",
+       "rcfilters-filter-previousrevision-label": "Net déi lescht Versioun",
        "rcfilters-filter-previousrevision-description": "All Ännerungen, déi net déi rezenst Ännerung vun enger Säit sinn.",
        "rcfilters-filter-excluded": "Ausgeschloss",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:net</strong> $1",
        "fileduplicatesearch-noresults": "Et gouf kee Fichier mam Numm \"$1\" fonnt.",
        "specialpages": "Spezialsäiten",
        "specialpages-note-top": "Erklärung",
-       "specialpages-note": "* Normal Spezialsäiten.\n* <span class=\"mw-specialpagerestricted\">Spezialsäite fir Benotzer mat méi Rechter.</span>",
        "specialpages-group-maintenance": "Maintenance-Rapporten",
        "specialpages-group-other": "Aner Spezialsäiten",
        "specialpages-group-login": "Aloggen / Benotzerkont uleeën",
index 4f5870a..2a89684 100644 (file)
        "tagline": "Van {{SITENAME}}",
        "help": "Hölp",
        "search": "Zeuke",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Köp die waere genegeerdj tiejes 't zeuke.\n# Verangeringe waere van kraf wen 'ne kop wuuertj geïndexeerd.\n# Doe kins oppernuuj indexere aafdwinge door 't oetveure van 'n laeg bewirking.\n# De syntaxis is es volg:\n#   * Alle teks vanaaf 't teike \"#\" tot 't ènj van de regel weurt gezeen es 'n opmerking;\n#   * Edere neet-laege regel is de precieze te negere kop, inclusief hooflittergebroek en dergelike.\nReferenties\nExtern links\nZuuch ouch\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Zeuk",
        "go": "OK",
        "searcharticle": "Gank",
        "databaseerror-query": "Zeukopdrach: $1",
        "databaseerror-function": "Functie: $1",
        "databaseerror-error": "Fout: $1",
+       "transaction-duration-limit-exceeded": "Dees transactie ($1) is aafgebraoke ómdet de limiet van $2 {{PLURAL:$2|sekónd}} is bereik. Hiemit waere groeate achterstenj bie 't replicere verkómme. \nWens se mieë items tegeliekertied bewirks, perbeer 't den in mieër klein opdrachte te doon.",
        "laggedslavemode": "Waarsjoewing: de pagina kin verauwerd zien.",
        "readonly": "Database geblokkeerd",
        "enterlockreason": "Gaef 'n rae veur de blokkering en wie lank 't dinkelik zal dore. De ingegaeve rae zal aan de gebroekers getuind waere.",
-       "readonlytext": "De database van {{SITENAME}} is geblokkeerd veur bewirkinge, waorsjienelik veur besjtandsongerhaud. Nao aafloup waert de functionaliteit weer hersteld.\n\nDe verantwoordelike systeembeheerder gaof de volgende rae op:\n<p>$1",
+       "readonlytext": "De database van is geblokkeerd veur bewirkinge, waorsjienelik veur besjtandsongerhaud. Nao aafloup waert de functionaliteit weer hersteld.\n\nDe verantwoordelike systeembeheerder gaof de volgende rae op: $1",
        "missing-article": "In de database is gein inhaud aangetróffe veur de pagina \"$1\" die d'r wel zou mote zien ($2).\n\nDit kan veurkómme as doe 'n auwer verwiezing nao 't versjil tösje twee versies van ein pagina volgs of 'n versie opvreugs die is eweggesjaf.\n\nEs dit neet 't geval is, höbs doe mesjins 'n fout in de software gevónje.\nMaak hievan melding bie 'ne [[Special:ListUsers/sysop|systeembeheerder]] van {{SITENAME}} en vermeld daobie de URL van dees pagina.",
        "missingarticle-rev": "(versienummer: $1)",
        "missingarticle-diff": "(Wijziging: $1, $2)",
        "readonly_lag": "De database is autematis vergrendeld terwiel de ongergesjikte databaseservers synchronisere mit de huidserver.",
+       "nonwrite-api-promise-error": "De HTTP-header 'Promise-Non-Write-API-Action' waas mitgesjik, mer 't verzeuk góng euver 'n API-sjriefverzeuk.",
        "internalerror": "Interne fout",
        "internalerror_info": "Interne fout: $1",
        "internalerror-fatal-exception": "Fatale fout van type \"$1\"",
        "cannotdelete": "Kós de pagina of 't besjtand \"$1\" neet ewegsjaffe.\nMesjiens haet emes angers det al gedaon.",
        "cannotdelete-title": "Pagina \"$1\" kin neet gewösj waere",
        "delete-hook-aborted": "'t Wösje is aafgebroke door 'ne 'hook'.\nD'r is gein toelichting besjikbaar.",
+       "no-null-revision": "Kós gein laeg nuuj versie make veure pagina \"$1\"",
        "badtitle": "Óngeljige paginatitel",
        "badtitletext": "De opgevraogde pagina is neet besjikbaar of laeg.",
+       "title-invalid-empty": "De opgevraogdje paginanaam is laeg of bevatj de naam van 'n naamruumdje.",
+       "title-invalid-utf8": "De opgevraogdje paginanaam bevatj 'nen óngeljige UFT-8-rits.",
+       "title-invalid-interwiki": "De aangevraogdje paginanaam bevatj 'nen interwiki dae neet kan waere gebroek in paginaname.",
+       "title-invalid-talk-namespace": "De aangevraogdje paginanaam verwies nao 'n euverlègkpagina die neet kan bestaon.",
+       "title-invalid-characters": "De aangevraogdje paginanaam bevatj óngeljige teikes: \"$1\".",
+       "title-invalid-relative": "De paginanaam bevatj e rilletief paag. Rilletief paginaname (\"./\", \"../\") zeen óngeljig ómdet ze dök ónbereikbaar zeen es de browser van 'ne gebroeker dees toupas.",
+       "title-invalid-magic-tilde": "De aangevraogdje paginanaam bevatj 'n óngeljige volgrie van magische tildes (<nowiki>~~~~</nowiki>).",
+       "title-invalid-too-long": "De aangevraogdje paginanaam is te lank. Dees moog neet langer zeen es $1 {{PLURAL:$1|byte|bytes}} in UTF-8-codering.",
+       "title-invalid-leading-colon": "De aangevraogdje paginanaam bevatj 'n óngeljige dóbbel puntj ane begin.",
        "perfcached": "De gegaeves koume oet 'n cache en zeen mäögelik neet actueel. 't Geuf {{PLURAL:$1|maximaal ei rizzeltaot|maximaal $1 rizzeltaote}} inne cache.",
        "perfcachedts": "De getuunde gegaeves komme oet 'n cache en zeen veur 't letst biejgewèrk op $1. Maximaal guuef 't {{PLURAL:$4|ei rizzeltaot|$4 rizzeltaote}} inne cache.",
        "querypage-no-updates": "Deze pagina kin op 't memènt neet biegewirk waere. Deze gegaeves waere neet verfrisj.",
        "viewsource-title": "Bekiek brón van $1",
        "actionthrottled": "Hanjeling taengegehauwe",
        "actionthrottledtext": "Es maotregel taege spam is 't aantal keer per tiedseinheid dets te dees hanjeling kèns verrichte beperk. De höbs de limiet euversjreje. Perbeer 't euver 'n aantal minute obbenuuj.",
-       "protectedpagetext": "Dees pagina is beveilig. Bewirke is neet meugelik.",
+       "protectedpagetext": "Dees pagina is beveilig. Bewirke of anger hanjelinge is neet meugelik.",
        "viewsourcetext": "De kans de brónteks van dees pagina bekieke en kopiëre.",
-       "viewyourtext": "Doe kans \"dien bewèrkinge\" ane brónteks van dees pagina bekieke en euverkopiëre:",
-       "protectedinterface": "Deze pagina bevat teks veur berichte van de software en is beveilig om misbroek te veurkomme.",
+       "viewyourtext": "Doe kans <strong>dien bewèrkinge</strong> ane brónteks van dees pagina bekieke en euverkopiëre.",
+       "protectedinterface": "Dees pagina bevat teks veur berichte van de software op deze wiki en is beveilig om misbroek te veurkomme. Gebroek [https://translatewiki.net/ translatewiki.net], 't euverzèttingsprojek veur MediaWiki, veur euverzèttinge veur alle wiki's tou te veuge of te verangere.",
        "editinginterface": "'''Waarsjoewing:''' Doe bewirks 'n pagina die gebroek waert door de software. Bewirkinge op dees pagina beïnvlode de gebroekersinterface van jederein. Euverwaeg veur euverzèttinge [https://translatewiki.net/wiki/Main_Page?setlang=li translatewiki.net] te gebroeke, 't euverzèttingssprojek veur MediaWiki.",
-       "cascadeprotected": "Deze pagina kin neet bewirk waere, omdet zie is opgenome in de volgende {{PLURAL:$1|pagina|pagina's}} die beveilig {{PLURAL:$1|is|zeen}} mèt de kaskaad-optie:\n$2",
+       "translateinterface": "Veur euverzèttinge veur alle wiki's tou te veuge of te verangere kans se gebroek make van [https://translatewiki.net/ translatewiki.net], 't euverzèttingsprojek veur MediaWiki.",
+       "cascadeprotected": "Dees pagina kin neet bewirk waere, omdet zie is opgenome in de volgende {{PLURAL:$1|pagina|pagina's}} die beveilig {{PLURAL:$1|is|zeen}} mèt de kaskaad-optie:\n$2",
        "namespaceprotected": "Doe höbs gein rechte om pagina's in de naamruumde '''$1''' te bewirke.",
        "customcssprotected": "De kèns dees CSS-pagina neet bewirke ómdet die persuunlike insjtèllinge van 'ne angere gebroeker bevat.",
        "customjsprotected": "De kèns dees javapagina neet bewirke ómdet die persuunlike insjtèllinge van 'ne angere gebroeker bevat.",
+       "mycustomcssprotected": "Doe höbs gein rechte veur dees CSS-pagina te bewirke.",
+       "mycustomjsprotected": "Doe höbs gein rechte veur dees JavaScript-pagina te bewirke.",
+       "myprivateinfoprotected": "Doe höbs gein rechte veur dien privaatgegaeves te bewirke.",
+       "mypreferencesprotected": "Doe höbs gein rechte veur dien veurkäöre bie te stèlle.",
        "ns-specialprotected": "Pagina's in de naamruumde \"{{ns:special}}\" kinne neet bewirk waere.",
        "titleprotected": "'t aanmake van deze pagina is beveilig door [[User:$1|$1]].\nDe gegaeve ree is <em>$2</em>.",
-       "filereadonlyerror": "'t Waar neet meugelik óm 't bestandj \"$1\" aan te passe went de bestandjsrepositoir \"$2\" steit noe op allein-laeze.\n\nd'n Opgegaeve raej vanne sloetendje admin waar \"''$3''\".",
+       "filereadonlyerror": "'t Waar neet meugelik óm 't bestandj \"$1\" aan te passe went de bestandjsrepositoir \"$2\" steit noe op allein-laeze.\n\nd'n Opgegaeve raej vanne sloetendje admin waar: \"''$3''\".",
        "invalidtitle-knownnamespace": "Óngèljige titel mit naamruumdje \"$2\" en teks \"$3\"",
        "invalidtitle-unknownnamespace": "Óngèljige titel mit ónbekèndj naamruumdenómmer $1 en teks \"$2\"",
        "exception-nologin": "Neet aangemèld",
-       "exception-nologin-text": "Óm dees pagina te betrachte of dees hanjeling te kinne doon mós se aangemèldj zeen bie deze wiki.",
+       "exception-nologin-text": "Melj dich aan veur dees pagina te betrachte of dees hanjeling oet te veure.",
+       "exception-nologin-text-manual": "Doe mós $1 veur dees pagina te kónne betrachte of de hanjeling oet te veure.",
        "virus-badscanner": "Slechte configuratie: onbekenge virusscanner: ''$1''",
        "virus-scanfailed": "scanne is mislukt (code $1)",
        "virus-unknownscanner": "onbekeng antivirus:",
-       "logouttext": "'''De bis noe aafgemeld.'''\n\nDe kèns {{SITENAME}} noe anoniem (mit vermeljing van IP-adres) gebroeke, of <span class='plainlinks'>[$1 opnuuj aanmelde]</span> ónger dezelfde of 'ne angere naam.\nMäögelik waert nog 'n deil pagina's getuind esofs te nog aangemeld bis pès te de cache van diene browser laeg maaks.",
+       "logouttext": "<strong>De bis noe aafgemeld.</strong>\n\nMäögelik waert nog 'n deil pagina's getuind esofs te nog aangemeld bis pès te de cache van diene browser laeg maaks.",
+       "cannotlogoutnow-title": "Doe kans dich noe neet aafmelje",
+       "cannotlogoutnow-text": "Aafmelje is neet meugelik bie 't gebroek van $1.",
        "welcomeuser": "Wilkóm, $1!",
+       "welcomecreation-msg": "Diene gebroeker is aangemaak.\nWen gewunsj kans se dien [[Special:Preferences|veurkäöre]] veur {{SITENAME}} biestèlle.",
        "yourname": "Diene gebroekersnaam",
        "userlogin-yourname": "Gebroekersnaam",
        "userlogin-yourname-ph": "Veur diene gebroekersnaam in",
+       "createacct-another-username-ph": "Veur de gebroekersnaam in",
        "yourpassword": "Die wachwaord",
        "userlogin-yourpassword": "Wachwaord",
        "userlogin-yourpassword-ph": "Veur dien wachwaord in",
        "createacct-yourpasswordagain": "Bevestig wachwaord",
        "createacct-yourpasswordagain-ph": "Veur 't wachwaord oppernuuj in",
        "userlogin-remembermypassword": "Haaj mich aangemeldj",
+       "userlogin-signwithsecure": "Gebroek beveiligdje verbinjing",
+       "cannotlogin-title": "Kan zich neet aanmelje",
+       "cannotlogin-text": "Aanmelje is neet meugelik.",
+       "cannotloginnow-title": "Kan zich noe neet aanmelje",
+       "cannotloginnow-text": "Aanmelje is neet meugelik bie 't gebroek van $1.",
+       "cannotcreateaccount-title": "Kan gein gebroekers aanmake",
+       "cannotcreateaccount-text": "'t Drek aanmake van 'ne gebroeker is oetgezatj op deze wiki.",
        "yourdomainname": "Die domein",
+       "password-change-forbidden": "Doe kans die wachwaord neet verangere op deze wiki.",
        "externaldberror": "d'r Is 'n fout opgetraoje biej 't aanmelje biej de database of doe höbs gén toesjtömming diene externe gebroeker biej te wèrke.",
        "login": "Aanmèlde",
+       "login-security": "Kóntroleer dien identiteit",
        "nav-login-createaccount": "Aanmelje / registrere",
        "logout": "Aafmelde",
        "userlogout": "Aafmelde",
        "createaccount": "Nuuj gebroekersprofiel aanmake.",
        "userlogin-resetpassword-link": "Wachwaord kwiet?",
        "userlogin-helplink2": "Hölp mit 't aanmelje",
+       "userlogin-loggedin": "Doe bös al aangemeldj es {{GENDER:$1|$1}}.\nBroek 't formeleer hie-ónger veur dich es 'nen angere gebroek aan te melje.",
+       "userlogin-reauth": "Doe mós dich oppernuuj aanmelje óm te bevestige det se {{GENDER:$1|$1}} bös.",
+       "userlogin-createanother": "Maak 'nen angere gebroeker",
+       "createacct-emailrequired": "E-mailadres",
        "createacct-emailoptional": "E-mailadres (optioneel)",
        "createacct-email-ph": "Veur dien e-mailadres in",
-       "createaccountmail": "via de e-mail",
+       "createacct-another-email-ph": "Veur e-mailadres in",
+       "createaccountmail": "Gebroek e tiejelik willekäörig wachwaord en sjik 't nao 't opgegaove e-mailadres",
+       "createaccountmail-help": "Kan waere gebroek veur 't aanmake van 'ne gebroeker veur emes anges zónger 't wachwaord te lieëre.",
+       "createacct-realname": "Echte naam (optioneel)",
+       "createacct-reason": "Raeje",
+       "createacct-reason-ph": "Woveur se 'nen angere gebroeker aanmaaks",
+       "createacct-reason-help": "Getoeandj berich in 't logbestandj van aangemaakdje gebroekers",
        "createacct-submit": "Maak diene gebroeker aan",
+       "createacct-another-submit": "Maak 'ne gebroeker aan",
+       "createacct-continue-submit": "Gank door mit 't make van 'ne gebroeker",
+       "createacct-another-continue-submit": "Gank door mit 't make van 'ne gebroeker",
        "createacct-benefit-heading": "{{SITENAME}} wuuertj gemaak door luuj wie dich.",
        "createacct-benefit-body1": "{{PLURAL:$1|bewèrking|bewèrkinge}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pazjena|pazjena's}}",
        "createacct-benefit-body3": "recènte {{PLURAL:$1|biedrager|biedragers}}",
        "badretype": "De ingeveurde wachwäörd versjille vanein.",
+       "usernameinprogress": "'t Aanmake van 'ne gebroeker mit daen naam is al ane geng.\nWach efkes estebleef.",
        "userexists": "De gebroekersnaam dae se höbs ingeveurd weurt al gebroek.\n\nKees estebleef 'ne angere naam.",
        "loginerror": "Inlogfout",
+       "createacct-error": "Fout bie 't aanmake van gebroeker",
        "createaccounterror": "Kós gebroeker neet aanmake: $1",
        "nocookiesnew": "De gebroeker is aangemaak mèr neet aangemeld. {{SITENAME}} gebroek cookies veur 't aanmelje van gebroekers. Sjakel die a.u.b. in en meld dao nao aan mèt diene nuje gebroekersnaam en wachwaord.",
        "nocookieslogin": "{{SITENAME}} gebroek cookies veur 't aanmelje van gebroekers. Doe accepteers gén cookies. Sjakel deze optie a.u.b. in en perbeer 't oppernuuj.",
        "nocookiesfornew": "De gebroeker is neet aangemaak ómdet de bron neet bevestig kos waere.\nZörg deveur dats te cookies höbs ingesjakeld, herlaaj dees pagina en perbeer 't obbenuuts.",
+       "createacct-loginerror": "De gebroeker is succesvol aangemaak gewaore, meh doe kós dich neet autematis aanmelje. Gank nao [[Special:UserLogin|handjmaesig aanmelje]].",
        "noname": "De mos 'n gebroekersnaam opgaeve.",
-       "loginsuccesstitle": "Aanmèlde geluk.",
+       "loginsuccesstitle": "Aangemeldj",
        "loginsuccess": "Doe bis noe es \"$1\" aangemeld bie {{SITENAME}}.",
-       "nosuchuser": "D'r besjteit geine gebroeker mit de naam \"$1\".\nDie seen huidlettegevullig\nControleer dien spelling, of gebroek ongersjtaond formuleer om 'n [[Special:CreateAccount|nuuj]] gebroekersprofiel aan te make.",
+       "nosuchuser": "D'r besjteit geine gebroeker mit de naam \"$1\".\nGebroekersname zeen huidlettergeveulig\nControleer dien spelling, [[Special:CreateAccount|maak 'ne nuuje gebroeker aan]].",
        "nosuchusershort": "De gebroeker \"$1\" besjteit neet. Konterleer de sjriefwieze.",
        "nouserspecified": "Doe deens 'ne gebroekersnaam op te gaeve.",
        "login-userblocked": "Deze gebroeker steit geblokkeerd. Aanmèlje is neet toegestange.",
        "wrongpassword": "'t Ingegaeve wachwaord is neet zjus. Perbeer 't obbenuujts.",
        "wrongpasswordempty": "'t Ingegaeve wachwoord waor laeg. Perbeer 't obbenuujts.",
        "passwordtooshort": "Dien wachwaord is te kort. 't Mót minstes oet {{PLURAL:$1|1 teike|$1 teikes}} besjtaon.",
+       "passwordtoolong": "Wachwäörd kónne neet lenger zeen es {{PLURAL:$1|ei teike| $1 teikes}}.",
+       "passwordtoopopular": "Dök-gekaoze wachwäörd kan me neet gebroeke. Kees 'n eigener wachwaord.",
        "password-name-match": "Die wachwaord mót anges zeen es diene gebroekersnaam.",
        "password-login-forbidden": "'t Gebroek van deze gebroekersnaam mit dit wachwoord is neet toegesjtange.",
-       "mailmypassword": "Sjik mich 'n nuuj wachwaord",
+       "mailmypassword": "Mail mich e nuuj wachwaord",
        "passwordremindertitle": "Nuuj tiedelik wachwaord van {{SITENAME}}",
        "passwordremindertext": "Emes (waorsjienlik dich zelf) haet vanaaf IP-adres $1 'n nuuj wachwoord veur {{SITENAME}} ($4) verzoch. 't Nuuj wachwoord veur gebroeker \"$2\" is \"$3\". Es dat dien bedoeling waor, mèl diech daan noe aan en kees 'n nuuj wachwoord. 't Tiedelik wachwoord verluip euver {{PLURAL:$5|$5 daag|$5 daag}}.\n\nEs emes anders dit verzeuk heet gedoon, of wens te diech dien wachwoord weer herinners en 't neet mie wèls wiezige, negeer dan dit berich en blief dien aud wachwoord gebroeke.",
        "noemail": "D'r is gein geregistreerd e-mailadres veur \"$1\".",
        "noemailcreate": "Doe mós e geljig e-mailadres ópgaeve.",
        "passwordsent": "D'r is 'n nuui wachwaord verzonde nao 't e-mailadres dat geregistreerd sjtit veur \"$1\".\nGelieve na ontvangst opnieuw aan te melden.",
        "blocked-mailpassword": "Dien IP-adres is geblokkeerd veur 't make van verangeringe. Om misbroek te veurkomme is 't neet meugelik om 'n nuuj wachwaord aan te vraoge.",
-       "eauthentsent": "Dao is 'ne bevèstigingse-mail nao 't genomineerd e-mailadres gesjik.\nIedat anger mail nao dat account versjik kan weure, mós te de insjtructies in daen e-mail volge,\nóm te bevèstige dat dit wirkelik dien account is.",
+       "eauthentsent": "Dao is 'ne bevèstigingse-mail nao 't opgegaeve e-mailadres gesjik.\nIedat anger mail nao dat account versjik kan weure, mós te de insjtructies in daen e-mail volge,\nóm te bevèstige dat dit wirkelik dien account is.",
        "throttled-mailpassword": "'n Wachwaordherinnering wörd gedurende de letste {{PLURAL:$1|1 oer|$1 oer}} verzönje. Om misbroek te veurkomme, wörd d'r sjlechs éin herinnering per {{PLURAL:$1|oer|$1 oer}} verzönje.",
        "mailerror": "Fout bie 't versjture van mail: $1",
-       "acct_creation_throttle_hit": "Bezeukers van deze wiki mit 'tzelfde IP-adres es doe höbbe de aafgeloupe daag {{PLURAL:$1|al 1 gebroeker|al $1 gebroekers}} geregistreerd, wat 't maximale aantal in deze periode is.\nDaorum kens doe vanaaf dit IP-adres op dit moment gein nuje gebroeker registrere.",
+       "acct_creation_throttle_hit": "Bezeukers van deze wiki mit 'tzelfde IP-adres es doe höbbe de aafgeloupe $2 daag {{PLURAL:$1|al 1 gebroeker|al $1 gebroekers}} geregistreerd, wat 't maximaal aantal in dees periode is.\nDaorum kens doe vanaaf dit IP-adres op dit moment gein nuuj gebroekers registrere.",
        "emailauthenticated": "Dien e-mailadres is op $2 um $3 bevestig.",
-       "emailnotauthenticated": "Dien e-mailadres is nog neet geauthentiseerd. De zals gein\ne-mail óntvange veur alle volgende toepassinge.",
+       "emailnotauthenticated": "Dien e-mailadres is nog neet geauthentiseerd. \nDe zals gein e-mail óntvange veur alle volgende toepassinge.",
        "noemailprefs": "Gaef 'n e-mailadres op om deze functies te gebroeke.",
        "emailconfirmlink": "Bevèstig dien e-mailadres",
        "invalidemailaddress": "'t E-mailadres is neet geaccepteerd omdet 't 'n ongeldige opmaak haet. Gaef a.u.b. 'n geldig e-mailadres op of laot 't veld laeg.",
        "cannotchangeemail": "E-mailadresse kinne neet waere verangerdj óp deze wiki.",
        "emaildisabled": "Dees site kin gein mails versjikke.",
        "accountcreated": "Gebroeker aangemaak",
-       "accountcreatedtext": "De gebroeker $1 is aangemaak.",
+       "accountcreatedtext": "De gebroeker [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|euverlègk]]) is aangemaak gewaore.",
        "createaccount-title": "Gebroekers aanmake veur {{SITENAME}}",
        "createaccount-text": "Emes genaamp \"$2\" haet 'ne gebroeker veur $2 aangemaak op {{SITENAME}}\n($4) mit 't wachwaord \"$3\". Meld dich aan en wiezig dien wachwaord.\n\nNegeer dit berich as deze gebroeker zonger dien medewete is aangemaak.",
-       "login-throttled": "Doe höbs te huifig geperbeerd aan te melje mèt 'n verkierd wachwaord.\nDoe mós effe wachte ierdets te 't obbenuuts kens perbere.",
+       "login-throttled": "Doe höbs te huifig geperbeerd aan te melje mèt 'n verkierd wachwaord.\nDoe mós effe $1 wachte ierdets te 't obbenuuts kens perbere.",
        "login-abort-generic": "Doe bös neet aangemèldj - Aafgebraoke",
+       "login-migrated-generic": "Diene gebroeker is van naam verangerdj en diene gebroekersnaam besteit nimmieë op deze wiki.",
        "loginlanguagelabel": "Taol: $1",
        "suspicious-userlogout": "Dien verzeuk óm aaf te melde is genegeerd, ómdet 't liek esof 't verzeuk is versjik door 'ne browser of cacheproxy dae kepot is.",
+       "createacct-another-realname-tip": "Echte naam is optineel.\nEs se dezen opgeufs, weurt deze naam gebroek t'r erkènning van die werk.",
        "pt-login": "Aanmèlde",
        "pt-login-button": "Aanmèlde",
+       "pt-login-continue-button": "Gank door mit aanmelje",
        "pt-createaccount": "Maak 'ne gebroeker aan",
        "pt-userlogout": "Aafmelde",
        "php-mail-error-unknown": "Dao haet ziech 'n ónbekénde fout veurgedaon in de mail()-functie van PHP",
        "user-mail-no-addy": "Perbeerdjes 'ne mail te sjikke zónger 'n adres",
+       "user-mail-no-body": "Verzóch 'nen e-mail te sjikke zónger inhaad of mit hieël min inhaad.",
        "changepassword": "Wachwaord verangere",
-       "resetpass_announce": "Doe bös aangemeld mèt 'ne tiejdelikke code dae per e-mail is toegezönje. Veur 'n nuuj wachwaord in om 't aanmelje te voltooie:",
+       "resetpass_announce": "Veur e nuuj wachwaord in veur 't aanmelje te voltoeaje.",
        "resetpass_header": "Wachwaord obbenuuts insjtèlle",
        "oldpassword": "Hujig wachwaord",
        "newpassword": "Nuuj wachwaord",
        "retypenew": "Veur 't nuuj wachwaord nogins in",
        "resetpass_submit": "Wachwaord instelle en aanmelje",
-       "changepassword-success": "Dien wachwaord is verangerd. Bezig mèt aanmelje...",
+       "changepassword-success": "Dien wachwaord is verangerd!",
+       "changepassword-throttled": "Doe höbs te huifig geperbeerd aan te melje.\nDoe mós effe $1 wachte ierdets te 't obbenuuts kens perbere.",
+       "botpasswords": "Botwachwäörd",
+       "botpasswords-summary": "<em>Botwachwäörd</em> zörge veur tougank tot de API via 'ne gebroeker zónger gebroek te make van aanmeljgegaeves van daen gebroeker. De gebroekersrechte die besjikbaar zint kónne aafwieke wen me is aangemeldj mit e botwachwaord.\n\nWen se neet wèts waat hie de gevölge van zeen den is 't henjiger dit neet te doon. Nemes huuert dich te vraoge e botwachwaord aan te make en dem aan hem door te gaeve.",
+       "botpasswords-disabled": "Botwachwäörd zint oetgezatj.",
+       "botpasswords-no-central-id": "Veur botwachwäörd te gebroeke mós se aangemeldj zeen mit 'ne gecentraliseerdje gebroeker.",
+       "botpasswords-existing": "Bestaonde botwachwäörd",
+       "botpasswords-createnew": "Maak e nuuj botwachwaord aan",
+       "botpasswords-editexisting": "Bewirk e bestaond botwachwaord",
+       "botpasswords-label-appid": "Botnaam:",
+       "botpasswords-label-create": "Maak aan",
+       "botpasswords-label-update": "Wirk bie",
+       "botpasswords-label-cancel": "Braek aaf",
+       "botpasswords-label-delete": "Sjaf eweg",
+       "botpasswords-label-resetpassword": "Stèl wachwaord oppernuuj in",
+       "botpasswords-label-grants": "Van toupassing zeende rechte:",
+       "botpasswords-help-grants": "Toestumminge gaeve tougank toet gebroekersrechte die se al höbs. 't Gaeve van 'n toustumming op dees plaats geuf dich geinen tougank toet gebroekersrechte die se anges neet zóds höbbe. Zuuch 't [[Special:ListGrants|euverzich van toestumminge]] veur mieë info.",
+       "botpasswords-label-grants-column": "Tougeweze",
+       "botpasswords-bad-appid": "De botnaam \"$1\" is neet geljig.",
+       "botpasswords-insert-failed": "'t Touveuge vanne botnaam \"$1\" is mislök. Is dae mesjiens al tougeveug?",
+       "botpasswords-update-failed": "'t Biewirke vanne botnaam \"$1\" is mislök. Is dae mesjiens eweggesjaf?",
+       "botpasswords-created-title": "Botwachwaord aangemaak",
+       "botpasswords-created-body": "'t Botwachwaord veure botnaam \"$1\" van gebroeker \"$2\" is gemaak.",
+       "botpasswords-updated-title": "Botwachwaord biegewirk",
+       "botpasswords-updated-body": "'t Botwachwaord veur de botnaam \"$1\" van gebroeker \"$2\" is biegewirk.",
+       "botpasswords-deleted-title": "Botwachwaord eweggesjaf",
+       "botpasswords-deleted-body": "'t Botwachwaord veure botnaam \"$1\" vanne gebroeker \"$2\" is eweggesjaf.",
+       "botpasswords-newpassword": "'t Nuuj wachwaord veur aan te melje mit <strong>$1</strong> is <strong>$2</strong>. <em>Bewaar dit goed voor toekomstig gebruik.</em> <br> (Veur aaj bots die vereisje det de aameljnaam 'tzelfde is es d'n eventuele gebroekersnaam, kan ouch <strong>$3</strong> es gebroekersnaam en <strong>$4</strong> es wachwaord waere gebroek.)",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider is neet besjikbaar.",
+       "botpasswords-restriction-failed": "Botwachwaordbepirkinge verkómme aanmelje.",
+       "botpasswords-invalid-name": "De gebroekersnaam bevatj neet 't sjeijingsteike van 't botwachwaord (\"$1\").",
+       "botpasswords-not-exist": "Gebroekers \"$1\" haet gei botwachwaord genaamp \"$2\".",
        "resetpass_forbidden": "Wachwäörd kónne neet verangerd waere",
+       "resetpass_forbidden-reason": "Wachwäörd kónne neet verangerd waere: $1",
        "resetpass-no-info": "Doe moos aangemeld zien ierdets doe dees pagina gebroeke kens.",
        "resetpass-submit-loggedin": "Wachwaord wiezige",
        "resetpass-submit-cancel": "Aafbraeke",
        "resetpass-wrong-oldpass": "'t Hujig of tiedelik wachwaord is ongeljig.\nMeugelik höbs doe dien wachwaord al gewiezig of 'n nuuj tiedelik wachwaord aangevraog.",
+       "resetpass-recycled": "Veranger die wachwaord nao get anges es 't hujig wachwaord.",
+       "resetpass-temp-emailed": "Doe bös aangemeldj mit 'n tiejelike code die se per e-mail höbs gekrege.\nVeur 't aanmelje aaf te make mós se hie e nuuj wachwaord instèlle:",
        "resetpass-temp-password": "Tiedelik wachwaord:",
+       "resetpass-abort-generic": "De wachwaordverangering is aafgebraoke door 'n oetbreijing.",
+       "resetpass-expired": "Die wachwaord is verloupe. Stèl e nuuj wachwaord in veur dich aan te melje.",
+       "resetpass-expired-soft": "Die wachwaord is verloupe en mót oppernuuj waeren ingestèldj.\nKees noe e nuuj wachwaord of klik op \"{{int:authprovider-resetpass-skip-label}}\" veur dit spaejer te doon.",
+       "resetpass-validity-soft": "Die wachwaord is neet geljig: $1\n\nKees noe e nuuj wachwaord of klik op \"{{int:authprovider-resetpass-skip-label}}\" veur dit spaejer oppernuuj in te stèlle.",
        "passwordreset": "Wachwaord obbenuuts insjtèlle",
+       "passwordreset-text-one": "Völ dit formeleer in veur die wachwaord oppernuuj in te stèlle (doe kriegs e berich via de e-mail).",
+       "passwordreset-text-many": "{{PLURAL:$1|Völ ei van de gegaevesveljer in veur per e-mail e tiedelijk wachwaord te kriege.}}",
        "passwordreset-disabled": "'t Is hie neet meugelik óm die wachwaord óbbenuits in te sjtelle.",
+       "passwordreset-emaildisabled": "E-mailmeugelikheje staon oet op deze wiki.",
        "passwordreset-username": "Gebroekersnaam:",
        "passwordreset-domain": "Domein:",
        "passwordreset-email": "E-mailadres:",
        "passwordreset-emailtitle": "Gebroekersgegaeves óp {{SITENAME}}",
-       "passwordreset-emailtext-ip": "Emes, wersjienlik doe, vanaaf 't IP-adres $1, haet dien gebroekersgegaeves veur {{SITENAME}} ($4) ópgevraog.\nDe volgende {{PLURAL:$3|gebroeker is|gebroekers zint}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tiedelik wachwaord vervilt|Dees tiedelike wachweurd vervallen}} euver {{PLURAL:$5|einen daag|$5 daag}}.\nMel dich aan en veranger 't wachwaord noe. Es se dit verzeuk neet zelf hes gedaon, of es se 't oorspronkelik wachwaord nog kins en 't neet anges wils, laot dit berich den en blief dien aad wachwaord gebroeke.",
-       "passwordreset-emailtext-user": "Gebroeker $1 op de site {{SITENAME}} haet dien gebroekersgegaeves veur {{SITENAME}} ($4) ópgevraog.\nDe volgende {{PLURAL:$3|gebroeker is|gebroekers zint}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tiedelik wachwaord vervilt|Dees tiedelike wachweurd vervallen}} euver {{PLURAL:$5|einen daag|$5 daag}}.\nMel dich aan en veranger 't wachwaord noe. Es se dit verzeuk neet zelf hes gedaon, of es se 't oorspronkelik wachwaord nog kins en 't neet anges wils, laot dit berich den en blief dien aad wachwaord gebroeke.",
+       "passwordreset-emailtext-ip": "Emes, wersjienlik doe, vanaaf 't IP-adres $1, haet dien gebroekersgegaeves veur {{SITENAME}} ($4) ópgevraog veur 't wachwaord oppernuuj in te stèlle.\nDe volgende {{PLURAL:$3|gebroeker is|gebroekers zint}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tiedelik wachwaord vervilt|Dees tiedelike wachweurd vervallen}} euver {{PLURAL:$5|einen daag|$5 daag}}.\nMel dich aan en veranger 't wachwaord noe. Es se dit verzeuk neet zelf hes gedaon, of es se 't oorspronkelik wachwaord nog kins en 't neet anges wils, laot dit berich den en blief dien aad wachwaord gebroeke.",
+       "passwordreset-emailtext-user": "Gebroeker $1 op de site {{SITENAME}} haet dien gebroekersgegaeves veur {{SITENAME}} ($4) ópgevraog veur die wachwaord oppernuuj in te stèlle.\nDe volgende {{PLURAL:$3|gebroeker is|gebroekers zint}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tiedelik wachwaord vervilt|Dees tiedelike wachweurd vervallen}} euver {{PLURAL:$5|einen daag|$5 daag}}.\nMel dich aan en veranger 't wachwaord noe. Es se dit verzeuk neet zelf hes gedaon, of es se 't oorspronkelik wachwaord nog kins en 't neet anges wils, laot dit berich den en blief dien aad wachwaord gebroeke.",
        "passwordreset-emailelement": "Gebroekersnaam: \n$1\n\nTiedelik wachwaord: \n$2",
-       "passwordreset-emailsentemail": "d'r Is per mail 'n herinnering versjik.",
-       "changeemail": "Veranger dien e-mailadres",
-       "changeemail-header": "Veranger 't e-mailadres van miene gebroekersnaam",
+       "passwordreset-emailsentemail": "Es dit e-mailadres aan diene gebroeker is gekoppeldj, den weurt 'nen e-mail gesjik veur die wachwaord oppernuuj in te stèlle.",
+       "passwordreset-emailsentusername": "Wen 'n e-mailadres is geregistreerd veur daen gebroekersnaam, den weurt 'nen e-mail gesjik veur die wachwaord oppernuuj in te stèlle.",
+       "passwordreset-nocaller": "'nen Aanroper mót waeren opgegaove",
+       "passwordreset-nosuchcaller": "Aanroper besteit neet: $1",
+       "passwordreset-ignored": "'t Oppernuuj instèlle van 't wachwaord is neet aafgehanjeldj gewaore.\nMesjiens steit geine provider geconfigureerdj?",
+       "passwordreset-invalidemail": "Óngeljig e-mailadres",
+       "passwordreset-nodata": "Zowaal geine gebroekersnaam es 'n e-mailadres is opgegaove gewaore",
+       "changeemail": "Veranger of haol dien e-mailadres eweg",
+       "changeemail-header": "Völ dit formeleer in veur dien e-mailadres te verangere. Wens se 't e-mailadres wils óntkoppele van diene gebroeker, laot 't e-mailadres den laeg wens se 't formeleer opsleis.",
        "changeemail-no-info": "Doe moos aangemeld zien ierdets doe dees pagina gebroeke kens.",
        "changeemail-oldemail": "Hujig mailadres:",
        "changeemail-newemail": "Nuuj mailadres:",
+       "changeemail-newemail-help": "Laot dit veldj laeg wens se dien e-mailadres eweg wils haole. Nao 't ewegsjaffe kóns se nimmieë e vergaete wachwaord oppernuuj instèlle en kriegs se gein e-mails mieë van deze wiki.",
        "changeemail-none": "(gein)",
+       "changeemail-password": "Die wachwaord veur {{SITENAME}}:",
        "changeemail-submit": "Veranger e-mail",
+       "changeemail-throttled": "Doe höbs te huifig geperbeerd dit aan te melje.\nDoe mós effe $1 wachte ierdets te 't obbenuuts kens perbere.",
+       "changeemail-nochange": "Veur 'n anger e-mailadres in.",
+       "resettokens": "Stèl teikes oppernuuj in",
+       "resettokens-no-tokens": "'t Geuf gein teikes veur oppernuuj in te stèlle.",
+       "resettokens-tokens": "Teikes:",
+       "resettokens-token-label": "$1 (hujige waerd: $2)",
+       "resettokens-watchlist-token": "Teike veur webfeed van [[Special:Watchlist|dien volglies]] (Atom/RSS)",
+       "resettokens-done": "Teikes oppernuuj ingestèldj.",
+       "resettokens-resetbutton": "Stèl gesillekteerde teikes oppernuuj in",
        "bold_sample": "Vètten teks",
        "bold_tip": "Vetten teks",
        "italic_sample": "Sjuunsen tèks",
        "sig_tip": "Dien handjteikening mit datum en tied",
        "hr_tip": "Horizontaal lien (gebroek spaarzaam)",
        "summary": "Samevatting:",
-       "subject": "Ongerwerp/kop:",
+       "subject": "Óngerwirp:",
        "minoredit": "Dit is 'n klein verangering",
        "watchthis": "Volg dees pagina",
-       "savearticle": "Pagina opsjlaon",
+       "savearticle": "Sjlaon pagina op",
+       "savechanges": "Slaon verangeringe op",
        "publishpage": "Pagina publicere",
        "publishchanges": "Verangeringe publicere",
        "preview": "Naokieke",
        "showpreview": "Betrach dees bewirking",
        "showdiff": "Toen verangeringe",
+       "blankarticle": "<strong>Waorsjoewing:</strong> de pagina die se wils aanmake is laeg.\nWens se oppernuuj op \"$1\" kliks, wuuertj de pagina aangemaak zónger welchen inhawd den ouch.",
        "anoneditwarning": "<strong>Waorsjoewing:</strong> Doe bös neet aangemeldj.\nDien IP-adres wuuertj opgeslage wen se verangeringe maaks op dees pagina. Wens doe <strong>[$1 dich aanmeljs]</strong> of <strong>[$2 'ne gebroeker aanmaaks]</strong> versjiene dien bewirkinge ónger diene gebroekersnaam, naeve anges veurdeiler.",
        "anonpreviewwarning": "''Doe bös neet aangemeldj.''\n''Door dien bewèrking op te slaon wört dien IP-adres opgeslagen in de paginagesjiedenis.''",
        "missingsummary": "'''Herinnering:''' doe höbs gein samevatting opgegaeve veur dien bewirking. Es te weer op ''Pagina opslaon'' kliks weurt de bewirking zonger samevatting opgesjlage.",
+       "selfredirect": "<strong>Waorsjoewing:</strong> Doe höbs 'ne redirek gemaak nao dees pagina.\nMeugelik höbs se 'n verkieërdje bestumming veure redirek gebroek of bewirks se de verkieërdje pagina.\nDoor nans op \"$1\" te klikke wuuertj de redirek tonna gemaak.",
        "missingcommenttext": "Plaats dien opmèrking hiej onger, a.u.b.",
        "missingcommentheader": "'''Let op:''' Doe höbs gén ongerwerp/kop veur deze opmèrking opgegaeve. Esse oppernuuj op \"$1\" kliks, wörd dien verangering zonger ongerwerp/kop opgeslage.",
-       "summary-preview": "Naokieke samevatting:",
-       "subject-preview": "Naokieke ongerwerp/kop:",
+       "summary-preview": "Veurvertoeaning van de bewirkingssamevatting:",
+       "subject-preview": "Veurvertoeaning van 't óngerwirp:",
+       "previewerrortext": "'n Fout is opgetraoje tiejes 't waergaeve van dien verangeringe.",
        "blockedtitle": "Gebroeker is geblokkeerd",
        "blockedtext": "'''Dien gebroekersaccount of IP-adres is geblokkeerd.'''\n\nDe blokkade is oetgeveurd door $1. De opgegaeve raej is ''$2''.\n\n* Aanvang blokkade: $8\n* Ènj blokkade: $6\n* Bedoeld te blokkere: $7\n\nDe kèns contak opnumme mit $1 of 'ne angere [[{{MediaWiki:Grouppage-sysop}}|systeemwèrker]] óm de blokkade te besjpraeke.\nDe kèns gein gebroek make van de functie 'e-mail deze gebroeker', behauve es te 'n geldig e-mailadres höbs opgegaeve in dien [[Special:Preferences|veurkäöre]] en 't gebroek van deze fónksie neet geblokkeerd is.\nDien hujig IP-adres is $3 en 't nómmer van de blokkade is #$5. Vermeld beide gegaeves wens te örges op dees blokkade reageers.",
        "autoblockedtext": "Dien IP-adres is automatisch geblokkeerd omdet 't gebroek is door 'ne gebroeker, dae is geblokkeerd door $1.\nDe opgegaeve reje is:\n\n:''$2''\n\n* Aanvang blokkade: $8\n* Einde blokkade: $6\n* Blóksmeining: $7\n\nDoe kins deze blokkaasj bespraeke mèt $1 of 'ne angere [[{{MediaWiki:Grouppage-sysop}}|beheerder]]. Doe kins gén gebroek make van de functie 'e-mail deze gebroeker', tenzijse 'n geldig e-mailadres opgegaeve höbs in dien [[Special:Preferences|veurkeure]] en 't gebroek van deze functie neet is geblokkeerd.\n\nDien nömmer vanne blokkaasj is #$5 èn dien IP-adres is $3.\nVermeld det esse örges euver deze blokkaasj reageers.",
        "updated": "(Biegewèrk)",
        "note": "'''Opmirking:'''",
        "previewnote": "'''Lèt op: dit is 'n controlepagina; dien teks is nog neet opgesjlage!'''",
-       "continue-editing": "Gank dórch mit bewirke",
+       "continue-editing": "Gank nao 't bewirkingsvönster",
        "previewconflict": "Dees versie toent wie de tèks in 't bôvesjte vèld oet git zeen es e zouws opsjlaon.",
        "session_fail_preview": "'''Sorry! Dien bewerking is neet verwerkt omdat sessiegegevens verlaore zeen gegaon.\nProbeer 't opnieuw. Als 't dan nog neet lukt, meldt dich dan aaf en weer aan.'''",
        "session_fail_preview_html": "'''Sorry! Dien bewerking is neet verwerk omdat sessiegegevens verlaore zeen gegaon.'''\n\n''Omdat in deze wiki ruwe HTML is ingesjakeld, is 'n voorvertoning neet meugelik als bescherming taege aanvalle met JavaScript.''\n\n'''Als dit een legitieme bewerking is, probeer 't dan opnieuw. Als 't dan nog neet lukt, meldt dich dan aaf en weer aan.'''",
        "edit-gone-missing": "De pagina is neet biegewirk.\nZe lik eweggesjaf te zien.",
        "edit-conflict": "Bewirkingsconflik.",
        "edit-no-change": "Dien bewirking is genegeerd, ómdet d'r gein verangering in de teks is gemaak.",
+       "postedit-confirmation-created": "De pagina is aangemaak gewaore.",
+       "postedit-confirmation-restored": "De pagina is herstèldj gewaore.",
        "postedit-confirmation-saved": "Dien bewirking is opgeslage gewaore.",
        "edit-already-exists": "De pagina is neet aangemaak.\nZie besjteit al.",
        "defaultmessagetext": "Obligaten teks",
+       "invalid-content-data": "Óngeljige inhawdsgegaeves",
        "editwarning-warning": "Es se dees pagina verleets verluus se meugelik wieziginge die se haes gemaak.\nEs se bös aangemeld, kins se dees waorsjoewing oetzètten in 't bewerkingstabblaad in dien veurkäöre.",
+       "editpage-invalidcontentmodel-title": "Inhaadsmodel wuuertj neet óngersteund",
+       "editpage-invalidcontentmodel-text": "'t Inhawdsmodel \"$1\" weurt neet óngersteund",
+       "editpage-notsupportedcontentformat-title": "Inhawdsformaat neet óngersteund",
+       "editpage-notsupportedcontentformat-text": "'t Inhawdstype $1 weurt neet óngersteund door 't inhawdsmodel $2.",
        "content-model-wikitext": "wikiteks",
        "content-model-text": "teks zónger opmaak",
        "content-model-javascript": "JavaScript",
        "content-json-empty-object": "Laeg objek",
        "content-json-empty-array": "Laege rits",
+       "deprecated-self-close-category": "Pagina's mit óngeljige zelfsloetende HTML-tags",
        "expensive-parserfunction-warning": "'''Waarschuwing:''' dees pagina gebroek te väöl kosbare parserfuncties.\n\nNoe {{PLURAL:$1|is|zeen}} 't d'r $1, terwiel 't d'r minder es $2 {{PLURAL:$2|mótte|mótte}} zeen.",
        "expensive-parserfunction-category": "Pagina's die te väöl kosbare parserfuncties gebroeke",
        "post-expand-template-inclusion-warning": "Waorsjuwing: de maximaal transclusiegruudje veur sjablone is euversjri-jje.\nSommige sjablone waere neet getranscludeerd.",
        "post-expand-template-argument-warning": "Waarsjoewing: dees pagina bevat winnigstes eine sjabloonparameter mit 'n te groete transclusiegruutde.\nDees parameters zeen eweggelaote.",
        "post-expand-template-argument-category": "Pagina's die missende sjabloonillemènte bevatte",
        "parser-template-loop-warning": "D'r is 'ne krinkloup in sjablone geconstateerd: [[$1]]",
+       "template-loop-category": "Pagina's mit sjeblone die zichzelf insloete",
        "parser-template-recursion-depth-warning": "De recursiedeepte veur sjablone is euversjrede ($1)",
        "language-converter-depth-warning": "De deepdjelimiet veure spraokómzètter is euversjreje ($1)",
        "node-count-exceeded-category": "Pagina's wo 't maximaal aantal nodes te väöl is",
        "history-feed-empty": "De gevraogde pagina besjteit neet.\nWellich is ze gewis of verplaats.\n[[Special:Search|Doorzeuk de wiki]] veur relevante pagina's.",
        "rev-deleted-comment": "(bewirkingssamevatting eweggesjaf)",
        "rev-deleted-user": "(gebroeker weggehaold)",
-       "rev-deleted-event": "(actie weggehaold)",
+       "rev-deleted-event": "(logbookregel weggehaold)",
        "rev-deleted-user-contribs": "[gebroeker of IP gewösj - bewèrking verbórge in biedraag]",
        "rev-deleted-text-permission": "Dees bewerking is '''gewusj'''.\nDao kónne details aanwezig zeen in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} wusjlogbook].",
+       "rev-suppressed-text-permission": "Dees paginaversie is <strong>óngerdrók</strong>.\nAchtergrönj zeen te vinjen in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logbook ven óngerdrökdje versies].",
        "rev-deleted-text-unhide": "Dees versie van de pagina is '''eweggesjaf'''.\nDetails zien meugelik te vinde in 't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wislogbook].\nEs beheerder kins se [$1 dees versie bekieke] es se wils.",
        "rev-suppressed-text-unhide": "Dees paginaversie is '''óngerdrök'''.\nAchtergrönj zeen meugelik te vinje in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logbook ven óngerdrökdje versies].\nEs behierder kèns toe [$1 de versjille bekieken] es se wils.",
        "rev-deleted-text-view": "Dees bewèrking is '''gewösj'''.\nEs beheerder kèns te deze zeen;\ndao kónne details aanwezig zeen in 't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wusjlogbook].",
        "revdelete-hide-user": "Verberg gebroekersnaam/IP van de gebroeker",
        "revdelete-hide-restricted": "Pas deze beperkinge toe op zowaal beheerders es angere",
        "revdelete-radio-same": "(anger neet)",
-       "revdelete-radio-set": "Jao",
-       "revdelete-radio-unset": "Nein",
+       "revdelete-radio-set": "Verstaoke",
+       "revdelete-radio-unset": "Zichbaar",
        "revdelete-suppress": "Ongerdruk gegaeves veur zowaal admins es angere",
        "revdelete-unsuppress": "Verwijder beperkinge op truuk gezatte wieziginge",
        "revdelete-log": "Reeje:",
        "revdelete-submit": "Pas toe op de geselecteerde {{PLURAL:$1|bewèrking|bewèrkinger}}",
-       "revdelete-success": "'''Wieziging zichbaarheid succesvol ingesteld.'''",
+       "revdelete-success": "Zichbaarheid van verangering biegewirk.",
        "revdelete-failure": "'''De zichbaarheid veur de versie kos neet ingesteld waere.'''\n$1",
-       "logdelete-success": "'''Zichbaarheid van de gebeurtenis succesvol ingesteld.'''",
+       "logdelete-success": "Zichbaarheid van gebäörtenis is ingestèldj gewaore.",
        "logdelete-failure": "'''De zichbaarheid van de logbookregel kos neet ingesteldj waere:'''\n$1",
        "revdel-restore": "Zichbaarheid verangere",
        "pagehist": "Paginagesjiedenis",
        "compareselectedversions": "Vergeliek geselecteerde versies",
        "showhideselectedversions": "Tuin/versjtaek geselecteerde versies",
        "editundo": "maak óngedaon",
+       "diff-empty": "(gei versjil)",
        "diff-multi-sameuser": "({{PLURAL:$1|Ein tösseligkendje versie|$1 tösseligkendje versies}} door dezelfdje gebroeker neet getoeandj)",
+       "diff-multi-otherusers": "({{PLURAL:$1|Ein tösseligkendje versie|$1 tösseligkendje versies}} door {{PLURAL:$2|einen angere gebroeker|$2 gebroekers}} neet getuind)",
        "diff-multi-manyusers": "($1 tösseligkende versies door mier es $2 gebroekers waere neet waergaeve)",
        "searchresults": "Zeukresultate",
        "searchresults-title": "Zeukresultate veur \"$1\"",
        "notextmatches": "Geen artikel gevonden met opgegeven zoekterm",
        "prevn": "veurige {{PLURAL:$1|$1}}",
        "nextn": "volgende {{PLURAL:$1|$1}}",
+       "prev-page": "veurige pazjena",
+       "next-page": "volgende pazjena",
        "prevn-title": "Vörge {{PLURAL:$1|resultaat|$1 resultate}}",
        "nextn-title": "Volgende {{PLURAL:$1|resultaat|$1 resultate}}",
        "shown-title": "$1 {{PLURAL:$1|resultaat|resultate}} per pagina weergaeve",
        "search-result-category-size": "{{PLURAL:$1|1 categorielid|$1 categorielede}} ({{PLURAL:$2|1 ongercategorie|$2 ongercategorieë}}, {{PLURAL:$3|1 bestandj|$3 bestenj}})",
        "search-redirect": "(redirek vanaaf $1)",
        "search-section": "(subkop $1)",
+       "search-category": "(categorie $1)",
        "search-file-match": "(kump euverein mit de bestandjsinhawd)",
        "search-suggest": "Meins te sóms: $1",
-       "search-interwiki-caption": "Zösterprojecte",
-       "search-interwiki-default": "$1 resultate:",
+       "search-rewritten": "De rizzeltaote veur $1 waere waergaeve. Zeuk inplaats nao $2.",
+       "search-interwiki-caption": "Rizzeltaote van zösterprojecte",
+       "search-interwiki-default": "Rizzeltaote van $1:",
        "search-interwiki-more": "(meer)",
+       "search-interwiki-more-results": "mieë rizzeltaote",
        "search-relatedarticle": "Gerelateerd",
        "searchrelated": "gerelateerd",
        "searchall": "alle",
        "showingresults": "Hieonger staon de <b>$1</b> {{PLURAL:$1|resultaat|resultaat}}, vanaaf #<b>$2</b>.",
        "search-showingresults": "{{PLURAL:$4|Rizzeltaot <strong>$1</strong> van <strong>$2</strong>|Rizzeltaote <strong>$1 - $2</strong> van <strong>$3</strong>}}",
        "search-nonefound": "D'r zien gein resultate veur diene zeukopdrach.",
+       "search-nonefound-thiswiki": "'t Goof gein rizzeltaote veur dien zeukopdrach op dees site.",
        "powersearch-legend": "Oetgebreid zeuke",
        "powersearch-ns": "Zeuke in naamruumdes:",
        "powersearch-togglelabel": "Conterleer:",
        "powersearch-toggleall": "Alle",
        "powersearch-togglenone": "Gein",
+       "powersearch-remember": "Ónthawt selectie veur toukumstige zeukopdrachte",
        "search-external": "Extern zeuke",
        "searchdisabled": "Zeuke op {{SITENAME}} is oetgesjakeld vanweige gebrek aan servercapaciteit.\nZoelang as de servers nog neet sjterk genog zunt kins e zeuke bie Google.\nMèrk op dat hun indexe van {{SITENAME}} content e bietje gedatierd kint zien.",
+       "search-error": "'n Fout is opgetraoje tiedes 't zeuke: $1",
+       "search-warning": "'n Waorsjoewing is opgetraoje tiedes 't zeuke: $1",
        "preferences": "Veurkäöre",
        "mypreferences": "Veurkäöre",
        "prefs-edits": "Aantal bewèrkinge:",
+       "prefsnologintext2": "Doe mós aanmelje veur dien veurkäöre in te stèlle.",
        "prefs-skin": "{{SITENAME}}-uterlik",
        "skin-preview": "Veurbesjouwing",
        "datedefault": "Gein veurkäör",
        "prefs-personal": "Gebroekersinfo",
        "prefs-rc": "Recènte verangeringe en weergaaf van sjtumpkes",
        "prefs-watchlist": "Volglies",
+       "prefs-editwatchlist": "Bewirk volglies",
+       "prefs-editwatchlist-label": "Bewirk items op dien volglies:",
+       "prefs-editwatchlist-edit": "Betrach en haol items op dien volglies eweg",
+       "prefs-editwatchlist-raw": "Bewirk roew volglies",
+       "prefs-editwatchlist-clear": "Maak dien volglies laeg",
        "prefs-watchlist-days": "Te tuine daag in de volglies:",
        "prefs-watchlist-days-max": "Maximaal $1 {{PLURAL:$1|daag|daag}}",
        "prefs-watchlist-edits": "Maximaal aantal bewirkinge in de oetgebreide volglies:",
        "prefs-watchlist-token": "Volgliessläötel:",
        "prefs-misc": "Anger insjtèllinge",
        "prefs-resetpass": "Wachwaord wiezige",
-       "prefs-changeemail": "Veranger e-mail",
+       "prefs-changeemail": "Veranger of haol dien e-mailadres eweg",
        "prefs-setemail": "Stel 'n e-mailadres in",
        "prefs-email": "E-mailopsjes",
        "prefs-rendering": "Oeterlik",
        "saveprefs": "Veurkäöre opsjlaon",
-       "restoreprefs": "Terug nao standaardinstellinge",
+       "restoreprefs": "Herstèl dien veurkäöre (veur alle instèllinge)",
        "prefs-editing": "Aafmeitinge tèksveld",
        "searchresultshead": "Insjtèllinge veur zeukresultate",
-       "stub-threshold": "Drempel veur markering <a href=\"#\" class=\"stub\">begske</a>:",
+       "stub-threshold": "Dörpel veur markering es sjtumpke ($1):",
+       "stub-threshold-sample-link": "veurbild",
        "stub-threshold-disabled": "Oetgezatj",
        "recentchangesdays": "Aantal daag te tuine in de recènte verangeringe:",
        "recentchangesdays-max": "(maximaal $1 {{PLURAL:$1|daag|daag}})",
        "timezoneregion-indian": "Indische Oceaan",
        "timezoneregion-pacific": "Stille Oceaan",
        "allowemail": "E-mail van anger gebroekers toesjtaon",
-       "prefs-searchoptions": "Zeukinstellinge",
+       "prefs-searchoptions": "Zeuke",
        "prefs-namespaces": "Naamruimte",
        "default": "sjtandaard",
        "prefs-files": "Bestenj",
        "prefs-reset-intro": "Gebroek dees functie om dien veurkäöre te herstelle nao de standaardinstellinge.\nDees hanjeling kin neet ongedaon gemaak waere.",
        "prefs-emailconfirm-label": "E-mailbevestiging:",
        "youremail": "Dien e-mailadres",
-       "username": "Gebroekersnaam:",
-       "prefs-memberingroups": "Lid van {{PLURAL:$1|gróp|gróppe}}:",
+       "username": "{{GENDER:$1|Gebroekersnaam}}:",
+       "prefs-memberingroups": "{{GENDER:$2|Lid}} van {{PLURAL:$1|gróp|gróppe}}:",
+       "group-membership-link-with-expiry": "$1 (toet $2)",
        "prefs-registration": "Registratiedatum:",
        "yourrealname": "Dienen echte naam*",
        "yourlanguage": "Taal van de gebroekersinterface",
        "badsiglength": "De handjteikening is te lank.\nZie maag neet mie es $1 {{PLURAL:$1|karakter|karakters}} bevatte.",
        "yourgender": "Geslach:",
        "gender-unknown": "Neet aangegaeve",
-       "gender-male": "Miensj",
-       "gender-female": "Vrów",
+       "gender-male": "Hae bewirk de wiki",
+       "gender-female": "Hèt bewirk de wiki",
        "prefs-help-gender": "Optioneel: dit wört gebroek om gebroekers correk aan te spraeke in de software.\nDeze informatie is zichbaar veur angere gebroekers.",
        "email": "E-mail",
-       "prefs-help-realname": "* Echte naam (opsjeneel): esse deze opgufs kin deze naam gebroek waere om dich erkinning te gaeve veur dien wèrk.",
+       "prefs-help-realname": "Echte naam is optioneel.\nEs se dezen opgeufs, kan deze naam waere gebroek veur dich erkènning te gaeve veur die werk.",
        "prefs-help-email": "E-mailadres is optioneel, mer maak 't muuëgelik óm dich e wachwaord te sjikke es s'n 't vergaete höbs.",
        "prefs-help-email-others": "Doe kans ouch angere in staat stelle per-email kóntak mit uch op te numme via 'n verwiezing op eur gebroekers- en euverlègkpazjena zónger det se diene identiteit luuëts weite.",
        "prefs-help-email-required": "Hiej veur is 'n e-mailadres neudig.",
        "prefs-signature": "Handjteikening",
        "prefs-dateformat": "Datumópmaak:",
        "prefs-timeoffset": "Tiedsversjèl",
-       "prefs-advancedediting": "Wiejer instèllinger",
+       "prefs-advancedediting": "Algemein instèllinge",
+       "prefs-editor": "Bewirker",
+       "prefs-preview": "Veurbesjouwing",
        "prefs-advancedrc": "Wiejer instèllinger",
        "prefs-advancedrendering": "Wiejer instèllinger",
        "prefs-advancedsearchoptions": "Wiejer instèllinger",
        "prefs-advancedwatchlist": "Wiejer instèllinger",
        "prefs-displayrc": "Toeaningsinstèllinger",
        "prefs-displaywatchlist": "Toeaningsinstèllinger",
+       "prefs-tokenwatchlist": "Teike",
        "prefs-diffs": "Vers",
-       "userrights": "Gebroekersrechtebeheer",
-       "userrights-lookup-user": "Beheer gebroekersgróppe",
+       "prefs-help-prefershttps": "Deze veurkäör weurt tougepas bie de volgendje aanmeljing",
+       "prefswarning-warning": "Doe höbs dees verangeringe gemaak in dien veurkäöre die nag neet zint opgeslage. Wen se de pagina verleuts zónger op \"$1\" te klikke waere dien veurkäöre neet biegewirk.",
+       "prefs-tabs-navigation-hint": "Hölp: doe kans de pielkesknuup nao links en rechs broeke veur te navigere tösse de tabblajer inne lies.",
+       "userrights": "Gebroekersrechte",
+       "userrights-lookup-user": "Selecteer 'ne gebroeker",
        "userrights-user-editname": "Veur 'ne gebroekersnaam in:",
-       "editusergroup": "Bewirk gebroekersgróppe",
-       "editinguser": "Bezig mit 't bewèrke van de gebroekersrechte van gebroeker '''[[User:$1|$1]]''' $2",
-       "userrights-editusergroup": "Bewirk gebroekersgróppe",
-       "saveusergroups": "Gebroekersgróppe opsjlaon",
+       "editusergroup": "Laaj gebroekersgruup",
+       "editinguser": "Bezig mit 't verangere van de gebroekersrechte van {{GENDER:$1|gebroeker}} '''[[User:$1|$1]]''' $2",
+       "viewinguserrights": "Gebroekersrechte betrachte van {{GENDER:$1|gebroeker}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "Bewirk {{GENDER:$1|gebroekersgruup}}",
+       "userrights-viewusergroup": "Toean {{GENDER:$1|gebroekersgruup}}",
+       "saveusergroups": "Slaon {{GENDER:$1|gebroekersgruup}} op",
        "userrights-groupsmember": "Leed van:",
        "userrights-groupsmember-auto": "Impliciet lid van:",
-       "userrights-groups-help": "De kèns de gróppe verangere woe deze gebroeker lid van is.\n* 'n Aangekruuts vinkvekske beteikent det de gebroeker lid is van de gróp.\n* 'n Neet aangekruuts vinkvekske beteikent det de gebroeker neet lid is van de gróp.\n* \"*\" Beteikent dets te 'ne gebroeker neet oet 'ne gróp eweg kèns haole naodets te die daobie höbs gedoon, of angersóm.",
+       "userrights-groups-help": "De kèns de gróppe verangere woe deze gebroeker lid van is.\n* 'n Aangekruuts vinkvekske beteikent det de gebroeker lid is van de gróp.\n* 'n Neet aangekruuts vinkvekske beteikent det de gebroeker neet lid is van de gróp.\n* \"*\" beteikent dets te 'ne gebroeker neet oet 'ne gróp eweg kèns haole naodets te die daobie höbs gedoon, of angersóm.\n* \"#\" beteikebt dets te dit grópslidmaotsjap allein kans verlinge. De kans 't neet verkorte.",
        "userrights-reason": "Reeje:",
        "userrights-no-interwiki": "Doe höbs gein rechte om gebroekersrechte op anger wiki's te wiezige.",
        "userrights-nodatabase": "Database $1 besteit neet of is gein plaatselike database.",
        "userrights-changeable-col": "Gróppe dies te kèns behere",
        "userrights-unchangeable-col": "Gróppe dies te neet kèns behere",
+       "userrights-expiry-current": "Verlöp $1",
+       "userrights-expiry-none": "Verlöp neet",
+       "userrights-expiry": "Verlöp:",
+       "userrights-expiry-existing": "Bestaonde verloupdatum: $2 $3",
+       "userrights-expiry-othertime": "Angere doer:",
+       "userrights-expiry-options": "1 daag:1 day,1 waek:1 week,1 maondj:1 month,3 maondj:3 months,6 maondj:6 months,1 jaor:1 year",
+       "userrights-invalid-expiry": "De verloiptied veure groep \"$1\" is óngeljig.",
+       "userrights-expiry-in-past": "De verlouptied veure groep \"$1\" is al gewaes.",
+       "userrights-cannot-shorten-expiry": "Doe kans de verlouptied van 't groepslidmaotsjap van groep \"$1\" neet verkorte. Allein gebroekers mit 't rech óm dees groep tou te veuge of eweg te sjaffe kónne de verlouptied verkorte.",
        "group": "Gróp:",
        "group-user": "Gebroekers",
        "group-autoconfirmed": "Geregistreerde gebroekers",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Beheerders",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrate",
-       "grouppage-suppress": "{{ns:project}}:Euverzich",
+       "grouppage-suppress": "{{ns:project}}:Toezich",
        "right-read": "Pagina's bekieke",
        "right-edit": "Pagina's bewerke",
        "right-createpage": "Pagina's aanmake",
        "right-editinterface": "De gebroekersinterface bewerke",
        "right-editusercss": "De CSS-bestande van angere gebroekers bewerke",
        "right-edituserjs": "De JS-bestande van angere gebroekers bewerke",
+       "right-editmyoptions": "Bewirk dien eige veurkäöre",
        "right-rollback": "Snel de letste bewerking(e) van 'n gebroeker van 'n pagina terugdraaie",
        "right-markbotedits": "Teruggedraaide bewerkinge markere es botbewerkinge",
        "right-noratelimit": "Heet gein ti'jdsafhankelijke beperkinge",
        "right-siteadmin": "De database blokkere en weer vriegaeve",
        "right-override-export-depth": "Export paazjes midin geslinkdje paazjes mit 'n deepdje ven 5",
        "right-sendemail": "Versjik e-mail aan anger gebroekers",
+       "grant-generic": "Rechtegroep \"$1\"",
+       "grant-group-page-interaction": "Wirk mit pagina's",
+       "grant-group-file-interaction": "Wirk mit media",
+       "grant-group-watchlist-interaction": "Wirk mit diene volglies",
+       "grant-group-email": "Sjik e-mail",
+       "grant-group-high-volume": "Veur aktiviteite mit hoeag voluum oet",
+       "grant-group-customization": "Aanpassinge en veurkäöre",
+       "grant-group-administration": "Veur behieërdershanjelinge oet",
+       "grant-group-private-information": "Betrach privaatgegaeves euver dich",
+       "grant-group-other": "Divers hanjelinge",
+       "grant-blockusers": "Blokkeer en deblokkeer gebroekers",
+       "grant-createaccount": "Maak gebroekers aan",
+       "grant-createeditmovepage": "Maak, bewirk en verplaats pagina's",
+       "grant-delete": "Wösj pagina's, bewirkinge en logbookregele",
+       "grant-basic": "Basisrechte",
        "newuserlogpage": "Logbook nuuj gebroekers",
        "newuserlogpagetext": "Hiej ónger saton de nuuj ingesjreve gebroekers.",
        "rightslog": "Gebroekersrechtelogbook",
        "recentchanges": "Lètste verangeringe",
        "recentchanges-legend": "Opties veur recènte verangeringe",
        "recentchanges-summary": "Volg de recènste bewirkinge op deze wiki op dees pagina.",
+       "recentchanges-noresult": "Gein verangeringe in dees periood kómme euverein mit de criteria.",
        "recentchanges-feed-description": "Volg de meis recente bewerkinge in deze wiki via deze feed.",
        "recentchanges-label-newpage": "Mit dees verangering is 'n nuuj pagina aangemaak",
        "recentchanges-label-minor": "Dit is 'n klein bewirking",
        "recentchanges-label-plusminus": "Dees paginagruuedje is verangerdj mit dit aantaal aan bytes",
        "recentchanges-legend-heading": "<strong>Legenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (zuuch ouch [[Special:NewPages|de nuuj pagina's]])",
+       "rcfilters-savedqueries-rename": "Herneum",
+       "rcfilters-savedqueries-setdefault": "Stèl in es standerd",
+       "rcfilters-savedqueries-unsetdefault": "Sjaf eweg es standerd",
+       "rcfilters-savedqueries-remove": "Sjaf eweg",
+       "rcfilters-savedqueries-new-name-label": "Naam",
+       "rcfilters-savedqueries-new-name-placeholder": "Besjrief 't doel van de filtjer",
+       "rcfilters-savedqueries-apply-label": "Maak filtjer aan",
+       "rcfilters-savedqueries-cancel-label": "Braek aaf",
+       "rcfilters-savedqueries-add-new-title": "Slaon hujige filtjerinstèllinge op",
+       "rcfilters-restore-default-filters": "Zèt standerd filtjers trögk",
+       "rcfilters-clear-all-filters": "Sjaf alle filtjers eweg",
+       "rcfilters-search-placeholder": "Filtjer recènte verangeringe (blajer of begin mit intikke)",
+       "rcfilters-invalid-filter": "Óngeljige filtjer",
+       "rcfilters-empty-filter": "Gein aktief filtjers. Alle biedrage waere waergaeve.",
+       "rcfilters-filterlist-title": "Filtjers",
+       "rcfilters-filterlist-whatsthis": "Wie wirk dit?",
+       "rcfilters-filterlist-feedbacklink": "Gaef trögksjaking op de nuuj (bèta)filtjers",
+       "rcfilters-highlightbutton-title": "Markeer rizzeltaote",
+       "rcfilters-highlightmenu-title": "Kees 'n kluuer",
+       "rcfilters-highlightmenu-help": "Kees 'n kluuer veur dees eigesjappe oet te lichte",
+       "rcfilters-filterlist-noresults": "Gein filtjers gevónje",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:neet</strong> $1",
        "rcnotefrom": "{{PLURAL:$5|Verangering|Verangeringe}} saer <strong>$3 óm $4</strong> (maximaal <strong>$1</strong> {{PLURAL:$1|verangering|verangeringe}}).",
        "rclistfrom": "Tuin de verangeringe vanaaf $3 $2",
        "rcshowhideminor": "$1 klein bewèrkinge",
        "booksources-text": "Hiej onger stuit 'n lies met koppelinge nao anger websites die nuuje of gebroekde beuk verkoupe, en die wellich meer informatie euver 't book detse zeuks höbbe:",
        "booksources-invalid-isbn": "t Ingegaeve ISBN liek neet geldig te zeen.\nControleer of se wellich n fout höbs gemaak bie de inveur.",
        "specialloguserlabel": "Oetveurder:",
-       "speciallogtitlelabel": "Doel (pagina of gebroeker):",
+       "speciallogtitlelabel": "Doel (paginanaam of {{ns:user}}:gebroekersnaam veur gebroeker):",
        "log": "Logbeuk",
        "all-logs-page": "Alle aopenbaar logbeuk",
        "alllogstext": "Dit is 't gecombineerd logbook ven {{SITENAME}}. De kins ouch 'n bepaald logbook keze, of filtere op gebroekersnaam of  pazjena, beide huidlettergeveulig.",
        "unwatchthispage": "Neet mië volge",
        "notanarticle": "Is gein artikel",
        "notvisiblerev": "Bewèrking is verwiederd",
-       "watchlist-details": "D'r {{PLURAL:$1|sjteit ein pagina|sjtaon $1 pagina's}} op dien volglies mit oetzunjering van de euverlèkpagina's.",
+       "watchlist-details": "D'r {{PLURAL:$1|sjteit ein pagina|sjtaon $1 pagina's}} op dien volglies mit de euverlèkpagina's neet mitgetèldj.",
        "wlheader-enotif": "Doe wörs per e-mail gewaarsjuwd",
        "wlheader-showupdated": "Pazjena's die verangerd zeen saers doe ze veur 't lètste bekeeks sjtaon '''vet'''",
-       "wlnote": "Hieónger {{PLURAL:$1|steit de lètste verangering|staon de lètste $1 verangeringe}} van {{PLURAL:$2|'t lètste oer|de lètste <b>$2</b> oer}} óp $3 óm $4.",
-       "wlshowlast": "Tuin lètste $1 ore $2 daag",
+       "wlnote": "Hieónger {{PLURAL:$1|steit de lètste verangering|staon de lètste <strong>$1</strong> verangeringe}} van {{PLURAL:$2|'t lètste oer|de lètste <strong>$2</strong> oer}} óp $3 óm $4.",
+       "wlshowlast": "Tuin lètste $1 oere $2 daag",
        "watchlist-options": "Opties veur volglies",
        "watching": "Bezig mit plaatse op de volglies...",
        "unwatching": "Oet de volglies aan 't haole...",
        "whatlinkshere-hideredirs": "$1 redireks",
        "whatlinkshere-hidetrans": "$1 transclusies",
        "whatlinkshere-hidelinks": "$1 links",
-       "whatlinkshere-hideimages": "$1 bestandjslinker",
+       "whatlinkshere-hideimages": "$1 bestandjslinke",
        "whatlinkshere-filters": "Filters",
        "autoblockid": "Autoblock #$1",
        "block": "Blok gebroeker",
        "tooltip-feed-rss": "RSS feed veur dees pagina",
        "tooltip-feed-atom": "Atom feed veur dees pagina",
        "tooltip-t-contributions": "Lies mit biedrages van {{GENDER:$1|deze gebroeker}}",
-       "tooltip-t-emailuser": "Sjtuur inne mail noa dizze gebroeker",
+       "tooltip-t-emailuser": "Sjtuur inne mail noa dizze {{GENDER:$1|gebroeker}}",
        "tooltip-t-upload": "Upload besjtande",
        "tooltip-t-specialpages": "Lies van alle speciaal pagina's",
        "tooltip-t-print": "Printvruntelike versie van deze pagina",
        "simpleantispam-label": "Antispamcontrole.\nVöl dit veld <strong>neet</strong> in!",
        "pageinfo-title": "Informatie euver \"$1\"",
        "pageinfo-header-basic": "Basisgegaeves",
-       "pageinfo-header-edits": "Bewirk",
+       "pageinfo-header-edits": "Bewirkingsgesjiechte",
        "pageinfo-header-restrictions": "Paginabesjirming",
+       "pageinfo-header-properties": "Pagina-eigesjappe",
        "pageinfo-display-title": "Toean paginanaam",
        "pageinfo-default-sort": "Standerd sortering",
        "pageinfo-length": "Paginalingdje (in bytes)",
        "pageinfo-language": "Spraok worin dees pagina steit",
        "pageinfo-content-model": "Paginainhawdmodel",
        "pageinfo-robot-policy": "Robot-indexering",
+       "pageinfo-robot-index": "Tougestange",
+       "pageinfo-robot-noindex": "Neet toegestange",
        "pageinfo-watchers": "Aantal paginavolgers",
+       "pageinfo-few-watchers": "Minder es  {{PLURAL:$1|eine volger|$1 volgers}}",
        "pageinfo-redirects-name": "Aantaal redireks nao dees pagina",
+       "pageinfo-subpages-name": "Subpagina's van dees pagina",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|redirek|redireks}}; $3 {{PLURAL:$3|neet-redirek|neet-redireks}})",
+       "pageinfo-firstuser": "Aanmaker",
+       "pageinfo-firsttime": "Datum vannen aanmaak",
+       "pageinfo-lastuser": "Litste bewirker",
+       "pageinfo-lasttime": "Litste bewirking",
        "pageinfo-edits": "Aantal bewèrkinge",
        "pageinfo-authors": "Aantal versjillende sjrievers",
+       "pageinfo-recent-edits": "Recènte bewirkinge (binne de aafgeloupe $1)",
+       "pageinfo-recent-authors": "Recènte sjrievers",
        "pageinfo-magic-words": "{{PLURAL:$1|Magisch waord|Magische wäörd}} ($1)",
+       "pageinfo-hidden-categories": "Verstaoke {{PLURAL:$1|categorie|categorieje}} ($1)",
+       "pageinfo-templates": "{{PLURAL:$1|Gebroek sjebloon|Gebroekde sjeblone}} ($1)",
        "pageinfo-toolboxlink": "Pazjena-infermasie",
+       "pageinfo-contentpage": "Getèldj es pagina mit inhawd",
        "pageinfo-contentpage-yes": "Jao",
        "markaspatrolleddiff": "Markeer es gecontroleerd",
        "markaspatrolledtext": "Markeer deze pagina es gecontroleerd",
        "watchlistedit-raw-done": "Dien volglies is biegewirk.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 pazjena is|$1 pazjena's zeen}} toegevoog:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 pazjena is|$1 pazjena's zeen}} eweggesjaf:",
+       "watchlisttools-clear": "Maak de volglies laeg",
        "watchlisttools-view": "Volglies bekieke",
        "watchlisttools-edit": "Volglies bekieke en bewirke",
        "watchlisttools-raw": "Roew volglies bewirke",
        "version-entrypoints": "Ingang-URLs",
        "version-entrypoints-header-entrypoint": "Ingank",
        "version-entrypoints-header-url": "URL",
+       "redirect": "Redirek op bestandj, gebroeker, pagina, versie of log-ID",
+       "redirect-summary": "Dees speciaal pagina verwies door nao e bestandj (es 'ne bestandjsnaam weurt opgegaeve), 'n pagina (es e paginanómmer of versienómmer weurt opgegaeve), 'ne gebroekerspagina (es e gebroekersnómmer weurt opgegaeve) of 'ne logbookregel (es 'n logboekregel-ID weurt opgegaeve). Gebroek: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] of [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-submit": "Gank",
+       "redirect-lookup": "Zeuk op:",
+       "redirect-value": "Waerd",
+       "redirect-user": "Gebroekersnómmer",
+       "redirect-page": "Paginanómmer",
+       "redirect-revision": "Paginaversie",
+       "redirect-file": "Bestandjsnaam",
        "fileduplicatesearch": "Zeuk veur döbbelbestaondje bestenj",
        "fileduplicatesearch-summary": "Zeuk veur döbbel bestaondje bestenj op basis van zien hashwaarde.",
        "fileduplicatesearch-filename": "Bestandjsnaam:",
        "fileduplicatesearch-result-n": "'t Bestandj \"$1\" haet {{PLURAL:$2|1 identieke döbbelversie|$2 identiek döbbelversies}}.",
        "fileduplicatesearch-noresults": "d'r Is gei bestandj mitte naam \"$1\" gevónje.",
        "specialpages": "Speciaal pagina's",
-       "specialpages-note": "* Normaal speciaal pagina's\n* <strong class=\"mw-specialpagerestricted\">Beperk toegankelike speciaal pagina's</strong>\n* <span class=\"mw-specialpagecached\">Speciaal pagina's mit allein gegaeves oete cache (meugelik verajerd)</span>",
        "specialpages-group-maintenance": "Óngerhajingsrapporter",
        "specialpages-group-other": "Euverige speciaal pazjena's",
        "specialpages-group-login": "Aanmelje / registrere",
        "tags-display-header": "Weergave in wiezigingslieste",
        "tags-description-header": "Volledige beschrieving van betekenis",
        "tags-hitcount-header": "Gelabelde bewerkinge",
+       "tags-active-yes": "Jao",
+       "tags-active-no": "Nae",
        "tags-edit": "bewerking",
        "tags-hitcount": "$1 {{PLURAL:$1|wieziging|wieziginge}}",
        "comparepages": "Vergeliek pazjena's",
        "htmlform-reset": "Maak verangeringe óngedaon",
        "htmlform-selectorother-other": "Anges",
        "logentry-delete-delete": "$1 {{GENDER:$1|haet}} de pagina $3 gewösj",
-       "logentry-delete-restore": "$1 haet de pagina $3 trögkgezat",
+       "logentry-delete-restore": "$1 {{GENDER:$2|haet}} de pagina $3 ($4) trögkgezatte",
        "logentry-delete-event": "$1 haet de zichbaarheid van {{PLURAL:$5|'ne logbookregel|$5 logbookregels}} van $3 gewiezig: $4",
-       "logentry-delete-revision": "$1 haet de zichbaarheid van {{PLURAL:$5|'n versie|$5 versies}} van $3 gewiezig: $4",
+       "logentry-delete-revision": "$1 {{GENDER:$2|haet}} de zichbaarheid van {{PLURAL:$5|'n versie|$5 versies}} van de pagina $3 verangerdj: $4",
        "logentry-delete-event-legacy": "$1 haet de zichbaarheid van logbookregels van $3 gewiezig",
        "logentry-delete-revision-legacy": "$1 haet de zichbaarheid van versies van de pagina $3 gewiezig.",
        "logentry-suppress-delete": "$1 haet de pagina $3 ongerdrök",
        "logentry-move-move_redir": "$1 {{GENDER:$2|verplaatsde}} pagina $3 nao $4 euver 'ne redirek",
        "logentry-move-move_redir-noredirect": "$1 verplaatsde pagina $3 nao $4 euver 'ne redirek zonger 'n doorverwiezing achter te laote",
        "logentry-patrol-patrol": "$1 haet versie $4 van pagina $3 es gecontroleerd gemarkeerd",
-       "logentry-patrol-patrol-auto": "$1 haet versie $4 van pagina $3 autematis es gecontroleerd gemarkeerd",
+       "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|haet}} versie $4 van pagina $3 autematis gemarkeerd es gecontroleerd",
        "logentry-newusers-newusers": "$1 haet 'ne gebroeker aangemaak",
        "logentry-newusers-create": "Gebroeker $1 {{GENDER:$2|is}} aangemaak gewaore",
        "logentry-newusers-create2": "$1 haet 'ne gebroeker $3 aangemaak",
-       "logentry-newusers-autocreate": "De gebroeker $1 is autematis aangemaak",
+       "logentry-newusers-autocreate": "De gebroeker $1 {{GENDER:$2|is}} autematis aangemaak gewaore",
        "logentry-upload-upload": "$1 {{GENDER:$2|loadje}} $3 up",
+       "logentry-upload-overwrite": "$1 {{GENDER:$2|haet}} 'n nuuj versie van $3 hoaggelaje",
        "rightsnone": "(gein)",
        "feedback-adding": "Feedback weurt aan pagina toegevoeg...",
        "feedback-bugcheck": "Good! Kónterleer ef of 't neet al ein vanne [$1 bekèndje bugs] is.",
        "special-characters-group-lao": "Lao",
        "special-characters-group-khmer": "Cambodzjaans",
        "mw-widgets-dateinput-placeholder-day": "JJJJ-MM-DD",
-       "mw-widgets-dateinput-placeholder-month": "JJJJ-MM"
+       "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
+       "randomrootpage": "Willekäörige wrootpagina"
 }
index cc344ef..3a0564b 100644 (file)
        "rcfilters-highlightmenu-title": "Izvēlies krāsu",
        "rcfilters-highlightmenu-help": "Izvēlies krāsu, lai izceltu šo īpašību",
        "rcfilters-filterlist-noresults": "Filtri nav atrasti",
-       "rcfilters-filtergroup-registration": "Lietotāja reģistrācija",
-       "rcfilters-filter-registered-label": "Reģistrēti",
-       "rcfilters-filter-registered-description": "Pieslēgušies redaktori.",
-       "rcfilters-filter-unregistered-label": "Nereģistrēti",
-       "rcfilters-filter-unregistered-description": "Nepieslēgušies redaktori.",
        "rcfilters-filtergroup-authorship": "Devuma autors",
        "rcfilters-filter-editsbyself-label": "Tavi labojumi",
        "rcfilters-filter-editsbyself-description": "Tevis veiktie labojumi.",
        "rcfilters-filter-editsbyother-label": "Citu labojumi",
        "rcfilters-filter-editsbyother-description": "Visas izmaiņas bez tavējām.",
        "rcfilters-filtergroup-userExpLevel": "Pieredzes līmenis (tikai reģistrētiem dalībniekiem)",
+       "rcfilters-filter-user-experience-level-registered-label": "Reģistrēti",
+       "rcfilters-filter-user-experience-level-registered-description": "Pieslēgušies redaktori.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Nereģistrēti",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Nepieslēgušies redaktori.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Jaunpienācēji",
        "rcfilters-filter-user-experience-level-newcomer-description": "Mazāk nekā 10 labojumi un 4 aktīvas dienas.",
        "rcfilters-filter-user-experience-level-learner-label": "Mācekļi",
        "pageinfo-edits": "Kopējais izmaiņu skaits",
        "pageinfo-authors": "Kopējais atsevišķu autoru skaits",
        "pageinfo-magic-words": "{{PLURAL:$1|Maģiskie vārdi|Maģiskais vārds|Maģiskie vārdi}} ($1)",
+       "pageinfo-hidden-categories": "{{PLURAL:$1|Slēptas kategorijas|Slēpta kategorija|Slēptas kategorijas}} ($1)",
        "pageinfo-toolboxlink": "Lapas informācija",
        "pageinfo-redirectsto": "Pāradresē uz",
        "pageinfo-redirectsto-info": "info",
        "mw-widgets-titleinput-description-new-page": "lapa vēl nepastāv",
        "mw-widgets-categoryselector-add-category-placeholder": "Pievienot kategoriju...",
        "sessionprovider-generic": "$1 sesijas",
+       "randomrootpage": "Nejauša saknes lapa",
        "log-action-filter-suppress": "Cenzēšanas veids:",
        "log-action-filter-suppress-event": "Reģistra ieraksta cenzēšana",
        "log-action-filter-suppress-delete": "Lapas cenzēšana",
index 6eed14e..0652808 100644 (file)
        "rcfilters-highlightmenu-help": "Misafidy loko hampanabaribariana ilay tondro",
        "rcfilters-filterlist-noresults": "Tsy nahitana sivana",
        "rcfilters-noresults-conflict": "Tsy nahitana valiny satria tsy mifanaraka ny mason-tsivana",
-       "rcfilters-filtergroup-registration": "Fisoratan'ny mpikambana",
-       "rcfilters-filter-registered-label": "Nisoratra anarana",
-       "rcfilters-filter-registered-description": "Mpanova tafiditra.",
-       "rcfilters-filter-unregistered-label": "Tsy nisoratra anarana",
-       "rcfilters-filter-unregistered-description": "Mpikambana tsy niditra.",
        "rcfilters-filtergroup-authorship": "Manova ny tompon-tsoratra",
        "rcfilters-filter-editsbyself-label": "Ny fiovanao",
        "rcfilters-filter-editsbyself-description": "Fiovana nataonao",
        "rcfilters-filter-editsbyother-label": "Fiovana nataon'ny hafa",
        "rcfilters-filter-editsbyother-description": "Fiovana noforonin'ny mpikambana hafa (tsy ianao).",
        "rcfilters-filtergroup-userExpLevel": "Lentan'ny traikefa (ho an'ireo mpikambana nisoratra anarana ihany)",
+       "rcfilters-filter-user-experience-level-registered-label": "Nisoratra anarana",
+       "rcfilters-filter-user-experience-level-registered-description": "Mpanova tafiditra.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Tsy nisoratra anarana",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Mpikambana tsy niditra.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Vao tonga",
        "rcfilters-filter-user-experience-level-newcomer-description": "Latsaky ny fiovana folo ary latsaky ny 4 andro niasana",
        "rcfilters-filter-user-experience-level-learner-label": "Mpianatra",
index eb697b2..5a81bab 100644 (file)
        "rcfilters-legend-heading": "<strong>Список на кратенки:</strong>",
        "rcfilters-activefilters": "Активни филтри",
        "rcfilters-advancedfilters": "Напредни филтри",
+       "rcfilters-limit-title": "Промени за приказ",
+       "rcfilters-limit-shownum": "Прикажи ги последните $1 промени",
+       "rcfilters-days-title": "Последниве денови",
+       "rcfilters-hours-title": "Последниве часови",
+       "rcfilters-days-show-days": "{{PLURAL:$1|еден ден|$1 дена}}",
+       "rcfilters-days-show-hours": "{{PLURAL:$1|еден час|$1 часа}}",
        "rcfilters-quickfilters": "Зачувани филтри",
        "rcfilters-quickfilters-placeholder-title": "Засега нема зачувани врски",
        "rcfilters-quickfilters-placeholder-description": "За да ги зачувате вашите филтерски псотавки за да ги употребите другпат, стиснете на иконката за бележник во подрачјето „Активен филтер“ подолу.",
        "rcfilters-invalid-filter": "Неважечки филтер",
        "rcfilters-empty-filter": "Нема активни филтри. Прикажани се сите придонеси.",
        "rcfilters-filterlist-title": "Филтри",
-       "rcfilters-filterlist-whatsthis": "ШÑ\82о Ðµ ова?",
+       "rcfilters-filterlist-whatsthis": "Ð\9aако Ñ\80абоÑ\82и ова?",
        "rcfilters-filterlist-feedbacklink": "Дајте мислење за новите (бета) филтри",
        "rcfilters-highlightbutton-title": "Истакнување на исход",
        "rcfilters-highlightmenu-title": "Изберете боја",
        "rcfilters-noresults-conflict": "Не пронајдов ништо бидејќи критериумите на барање се спротиставени.",
        "rcfilters-state-message-subset": "Филтерот не делува бидејќи неговиот исход веќе се содржи во {{PLURAL:$2|следниов поопфатен филтер|следниве поопфатни филтри}} (истакнете го за да го издвоите): $1",
        "rcfilters-state-message-fullcoverage": "Избирањето на сите филтри во групата е исто како да не сте избрале ниеден, така што овој филтер не делува. Во групата се опфатени: $1",
-       "rcfilters-filtergroup-registration": "Регистрација на корисници",
-       "rcfilters-filter-registered-label": "Регистрирани",
-       "rcfilters-filter-registered-description": "Најавени уредници.",
-       "rcfilters-filter-unregistered-label": "Нерегистрирани",
-       "rcfilters-filter-unregistered-description": "Уредници кои не се најавени.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Овој филтер е протиставен со {{PLURAL:$2|следниов филтер|следниве филтри}} за искуство, {{PLURAL:$2|кој наоѓа|кои наоѓаат}} само регистрирани корисници: $1",
        "rcfilters-filtergroup-authorship": "Авторство на придонесот",
        "rcfilters-filter-editsbyself-label": "Ваши промени",
        "rcfilters-filter-editsbyself-description": "Ваши сопствени придонеси.",
        "rcfilters-filter-editsbyother-label": "Туѓи промени",
        "rcfilters-filter-editsbyother-description": "Сите промени направени од други уредници",
-       "rcfilters-filtergroup-userExpLevel": "Корисничка искусност (само за регистрирани)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Филтрите за искуство пронаоѓаат само регистрирани корисници, така што овој филтер е простиставен со филтерот „Нерегистрирани“.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Филтерот „Нерегистрирани“ е спротиставен на еден или повеќе од филтрите за искуство. Тие филтри наоѓаат само регистрирани корисници. Спротиставените филтри погоре се означени во одделот Активни филтри.",
+       "rcfilters-filtergroup-userExpLevel": "Корисничка регистрација и искусност",
+       "rcfilters-filter-user-experience-level-registered-label": "Регистрирани",
+       "rcfilters-filter-user-experience-level-registered-description": "Најавени уредници.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Нерегистрирани",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Уредници кои не се најавени.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новодојденци",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9fомалку од 10 уредувања и 4 дена активност.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "РегиÑ\81Ñ\82Ñ\80иÑ\80ани Ñ\83Ñ\80едниÑ\86и Ñ\81о Ð¿омалку од 10 уредувања и 4 дена активност.",
        "rcfilters-filter-user-experience-level-learner-label": "Ученици",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\9fоиÑ\81кÑ\83Ñ\81ни Ð¾Ð´ â\80\9eноводоÑ\98денÑ\86иÑ\82еâ\80\9c, Ð½Ð¾ Ð¿Ð¾Ð¼Ð°Ð»ÐºÑ\83 Ð¾Ð´ â\80\9eиÑ\81кÑ\83Ñ\81ниÑ\82е корисници“.",
+       "rcfilters-filter-user-experience-level-learner-description": "РегиÑ\81Ñ\82Ñ\80иÑ\80ани Ñ\83Ñ\80едниÑ\86и Ñ\87ие Ð¸Ñ\81кÑ\83Ñ\81Ñ\82во Ðµ Ð½ÐµÐºÐ°Ð´Ðµ Ð¼ÐµÑ\88Ñ\83 â\80\9eноводоÑ\98денÑ\86иâ\80\9c Ð¸ â\80\9eиÑ\81кÑ\83Ñ\81ни корисници“.",
        "rcfilters-filter-user-experience-level-experienced-label": "Искусни корисници",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ð\9fовеÑ\9cе Ð¾Ð´ 30 Ð´ÐµÐ½Ð° Ð°ÐºÑ\82ивноÑ\81Ñ\82 Ð¸ 500 Ñ\83Ñ\80едÑ\83ваÑ\9aа.",
+       "rcfilters-filter-user-experience-level-experienced-description": "РегиÑ\81Ñ\82Ñ\80иÑ\80ани Ñ\83Ñ\80едниÑ\86и Ñ\81о Ð¿Ð¾Ð²ÐµÑ\9cе Ð¾Ð´ 500 Ñ\83Ñ\80едÑ\83ваÑ\9aа Ð¸ 30 Ð´ÐµÐ½Ð° Ð°ÐºÑ\82ивноÑ\81Ñ\82.",
        "rcfilters-filtergroup-automated": "Автоматизирани придонеси",
        "rcfilters-filter-bots-label": "Ботовски",
        "rcfilters-filter-bots-description": "Уредувања со автоматизирани алатки.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Филтерот „Ситни уредувања“ е спротиставен на еден или повеќе од филтрите за видови измена, бидејќи извеси видови не можат да се означат како ситни. Спротиставените филтри се означени во делот Неактивни филтри погоре.",
        "rcfilters-hideminor-conflicts-typeofchange": "Извезни видови промени не можат да се означат како „ситни“, па затоа овој филтер е во спротиставеност со следниве филтри за видови промени: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Овој филтер за видови промени е во спротиставеност со филтерот „Ситни уредувања“. Извсни видови промени не можат да се означат како „ситни“.",
-       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледна Ð¿Ñ\80еÑ\80абоÑ\82ка",
+       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледни Ð¿Ñ\80еÑ\80абоÑ\82ки",
        "rcfilters-filter-lastrevision-label": "Последна преработка",
-       "rcfilters-filter-lastrevision-description": "Ð\9dаÑ\98нови Ð¿Ñ\80еÑ\80абоÑ\82ки Ð½Ð° страница.",
-       "rcfilters-filter-previousrevision-label": "Ð\9fÑ\80еÑ\82Ñ\85одни Ð¿Ñ\80еÑ\80абоÑ\82ки",
-       "rcfilters-filter-previousrevision-description": "Сите промени кои не се најнови преработки на страницата.",
+       "rcfilters-filter-lastrevision-description": "Само Ð½Ð°Ñ\98нови Ð¿Ñ\80еÑ\80абоÑ\82ки Ð²Ð¾ страница.",
+       "rcfilters-filter-previousrevision-label": "Ð\9dе Ð¿Ð¾Ñ\81леднаÑ\82а Ð¿Ñ\80еÑ\80абоÑ\82ка",
+       "rcfilters-filter-previousrevision-description": "Сите промени кои не се „последна преработка“.",
        "rcfilters-filter-excluded": "Исклучени",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:не</strong> $1",
+       "rcfilters-exclude-button-off": "Изземи избрано",
+       "rcfilters-exclude-button-on": "Изземи избрано",
        "rcfilters-view-tags": "Означени уредувања",
        "rcfilters-view-namespaces-tooltip": "Филтрирај исход по именски постор",
        "rcfilters-view-tags-tooltip": "Филтрирај исход по уредувачки ознаки",
        "delete-warning-toobig": "Оваа страница има долга историја на уредување, преку $1 {{PLURAL:$1|преработка|преработки}}.\nБришењето може да предизвика проблеми при работењето на базата на податоци на {{SITENAME}};\nпродолжете доколку сте сигруни дека треба тоа да го сторите.",
        "deleteprotected": "Не можете да ја избришете страницава бидејќи е заштитена.",
        "deleting-backlinks-warning": "<strong>Предупредување:</strong>  До страницата што сакате да ја избришете водат [[Special:WhatLinksHere/{{FULLPAGENAME}}|други страници]] или пак се превметнуваат во неа.",
+       "deleting-subpages-warning": "<strong>Предупредување:</strong> Страницата што сакате да ја избришете има [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|потстраница|$1 потстраници|51=преку 50 потстраници}}]].",
        "rollback": "Отповикај промени",
        "rollbacklink": "отповикај",
        "rollbacklinkcount": "отповикај $1 {{PLURAL:$1|уредување|уредувања}}",
        "fileduplicatesearch-noresults": "Не пронајдов податотека со име „$1“.",
        "specialpages": "Службени страници",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Нормални службени страници.\n* <span class=\"mw-specialpagerestricted\">Ограничени службени страници.</span>",
+       "specialpages-note-restricted": "* Нормални службени страници.\n* <span class=\"mw-specialpagerestricted\">Ограничени службени страници.</span>",
        "specialpages-group-maintenance": "Извештаи за одржување",
        "specialpages-group-other": "Други службени страници",
        "specialpages-group-login": "Најава / направи сметка",
index a3b47fb..f98e7f6 100644 (file)
        "minoredit": "Che sī sió siu-kái",
        "watchthis": "Kàm-sī chit ia̍h",
        "savearticle": "Pó-chûn chit ia̍h",
+       "publishpage": "Hoat-pò͘ bûn-chiuⁿ",
+       "publishchanges": "Hoat-pò͘ siu-kái",
        "preview": "Seng khoàⁿ-māi",
        "showpreview": "Seng khoàⁿ-māi",
        "showdiff": "Khòaⁿ kái-piàn ê pō·-hūn",
        "permissionserrorstext-withaction": "Lí bô ún-chún chò $2, in-ūi ē-kha\n{{PLURAL:$1|iân-kò͘|iân-kò͘}}:",
        "recreate-moveddeleted-warn": "'''Sè-jī: Lí taⁿ chún-pī beh khui ê ia̍h, chêng bat hō͘ lâng thâi tiāu koè.''' Lí tio̍h chim-chiok soà-chiap pian-chi̍p chit ia̍h ê pit-iàu-sèng. Chia ū chit ia̍h ê san-tû kì-lo̍k (deletion log) hō͘ lí chham-khó:",
        "edit-conflict": "Siu-kái sio-chhiong",
+       "postedit-confirmation-saved": "Lí ê siu-kái í-keng pó-chûn.",
        "defaultmessagetext": "Siat piān ê bûn-jī",
        "content-model-wikitext": "wikitext",
        "content-model-text": "sûn bûn-pún",
index 116b216..911fc80 100644 (file)
@@ -41,7 +41,7 @@
        "tog-enotifminoredits": "Famme na masciata mail pure quanno se fanno cagnamiente piccerille 'e paggene e files",
        "tog-enotifrevealaddr": "Fa' vedé 'o ndirizzo mail ncopp'e mmasciate 'e notifica",
        "tog-shownumberswatching": "Fa' vedé 'o nummero d'utente che teneno 'a paggena cuntrullata",
-       "tog-oldsig": "Firma 'e mmo:",
+       "tog-oldsig": "'A firma vosta (mo' mo'):",
        "tog-fancysig": "Piglia 'a firma comme fosse nu wikitesto (senza fà link automatico)",
        "tog-uselivepreview": "Abbìa 'o \"Live preview\"",
        "tog-forceeditsummary": "Chiere a mme quanno se sta azzeccanno nu campo oggetto abbacante",
@@ -58,7 +58,7 @@
        "tog-showhiddencats": "Fa' vedé 'e categurie annascunnute",
        "tog-norollbackdiff": "Nun fà vedé 'o cunfronto nfra verziune quanno se fà nu rollback ('o torna arreto)",
        "tog-useeditwarning": "Famme sapé quanno lasso na paggena 'e mudifeca senza sarvà 'e cagnamiente",
-       "tog-prefershttps": "Usa sempe na connessione sicura quanno s'accummincia sessione",
+       "tog-prefershttps": "Usa sempe na connessione sicura pe' tramente ca s'accummincia sessione",
        "underline-always": "Sèmpe",
        "underline-never": "Màje",
        "underline-default": "Tiene sempe le mpostazzione d' 'o navigatóre",
        "newwindow": "(s'arape n'ata fenèsta)",
        "cancel": "Scancèlla",
        "moredotdotdot": "Cchiù...",
-       "morenotlisted": "Chisto elenco nun è cumpreto.",
+       "morenotlisted": "Chisto elenco putesse nun essere cumpleto sano sano.",
        "mypage": "Paggena",
        "mytalk": "'E chiàcchieriate mmie",
        "anontalk": "Chiacchierate",
        "navigation": "Navigazzione",
        "and": "&#32;e",
-       "qbfind": "Truòva",
-       "qbbrowse": "Sfoglia",
-       "qbedit": "Càgna",
-       "qbpageoptions": "Chesta paggena",
-       "qbmyoptions": "'E ppaggene mie",
        "faq": "FAQ",
-       "faqpage": "Project:Domanne frequente",
        "actions": "Azione",
        "namespaces": "Namespace",
        "variants": "Variante",
        "searcharticle": "Vàje",
        "history": "Verziune 'e primma",
        "history_short": "Cronologgia",
+       "history_small": "cronologgia",
        "updatedmarker": "cagnamiénte 'e ll'urdema visita d' 'a mia",
        "printableversion": "Verzione pe' stampa",
        "permalink": "Jonta permanente",
        "edit-local": "Càgna descrizione lucale",
        "create": "Crèa",
        "create-local": "Azzecca descrizione lucale",
-       "editthispage": "Càgna chesta paggena",
-       "create-this-page": "Crèa sta paggena",
        "delete": "Scancèlla",
-       "deletethispage": "Scancèlla chésta paggena",
-       "undeletethispage": "Arrepiglia chista paggena",
        "undelete_short": "Arremedia {{PLURAL:$1|na verziona|$1 vverziune}}",
        "viewdeleted_short": "Vide {{PLURAL:$1|nu cagnamiénto scancellato|$1 cagnamiénte scancellate}}",
        "protect": "Prutegge",
        "protect_change": "càgna",
-       "protectthispage": "Ferma chesta paggena",
        "unprotect": "Càgna prutezzione",
-       "unprotectthispage": "Càgna prutezzione 'e chesta paggena",
        "newpage": "Paggena nòva",
-       "talkpage": "Paggena 'e chiàcchiera",
        "talkpagelinktext": "Chiàcchiera",
        "specialpage": "Paggena speciàle",
        "personaltools": "Strumiente perzonale",
-       "articlepage": "Vere a paggena e contenuto",
        "talk": "Chiàcchiera",
        "views": "Visite",
        "toolbox": "Strumiente",
-       "userpage": "Vere a paggena utente",
-       "projectpage": "Vere a paggena 'e servizio",
+       "tool-link-userrights": "Càgna gruppe {{GENDER:$1|utente}}",
+       "tool-link-userrights-readonly": "Vire gruppe {{GENDER:$1|utente}}",
+       "tool-link-emailuser": "Manna na masciata email a st'{{GENDER:$1|utente}}",
        "imagepage": "Vere a paggena d' 'o file",
        "mediawikipage": "Vere 'a mmasciata",
        "templatepage": "Vere 'o template",
        "redirectedfrom": "(Redirect 'a $1)",
        "redirectpagesub": "Paggena 'e redirect",
        "redirectto": "Reindirizza a:",
-       "lastmodifiedat": "Urdemo cagnamiénto pe' a paggena: $2, $1.",
+       "lastmodifiedat": "Sta paggena fuje, n'urdema vota, cagnàta 'o $1, 'e $2.",
        "viewcount": "Chesta paggena è stata liggiùta {{PLURAL:$1|una vòta|$1 vòte}}.",
        "protectedpage": "Paggena prutetta",
        "jumpto": "Vaje a:",
        "createacct-another-username-ph": "'Nserisce 'o nomme utente",
        "yourpassword": "Password:",
        "userlogin-yourpassword": "Password",
-       "userlogin-yourpassword-ph": "'Nserisce 'a toja password",
+       "userlogin-yourpassword-ph": "Nzertàte 'a password vuosta",
        "createacct-yourpassword-ph": "'Nserisce 'na password",
        "yourpasswordagain": "Ripete 'a password:",
        "createacct-yourpasswordagain": "Cunferma password",
        "eauthentsent": "Na mmasciata 'e conferma t'è stata mannata a l'indirizzo e-mail nzignàto.\nApprimm' 'e te mannà n'atu mail, hè 'a stà 'a ffà 'e struzione dint'a l'e-mail, pe' cunfermà ca 'o cunto fosse d' 'o tujo overo.",
        "throttled-mailpassword": "S'è mannata na mail pe te' riabbià 'a password 'a meno 'e {{PLURAL:$1|n'ora|$1 ore}}.\nPe' ce sparagnà abbuse, 'a funzione 'e riabbiamento d' 'a password se può usa sulamente na vota ogne {{PLURAL:$1|ora|$1 ore}}.",
        "mailerror": "Errore pe' tramente ca se mannava na mmasciata: $1",
-       "acct_creation_throttle_hit": "{{PLURAL:$1|1 registrazzione è già stata effettuata|$1 registrazzione song già state effettuate}} 'e qualcuno cu 'o tujo stisso innerezzo IP dint'ô urdemo juorno: è 'o massimo cunsentito 'n chisto periodo 'e tiempo.\nPerciò, 'e utente ca ausano chisto innerezzo IP nun possono registrarse ppe 'o mumiento.",
+       "acct_creation_throttle_hit": "'E vvisite a sta wiki ausanno l'IP tuoja se so' mise a crià {{PLURAL:$1|1 registrazzione|$1 registrazzione}} int'a ll'urdeme juorne ($2), chesto fosse 'o massimo premmesso pe' stu periodo 'e tiempo.\nPerciò, 'e utente ca ausano chisto innerezzo IP nun se ponno riggistrà ancora mò mò.",
        "emailauthenticated": "'O ndirizzo email è stato cunfermato 'o $2 a 'e $3.",
        "emailnotauthenticated": "'O ndirizzo 'e posta elettronica nun è stat'ancora cunfermato.\nNun se mannarranno mmasciate e-mail p' ' funzione ccà abbascio.",
        "noemailprefs": "Avite 'a specificà nu ndirizzo e-mail pe ll'attivà sti funzione.",
        "botpasswords-label-delete": "Scancèlla",
        "botpasswords-label-resetpassword": "Riabbìa 'a password",
        "botpasswords-label-grants": "Assegnaziune apprecabbele:",
-       "botpasswords-help-grants": "Ogne assegnazione dà acciesso a 'e deritte utente elencate ca n'utenza avesse già. Vedite 'a [[Special:ListGrants|tabbella 'e ll'assegnaziune]] pe' ne mòvere cchiù nfurmaziune.",
+       "botpasswords-help-grants": "L'assegnazione premmettessero ausà deritte utente elencate ca n'utenza avesse già. Premmettenno st'assegnazione ccà nun è ca ve facesse trasì int'a sti deritte, pecché 'o cunto vuosto nun 'e tenisse pe' n'atu mezzo. Vedite 'a [[Special:ListGrants|tabbella 'e ll'assegnaziune]] pe' ne mòvere cchiù nfurmaziune.",
        "botpasswords-label-grants-column": "Assegnaziune date",
        "botpasswords-bad-appid": "'O nomme bot \"$1\" nun è bbuono.",
        "botpasswords-insert-failed": "Nun se pò azzeccà 'o nomme bot \"$1\". Fosse stato già azzeccato?",
        "botpasswords-updated-body": "'A password bot \"$1\" 'a ll'utente \"$2\" fuje agghiurnata.",
        "botpasswords-deleted-title": "Password bot scancellata",
        "botpasswords-deleted-body": "'A password bot \"$1\" 'a ll'utente \"$2\" è stata scancellata.",
-       "botpasswords-newpassword": "'A password nòva pe' puté trasì cu <strong>$1</strong> è <strong>$2</strong>. <em>Pe' piacere signatevello chesto pe' ve ffà conzurtaziune future.</em>",
+       "botpasswords-newpassword": "'A password nòva pe' puté trasì cu <strong>$1</strong> è <strong>$2</strong>. <em>Pe' piacere signatevello chesto pe' ve ffà conzurtaziune future.</em> <br>('E bott viecchie addò servisse nu nomme utente comm'a chell' 'e l'utente, putite ancora ausà <strong>$3</strong> comm' 'o nomm' 'utente e <strong>$4</strong> comm' 'a password.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider nun è disponibbele.",
        "botpasswords-restriction-failed": "'E restriziune 'e password bot nun ve permettessero st'acciesso.",
        "botpasswords-invalid-name": "'O nomme utente nnecato nun cuntenesse nu spartetóre 'e bot password (\"$1\").",
        "blockedtitle": "Utente bloccato.",
        "blockedtext": "<strong>'O nomme utente o ll'IP vuosto è stato bloccato.</strong>\n\n'O blocco è stato mpustato 'a $1. 'O mutivo d' 'o blocco è chesto: ''$2''\n\n* Abbiàta d' 'o blocco: $8\n* Ammaturità d' 'o blocco: $6\n* Tiempo 'e blocco: $7\n\nPutite cuntattà $1 o n'atu [[{{MediaWiki:Grouppage-sysop}}|ammenistratore]] pe' discutere 'o blocco.\n\nVedite c' 'a funzione 'Scrivete a ll'utente' nun è attiva si nun s'è riggistrato 'o ndirizzo e-mail buono dint' 'e [[Special:Preferences|preferenze]] o pùre si ll'uso 'e tale funzione è stato bloccato.\n\n'O ndirizzo IP attuale è $3, 'o nummero ID d' 'o blocco è #$5.\nPe' piacere avite 'e specificà tutte sti dettaglie ccà ncoppa quanno facite cocche dumanna.",
        "autoblockedtext": "Ll'IP vuosto è stato bloccato pecché 'o steva piglianno n'atu utente, ch'è stato bloccato pe' $1.\n\n'O mutivo d' 'o blocco è chesto:\n\n:''$2''\n\n* Abbiàta d' 'o blocco: $8\n* Ammaturità d' 'o blocco: $6\n* Tiempo 'e blocco: $7\n\nPutite cuntattà $1 o n'atu [[{{MediaWiki:Grouppage-sysop}}|ammenistratore]] pe' discutere 'o blocco.\n\nVedite c' 'a funzione 'Scrivete a ll'utente' nun è attiva si nun s'è riggistrato 'o ndirizzo e-mail buono dint' 'e [[Special:Preferences|preferenze]] o pùre si ll'uso 'e tale funzione è stato bloccato.\n\n'O ndirizzo IP attuale è $3, 'o nummero ID d' 'o blocco è #$5.\nPe' piacere avite 'e specificà tutte sti dettaglie ccà ncoppa quanno facite cocche dumanna.",
+       "systemblockedtext": "'O nomme utente d' 'o vuosto o ll'IP address fosse stata automaticamente bluccata 'a MediaWiki.\n'O mutivo fosse chesto:\n\n:<em>$2</em>\n\n* Inizio d' 'o blocco: $8\n* Ammatura 'o blocco: $6\n* Intervall' 'e blocco: $7\n\n'O indirizzo IP 'e mò fosse $3.\nPe' piacere, facite specifice tuttuquante 'e ddettaglie ccà quanno iate a ghienchere na richiesta 'e chiarimiente.",
        "blockednoreason": "nisciuna ragione è stata indicata",
        "whitelistedittext": "Pe' cagnà 'e ppaggene è necessario $1.",
        "confirmedittext": "Pe puté cagnà paggene avite 'a cunfermà l'indirizzo e-mail.\nPe' piacere abbiate e ffà 'a validazione d' 'o ndirizzo e-mail pe' bbìa d' 'e [[Special:Preferences|preferenze d'utente]].",
        "readonlywarning": "<strong>Attenziò</strong>: 'o database è bloccato pe se ffà 'a manutenzione. P' 'o mumento nun se ponno sarvà 'e cagnamiente fatte.\nPe' nun 'e sperdere, copia sti cuntenute dint'a nu file 'e testo e sarvatillo pe' tramente c'aspiette 'o sblocco d' 'o database.\n\nL'ammenistratore 'e sistema ca mpustaje 'o blocco ave scritto sta spiegazione: $1.",
        "protectedpagewarning": "'''Attenziò: sta paggena è stata bloccata 'n modo tale ca sulamente l'utente ch' 'e privilegge d'ammenistratore 'a ponno cagnà.'''\nL'urdemo elemento d' 'o riggistro è scritto ccà abbascio pe' n'avé riferimento:",
        "semiprotectedpagewarning": "'''Nota:''' Sta paggena è stata bloccata 'n modo ca sulamente l'utente riggistrate 'a ponno cagnà.\nL'urdemo elemento d' 'o riggistro è scritto ccà abbascio pe n'avé nfurmazione:",
-       "cascadeprotectedwarning": "'''Attenziò:''' Sta paggena è stata bloccata 'n modo ca sulamente l'utente ch' 'e privilegge d'ammenistratore 'a ponno cagnà. Chesto succiere pecché 'a paggena è appennuta dint'a {{PLURAL:$1|la paggena innecata ccà abbascio, ch'è stata prutetta|'e paggene innecate ccà abbascio, che so' state prutette}} sciglienno 'a prutezione \"ricurziva\":",
+       "cascadeprotectedwarning": "<strong>Attenziò:</strong> Sta paggena è stata bloccata 'n modo ca sulamente l'utente ch' 'e [[Special:ListGroupRights|privilegge specifiche]] 'a ponno cagnà. Chesto succiere pecché 'a paggena è appennuta dint'a {{PLURAL:$1|la paggena innecata ccà abbascio, ch'è stata prutetta|'e paggene innecate ccà abbascio, che so' state prutette}} sciglienno 'a prutezione \"ricurziva\":",
        "titleprotectedwarning": "'''Attenziò: sta paggena è stata bloccata 'n modo ca fossero necessarie [[Special:ListGroupRights|deritte specifici]] p' 'a crià.'''\nL'urdemo elemento d' 'o riggistro è riportato ccà abbascio pe nfurmazione:",
        "templatesused": "{{PLURAL:$1|Template|Templates}} ausate 'a chesta paggena:",
        "templatesusedpreview": "{{PLURAL:$1|Template|Templates}} ausate dint'a st'anteprimma:",
        "invalid-content-data": "Date cuntenute nun buone",
        "content-not-allowed-here": "'O cuntenuto \"$1\" nun è permesso dint'a paggena [[$2]]",
        "editwarning-warning": "Ascenno 'e sta paggena putisse ffà sperdere 'e cagnamiente fatte.\nSi sì trasuto, allora può stutà st'avviso dint'a sezziona \"{{int:prefs-editing}}\" d' 'e preferenze.",
+       "editpage-invalidcontentmodel-title": "Mudell' 'e cuntenute nun suppurtato",
+       "editpage-invalidcontentmodel-text": "'O mudell' 'e cuntenute \"$1\" nun è suppurtato.",
        "editpage-notsupportedcontentformat-title": "Furmato d' 'o cuntenuto nun suppurtato",
        "editpage-notsupportedcontentformat-text": "'O furmato d' 'o cuntenuto $1 nun è suppurtato d' 'o mudello 'e cuntenuto $2.",
        "content-model-wikitext": "wikitesto",
        "post-expand-template-argument-warning": "'''Attenziò:''' sta paggena cuntene uno o cchiù argumente 'e template troppo gruosse pe' 'a spannere. Sti argumente se lassarranno fore.",
        "post-expand-template-argument-category": "Paggene ca cunteneno argumente nun cunziderate",
        "parser-template-loop-warning": "È stato scummigliato n'aniello d' 'o template: [[$1]]",
+       "template-loop-category": "Paggene ca chiammassero a esse stisse",
+       "template-loop-category-desc": "Sta paggena tenesse nu template ca chiammasse a essa stissa, cioè nu template addò sta mmescat' 'o template ca 'o chiammasse.",
        "parser-template-recursion-depth-warning": "È arrivato 'o lemmeto 'e ricurzione d' 'o template ($1)",
        "language-converter-depth-warning": "'O fùto d' 'o lemmeto d' 'o scagnatòre 'e lengua è appassato ($1)",
        "node-count-exceeded-category": "Paggene addò 'o nummero 'e núrece è stato appassato",
        "page_first": "primma",
        "page_last": "úrdema",
        "histlegend": "Confronto nfra verziune: sciglite 'e casciulelle c'attoccassero a 'e verziune che vulite cunfruntà e spremmite Invio o pure 'o buttóne ccà abbascio.\n\nLiggenda: '''({{int:cur}})''' = differenze c' 'a verzione 'e mmò, '''({{int:last}})''' = differenze c' 'a verzione 'e primma, '''{{int:minoreditletter}}''' = cagnamiento minore",
-       "history-fieldset-title": "Naviga dint' 'a cronologgia",
-       "history-show-deleted": "Solo chille canciellate",
+       "history-fieldset-title": "Circa pe' verziune",
+       "history-show-deleted": "Sulo 'e verziune scancellate",
        "histfirst": "primma",
        "histlast": "urdema",
        "historysize": "({{PLURAL:$1|1 byte|$1 byte}})",
        "searchprofile-advanced-tooltip": "Circa dint'e namespace perzonalizzate",
        "search-result-size": "$1 ({{PLURAL:$2|'na parola|$2 parole}})",
        "search-result-category-size": "{{PLURAL:$1|1 utente|$1 utente}} ({{PLURAL:$2|1 sottocategurìa|$2 sottocategurìe}}, {{PLURAL:$3|1 file|$3 files}})",
-       "search-redirect": "(redirect $1)",
+       "search-redirect": "(redirect 'a $1)",
        "search-section": "(sezzione $1)",
        "search-category": "(categurìa $1)",
        "search-file-match": "(currispunnenza dint' 'e cuntenute d' 'o file)",
        "search-suggest": "Prova chisto: $1",
        "search-rewritten": "Mmustann' 'e risultate pe' $1. Circa mmece pe' $2.",
-       "search-interwiki-caption": "Prugiette frate",
+       "search-interwiki-caption": "Risultate 'a prugiette frate",
        "search-interwiki-default": "Risultate 'a $1:",
        "search-interwiki-more": "(cchiù)",
+       "search-interwiki-more-results": "cchiù risultate",
        "search-relatedarticle": "Azzeccato",
        "searchrelated": "azzeccato",
        "searchall": "Tutte",
        "search-external": "Ricerca 'a fore",
        "searchdisabled": "'A ricerca dint'a {{SITENAME}} nun è attiva; pe' tramente se putesse ausà nu mutore 'e cerca sterno comm'a Google. (Avite 'e sapé però, ca sti cuntenute d' 'o {{SITENAME}} dint' 'e mutore, può darse ca nun stanno agghiurnate.)",
        "search-error": "È succiesso n'errore pe' tramente ca se faceva 'a ricerca: $1",
+       "search-warning": "È succiesso n'avvertimento pe' tramente ca se vaceva 'a ricerca: $1",
        "preferences": "Preferenze d''e mmeje",
        "mypreferences": "Preferenze d''e mmeje",
        "prefs-edits": "Cagnamiente affettuate:",
        "prefs-help-recentchangescount": "Chesto ntenne ll'urdeme cagnamiente, 'e cronologgie 'e paggena, e riggistre.",
        "prefs-help-watchlist-token2": "Chest'è 'a chiave segreta pe se ffà 'o feed web 'e l'elenco 'e cuntrolo d' 'o vuosto.\nSi coccheruno 'a cunoscesse, allora putesse vedé l'elenco 'e cuntrollo, picciò nun 'a spartite. [[Special:ResetTokens|Cliccate ccà se tenite necessità d' 'a rimpizzà]].",
        "savedprefs": "'E preferenze songo state sarvate.",
-       "savedrights": "'E dritte 'e l'utente {{GENDER:$1|$1}} sto state sarvate.",
+       "savedrights": "'E dritte 'e gruppe 'utente {{GENDER:$1|$1}} sto state sarvate.",
        "timezonelegend": "Fuso orario:",
        "localtime": "Ora lucale:",
        "timezoneuseserverdefault": "Aúsa ora predefinita d' 'o wiki ($1)",
        "youremail": "E-mail:",
        "username": "{{GENDER:$1|Nomme utente}}:",
        "prefs-memberingroups": "{{GENDER:$2|Membro}} {{PLURAL:$1|d' 'o gruppo|d' 'e gruppe}}:",
+       "group-membership-link-with-expiry": "$1 (nzin' 'a $2)",
        "prefs-registration": "Data 'e riggistrazione:",
        "yourrealname": "Nomme vero",
        "yourlanguage": "Lengua:",
        "prefs-help-prefershttps": "Sta preferenza averrà affetto 'a 'o prossimo acciesso vuosto.",
        "prefswarning-warning": "Avite fatto cagnamiente a 'e preferenze d' 'e vuoste ca nun so' stat'ancora sarvate.\nSi ascite 'a sta paggena senza clickà \"$1\" 'e preferenze d' 'e vuoste nun sarranno agghiurnate.",
        "prefs-tabs-navigation-hint": "Suggerimento: se ponno ausà 'e buttòne 'e freccia a manca e a dritta pe' ve muovere nfra 'e schede dint'a l'elenco d' 'e schede.",
-       "userrights": "Gestione d' 'e permesse 'e l'utente",
-       "userrights-lookup-user": "Gestione 'e gruppe d'utenza",
+       "userrights": "Deritte utente",
+       "userrights-lookup-user": "Sciglie n'utente",
        "userrights-user-editname": "Nzertàte nu nomme utente:",
-       "editusergroup": "Cagnate 'e gruppe d'{{GENDER:$1|utenze}}",
+       "editusergroup": "Càrreca gruppe 'utente",
        "editinguser": "Cagnamiento d' 'e deritte d'{{GENDER:$1|utente}} '''[[User:$1|$1]]''' $2",
-       "userrights-editusergroup": "Cagnate 'e gruppe d'utenze",
+       "viewinguserrights": "Verenn' 'e deritte '{{GENDER:$1|utente}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "Cagna 'e gruppe '{{GENDER:$1|utente}}",
+       "userrights-viewusergroup": "Vire gruppe {{GENDER:$1|utente}}",
        "saveusergroups": "Sarvate 'e gruppe d'{{GENDER:$1|utenza}}",
        "userrights-groupsmember": "Ffà parte {{PLURAL:$1|d' 'o gruppo|d' 'e gruppe}}:",
        "userrights-groupsmember-auto": "Membro mplicito 'e:",
-       "userrights-groups-help": "Putite cagnà 'e gruppe assegnate a l'utente:\n* Na cascia 'e spunta scigliuta significasse ca appartenenza 'e l'utente a 'o gruppo\n* Na cascia 'e spunta nun scigliuta significasse 'a nun appartenenza a 'o gruppo.\n* 'O simmolo * significasse ca nun se può scancellà l'appartenenza a 'o gruppo aropp'a ll'avé miso (o viceversa).",
+       "userrights-groups-help": "Putite cagnà 'e gruppe assegnate a l'utente:\n* Na cascia 'e spunta scigliuta significasse ca appartenenza 'e l'utente a 'o gruppo\n* Na cascia 'e spunta nun scigliuta significasse 'a nun appartenenza a 'o gruppo.\n* 'O simmolo * significasse ca nun se può scancellà l'appartenenza a 'o gruppo aropp'a ll'avé miso (o viceversa).\n* 'O # significasse ca vuje putite surtanto tirà arreto nu tiempo 'e ammatura 'e stu gruppo utente; nun 'o putite fà annanze.",
        "userrights-reason": "Mutivo:",
        "userrights-no-interwiki": "Nun tenite permesse pe' cagnà 'e deritte 'e l'utente ncopp'a l'ati wiki.",
        "userrights-nodatabase": "'O database $1 nun esiste o nun è nu database lucale.",
        "userrights-changeable-col": "Gruppe ca putite cagnà",
        "userrights-unchangeable-col": "Gruppe ca nun putite cagnà",
+       "userrights-expiry-current": "Ammatura 'o $1",
+       "userrights-expiry-none": "Nun ammaturasse",
+       "userrights-expiry": "Ammatura:",
+       "userrights-expiry-existing": "'O tiempo d'ammaturamiento esistente: $3, $2",
+       "userrights-expiry-othertime": "N'ata durata:",
+       "userrights-expiry-options": "1 juorne:1 day,1 semmana:1 week,1 mese:1 month,3 mise:3 mise,6 mesi:6 months,1 anno:1 year",
+       "userrights-invalid-expiry": "'O tiempo pe' quanno ammatura 'o gruppo \"$1\" nun è buono.",
+       "userrights-expiry-in-past": "'O tiempo pe' quanno ammatura 'o gruppo \"$1\" fosse int' 'o passato.",
+       "userrights-cannot-shorten-expiry": "Nun putite turnà arreto 'o tiempo ammatura int' 'o gruppo \"$1\". Surtanto ll'utente cu nu permesso pe' puté azzeccà o luvà stu gruppo ponno ffà annanze 'e tiempe ammaturamiento.",
        "userrights-conflict": "Conflitto 'e cagnamiento 'e deritte utente! Cuntrullate e cunfermate 'e cagnamiente vuoste.",
        "group": "Gruppo:",
        "group-user": "Utente",
        "grant-basic": "Deritte 'e base",
        "grant-viewdeleted": "Vide 'e file e paggene scancellate",
        "grant-viewmywatchlist": "Vide l'elenco 'e cuntrullate",
+       "grant-viewrestrictedlogs": "Vide 'e valure private d' 'o riggistro",
        "newuserlogpage": "Riggistro 'e nuove utente",
        "newuserlogpagetext": "Chest'è nu riggistro 'e criazione d'utenze.",
        "rightslog": "Deritte 'e ll'utente",
        "action-upload_by_url": "carreca stu file 'a n'indirizzo URL",
        "action-writeapi": "usa l'API 'n scrittura",
        "action-delete": "scancèlla chista paggena",
-       "action-deleterevision": "scancellà sta verziona",
+       "action-deleterevision": "scancellà 'e verziune",
        "action-deletedhistory": "vide 'a cronologgia scancellata 'e sta paggena",
        "action-browsearchive": "ascìa dint' 'e paggene scancellate",
        "action-undelete": "arripiglia chista paggena",
        "fileduplicatesearch-noresults": "Nisciuno file chiamato \"$1\" è stato accucchiato.",
        "specialpages": "Paggene speciale",
        "specialpages-note-top": "Liggenda",
-       "specialpages-note": "* Paggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Paggene speciale ch' 'e restriziune.</span>",
        "specialpages-group-maintenance": "Report 'e manutenzione",
        "specialpages-group-other": "Ati paggene speciale",
        "specialpages-group-login": "Trasite o criate n'acciesso nuovo",
index 1bae5d8..2763df2 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (se også [[Special:NewPages|liste over nye sider]])",
        "recentchanges-legend-plusminus": "«(±123)»",
        "recentchanges-submit": "Vis",
+       "rcfilters-legend-heading": "<strong>Liste over forkortelser:</strong>",
        "rcfilters-activefilters": "Aktive filtre",
        "rcfilters-advancedfilters": "Avanserte filtre",
+       "rcfilters-limit-title": "Endringer som skal vises",
+       "rcfilters-limit-shownum": "Vis de siste $1 endringene",
+       "rcfilters-days-title": "De siste dagene",
+       "rcfilters-hours-title": "De siste timene",
+       "rcfilters-days-show-days": "{{PLURAL:$1|Én dag|$1 dager}}",
+       "rcfilters-days-show-hours": "{{PLURAL:$1|Én time|$1 timer}}",
        "rcfilters-quickfilters": "Lagrede filtre",
        "rcfilters-quickfilters-placeholder-title": "Ingen lenker lagret enda",
        "rcfilters-quickfilters-placeholder-description": "For å lagre filterinnstillingene og gjenbruk dem senere, klikk på bokmerkeikonet i området Aktive Filtre under.",
        "rcfilters-invalid-filter": "Ugyldig filter",
        "rcfilters-empty-filter": "Ingen aktive filtre. Alle bidrag vises.",
        "rcfilters-filterlist-title": "Filtre",
-       "rcfilters-filterlist-whatsthis": "Hver dette?",
+       "rcfilters-filterlist-whatsthis": "Hvordan virker dette?",
        "rcfilters-filterlist-feedbacklink": "Gi tilbakemelding på de nye (beta)filtrene",
        "rcfilters-highlightbutton-title": "Marker resultater",
        "rcfilters-highlightmenu-title": "Velg en farge",
        "rcfilters-noresults-conflict": "Ingen resultater funnet fordi søkekriteriene konflikterer",
        "rcfilters-state-message-subset": "Dette filteret har ingen effekt fordi resultatene dens inkluderes i følgende, bredere {{PLURAL:$2|filter|filtre}} (prøv å merke for å skille det ut): $1",
        "rcfilters-state-message-fullcoverage": "Å velge alle filtre i en gruppe er det samme som å velge ingen, så dette filteret har ingen effekt. Gruppa inkluderer: $1",
-       "rcfilters-filtergroup-registration": "Brukerregistrering",
-       "rcfilters-filter-registered-label": "Registrerte",
-       "rcfilters-filter-registered-description": "Innloggede brukere.",
-       "rcfilters-filter-unregistered-label": "Uregistrerte",
-       "rcfilters-filter-unregistered-description": "Brukere som ikke er logget inn.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Dette filteret konflikterer med følgende {{PLURAL:$2|erfaringsfilter|erfaringsfiltre}}, som finner kun registrerte brukere: $1",
        "rcfilters-filtergroup-authorship": "Bidragets forfatter",
        "rcfilters-filter-editsbyself-label": "Dine redigeringer",
        "rcfilters-filter-editsbyself-description": "Dine egne bidrag.",
        "rcfilters-filter-editsbyother-label": "Endringer av andre",
        "rcfilters-filter-editsbyother-description": "Alle endringer utenom dine egne.",
        "rcfilters-filtergroup-userExpLevel": "Erfaringsnivå (kun for registrerte brukere)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Erfaringsfiltre finner kun registrerte brukere, så dette filteret konflikterer med filteret «Uregistrerte».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filteret «Uregistrerte» konflikterer med ett eller flere erfaringsfiltre, som kun finner registrerte brukere. De konflikterende filtrene merkes i området for aktive filtre ovenfor.",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrerte",
+       "rcfilters-filter-user-experience-level-registered-description": "Innloggede brukere.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Uregistrerte",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Brukere som ikke er logget inn.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nykommere",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Færre enn 10 redigeringer og 4 dagers aktivitet.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registrerte brukere med færre enn 10 redigeringer og 4 dagers aktivitet.",
        "rcfilters-filter-user-experience-level-learner-label": "Nybegynnere",
-       "rcfilters-filter-user-experience-level-learner-description": "Mer erfaring enn «Nykommere», men mindre enn «Erfarne brukere».",
+       "rcfilters-filter-user-experience-level-learner-description": "Registrerte brukere med mer erfaring enn «Nykommere», men mindre enn «Erfarne brukere».",
        "rcfilters-filter-user-experience-level-experienced-label": "Erfarne brukere",
-       "rcfilters-filter-user-experience-level-experienced-description": "Mer enn 30 dagers aktivitet og 500 redigeringer.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registrerte brukere med mer enn 500 redigeringer og 30 dagers aktivitet.",
        "rcfilters-filtergroup-automated": "Automatiske bidrag",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Redigeringer gjort med automatiske verktøy.",
        "rcfilters-filter-previousrevision-description": "Alle endringer som ikke er den nyeste endringen av en side.",
        "rcfilters-filter-excluded": "Ekskludert",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Ekskluder de valgte",
+       "rcfilters-exclude-button-on": "Ekskluderer de valgte",
        "rcfilters-view-tags": "Taggede redigeringer",
+       "rcfilters-view-namespaces-tooltip": "Filtrer resultater etter navnerom",
+       "rcfilters-view-tags-tooltip": "Filtrer resultater med redigeringstagger",
+       "rcfilters-view-return-to-default-tooltip": "Tilbake til hovedmenyen for filtre",
+       "rcfilters-liveupdates-button": "Live-oppdateringer",
        "rcnotefrom": "Nedenfor er vist {{PLURAL:$5|endringen|endringene}} som er gjort siden <strong>$3, $4</strong> (frem til <strong>$1</strong>).",
        "rclistfromreset": "Nullstill datovalg",
        "rclistfrom": "Vis nye endringer fra og med $3 $2",
        "undelete-search-title": "Søk i slettede sider",
        "undelete-search-box": "Søk i slettede sider",
        "undelete-search-prefix": "Vis sider som starter med:",
+       "undelete-search-full": "Vis sidetitler som inneholder:",
        "undelete-search-submit": "Søk",
        "undelete-no-results": "Ingen passende sider funnet i slettingsarkivet.",
        "undelete-filename-mismatch": "Kan ikke gjenopprette filrevisjon med tidstrykk $1: ikke samsvarende filnavn",
        "fileduplicatesearch-noresults": "Ingen ved navn «$1» funnet.",
        "specialpages": "Spesialsider",
        "specialpages-note-top": "Tegnforklaring",
-       "specialpages-note": "* Normale spesialsider.\n* <span class=\"mw-specialpagerestricted\">Spesialsider med begrenset tilgang.</span>",
        "specialpages-group-maintenance": "Vedlikeholdsrapporter",
        "specialpages-group-other": "Andre spesialsider",
        "specialpages-group-login": "Innlogging / opprette bruker",
index 0334cd8..99041d4 100644 (file)
@@ -85,7 +85,8 @@
                        "Mainframe98",
                        "QZanden",
                        "Huhbakker",
-                       "Jos1950"
+                       "Jos1950",
+                       "Ooswesthoesbes"
                ]
        },
        "tog-underline": "Verwijzingen onderstrepen:",
        "tagline": "Uit {{SITENAME}}",
        "help": "Hulp",
        "search": "Zoeken",
-       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Koppen die worden genegeerd tijdens het zoeken.\n# Wijzigingen worden van kracht als een kop wordt geïndexeerd.\n# U kunt opnieuw indexeren afdwingen door het uitvoeren van een nullbewerking.\n# De syntaxis is al volgt:\n#   * All tekst vanaf het teken \"#\" tot het einde van de regel wordt gezien als een opmerking;\n#   * Iedere niet-lege regel is de precieze te negeren kop, inclusief hoofdlettergebruik en degelijke.\nReferenties\nExterne koppelingen\nZie ook\n #</pre> <!-- leave this line exactly as it is -->",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Koppen die worden genegeerd tijdens het zoeken.\n# Wijzigingen worden van kracht als een kop wordt geïndexeerd.\n# U kunt opnieuw indexeren afdwingen door het uitvoeren van een nullbewerking.\n# De syntaxis is als volgt:\n#   * Alle tekst vanaf het teken \"#\" tot het einde van de regel wordt gezien als een opmerking;\n#   * Iedere niet-lege regel is de precieze te negeren kop, inclusief hoofdlettergebruik en degelijke.\nReferenties\nExterne koppelingen\nZie ook\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Zoeken",
        "go": "OK",
        "searcharticle": "OK",
        "right-autocreateaccount": "Automatisch aanmelden met een extern gebruikersaccount",
        "right-minoredit": "Bewerkingen als klein markeren",
        "right-move": "Pagina's hernoemen",
-       "right-move-subpages": "Pagina's inclusief subpagina's verplaatsen",
+       "right-move-subpages": "Pagina's inclusief deelpagina's verplaatsen",
        "right-move-rootuserpages": "Gebruikerspagina's van het hoogste niveau hernoemen",
        "right-move-categorypages": "Categoriepagina's hernoemen",
        "right-movefile": "Bestanden hernoemen",
        "action-history": "de geschiedenis van deze pagina te bekijken",
        "action-minoredit": "deze bewerking als klein te markeren",
        "action-move": "deze pagina te hernoemen",
-       "action-move-subpages": "deze pagina en bijbehorende subpagina's te hernoemen",
+       "action-move-subpages": "deze pagina en bijbehorende deelpagina's te hernoemen",
        "action-move-rootuserpages": "gebruikerspagina's van het hoogste niveau te hernoemen",
        "action-move-categorypages": "categoriepagina's te hernoemen",
        "action-movefile": "dit bestand te hernoemen",
        "rcfilters-legend-heading": "<strong>Lijst met afkortingen:</strong>",
        "rcfilters-activefilters": "Actieve filters",
        "rcfilters-advancedfilters": "Geavanceerde filters",
+       "rcfilters-limit-title": "Wijzigingen om te tonen",
+       "rcfilters-limit-shownum": "Toon laatste $1 wijzigingen",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dagen}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|uur|uren}}",
        "rcfilters-quickfilters": "Opgeslagen filters",
        "rcfilters-quickfilters-placeholder-title": "Nog geen koppelingen opgeslagen",
        "rcfilters-quickfilters-placeholder-description": "Om uw filterinstellingen op te slaan en later te kunnen hergebruiken, klik op het bladwijzer pictogram in het Actieve Filter gebied beneden.",
        "rcfilters-savedqueries-unsetdefault": "Als standaard verwijderen",
        "rcfilters-savedqueries-remove": "Verwijderen",
        "rcfilters-savedqueries-new-name-label": "Naam",
+       "rcfilters-savedqueries-new-name-placeholder": "Beschrijf het doel van het filter",
        "rcfilters-savedqueries-apply-label": "Filter aanmaken",
        "rcfilters-savedqueries-cancel-label": "Annuleren",
        "rcfilters-savedqueries-add-new-title": "Huidige filter instellingen opslaan",
        "rcfilters-invalid-filter": "Ongeldig filter",
        "rcfilters-empty-filter": "Geen actieve filters. Alle bijdragen worden weergeven.",
        "rcfilters-filterlist-title": "Filters",
-       "rcfilters-filterlist-whatsthis": "Wat is dit?",
+       "rcfilters-filterlist-whatsthis": "Hoe werkt dit?",
        "rcfilters-filterlist-feedbacklink": "Geef terugkoppeling op de nieuwe (beta)filters",
        "rcfilters-highlightbutton-title": "Resultaten markeren",
        "rcfilters-highlightmenu-title": "Kies een kleur",
        "rcfilters-noresults-conflict": "Geen resultaten gevonden omdat de zoekcriteria met elkaar conflicteren",
        "rcfilters-state-message-subset": "Dit filter heeft geen effect omdat de resultaten ook worden gefilterd door {{PLURAL:$2|het|de}} bredere {{PLURAL:$2|filter|filters}} (probeer ze te markeren om ze te onderscheiden): $1",
        "rcfilters-state-message-fullcoverage": "Het selecteren van alle filters in deze groep is hetzelfde als geen filter selecteren, waardoor dit filter geen effect heeft. De groep bevat: $1",
-       "rcfilters-filtergroup-registration": "Gebruikersregistratie",
-       "rcfilters-filter-registered-label": "Geregistreerd",
-       "rcfilters-filter-registered-description": "Ingelogde gebruikers.",
-       "rcfilters-filter-unregistered-label": "Niet-geregistreerd",
-       "rcfilters-filter-unregistered-description": "Gebruikers die niet zijn ingelogd.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Het \"Niet geregistreerd\" filter is niet actief, omdat het effect ongedaan wordt gemaakt door de volgende {{PLURAL:$2|filter|filters}} die alleen geregistreerde gebruikers {{PLURAL:$2|vindt|vinden}}: $1",
        "rcfilters-filtergroup-authorship": "Auteur van de bijdragen",
        "rcfilters-filter-editsbyself-label": "Uw eigen wijzigingen",
        "rcfilters-filter-editsbyself-description": "Uw eigen bijdragen.",
        "rcfilters-filter-editsbyother-label": "Wijzigingen door anderen",
        "rcfilters-filter-editsbyother-description": "Alle wijzigingen behalve die door u gemaakt zijn.",
-       "rcfilters-filtergroup-userExpLevel": "Ervaringsniveau (alleen voor geregistreerde gebruikers)",
+       "rcfilters-filtergroup-userExpLevel": "Gebruikersregistratie en ervaring",
+       "rcfilters-filter-user-experience-level-registered-label": "Geregistreerd",
+       "rcfilters-filter-user-experience-level-registered-description": "Aangemelde bewerkers.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Niet-geregistreerd",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Bewerkers die niet zijn aangemeld.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nieuwkomers",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Minder dan 10 bewerkingen en 4 dagen van activiteit.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Geregistreerde bewerkers met minder dan 10 bewerkingen en 4 dagen van activiteit.",
        "rcfilters-filter-user-experience-level-learner-label": "Leerlingen",
-       "rcfilters-filter-user-experience-level-learner-description": "Meer ervaring dan \"nieuwkomers\", maar minder dan \"ervaren gebruikers\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Geregistreerde bewerkers met meer ervaring dan \"nieuwkomers\", maar minder dan \"ervaren gebruikers\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Ervaren gebruikers",
-       "rcfilters-filter-user-experience-level-experienced-description": "Meer dan 30 dagen van activiteit en 500 bewerkingen.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Geregistreerde bewerkers met meer dan 500 bewerkingen en 30 dagen van activiteit.",
        "rcfilters-filtergroup-automated": "Automatische bijdragen",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "De wijzigingen van geautomatiseerde hulpmiddelen.",
        "rcfilters-filter-logactions-label": "Geregistreerde acties",
        "rcfilters-filter-logactions-description": "Administratieve handelingen, account creaties, pagina verwijderingen, uploads…",
        "rcfilters-hideminor-conflicts-typeofchange": "Bepaalde soorten wijzigingen kunnen niet worden aangemerkt als \"klein\", dus dit filter is in conflict met de volgende soorten wijzigingenfilters: $1",
-       "rcfilters-filtergroup-lastRevision": "Laatste versie",
+       "rcfilters-filtergroup-lastRevision": "Laatste versies",
        "rcfilters-filter-lastrevision-label": "Laatste versie",
-       "rcfilters-filter-lastrevision-description": "De meest recente wijziging aan de pagina.",
-       "rcfilters-filter-previousrevision-label": "Eerdere versies",
-       "rcfilters-filter-previousrevision-description": "Alle wijzigingen die niet de meest recente wijziging op de pagina zijn.",
+       "rcfilters-filter-lastrevision-description": "Alleen de meest recente wijziging aan de pagina.",
+       "rcfilters-filter-previousrevision-label": "Niet de laatste versie",
+       "rcfilters-filter-previousrevision-description": "Alle wijzigingen die niet de \"laatste versie\" zijn.",
        "rcfilters-filter-excluded": "Uitgesloten",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:niet</strong> $1",
+       "rcfilters-exclude-button-off": "Geselecteerde uitsluiten",
        "rcfilters-view-tags": "Gelabelde bewerkingen",
        "rcfilters-view-namespaces-tooltip": "Filter resultaten op naamruimte",
        "rcfilters-view-tags-tooltip": "Filter resultaten door middel van bewerkingslabels",
        "delete-warning-toobig": "Deze pagina heeft een lange bewerkingsgeschiedenis, meer dan $1 {{PLURAL:$1|versie|versies}}.\nHet verwijderen van deze pagina kan de werking van de database van {{SITENAME}} verstoren.\nWees voorzichtig.",
        "deleteprotected": "U kunt deze pagina niet verwijderen omdat hij is beveiligd.",
        "deleting-backlinks-warning": "<strong>Waarschuwing:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|andere pagina's]] gebruiken of verwijzen naar de pagina die u wilt verwijderen.",
+       "deleting-subpages-warning": "<strong>Waarschuwing:</strong>De pagina die u wilt verwijderen heeft [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|een deelpagina|$1 deelpagina's|51=meer dan 50 deelpagina's}}]].",
        "rollback": "Wijzigingen ongedaan maken",
        "rollbacklink": "terugdraaien",
        "rollbacklinkcount": "{{PLURAL:$1|één bewerking|$1 bewerkingen}} terugdraaien",
        "undelete-search-title": "Verwijderde pagina's zoeken",
        "undelete-search-box": "Verwijderde pagina's doorzoeken",
        "undelete-search-prefix": "Pagina's bekijken die beginnen met:",
-       "undelete-search-full": "Toon pagina titels met de volgende inhoud:",
+       "undelete-search-full": "Toon paginatitels die het volgende bevatten:",
        "undelete-search-submit": "Zoeken",
        "undelete-no-results": "Geen pagina's gevonden in het archief met verwijderde pagina's.",
        "undelete-filename-mismatch": "Bestandsversie van tijdstip $1 kon niet hersteld worden: bestandsnaam klopte niet",
        "articleexists": "De pagina bestaat al of de paginanaam is ongeldig.\nKies een andere paginanaam.",
        "cantmove-titleprotected": "U kunt geen pagina naar deze naam hernoemen, omdat deze naam beveiligd is tegen het aanmaken ervan.",
        "movetalk": "Bijbehorende overlegpagina hernoemen",
-       "move-subpages": "Subpagina's hernoemen (maximaal $1)",
-       "move-talk-subpages": "Subpagina's van overlegpagina's hernoemen (maximaal $1)",
+       "move-subpages": "Deelpagina's hernoemen (maximaal $1)",
+       "move-talk-subpages": "Deelpagina's van overlegpagina's hernoemen (maximaal $1)",
        "movepage-page-exists": "De pagina $1 bestaat al en kan niet automatisch verwijderd worden.",
        "movepage-page-moved": "De pagina $1 is hernoemd naar $2.",
        "movepage-page-unmoved": "De pagina $1 kon niet hernoemd worden naar $2.",
        "movepage-max-pages": "Het maximale aantal automatisch te hernoemen pagina's is bereikt ({{PLURAL:$1|$1|$1}}).\nDe overige pagina's worden niet automatisch hernoemd.",
        "movelogpage": "Hernoemingslogboek",
        "movelogpagetext": "Hieronder staan hernoemde pagina's.",
-       "movesubpage": "{{PLURAL:$1|Subpagina|Subpagina's}}",
-       "movesubpagetext": "De {{PLURAL:$1|subpagina|$1 subpagina's}} van deze pagina {{PLURAL:$1|wordt|worden}} hieronder weergegeven.",
+       "movesubpage": "{{PLURAL:$1|Deelpagina|Deelpagina's}}",
+       "movesubpagetext": "De {{PLURAL:$1|deelpagina|$1 deelpagina's}} van deze pagina {{PLURAL:$1|wordt|worden}} hieronder weergegeven.",
        "movesubpagetalktext": "De bijbehorende overlegpagina heeft $1 {{PLURAL:$1|deelpagina|deelpagina's}} hierbeneden getoond.",
-       "movenosubpage": "Deze pagina heeft geen subpagina's.",
+       "movenosubpage": "Deze pagina heeft geen deelpagina's.",
        "movereason": "Reden:",
        "revertmove": "terugdraaien",
        "delete_and_move_text": "Onder de naam \"[[:$1]]\" bestaat al een pagina.\nWilt u deze verwijderen om plaats te maken voor de te hernoemen pagina?",
        "import-interwiki-submit": "Importeren",
        "import-mapping-default": "Importeren naar standaardplaatsen",
        "import-mapping-namespace": "Importeren naar een naamruimte:",
-       "import-mapping-subpage": "Importeren als subpagina's van de volgende pagina:",
+       "import-mapping-subpage": "Importeren als deelpagina's van de volgende pagina:",
        "import-upload-filename": "Bestandsnaam:",
        "import-comment": "Opmerking:",
        "importtext": "Gebruik de [[Special:Export|exportfunctie]] in de wiki waar de informatie vandaan komt.\nSla de uitvoer op uw eigen computer op, en voeg die daarna hier toe.",
        "import-error-bad-location": "Versie $2 met behulp van model $3 kan niet worden opgeslagen als \"$1\" op deze wiki, aangezien dat model niet ondersteund wordt op die pagina.",
        "import-options-wrong": "Verkeerde {{PLURAL:$2|optie|opties}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "De opgegeven basispagina is ongeldig.",
-       "import-rootpage-nosubpage": "In de naamruimte \"$1\" van de basispagina is het aanmaken van subpagina's niet mogelijk.",
+       "import-rootpage-nosubpage": "In de naamruimte \"$1\" van de basispagina is het aanmaken van deelpagina's niet mogelijk.",
        "importlogpage": "Importlogboek",
        "importlogpagetext": "Administratieve import van pagina's met geschiedenis van andere wiki's.",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|versie|versies}} geïmporteerd",
        "tooltip-minoredit": "Deze wijziging als een kleine wijziging markeren",
        "tooltip-save": "Wijzigingen opslaan",
        "tooltip-publish": "Uw wijzigingen publiceren",
-       "tooltip-preview": "Een voorvertoning maken. Gebruik dit voordat u opslaat!",
+       "tooltip-preview": "Een voorbeeldweergave ophalen. Gebruik dit voordat u opslaat.",
        "tooltip-diff": "Weergeven welke wijzigingen u aan de tekst hebt gemaakt",
        "tooltip-compareselectedversions": "De verschillen tussen de geselecteerde versies van deze pagina bekijken.",
        "tooltip-watch": "Deze pagina aan uw volglijst toevoegen",
        "pageinfo-few-watchers": "Minder dan  {{PLURAL:$1|één volger|$1 volgers}}",
        "pageinfo-few-visiting-watchers": "Er kan wel of niet een volger zijn die de laatste bewerkingen hier bezoekt",
        "pageinfo-redirects-name": "Aantal doorverwijzingen naar deze pagina",
-       "pageinfo-subpages-name": "Subpagina's van deze pagina",
+       "pageinfo-subpages-name": "Aantal deelpagina's van deze pagina",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|doorverwijzing|doorverwijzingen}}; $3 {{PLURAL:$3|niet-doorverwijzing|niet-doorverwijzingen}})",
        "pageinfo-firstuser": "Gebruiker die de pagina heeft aangemaakt",
        "pageinfo-firsttime": "Datum waarop de pagina is aangemaakt",
        "fileduplicatesearch-noresults": "Er is geen bestand met de naam \"$1\" gevonden.",
        "specialpages": "Speciale pagina's",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normale speciale pagina's\n* <span class=\"mw-specialpagerestricted\">Beperkt toegankelijke speciale pagina's</span>",
+       "specialpages-note-restricted": "* Normale speciale pagina's.\n* <span class=\"mw-specialpagerestricted\">Beperkt toegankelijke speciale pagina's.</span>",
        "specialpages-group-maintenance": "Onderhoudsrapporten",
        "specialpages-group-other": "Overige speciale pagina's",
        "specialpages-group-login": "Aanmelden / registreren",
index 21533aa..c6c66f0 100644 (file)
        "preview": "Førehandsvising",
        "showpreview": "Førehandsvis",
        "showdiff": "Sjå skilnader",
+       "blankarticle": "<strong>Åtvaring:</strong> Sida du er i ferd med å oppretta er tom.\nKlikkar du på «$1» ein gong til vil sida opprettast utan innhald.",
        "anoneditwarning": "'''Åtvaring:''' Du er ikkje innlogga.\nIP-adressa di vil verta lagra i den offentlege endringshistorikken til sida. Om du <strong>[$1 loggar inn]</strong> eller <strong>[$2 lagar ein konto]</strong>, vil endringane dine knytast til brukarnamnet ditt, saman med andre fordelar.",
        "anonpreviewwarning": "''Du er ikkje innlogga. Lagrar du vil IP-adressa di verta ført opp i endringshistorikken til denne sida.''",
        "missingsummary": "'''Påminning:''' Du har ikkje skrive noko endringssamandrag. Dersom du trykkjer «Lagre» ein gong til, vert endringa di lagra utan.",
        "post-expand-template-inclusion-category": "Sider som inneheld for store malar",
        "post-expand-template-argument-warning": "Åtvaring: Sida inneheld ein eller fleire malparameterar som vert for lange når dei utvidast.\nDesse parameterane har vorte utelatne.",
        "post-expand-template-argument-category": "Sider med utelatne malparameterar",
-       "parser-template-loop-warning": "Malløkka oppdaga: [[$1]]",
+       "parser-template-loop-warning": "Mallykkje oppdaga: [[$1]]",
        "parser-template-recursion-depth-warning": "Malen er inkludert for mange gonger ($1)",
        "language-converter-depth-warning": "Språkomformaren si djubdegrense vart overstege ($1)",
        "node-count-exceeded-category": "Sider der talet på knutepunkt er overskride",
        "rcfilters-highlightbutton-title": "Uthev resultat",
        "rcfilters-highlightmenu-title": "Vel ein farge",
        "rcfilters-filterlist-noresults": "Fann ingen filter",
-       "rcfilters-filtergroup-registration": "Brukarregistrering",
-       "rcfilters-filter-registered-label": "Registrerte",
-       "rcfilters-filter-registered-description": "Innlogga brukarar.",
-       "rcfilters-filter-unregistered-label": "Uregistrerte",
-       "rcfilters-filter-unregistered-description": "Brukarar som ikkje er innlogga.",
        "rcfilters-filtergroup-authorship": "Forfattar",
        "rcfilters-filter-editsbyself-label": "Endringar av deg",
        "rcfilters-filter-editsbyself-description": "Dine eigne bidrag.",
        "rcfilters-filter-editsbyother-label": "Endringar av andre",
        "rcfilters-filter-editsbyother-description": "Alle endringar utanom dine eigne.",
        "rcfilters-filtergroup-userExpLevel": "Røynslenivå (berre for registrerte brukarar)",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrerte",
+       "rcfilters-filter-user-experience-level-registered-description": "Innlogga brukarar.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Uregistrerte",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Brukarar som ikkje er innlogga.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nykomarar",
        "rcfilters-filter-user-experience-level-newcomer-description": "Færre enn 10 endringar og 4 dagar med aktivitet.",
        "rcfilters-filter-user-experience-level-learner-label": "Nybyrjarar",
        "fileduplicatesearch-noresults": "Fann inga fil med namnet «$1».",
        "specialpages": "Spesialsider",
        "specialpages-note-top": "Tyding",
-       "specialpages-note": "* Vanlege spesialsider.\n* <span class=\"mw-specialpagerestricted\">Spesialsider med avgrensa tilgang.</span>",
        "specialpages-group-maintenance": "Vedlikehaldsrapportar",
        "specialpages-group-other": "Andre spesialsider",
        "specialpages-group-login": "Logga inn / oppretta brukarkonto",
        "logentry-move-move_redir": "$1 {{GENDER:$2|flytte}} sida $3 til $4 over ei omdirigering",
        "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|flytte}} sida $3 til $4 over ei omdirigering utan å lata etter ei omdirigering",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|merkte}} versjon $4 av sida $3 som patruljert",
-       "logentry-patrol-patrol-auto": "$1{{GENDER:$2| merkte}} automatisk versjon $4 av sida $3 som patruljert",
+       "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|merkte}} automatisk versjon $4 av sida $3 som patruljert",
        "logentry-newusers-newusers": "Brukarkontoen $1 vart {{GENDER:$2|oppretta}}",
        "logentry-newusers-create": "Brukarkontoen $1 vart {{GENDER:$2|oppretta}}",
        "logentry-newusers-create2": "Brukarkontoen $3 vart {{GENDER:$2|oppretta}} av $1",
index 788ef3f..61a40b3 100644 (file)
        "rcfilters-legend-heading": "<strong>Wykaz skrótów:</strong>",
        "rcfilters-activefilters": "Aktywne filtry",
        "rcfilters-advancedfilters": "Zaawansowane filtry",
+       "rcfilters-limit-title": "Zmian do pokazania",
+       "rcfilters-limit-shownum": "Pokaż ostatnie $1 zmian",
+       "rcfilters-days-title": "Ostatnich dni",
+       "rcfilters-hours-title": "Ostatnich godzin",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dzień|dni}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|godzina|godziny|godzin}}",
        "rcfilters-quickfilters": "Zapisane filtry",
        "rcfilters-quickfilters-placeholder-title": "Nie masz jeszcze zapisanych linków",
        "rcfilters-quickfilters-placeholder-description": "Aby zapisać ustawienia filtrów i używać ich później, kliknij ikonkę zakładki w polu aktywnych filtrów znajdującym się niżej.",
        "rcfilters-invalid-filter": "Nieprawidłowy filtr",
        "rcfilters-empty-filter": "Brak aktywnych filtrów. Wyświetlane są wszystkie zmiany.",
        "rcfilters-filterlist-title": "Filtry",
-       "rcfilters-filterlist-whatsthis": "Co to jest?",
+       "rcfilters-filterlist-whatsthis": "Jak działają?",
        "rcfilters-filterlist-feedbacklink": "Podziel się swoją opinią na temat tych nowych (beta) filtrów",
        "rcfilters-highlightbutton-title": "Podświetl wyniki",
        "rcfilters-highlightmenu-title": "Wybierz kolor",
        "rcfilters-noresults-conflict": "Nie znaleziono wyników z powodu konfliktu kryteriów wyszukiwania",
        "rcfilters-state-message-subset": "Ten filtr nie wywoła efektu, ponieważ pasujące do niego wyniki są zawarte w bardziej {{PLURAL:$2|szczegółowym filtrze|szczegółowych filtrach}} (spróbuj podświetlić, aby je rozróżnić): $1",
        "rcfilters-state-message-fullcoverage": "Wybranie wszystkich filtrów w grupie działa tak samo jak niewybranie żadnego, więc taki filtr nie da żadnego efektu. W grupie zawarto też: $1",
-       "rcfilters-filtergroup-registration": "Rejestracja użytkownika",
-       "rcfilters-filter-registered-label": "Zarejestrowani",
-       "rcfilters-filter-registered-description": "Zalogowani edytorzy.",
-       "rcfilters-filter-unregistered-label": "Niezarejestrowani",
-       "rcfilters-filter-unregistered-description": "Niezalogowani",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Ten filtr koliduje z {{PLURAL:$2|poniższym filtrem|poniższymi filtrami}} doświadczenia, {{PLURAL:$2|który wyszukuje|które wyszukują}} tylko zarejestrowanych użytkowników: $1",
        "rcfilters-filtergroup-authorship": "Autorstwo wkładu",
        "rcfilters-filter-editsbyself-label": "Zmiany dokonane przeze mnie",
        "rcfilters-filter-editsbyself-description": "Czynności dokonane przez Ciebie.",
        "rcfilters-filter-editsbyother-label": "Zmiany dokonane przez innych",
        "rcfilters-filter-editsbyother-description": "Wszystkie zmiany oprócz Twoich.",
-       "rcfilters-filtergroup-userExpLevel": "Poziom doświadczenia (tylko o zarejestrowanych użytkownikach)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filtry doświadczenia wyszukują tylko edycje zarejestrowanych użytkowników, więc ten filtr koliduje z filtrem „Niezarejestrowani”.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filtr \"Niezarejestrowani\" koliduje z jednym lub więcej filtrów z filtrów poziomu doświadczenia, które służą do wyszukiwania wkładu tylko zarejestrowanych użytkowników. Kolidujące filtry zostały odpowiednio oznaczone na pasku Aktywnych filtrów.",
+       "rcfilters-filtergroup-userExpLevel": "Zarejestrowanie użytkownika i doświadczenie",
+       "rcfilters-filter-user-experience-level-registered-label": "Zarejestrowani",
+       "rcfilters-filter-user-experience-level-registered-description": "Zalogowani edytorzy.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Niezarejestrowani",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Niezalogowani",
        "rcfilters-filter-user-experience-level-newcomer-label": "Początkujący",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Mniej niż 10 edycji i 4 dni aktywności.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Zarejestrowani edytorzy z mniej niż 10 edycji i 4 dni aktywności.",
        "rcfilters-filter-user-experience-level-learner-label": "Uczący się",
-       "rcfilters-filter-user-experience-level-learner-description": "Większe doświadczenie niż „Nowicjusze”, ale mniejsze niż „Doświadczeni użytkownicy”.",
+       "rcfilters-filter-user-experience-level-learner-description": "Zarejestrowani edytujący, których doświadczenie plasuje się między „Nowicjuszami”, a „Doświadczonymi użytkownikami”.",
        "rcfilters-filter-user-experience-level-experienced-label": "Doświadczeni użytkownicy",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ponad 30 dni aktywności i 500 edycji.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Zarejestrowani edytujący z ponad 500 edycji i 30 dni aktywności.",
        "rcfilters-filtergroup-automated": "Zmiany zautomatyzowane",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Zmiany wykonane z użyciem zautomatyzowanych narzędzi.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filtr „Drobne zmiany” koliduje z jednym lub wieloma filtrami Rodzaju zmian, ponieważ niektóre rodzaje zmian nie mogą być uznawane za  „drobne”. Kolidujące filtry zostały powyżej odpowiednio zaznaczone na pasku aktywnych filtrów.",
        "rcfilters-hideminor-conflicts-typeofchange": "Niektóre rodzaje zmian nie mogą być uznawane za „drobne”, dlatego ten filtr koliduje z następującymi filtrami Rodzaju zmian: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Ten filtr Rodzaju zmian koliduje z filtrem „Drobne zmiany”. Nie wszystkie zmiany mogą być uznawane za „drobne”.",
-       "rcfilters-filtergroup-lastRevision": "Ostatnia wersja",
-       "rcfilters-filter-lastrevision-label": "Ostatnie wersje",
+       "rcfilters-filtergroup-lastRevision": "Ostatnie wersje",
+       "rcfilters-filter-lastrevision-label": "Najnowsza wersja",
        "rcfilters-filter-lastrevision-description": "Tylko najnowsze zmiany dla każdej ze stron.",
-       "rcfilters-filter-previousrevision-label": "Wcześniejsze wersje",
-       "rcfilters-filter-previousrevision-description": "Wszystkie edycje, które nie są najnowszą zmianą strony.",
+       "rcfilters-filter-previousrevision-label": "Wersje inne niż najnowsza",
+       "rcfilters-filter-previousrevision-description": "Wszystkie edycje, które nie są najnowszą wersją strony.",
        "rcfilters-filter-excluded": "Wykluczono",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:nie z</strong> $1",
+       "rcfilters-exclude-button-off": "Wyklucz zaznaczone",
+       "rcfilters-exclude-button-on": "Zaznaczone są wykluczone",
        "rcfilters-view-tags": "Edycje ze znacznikami zmian",
        "rcfilters-view-namespaces-tooltip": "Przefiltruj wyniki według przestrzeni nazw",
        "rcfilters-view-tags-tooltip": "Przefiltruj wyniki według znaczników zmian",
        "delete-warning-toobig": "Ta strona ma bardzo długą historię edycji – ponad $1 {{PLURAL:$1|zmianę|zmiany|zmian}}.<br />\nBądź ostrożny, ponieważ usunięcie jej może spowodować zakłócenia w pracy {{GRAMMAR:D.lp|{{SITENAME}}}}.",
        "deleteprotected": "Nie możesz usunąć tej strony, ponieważ została zabezpieczona.",
        "deleting-backlinks-warning": "<strong>Uwaga:</strong> Do strony, którą masz zamiar usunąć, odwołują się [[Special:WhatLinksHere/{{FULLPAGENAME}}|inne strony]].",
+       "deleting-subpages-warning": "<strong>Ostrzeżenie:</strong> Strona którą chcesz usunąć ma [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|jedną podstronę|$1 podstrony|$1 podstron|51=ponad 50 podstron}}]].",
        "rollback": "Cofnij edycję",
        "rollbacklink": "cofnij",
        "rollbacklinkcount": "cofnij $1 {{PLURAL:$1|edycję|edycje|edycji}}",
        "lastmodifiedatby": "Ostatnia edycja tej strony: $2, $1 (autor zmian: $3)",
        "othercontribs": "Inni autorzy: $1.",
        "others": "inni",
-       "siteusers": "{{PLURAL:$2|użytkownik |użytkownicy}}{{GRAMMAR:D.lp|{{SITENAME}}}}{{PLURAL:$2||:}} $1",
+       "siteusers": "{{PLURAL:$2|użytkownik|użytkownicy}} {{GRAMMAR:D.lp|{{SITENAME}}}}{{PLURAL:$2||:}} $1",
        "anonusers": "{{PLURAL:$2|niezalogowany użytkownik|niezalogowani użytkownicy}} {{GRAMMAR:D.lp|{{SITENAME}}}}{{PLURAL:$2||:}} $1",
        "creditspage": "Autorzy",
        "nocredits": "Brak informacji o autorach tej strony.",
        "fileduplicatesearch-noresults": "Brak pliku o nazwie „$1”.",
        "specialpages": "Strony specjalne",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne strony specjalne.\n* <span class=\"mw-specialpagerestricted\">Zastrzeżone strony specjalne.</span>",
+       "specialpages-note-restricted": "* Normalne strony specjalne.\n* <span class=\"mw-specialpagerestricted\">Zastrzeżone strony specjalne.</span>",
        "specialpages-group-maintenance": "Raporty konserwacyjne",
        "specialpages-group-other": "Inne strony specjalne",
        "specialpages-group-login": "Logowanie / rejestracja",
        "log-action-filter-suppress-reblock": "Ukrycie użytkownika poprzez ponowną blokadę",
        "log-action-filter-upload-upload": "Nowe przesłane",
        "log-action-filter-upload-overwrite": "Przesłane ponownie",
+       "authmanager-authn-not-in-progress": "Tworzenie konta nie jest wykonywane lub dane sesji zostały utracone. Zacznij od początku.",
        "authmanager-authn-no-primary": "Podane dane nie mogą być użyte do kontroli poświadczeń.",
        "authmanager-authn-no-local-user": "Żaden użytkownik nie korzysta z tych danych.",
        "authmanager-authn-autocreate-failed": "Automatyczne tworzenie lokalnego konta nie powiodło się: $1",
index 81b6f72..eda97ca 100644 (file)
@@ -14,7 +14,8 @@
                        "Matma Rex",
                        "Saanvel",
                        "Satdeep gill",
-                       "Abbas dhothar"
+                       "Abbas dhothar",
+                       "Saraiki"
                ]
        },
        "tog-underline": "جوڑ تھلے لین:",
        "category-file-count-limited": "اس گٹھ چ اے {{PLURAL:$1|فائل اے|$1 فائلاں نیں}}۔",
        "listingcontinuesabbrev": "جاری",
        "index-category": "انڈیکسڈ صفے",
-       "noindex-category": "نان انڈیکسڈ صفے",
+       "noindex-category": "نان انڈیکسڈ ورقے",
        "broken-file-category": "ٹٹے ہوۓ جوڑاں آلے صفحے",
        "about": "بارے چ",
        "article": "آرٹیکل والا صفہ",
        "anontalk": "گل",
        "navigation": "کھوج",
        "and": "&#32;تے",
-       "qbfind": "کھوج",
-       "qbbrowse": "لبو",
-       "qbedit": "لکھو",
-       "qbpageoptions": "اے صفہ",
-       "qbmyoptions": "میرے صفے",
        "faq": "FAQ",
-       "faqpage": "Project:FAQ",
        "actions": "کم",
        "namespaces": "ناں تھانواں:",
        "variants": "قسماں",
        "view": "وکھالہ",
        "view-foreign": "$1 تے ویکھو",
        "edit": "لکھو",
+       "edit-local": "مقامی تفصیل درج کرو",
        "create": "بناؤ",
        "create-local": "آپنی لکھت رلاؤ",
-       "editthispage": "اس صفحہ تے لکھو",
-       "create-this-page": "اے صفحہ بناؤ",
        "delete": "مٹاؤ",
-       "deletethispage": "اے صفحہ مٹاؤ",
-       "undeletethispage": "اس صفحے نوں واپس لیاؤ",
        "undelete_short": "مٹانا واپس {{PLURAL:$1|اکتبدیلی|$1 تبدیلی}}",
        "viewdeleted_short": "ویکھو {{PLURAL:$1|اک مٹائی گئی تبدیلی|$1 مٹائیاں گئیاں تبدیلیاں}}",
        "protect": "بچاؤ",
        "protect_change": "تبدیل کرو",
-       "protectthispage": "اے صفحہ بچاؤ",
        "unprotect": "اینا بچاؤ",
-       "unprotectthispage": "اے صفحہ اینا بچاؤ",
        "newpage": "نواں صفہ",
-       "talkpage": "اس صفحے دے بارے چ گل بات کرو",
        "talkpagelinktext": "گل بات",
        "specialpage": "خاص صفحہ",
        "personaltools": "ذاتی اوزار",
-       "articlepage": "مضمون آلا صفحہ",
        "talk": "گل بات",
        "views": "وکھالے",
        "toolbox": "سَند",
-       "userpage": "ورتن آلے دا صفہ ویکھو",
-       "projectpage": "ویونت والا صفہ ویکھو",
        "imagepage": "فائل آلا صفہ ویکھو",
        "mediawikipage": "سنیعا آلا صفحہ ویکھو",
        "templatepage": "سچے آلا صفحہ ویکھو",
        "fileduplicatesearch-result-n": "فائل ''$1'' چ {{PLURAL:$2|1 رلدی نقل|$2 رلدیاں نقلں}} نیں۔",
        "fileduplicatesearch-noresults": "\"$1\" ناں دی کوئی فائل نئیں لبی۔",
        "specialpages": "خاص صفے",
-       "specialpages-note": "* نارمل خاص صفے.\n* <span class=\"mw-specialpagerestricted\">روکے گۓ خاص صفے.</span>\n* <span class=\"mw-specialpagecached\">کاشے خاص صفے (پرانے ہوگۓ ہون).</span>",
        "specialpages-group-maintenance": "مرمت رپورٹ",
        "specialpages-group-other": "ہور خاص صفے",
        "specialpages-group-login": "لاگان / کھاتہ کھولو",
index 3116f42..292fe61 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista de abreviaturas:</strong>",
        "rcfilters-activefilters": "Filtros ativos",
        "rcfilters-advancedfilters": "Filtros avançados",
+       "rcfilters-limit-title": "Mudanças para mostrar",
+       "rcfilters-limit-shownum": "Mostrar as últimas $1 modificações",
+       "rcfilters-days-title": "Dias recentes",
+       "rcfilters-hours-title": "Horas recentes",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dia|dias}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros salvos",
        "rcfilters-quickfilters-placeholder-title": "Ainda não foi gravado nenhum link",
        "rcfilters-quickfilters-placeholder-description": "Para gravar as suas configurações dos filtros e reutilizá-las mais tarde, clique o ícone do marcador de página, na área Filtro Ativo abaixo.",
        "rcfilters-invalid-filter": "Filtro inválido",
        "rcfilters-empty-filter": "Nenhum filtro ativo. Todas as contribuições são mostradas.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "O que é isso?",
+       "rcfilters-filterlist-whatsthis": "Como funcionam estes?",
        "rcfilters-filterlist-feedbacklink": "Forneça feedback sobre os novos filtros (beta)",
        "rcfilters-highlightbutton-title": "Realçar os resultados",
        "rcfilters-highlightmenu-title": "Selecione uma cor",
        "rcfilters-noresults-conflict": "Nenhum resultado encontrado porque os critérios de pesquisa estão em conflito",
        "rcfilters-state-message-subset": "Este filtro não tem efeito porque seus resultados estão incluídos com {{PLURAL:$2|o seguinte filtro mais amplo|os seguintes filtros mais amplos}} (tente realçar para distingui-lo): $1",
        "rcfilters-state-message-fullcoverage": "Selecionar todos os filtros em um grupo é o mesmo que não selecionar nenhum, portanto este filtro não tem efeito. O grupo inclui: $1",
-       "rcfilters-filtergroup-registration": "Registro de usuário",
-       "rcfilters-filter-registered-label": "Registrado",
-       "rcfilters-filter-registered-description": "Editores conectados.",
-       "rcfilters-filter-unregistered-label": "Não registrado",
-       "rcfilters-filter-unregistered-description": "Editores que não estão conectados.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Este filtro entra em conflito com {{PLURAL:$2|o seguinte filtro|os seguintes filtros}} de Experiência, que {{PLURAL:$2|encontra|encontram}} somente usuários registrados: $1",
        "rcfilters-filtergroup-authorship": "Autoria da contribuição",
        "rcfilters-filter-editsbyself-label": "Mudanças por você",
        "rcfilters-filter-editsbyself-description": "Suas proprias contribuições.",
        "rcfilters-filter-editsbyother-label": "Mudanças de outros",
        "rcfilters-filter-editsbyother-description": "Todas as mudanças, exceto a sua.",
-       "rcfilters-filtergroup-userExpLevel": "Nível de experiência (apenas para usuário registados)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Os filtros de experiência encontram apenas usuários registados, assim este filtro entra em conflito com o filtro “Não registado”.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "O filtro \"Não Registrado\" entra em conflito com um ou mais filtros de Experiência, que localizam apenas usuários registrados. Os filtros em conflito são marcados na área Filtros Ativos, acima.",
+       "rcfilters-filtergroup-userExpLevel": "Registro e experiência do usuário",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrado",
+       "rcfilters-filter-user-experience-level-registered-description": "Editores registrados.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Não registados",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que não estão autenticados.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Recém-chegados",
        "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 edições e 4 dias de atividade.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendizes",
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"Edições menores\" conflita com um ou mais filtros de Tipo de Alteração, porque certos tipos de alteração não podem ser designadas como \"menores\". Os filtros em conflito estão marcados na área Filtros Ativos, acima.",
        "rcfilters-hideminor-conflicts-typeofchange": "Determinados tipos de alteração não podem ser designados como \"menor\", portanto, este filtro entra em conflito com os seguintes filtros de Tipo de Alteração: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro de Tipo de Alteração entra em conflito com o filtro \"Edições menores\". Certos tipos de mudança não podem ser designadas como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Última revisão",
-       "rcfilters-filter-lastrevision-label": "Última revisão",
-       "rcfilters-filter-lastrevision-description": "A alteração mais recente para uma página.",
-       "rcfilters-filter-previousrevision-label": "Revisões anteriores",
-       "rcfilters-filter-previousrevision-description": "Todas as alterações que não são a alteração mais recente para uma página.",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisões",
+       "rcfilters-filter-lastrevision-label": "Revisão atual",
+       "rcfilters-filter-lastrevision-description": "Somente a mudança mais recente para uma página.",
+       "rcfilters-filter-previousrevision-label": "Não é a última revisão",
+       "rcfilters-filter-previousrevision-description": "Todas as mudanças que não são as \"ultimas revisões\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:não</strong> $1",
+       "rcfilters-exclude-button-off": "Excluir selecionado",
+       "rcfilters-exclude-button-on": "Excluindo selecionados",
        "rcfilters-view-tags": "Edições marcadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por namespace",
        "rcfilters-view-tags-tooltip": "Filtre os resultados usando edit tags",
        "delete-warning-toobig": "Esta página possui um longo histórico de edições, com mais de $1 {{PLURAL:$1|edição|edições}}.\nEliminá-la poderá causar problemas na base de dados de {{SITENAME}};\nprossiga com cuidado.",
        "deleteprotected": "Não é possível eliminar esta página porque foi protegida.",
        "deleting-backlinks-warning": "'''Cuidado:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|outras páginas]] ligam ou redirecionam para a página que você está prestes a eliminar.",
+       "deleting-subpages-warning": "<strong>Aviso:</strong> A página que você está prestes a excluir tem [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|uma subpágina|$1 subpáginas|51=mais de 50 subpáginas}}]].",
        "rollback": "Reverter edições",
        "rollbacklink": "reverter",
        "rollbacklinkcount": "reverter $1 {{PLURAL:$1|edição|edições}}",
        "fileduplicatesearch-noresults": "Não foi encontrado nenhum arquivo com o nome \"$1\".",
        "specialpages": "Páginas especiais",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
        "specialpages-group-maintenance": "Relatórios de manutenção",
        "specialpages-group-other": "Outras páginas especiais",
        "specialpages-group-login": "Entrar / Criar conta",
index 70b030c..23f56c0 100644 (file)
        "tog-minordefault": "Por omissão, marcar todas as edições como menores",
        "tog-previewontop": "Mostrar a antevisão antes da caixa de edição",
        "tog-previewonfirst": "Mostrar a antevisão na primeira edição",
-       "tog-enotifwatchlistpages": "Notificar-me por correio electrónico quando uma página ou ficheiro vigiado for alterado",
-       "tog-enotifusertalkpages": "Notificar-me por correio electrónico quando a minha página de discussão é editada",
-       "tog-enotifminoredits": "Notificar-me por correio electrónico também sobre edições menores de páginas ou ficheiros",
-       "tog-enotifrevealaddr": "Revelar o meu endereço de correio electrónico nas notificações",
+       "tog-enotifwatchlistpages": "Notificar-me por correio eletrónico quando uma página ou ficheiro vigiado for alterado",
+       "tog-enotifusertalkpages": "Notificar-me por correio eletrónico quando a minha página de discussão é editada",
+       "tog-enotifminoredits": "Notificar-me por correio eletrónico também sobre edições menores de páginas ou ficheiros",
+       "tog-enotifrevealaddr": "Revelar o meu endereço de correio eletrónico nas notificações",
        "tog-shownumberswatching": "Mostrar o número de utilizadores a vigiar",
        "tog-oldsig": "A sua assinatura atual:",
        "tog-fancysig": "Tratar assinatura como texto wiki (sem hiperligações automáticas)",
        "tog-watchlisthideanons": "Esconder edições de utilizadores anónimos ao listar mudanças às páginas vigiadas",
        "tog-watchlisthidepatrolled": "Esconder edições patrulhadas ao listar mudanças às páginas vigiadas",
        "tog-watchlisthidecategorization": "Ocultar categorização de páginas",
-       "tog-ccmeonemails": "Enviar-me cópias das mensagens por correio electrónico que eu enviar a outros utilizadores",
+       "tog-ccmeonemails": "Enviar-me cópias das mensagens por correio eletrónico que eu enviar a outros utilizadores",
        "tog-diffonly": "Não mostrar o conteúdo da página ao comparar duas edições",
        "tog-showhiddencats": "Mostrar categorias ocultas",
        "tog-norollbackdiff": "Ocultar diferenças depois de reverter edições em bloco",
        "blocked-mailpassword": "O seu endereço IP está bloqueado para edição. Para evitar abusos, não é permitido a utilização da recuperação de palavra-passe a partir deste endereço IP.",
        "eauthentsent": "Foi enviada uma mensagem de confirmação para o endereço de correio eletrónico que especificou.\nAntes que seja enviada qualquer outra mensagem para a conta, terá de seguir as instruções na mensagem enviada, de modo a confirmar que a conta lhe pertence.",
        "throttled-mailpassword": "Já foi enviada um email de recuperação de palavra-passe {{PLURAL:$1|na última hora|nas últimas $1 horas}}.\nPara prevenir abusos, só um email de recuperação de palavra-passe pode ser enviado a cada {{PLURAL:$1|hora|$1 horas}}.",
-       "mailerror": "Erro ao enviar correio electrónico: $1",
+       "mailerror": "Erro ao enviar correio eletrónico: $1",
        "acct_creation_throttle_hit": "Visitantes desta wiki com endereço IP igual ao seu criaram {{PLURAL:$1|uma conta|$1 contas}} nos últimos (ou últimas) $2, o que é o máximo permitido neste período de tempo.\nEm resultado, visitantes com este endereço IP não podem criar mais nenhuma conta de momento.",
        "emailauthenticated": "O seu endereço de correio eletrónico foi confirmado a $2, às $3.",
        "emailnotauthenticated": "O seu endereço de correio eletrónico ainda não foi confirmado.\nNão lhe serão enviadas mensagens por nenhuma das seguintes funcionalidades.",
        "grant-group-page-interaction": "Interagir com páginas",
        "grant-group-file-interaction": "Interagir com conteúdo multimédia",
        "grant-group-watchlist-interaction": "Interagir com a sua lista de páginas vigiadas",
-       "grant-group-email": "Enviar correio electrónico",
+       "grant-group-email": "Enviar correio eletrónico",
        "grant-group-high-volume": "Realizar atividades em grande quantidade",
        "grant-group-customization": "Personalização e preferências",
        "grant-group-administration": "Executar ações administrativas",
        "grant-privateinfo": "Aceder a informação privada",
        "grant-protect": "Proteger e desproteger páginas",
        "grant-rollback": "Reverter alterações a páginas",
-       "grant-sendemail": "Enviar correio electrónico a outros utilizadores",
+       "grant-sendemail": "Enviar correio eletrónico a outros utilizadores",
        "grant-uploadeditmovefile": "Carregar, substituir e mover ficheiros",
        "grant-uploadfile": "Carregar novos ficheiros",
        "grant-basic": "Direitos básicos",
        "rcfilters-legend-heading": "<strong>Lista de abreviações:</strong>",
        "rcfilters-activefilters": "Filtros ativos",
        "rcfilters-advancedfilters": "Filtros avançados",
+       "rcfilters-limit-title": "Modificações a mostrar",
+       "rcfilters-limit-shownum": "Mostrar as últimas $1 modificações",
+       "rcfilters-days-title": "Dias recentes",
+       "rcfilters-hours-title": "Horas recentes",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dia|dias}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros gravados",
        "rcfilters-quickfilters-placeholder-title": "Ainda não foi gravado nenhum link",
        "rcfilters-quickfilters-placeholder-description": "Para gravar as suas configurações dos filtros e reutilizá-las mais tarde, clique o ícone do marcador de página, na área Filtro Ativo abaixo.",
        "rcfilters-invalid-filter": "Filtro inválido",
        "rcfilters-empty-filter": "Não há filtros ativos. São mostradas todas as contribuições.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "O que é isto?",
+       "rcfilters-filterlist-whatsthis": "Como é que funcionam?",
        "rcfilters-filterlist-feedbacklink": "Dê-nos a sua opinião sobre os novos filtros (beta)",
        "rcfilters-highlightbutton-title": "Realçar resultados",
        "rcfilters-highlightmenu-title": "Selecionar uma cor",
        "rcfilters-noresults-conflict": "Não foram encontrados resultados porque os critérios de pesquisa estão em conflito",
        "rcfilters-state-message-subset": "Este filtro não produz efeito porque os seus resultados já estão incluídos nos {{PLURAL:$2|do seguinte filtro mais abrangente|dos seguintes filtros mais abrangentes}} (tente usar o realce para diferenciá-lo): $1",
        "rcfilters-state-message-fullcoverage": "Selecionar todos os filtros de um grupo é o mesmo que não selecionar nenhum, portanto este filtro não produz efeito. O grupo inclui: $1",
-       "rcfilters-filtergroup-registration": "Registo de utilizador",
-       "rcfilters-filter-registered-label": "Registado",
-       "rcfilters-filter-registered-description": "Editores autenticados.",
-       "rcfilters-filter-unregistered-label": "Não registado",
-       "rcfilters-filter-unregistered-description": " Editores que não estão autenticados.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Este filtro entra em conflito com {{PLURAL:$2|o seguinte filtro|os seguintes filtros}} de Experiência, que só {{PLURAL:$2|encontra|encontram}} utilizadores registados: $1",
        "rcfilters-filtergroup-authorship": "Autoria da contribuição",
        "rcfilters-filter-editsbyself-label": "Modificações feitas por si",
        "rcfilters-filter-editsbyself-description": "As suas edições.",
        "rcfilters-filter-editsbyother-label": "Modificações de outros",
        "rcfilters-filter-editsbyother-description": "Todas as modificações, exceto as suas.",
-       "rcfilters-filtergroup-userExpLevel": "Nível de experiência (apenas para utilizadores registados)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Os filtros de Experiência só encontram utilizadores registados, portanto este filtro entra em conflito com o filtro “Não registado”.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "O filtro \"Não registado\" entra em conflito com um ou mais filtros de Experiência, que só encontram utilizadores registados. Os filtros em conflito estão marcados na área Filtros Ativos, acima.",
+       "rcfilters-filtergroup-userExpLevel": "Registo de utilizadores e experiência",
+       "rcfilters-filter-user-experience-level-registered-label": "Registados",
+       "rcfilters-filter-user-experience-level-registered-description": "Editores autenticados.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Não registados",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que não estão autenticados.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novatos",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 edições e 4 dias de atividade.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editores registados, com menos de 10 edições e de 4 dias de atividade.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendizes",
-       "rcfilters-filter-user-experience-level-learner-description": "Mais experiência do que \"Novatos\", mas menos do que \"Utilizadores experientes\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Editores registados, com mais experiência do que \"Novatos\", mas menos do que \"Utilizadores experientes\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Utilizadores experientes",
-       "rcfilters-filter-user-experience-level-experienced-description": "Mais de 30 dias de atividade e 500 edições.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Editores registados, com mais de 500 edições e de 30 dias de atividade.",
        "rcfilters-filtergroup-automated": "Contribuições automatizadas",
        "rcfilters-filter-bots-label": "Robô",
        "rcfilters-filter-bots-description": "Edições efetuadas por ferramentas automatizadas.",
        "rcfilters-filter-humans-label": "Ser humano (não robô)",
-       "rcfilters-filter-humans-description": "Edições efetuadas por editores humanos.",
+       "rcfilters-filter-humans-description": "Edições efetuadas por pessoas.",
        "rcfilters-filtergroup-reviewstatus": "Estado da revisão",
        "rcfilters-filter-patrolled-label": "Patrulhadas",
        "rcfilters-filter-patrolled-description": "Edições marcadas como patrulhadas.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"Edições menores\" entra em conflito com um ou mais filtros de Tipo de Modificação, porque certos tipos de modificações não podem ser classificados como \"menores\". Os filtros em conflito estão marcados na área Filtros Ativos, acima.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certos tipos de modificações não podem ser classificados como \"menores\", portanto este filtro entra em conflito com os seguintes filtros de Tipo de Modificação: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro de Tipo de Modificação entra em conflito com o filtro \"Edições menores\". Certos tipos de modificações não podem ser classificados como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Última revisão",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisões",
        "rcfilters-filter-lastrevision-label": "Última revisão",
-       "rcfilters-filter-lastrevision-description": "A modificação mais recente de uma página.",
-       "rcfilters-filter-previousrevision-label": "Revisões anteriores",
-       "rcfilters-filter-previousrevision-description": "Todas as modificações que não sejam a modificação mais recente de uma página.",
+       "rcfilters-filter-lastrevision-description": "Só a modificação mais recente de uma página.",
+       "rcfilters-filter-previousrevision-label": "Revisões menos a mais recente",
+       "rcfilters-filter-previousrevision-description": "Todas as modificações que não são a \"última revisão\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:não</strong> $1",
+       "rcfilters-exclude-button-off": "Excluir os selecionados",
+       "rcfilters-exclude-button-on": "A excluir os selecionados",
        "rcfilters-view-tags": "Edições marcadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por espaço nominal",
        "rcfilters-view-tags-tooltip": "Filtrar resultados usando etiquetas de edição",
        "delete-warning-toobig": "Esta página tem um histórico de edições longo, com mais de $1 {{PLURAL:$1|edição|edições}}.\nEliminá-la poderá causar problemas na base de dados da wiki {{SITENAME}};\nprossiga com precaução.",
        "deleteprotected": "Não é possível eliminar esta página porque foi protegida.",
        "deleting-backlinks-warning": "<strong>Aviso:</strong> Existem [[Special:WhatLinksHere/{{FULLPAGENAME}}|páginas]] que contêm ligações para a página que está prestes a eliminar ou que a transcluem.",
+       "deleting-subpages-warning": "<strong>Aviso:</strong> A página que está prestes a eliminar tem [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|uma subpágina|$1 subpáginas|51=mais de 50 subpáginas}}]].",
        "rollback": "Reverter edições",
        "rollbacklink": "reverter",
        "rollbacklinkcount": "reverter $1 {{PLURAL:$1|edição|edições}}",
        "undelete-search-title": "Pesquisar páginas eliminadas",
        "undelete-search-box": "Pesquisar páginas eliminadas",
        "undelete-search-prefix": "Mostrar páginas que começam por:",
+       "undelete-search-full": "Mostrar os títulos de página que contenham:",
        "undelete-search-submit": "Pesquisar",
        "undelete-no-results": "Não foram encontradas páginas eliminadas, para esse critério de pesquisa, no arquivo de eliminações.",
        "undelete-filename-mismatch": "Não foi possível restaurar a versão do ficheiro de $1: nome de ficheiro não combina",
        "anononlyblock": "apenas anón.",
        "noautoblockblock": "bloqueio automático desativado",
        "createaccountblock": "criação de conta bloqueada",
-       "emailblock": "correio electrónico desativado",
+       "emailblock": "correio eletrónico desativado",
        "blocklist-nousertalk": "impedido de editar a própria página de discussão",
        "ipblocklist-empty": "A lista de bloqueios encontra-se vazia.",
        "ipblocklist-no-results": "O endereço IP ou nome de utilizador procurado não se encontra bloqueado.",
        "fileduplicatesearch-noresults": "Não foi encontrado nenhum ficheiro com o nome \"$1\".",
        "specialpages": "Páginas especiais",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
+       "specialpages-note-restricted": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
        "specialpages-group-maintenance": "Relatórios de manutenção",
        "specialpages-group-other": "Outras páginas especiais",
        "specialpages-group-login": "Entrar / criar conta",
index 72f7aa9..699ac96 100644 (file)
        "rcfilters-legend-heading": "Used as a heading for legend box on [[Special:RecentChanges]] and [[Special:Watchlist]] when RCFilters are enabled.",
        "rcfilters-activefilters": "Title for the filters selection showing the active filters.",
        "rcfilters-advancedfilters": "Title for the buttons allowing the user to switch to the various advanced filters views.",
+       "rcfilters-limit-title": "Title for the options to change the number of results shown.",
+       "rcfilters-limit-shownum": "Title for the button that opens the operation to control how many results are shown. \n\nParameters: $1 - Number of results shown",
+       "rcfilters-days-title": "Title for the options to change the number of days for the results shown.",
+       "rcfilters-hours-title": "Title for the options to change the number of hours for the results shown.",
+       "rcfilters-days-show-days": "Title for the button that opens the operation to control the day range for the results. \n\nParameters: $1 - Number of days shown",
+       "rcfilters-days-show-hours": "Title for the button that opens the operation to control the hour range for the results. \n\nParameters: $1 - Number of hours shown",
        "rcfilters-quickfilters": "Label for the button that opens the saved filter settings menu in [[Special:RecentChanges]]",
        "rcfilters-quickfilters-placeholder-title": "Title for the text shown in the quick filters menu on [[Special:RecentChanges]] if the user has not saved any quick filters.",
        "rcfilters-quickfilters-placeholder-description": "Description for the text shown in the quick filters menu on [[Special:RecentChanges]] if the user has not saved any quick filters.",
        "rcfilters-invalid-filter": "A label for an invalid filter.",
        "rcfilters-empty-filter": "Placeholder for the filter list when no filters were chosen.",
        "rcfilters-filterlist-title": "Title for the filters list.\n{{Identical|Filter}}",
-       "rcfilters-filterlist-whatsthis": "Caption for the link that opens a popup with explanations about this filter group.",
+       "rcfilters-filterlist-whatsthis": "Caption for the link that opens a popup with explanations about this filter group, explaining what it is and how it works.",
        "rcfilters-filterlist-feedbacklink": "Caption for the link to the feedback page about the filters beta feature.",
        "rcfilters-highlightbutton-title": "Title for the highlight button used to toggle the highlight feature on and off.",
        "rcfilters-highlightmenu-title": "Title for the highlight menu used to select the highlight color for an individual filter.",
        "rcfilters-noresults-conflict": "A message displayed in the results area when no results found because there are filters in conflict with one another.",
        "rcfilters-state-message-subset": "Tooltip shown when hovering over a filter tag when one or more broader filters that contain the hovered filter are also selected. This indicates that the hovered filter has no effect because all the results it matches are also matched by the broader filter(s).  Parameters:\n* $1 - Comma-separated string of selected broader filters that this filter is a subset of\n* $2 - Count of filters in $1, for PLURAL",
        "rcfilters-state-message-fullcoverage": "Tooltip shown when hovering over a filter tag when all the filters in its group are selected. This indicates that the hovered filter has no effect because the selected filters in the group cover all changes. Parameters:\n* $1 - Comma-separated string of selected filters in the group\n* $2 - Count of filters in $1, for PLURAL",
-       "rcfilters-filtergroup-registration": "Title for the filter group for editor registration type.",
-       "rcfilters-filter-registered-label": "Label for the filter for showing edits made by logged-in users.\n{{Identical|Registered}}",
-       "rcfilters-filter-registered-description": "Description for the filter for showing edits made by logged-in users.",
-       "rcfilters-filter-unregistered-label": "Label for the filter for showing edits made by logged-out users.\n{{Identical|Unregistered}}",
-       "rcfilters-filter-unregistered-description": "Description for the filter for showing edits made by logged-out users.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Tooltip shown when hovering over a Unregistered filter tag, when a User Experience Level filter is also selected.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.\n\n\"Experience\" is based on {{msg-mw|Rcfilters-filtergroup-userExpLevel}}.\n\nThis indicates that no results will be shown, because users matched by the User Experience Level groups are never unregistered.  Parameters:\n* $1 - Comma-separated string of selected User Experience Level filters, e.g. \"Newcomer, Experienced\"\n* $2 - Count of selected User Experience Level filters, for PLURAL",
        "rcfilters-filtergroup-authorship": "Title for the filter group for edit authorship. This filter group allows the user to choose between \"Your own edits\" and \"Edits by others\". More info: https://phabricator.wikimedia.org/T149859",
        "rcfilters-filter-editsbyself-label": "Label for the filter for showing edits made by the current user.",
        "rcfilters-filter-editsbyself-description": "Description for the filter for showing edits made by the current user.",
        "rcfilters-filter-editsbyother-label": "Label for the filter for showing edits made by anyone other than the current user.",
        "rcfilters-filter-editsbyother-description": "Description for the filter for showing edits made by anyone other than the current user.",
        "rcfilters-filtergroup-userExpLevel": "Title for the filter group for user experience levels.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Tooltip shown when hovering over a User Experience Level filter tag, when only Unregistered users are being shown.  This indicates that no results will be shown, because users matched by the User Experience Level groups are never unregistered.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Message shown in the result area when both a User Experience Level filter and the Unregistered filter are selected.  This indicates that no results will be shown because users selected by the User Experience Filter are never unregistered.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.\n\n\"Experience\" is based on {{msg-mw|Rcfilters-filtergroup-userExpLevel}}.",
+       "rcfilters-filter-user-experience-level-registered-label": "Label for the filter for showing edits made by logged-in editors.",
+       "rcfilters-filter-user-experience-level-registered-description": "Description for the filter for showing edits made by logged-in editors.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Label for the filter for showing edits made by anonymous editors.",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Description for the filter for showing edits made by anonymous editors.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Label for the filter for showing edits made by new editors.",
        "rcfilters-filter-user-experience-level-newcomer-description": "Description for the filter for showing edits made by new editors.",
        "rcfilters-filter-user-experience-level-learner-label": "Label for the filter for showing edits made by learning editors.",
        "rcfilters-filter-previousrevision-description": "Description for the filter for showing changes on previous revisions of a page.",
        "rcfilters-filter-excluded": "Label for a menu item in [[Special:RecentChanges]] noting that the item is being excluded from the results.",
        "rcfilters-tag-prefix-namespace": "Prefix for the namespace tags in [[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please keep this format.\n\nParameters:\n* $1 - Filter name.",
-       "rcfilters-tag-prefix-namespace-inverted": "Prefix for the namespace inverted tags in [[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please keep this format.\n\nParameters:\n* $1 - Filter name.",
+       "rcfilters-tag-prefix-namespace-inverted": "Prefix for the namespace inverted tags in [[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please keep this format.\n\nParameters:\n* $1 - Filter name.\n{{Identical|Not}}",
        "rcfilters-tag-prefix-tags": "Prefix for the edit tags in [[Special:RecentChanges]]. Edit tags use a hash (#) as prefix. Please keep this format.\n\nParameters:\n* $1 - Tag display name.",
+       "rcfilters-exclude-button-off": "Title for the button that excludes selected namespaces, when it is not yet active.",
+       "rcfilters-exclude-button-on": "Title for the button that excludes selected namespaces, when it is not yet active.",
        "rcfilters-view-tags": "Title for the tags view in [[Special:RecentChanges]]\n{{Identical|Tag}}",
        "rcfilters-view-namespaces-tooltip": "Tooltip for the button that loads the namespace view in [[Special:RecentChanges]]",
        "rcfilters-view-tags-tooltip": "Tooltip for the button that loads the tags view in [[Special:RecentChanges]]",
        "delete-warning-toobig": "Parameters:\n* $1 - the upper limit of number of revisions\nSee also:\n* {{msg-mw|Delete-toobig}}",
        "deleteprotected": "Used as error message when deleting the page.\n\n\"If protection keeps them from editing, they shouldn't be able to delete.\"",
        "deleting-backlinks-warning": "A warning shown when a page that is being deleted has at least one link to it or is transcluded in at least one page.",
+       "deleting-subpages-warning": "A warning shown when a page that is being deleted has at least one subpage. $1 is the number of subpages of the page. For any number of subpages over 50, $1 will be 51.\nSee also:\n* {{msg-mw|Deleting-backlinks-warning}}",
        "rollback": "{{Identical|Rollback}}",
        "rollbacklink": "{{Doc-actionlink}}\nThis link text appears on the recent changes page to users who have the \"rollback\" right.\nThis message has a tooltip {{msg-mw|tooltip-rollback}}\n{{Identical|Rollback}}",
        "rollbacklinkcount": "{{doc-actionlink}}\nText of the rollback link showing the number of edits to be rolled back. See also {{msg-mw|rollbacklink}}.\n\nParameters:\n* $1 - the number of edits that will be rolled back. If $1 is over the value of <code>$wgShowRollbackEditCount</code> (default: 10) {{msg-mw|rollbacklinkcount-morethan}} is used.\n\nThe rollback link is displayed with a tooltip {{msg-mw|Tooltip-rollback}}",
        "specialpages": "{{doc-special|SpecialPages|unlisted=1}}\nDisplay name of link to [[Special:SpecialPages]] shown on all pages in the toolbox.\n\nSee also:\n* {{msg-mw|Specialpages}}\n* {{msg-mw|Accesskey-t-specialpages}}\n* {{msg-mw|Tooltip-t-specialpages}}\n{{Identical|Special page}}",
        "specialpages-summary": "{{doc-specialpagesummary|specialpages}}",
        "specialpages-note-top": "Heading for {{msg-mw|specialpages-note}}.\n{{Identical|Legend}}",
-       "specialpages-note": "Footer note for the [[Special:SpecialPages]] page",
+       "specialpages-note-restricted": "Footer note for the [[Special:SpecialPages]] page",
+       "specialpages-note-cached": "{{ignore}}\nFooter note for the [[Special:SpecialPages]] page",
        "specialpages-group-maintenance": "{{doc-special-group|like=[[Special:DoubleRedirects]], [[Special:LonelyPages]] and [[Special:WantedPages]]}}",
        "specialpages-group-other": "{{doc-special-group|like=[[Special:AdminLinks]] and [[Special:BookSources]]}}",
        "specialpages-group-login": "{{doc-special-group|like=[[Special:UserLogin]]}}",
index 9da374a..95ac6bd 100644 (file)
        "privacypage": "Project:Tasertit n tusligi",
        "retrievedfrom": "Itwarr-d zi \"$1\"",
        "youhavenewmessages": "Ghar-k / Ghar-m $1 ($2).",
-       "editsection": "Ẓṛeg",
-       "editold": "ẓṛeg",
+       "editsection": "ⵙⵏⴼⵍ",
+       "editold": "ⵙⵏⴼⵍ",
        "viewsourceold": "ẓeṛ aɣbalu",
-       "editlink": "ẓṛg",
+       "editlink": "ⵙⵏⴼⵍ",
        "viewsourcelink": "ẓṛ aghbalu",
        "editsectionhint": "Ẓṛeg tigezmi: $1",
        "toc": "ⵜⵓⵎⴰⵢⵉⵏ",
        "yourpassword": "Tawalt n wadaf:",
        "login": "ⴰⴷⴼ",
        "nav-login-createaccount": "Adef / egg amiḍan",
-       "logout": "Ufugh",
-       "userlogout": "Ufugh",
+       "logout": "ⴼⴼⵖ",
+       "userlogout": "ⴼⴼⵖ",
        "createaccount": "Egg amiḍan",
        "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "loginsuccesstitle": "Adaf icna",
        "newarticletext": "Tdefar-d tazdayt n Tasna εad war telli .\nbac ad tegged , arri di taflwit a swadday (xemm i [$1  Tasna n Tallalt] i ineɣmisen ifruryen).\nmala qacek da s ɣalaṭ waha, tecca di tbutunt n '''deffar''' di (browser) inec .",
        "noarticletext": "Rxxu ur din llint ca tira di tasna ya.\nTzmmard [[Special:Search/{{PAGENAME}}|rzu xf yizwl n tasna ya]] di tasniwin nnḍni,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nnigh [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit this page]</span>.",
        "previewnote": "'''Wa d Azar-ascan waha;\ntiẓṛigin εad war twaḥḍent!'''",
-       "editing": "Aẓṛag di $1",
-       "editingsection": "Aẓrag  di $1 (tigezmi)",
+       "editing": "ⴰⵙⵏⴼⵍ ⵏ $1",
+       "editingsection": "ⴰⵙⵏⴼⵍ ⵏ $1 (ⵜⵉⴳⵣⵎⵉ)",
        "copyrightwarning": "Maṛṛa tirra di {{SITENAME}} twaggent swadday i $2 (ẓar da $1).\nmala war texsed tirra inac ad twaẓṛegent , ad twamsebḍant .\nUr ten-teg ca da.<br />\ntjadjid-anɣ Ɛawt ila qa d cekk ig yuran manaya, niɣ tesneɣlet-id zi ca n uɣbal nniḍn d alelli.\n'''UR SADDAF CA TIRRA ƔARSENT COPYRIGHTE BLA MA AD-IXES BAB-INES !'''",
        "templatesused": "Timudmiwin itwaggen di Tasna ya:",
        "templatesusedpreview": "Timudmiwin igg itwasxdemen dg uzar-ascan a :",
        "action-read": "ⵖⵔ ⵜⴰⵙⵏⴰ ⴰ",
        "action-edit": "ⵙⵏⴼⵍ ⵜⴰⵙⵏⴰ ⴰ",
        "action-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰ",
-       "nchanges": "$1 {{PLURAL:$1|tiẓṛegt|tiẓṛigin}}",
+       "nchanges": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵉⵍⵏ}}",
        "enhancedrc-history": "ⴰⵎⵣⵔⵓⵢ",
        "recentchanges": "Tiẓṛigin tineggura",
        "recentchanges-feed-description": "Bbar tiẓṛigin timayutin n wiki deg usudem(feed) a .",
        "rcshowhideliu": "$1 users ig yudeffen",
        "rcshowhideanons": "$1 users war twasnen",
        "rcshowhidepatr": "Tiẓṛigin ig itwaẓrent di $1",
-       "rcshowhidemine": "$1 tiẓṛigin inu",
+       "rcshowhidemine": "$1 ⵉⵙⵏⴼⵉⵍⵏ ⵉⵏⵓ",
        "rclinks": "Ẓar $1 tiẓṛigin tinggura di $2 n ussan inggura",
        "diff": "imṣebḍan",
        "hist": "ⴰⵎⵣⵔⵓⵢ",
        "filehist": "Amzruy n usatul",
        "filehist-help": "Tka di date/time bac ad tẓerd afaylu mamec ja d-itban di Lwaqt a .",
        "filehist-deleteall": "ⴽⴽⵙ ⵎⴰⵔⵔⴰ",
-       "filehist-deleteone": "sfaḍ",
+       "filehist-deleteone": "ⴽⴽⵙ",
        "filehist-current": "ⴰⵎⵉⵔⴰⵏ",
        "filehist-datetime": "ⴰⵙⴰⴽⵓⴷ/ⴰⴽⵓⴷ",
        "filehist-user": "Aseqdac",
        "filehist-dimensions": "Tisektiwin",
-       "filehist-filesize": "Tiddi n ufaylu",
+       "filehist-filesize": "ⵜⵉⴷⴷⵉ ⵏ ⵓⴼⴰⵢⵍⵓ",
        "filehist-comment": "ⴰⵅⴼⴰⵡⴰⵍ",
        "imagelinks": "Aseqdec usatul",
        "linkstoimage": "{{PLURAL:$1|Tasna ya teqn-ad|$1 Tasniwin a qnent-id}} ɣa ufaylu ya :",
        "statistics-pages": "ⵜⴰⵙⵏⵉⵡⵉⵏ",
        "doubleredirects": "(redirects) ɛɛawdent",
        "brokenredirects": "(redirects) arẓent",
-       "brokenredirects-edit": "arri",
+       "brokenredirects-edit": "ⵙⵏⴼⵍ",
        "brokenredirects-delete": "ⴽⴽⵙ",
        "withoutinterwiki": "Tasna bla tiẓdayin n tutlayt",
        "withoutinterwiki-submit": "Smmrad",
        "actioncomplete": "Tiggawt tsala",
        "deletedtext": "\"$1\" Twakkes.\nXemm $2 i tikkas timaynutin.",
        "dellogpage": "Aɣmis n uṣfaḍ",
-       "deletecomment": "Ssebba:",
+       "deletecomment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
        "deleteotherreason": "Ca n ssebba nniḍn:",
        "deletereasonotherlist": "Ssebba nniḍn",
        "rollbacklink": "Sdwl ghar dffar",
        "whatlinkshere-hidelinks": "$1 timqqan",
        "blockip": "Sbdd asqdac a",
        "ipbreason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
-       "ipboptions": "2 n timirin:2 hours,1 n wass:1 day,3 n wussan:3 days,1 imalass:1 week,2 imallassn:2 weeks,1 wayur:1 month,3 wayurn:3 months,6 wayurn:6 months,1 asggwas:1 year,tartalla:infinite",
+       "ipboptions": "2 ⵜⵙⵔⴰⴳⵉⵏ:2 hours,1 ⵡⴰⵙⵙ:1 day,3 ⵡⵓⵙⵙⴰⵏ:3 days,1 ⵉⵎⴰⵍⴰⵙⵙ:1 week,2 ⵉⵎⴰⵍⴰⵙⵙⵏ:2 weeks,1 ⵡⴰⵢⵢⵓⵓⵔ:1 month,3 ⵡⴰⵢⵢⵓⵔⵏ:3 months,6 ⵡⴰⵢⵢⵓⵔⵏ:6 months,1 ⵓⵙⴳⴳⵯⴰⵙ:1 year,ⵍⴱⴷⴰ:infinite",
        "autoblocklist-submit": "ⵔⵣⵓ",
        "ipblocklist": "Tabdart n tansiwin IP d isemawen n iseqdacen ig iteblukan",
        "blocklist-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ",
index 5cfc039..1161326 100644 (file)
        "hidetoc": "zuppentar",
        "collapsible-collapse": "Cumprimer",
        "collapsible-expand": "Extender",
+       "confirmable-yes": "Gea",
+       "confirmable-no": "Na",
        "thisisdeleted": "Guardar u restaurar $1?",
        "viewdeleted": "Mussa $1?",
        "restorelink": "{{PLURAL:$1|ina modificaziun stizzada|$1 modificaziuns stizzadas}}",
        "listfiles_size": "Grondezza",
        "listfiles_description": "Descripziun",
        "listfiles_count": "Versiuns",
+       "listfiles-latestversion-yes": "Gea",
+       "listfiles-latestversion-no": "Na",
        "file-anchor-link": "Datoteca",
        "filehist": "Istorgia da las versiuns",
        "filehist-help": "Clicca sin ina data/temps per vesair la versiun da lura.",
        "fileduplicatesearch-noresults": "Betg chattà ina datoteca cun il num \"$1\".",
        "specialpages": "Paginas spezialas",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Paginas spezialas normalas.\n* <span class=\"mw-specialpagerestricted\">Paginas spezialas restrenschidas.</span>",
        "specialpages-group-maintenance": "Rapports da mantegnamant",
        "specialpages-group-other": "Autras paginas spezialas",
        "specialpages-group-login": "S'annunziar / crear in conto",
        "tags": "Signalisaziuns per midadas",
        "tag-filter": "Filtrar las [[Special:Tags|Signalisaziuns]]:",
        "tag-filter-submit": "Filtrar",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|signalisaziun|signalisaziuns}}]]: $2)",
        "tags-title": "Signalisaziuns",
        "tags-intro": "Questa pagina mussa tut las singalisaziuns che la software po duvrar per marcar modificaziuns e declera tge ch'ellas muntan.",
        "tags-tag": "Num da la signalisaziun",
        "htmlform-submit": "Trametter",
        "htmlform-reset": "Revocar las midadas",
        "htmlform-selectorother-other": "Auters",
+       "htmlform-no": "Na",
+       "htmlform-yes": "Gea",
        "logentry-delete-delete": "$1 {{GENDER:$2|ha stizzà}} la pagina $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|ha restaurà}} la pagina $3 ($4)",
        "logentry-delete-event": "$1 ha midà la visibilitad da{{PLURAL:$5|d ina occurrenza en il protocol| $5 occurrenzas en il protocol}} da '''$3''': $4",
index 55d3bdb..a24c6c2 100644 (file)
        "rcfilters-noresults-conflict": "Nu s-au găsit rezultate deoarece criteriile de căutare sunt în conflict",
        "rcfilters-state-message-subset": "Acest filtru nu are efecte deoarece rezultatele sale sunt incluse în filtrele cu selectie mai largă {{PLURAL:$2|filtru|filtre}} (încercați să subliniați pentru a o deosebi): $1",
        "rcfilters-state-message-fullcoverage": "Selectarea tuturor filtrelor dintr-un grup este aceeași cu cea selectată, astfel încât acest filtru nu are efect. Grupul include: $1",
-       "rcfilters-filtergroup-registration": "Înregistrare utilizator",
-       "rcfilters-filter-registered-label": "Înregistrat",
-       "rcfilters-filter-registered-description": "Editorii conectați.",
-       "rcfilters-filter-unregistered-label": "Neînregistrat",
-       "rcfilters-filter-unregistered-description": "Editorii care nu sunt conectați.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Acest filtru contravine {{PLURAL:$2|filtru|filtre}} de Experiență, care {{PLURAL:$2|găsesc|gasește}} doar userii inreistrați: $1",
        "rcfilters-filtergroup-authorship": "Contribuția autorului",
        "rcfilters-filter-editsbyself-label": "Modificările tale",
        "rcfilters-filter-editsbyself-description": "Contribuțiile tale.",
        "rcfilters-filter-editsbyother-label": "Contribuțiile altora",
        "rcfilters-filter-editsbyother-description": "Toate modificările mai puțin ale tale.",
        "rcfilters-filtergroup-userExpLevel": "Nivel de experiență (numai pentru utilizatorii înregistrați)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filtrele Experiență găsesc numai utilizatori înregistrați, deci acest filtru este în conflict cu filtrul \"Înregistrat\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filtrul \"Înregistrat\" ​​este în conflict cu unul sau mai multe filtre Experiență, care găsește numai utilizatorii înregistrați. Filtrele conflictuale sunt marcate în zona Filtre active, de mai sus.",
+       "rcfilters-filter-user-experience-level-registered-label": "Înregistrat",
+       "rcfilters-filter-user-experience-level-registered-description": "Editorii conectați.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neînregistrat",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editorii care nu sunt conectați.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nou veniți",
        "rcfilters-filter-user-experience-level-newcomer-description": "Mai puțin de 10 editări și 4 zile de activitate.",
        "rcfilters-filter-user-experience-level-learner-label": "Cursanți",
        "fileduplicatesearch-noresults": "Nu s-a găsit niciun fișier cu numele „$1”.",
        "specialpages": "Pagini speciale",
        "specialpages-note-top": "Legendă",
-       "specialpages-note": "* Pagini speciale normale.\n* <span class=\"mw-specialpagerestricted\">Pagini speciale restricționate.</span>",
        "specialpages-group-maintenance": "Întreținere",
        "specialpages-group-other": "Alte pagini speciale",
        "specialpages-group-login": "Autentificare / creare cont",
        "logentry-delete-delete": "$1 {{GENDER:$2|a șters}} pagina $3",
        "logentry-delete-delete_redir": "$1 {{GENDER:$2|a șters}} pagina de redirecționare $3 prin suprascriere",
        "logentry-delete-restore": "$1 {{GENDER:$2|a restaurat}} pagina $3 ($4)",
-       "restore-count-files": "{{PLURAL:$1|1 fișier|$1 fișiere}}",
+       "restore-count-revisions": "{{PLURAL:$1|1 versiune|$1 versiuni|$1 de versiuni}}",
+       "restore-count-files": "{{PLURAL:$1|1 fișier|$1 fișiere|$1 de fișiere}}",
        "logentry-delete-event": "$1 {{GENDER:$2|a schimbat}} vizibilitatea {{PLURAL:$5|unui eveniment din jurnal|a $5 evenimente din jurnal|a $5 de evenimente din jurnal}} pentru $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|a schimbat}} vizibilitatea {{PLURAL:$5|unei versiuni|a $5 versiuni|a $5 de versiuni}} pentru pagina $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|a modificat}} vizibilitatea evenimentelor din jurnal pentru $3",
index 92c4bb1..41c9360 100644 (file)
        "changepassword-success": "'A password toje ha state cangiate!",
        "changepassword-throttled": "Tu è pruvate 'nu sacche de vote a trasè.\nPe piacere aspitte $1 apprime de pruvà arrete.",
        "botpasswords": "Password d'u bot",
+       "botpasswords-existing": "Passuord de le bot esistende",
+       "botpasswords-createnew": "Ccreje 'na passuord nove pu bot",
+       "botpasswords-editexisting": "Cange 'na passuord d'u bot ca esiste ggià",
        "botpasswords-label-appid": "Nome d'u bot:",
        "botpasswords-label-create": "Ccreje",
        "botpasswords-label-update": "Aggiorne",
        "botpasswords-updated-title": "Passuord d'u bot cangiate",
        "botpasswords-deleted-title": "Passuord d'u bot scangellate",
        "resetpass_forbidden": "Le Password non ge ponne cangià",
+       "resetpass_forbidden-reason": "Le passuord non ge ponne essere cangiate: $1",
        "resetpass-no-info": "Tu a essere colleghete pe accedere a sta pàgene direttamende.",
        "resetpass-submit-loggedin": "Cange 'a password",
        "resetpass-submit-cancel": "Annulle",
        "minoredit": "Cangiaminde stuèdeche",
        "watchthis": "Condrolle sta pàgene",
        "savearticle": "Registre 'a vôsce",
+       "savechanges": "Reggistre le cangiaminde",
+       "publishpage": "Pubbleche 'a pàgene",
+       "publishchanges": "Pubbleche le cangiaminde",
        "preview": "Andeprime",
        "showpreview": "Vide l'andeprime",
        "showdiff": "Fa vedè le cangiaminde",
        "invalid-content-data": "Condenute d'u date invalide",
        "content-not-allowed-here": "\"$1\" condenute non g'è permesse sus 'a pàgene [[$2]]",
        "editwarning-warning": "Assenne da sta pàgene tu puè perdè tutte le date ca è cangiate.\nCe tu è trasute, tu puè disabbilità st'avvertimende jndr'à sezione \"{{int:prefs-editing}}\" de le preferenze tune.",
+       "editpage-invalidcontentmodel-title": "'U Modelle d'u condenute non gè supportate",
+       "editpage-invalidcontentmodel-text": "'U modelle d'u condenute \"$1\" non g'è supportate.",
        "editpage-notsupportedcontentformat-title": "'U formate d'u condenute non gè supportate",
        "editpage-notsupportedcontentformat-text": "'U formate d'u condenute $1 non g'è supportate da 'u modelle de condenute $2.",
        "content-model-wikitext": "Uicchiteste",
        "grant-group-file-interaction": "Inderaggisce cu le media",
        "grant-group-watchlist-interaction": "Inderaggisce cu le pàggene condrollate",
        "grant-group-email": "Manne 'n'e-mail",
+       "grant-createaccount": "Ccreje le cunde utinde",
+       "grant-createeditmovepage": "Ccreje, cange e spueste le pàggene",
+       "grant-delete": "Scangille pàggene, revisiune e vôsce de l'archivije",
        "newuserlogpage": "Archivije de ccreazione de le utinde",
        "newuserlogpagetext": "Quiste ète l'archivije de le creazziune de l'utinde.",
        "rightslog": "Archivie de le diritte de l'utende",
        "recentchanges-submit": "Fà 'ndrucà",
        "rcfilters-activefilters": "Filtre attive",
        "rcfilters-advancedfilters": "Filtre avanzate",
+       "rcfilters-limit-title": "Cangiaminde da 'ndrucà",
+       "rcfilters-limit-shownum": "Fà 'ndrucà le urteme $1 cangiaminde",
+       "rcfilters-days-title": "Urteme sciurne",
+       "rcfilters-hours-title": "Urteme ore",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|sciurne}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ore}}",
        "rcfilters-quickfilters": "Filtre reggistrate",
        "rcfilters-quickfilters-placeholder-title": "Nisciune collegamende reggistrate",
+       "rcfilters-savedqueries-defaultlabel": "Filtre reggistrate",
+       "rcfilters-savedqueries-rename": "Renomene",
+       "rcfilters-savedqueries-setdefault": "'Mboste cumme predefinite",
+       "rcfilters-savedqueries-unsetdefault": "Live cumme predefinite",
+       "rcfilters-savedqueries-remove": "Live",
+       "rcfilters-savedqueries-new-name-label": "Nome",
+       "rcfilters-savedqueries-new-name-placeholder": "Dì a ce serve 'u filtre",
        "rcfilters-savedqueries-apply-label": "Ccrèje 'nu filtre",
+       "rcfilters-savedqueries-cancel-label": "Annulle",
+       "rcfilters-filterlist-title": "Filtre",
+       "rcfilters-filterlist-whatsthis": "Cumme funzionane?",
+       "rcfilters-highlightmenu-title": "Scacchie 'nu culore",
+       "rcfilters-highlightmenu-help": "Scacchie 'nu culore pe evidenzià sta probbietà",
+       "rcfilters-filterlist-noresults": "Nisciune filtre acchiate",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-filter-patrolled-label": "Condrollate",
+       "rcfilters-filter-patrolled-description": "Cangiaminde signate cumme condrollate.",
+       "rcfilters-filter-unpatrolled-label": "Non condrollate",
+       "rcfilters-filter-unpatrolled-description": "Cangiaminde non signate cumme condrollate.",
+       "rcfilters-filtergroup-significance": "Significate",
+       "rcfilters-filter-minor-label": "Cangiaminde stuèdeche",
        "rcnotefrom": "Sotte {{PLURAL:$5|ste 'u cangiamende|stonne le cangiaminde}} da <strong>$3, $4</strong> ('nzigne a <strong>$1</strong> fatte vedè).",
        "rclistfrom": "Fà vedè le urteme cangiaminde partenne da $3 $2",
        "rcshowhideminor": "$1 cangiaminde stuèdeche",
        "rcshowhidemine": "$1 cangiaminde mie",
        "rcshowhidemine-show": "Fà vedè",
        "rcshowhidemine-hide": "Scunne",
+       "rcshowhidecategorization-show": "Fà 'ndrucà",
+       "rcshowhidecategorization-hide": "Scunne",
        "rclinks": "Vide l'urteme $1 cangiaminde jndr'à l'urteme $2 sciurne",
        "diff": "diff",
        "hist": "cunde",
        "mostrevisions": "Pàggene cchiù cangete",
        "prefixindex": "Tutte le pàggene cu 'u prefisse",
        "prefixindex-namespace": "Tutte le pàggene cu 'u prefisse ($1 namespace)",
+       "prefixindex-submit": "Fà 'ndrucà",
        "prefixindex-strip": "Strisce d'u prefisse jndr'à l'elenghe",
        "shortpages": "Pàggene corte",
        "longpages": "Pàggene longhe",
        "protectedpages-performer": "Stoche a protegge l'utende",
        "protectedpages-params": "Parametre de protezzione",
        "protectedpages-reason": "Mutive",
+       "protectedpages-submit": "Fà 'ndrucà le pàggene",
        "protectedpages-unknown-timestamp": "Scanusciute",
        "protectedpages-unknown-performer": "Utende scanusciute",
        "protectedtitles": "Titele prutette",
        "protectedtitles-summary": "Sta pàgene elenghe le titole ca so prutette da 'a ccrejazzione. Pe 'n'elenghe de le pàggene ca sò prutette, 'ndruche [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Nisciune titele jè pe mò prutette cu ste parametre.",
+       "protectedtitles-submit": "Fà 'ndrucà le titole",
        "listusers": "Liste de l'utende",
        "listusers-editsonly": "Fà vedè sulamende l'utinde cu cangiaminde fatte",
        "listusers-creationsort": "Arrenghete pe date de ccreazione",
        "usereditcount": "$1 {{PLURAL:$1|cangiamende|cangiaminde}}",
        "usercreated": "{{GENDER:$3|Ccrejate}} 'u $1 a le ore $2",
        "newpages": "Pàggene nuève",
+       "newpages-submit": "Fà 'ndrucà",
        "newpages-username": "Nome de l'utende:",
        "ancientpages": "Pàggene vìcchje",
        "move": "Spuèste",
        "apisandbox-intro": "Ause sta pàgene pe sperimendà cu le <strong>API de le web service pe MediaUicchi</strong>.\nFà referimende a [[mw:API:Main page| 'a documendazione de l'API]] pe cchiù dettaglie de l'ause de l'API.\nEsembie: [https://www.mediawiki.org/wiki/API#A_simple_example pigghie 'u condenute d'a Pàgene Prengepàle]. Scacchie 'n'azione pe 'ndrucà otre esembie.\n\nVide ca, pure ca queste jè 'na buatte de sabbie tu puè carrescià le cangiaminde de sta pàgene sus 'a uicchi.",
        "apisandbox-submit": "Fà 'na richieste",
        "apisandbox-reset": "Pulizze",
+       "apisandbox-retry": "Pruève arrete",
        "apisandbox-examples": "Esembie",
        "apisandbox-results": "Resultate",
        "apisandbox-request-url-label": "URL richieste:",
        "apisandbox-request-time": "Tiembe cercate: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-continue": "Condinue",
+       "apisandbox-continue-clear": "Pulizze",
        "booksources": "Sorgende de le libbre",
        "booksources-search-legend": "Cirche pe le fonde de le libbre",
        "booksources-isbn": "ISBN:",
        "lockedbyandtime": "(de $1 'u $2 a le $3)",
        "move-page": "Spuèste $1",
        "move-page-legend": "Spuèste 'a pàgene",
-       "movepagetext": "Ausanne 'u form aqquà sotte ste cange 'u nome d'a pàgene, spustanne tutte 'a storia soje sus a 'u nome nuéve.\nU' vecchie titole devènde 'nu ridirezionamende sus 'a pàgena nove.\nTu puè aggiornà 'u ridirezionamende ca apponde a 'u titole origgenale automaticamende.\nCe tu no ste scacchie, sta secure de condrollà [[Special:DoubleRedirects|doppie ridirezionaminde]] o [[Special:BrokenRedirects|ridirezionaminde scuasciate]].\nTu si 'u responsabbile de quidde ca cumbine, assicurate ca 'u collegamende condinue a appondà addò avessa scè.\n\nVide Bbuene ca 'a pàgene '''non''' g'avène spustate ce esiste n'otra pàgene cu 'u titole nuéve, a mene ca jè vacande o jè 'na pàgene de ridirezionamende senza storie.\nQuieste significhe ca tu puè fà turnà 'u vecchie nome 'a pàgene ce jedde ha state renomenate e t'è rese conde ca è fatte 'na studecarije sovrascrevènne 'na pàgene esistende.\n\n'''ATTENZIONE!'''\nQuiste pò essere 'nu cangiamende drastiche e inaspettate de 'na pàgene famose assaje;\npe piacere a essere secure-secure de le conseguenze apprime de condinuà.",
-       "movepagetext-noredirectfixer": "Ausanne 'u module aqquà sotte puè renomenà 'na pàgene, spustanne tutte 'a storia soje sotte a 'u nome nuève.\n'U titele vecchie addevende 'na pàgene de ridirezionamende a 'u titele nuève.\nMe raccomande condrolle le redirezionaminde [[Special:DoubleRedirects|a doppie]] o [[Special:BrokenRedirects|scuasciate]].\nTu si responsabbele de assicurarte ca le collagaminde appondene a 'u punde giuste.\n\nVide ca 'a pàgene '''non''' g'avene spustate ce già stè 'na pàgene cu 'u titele nuève, a meno che non g'è vacande o jè 'nu ridirezionamende e non ge tène 'na storie de cangiaminde.\nQuiste signifeche ca jè possibbele renominà 'na pàgene accume se chiamave apprime addò tu è fatte 'n'errore e non g'è possibbele sovrascirevere 'na pàgene esistende.\n\n'''Fà Attenziò!'''\nQuiste pò essere 'nu cangiamende inaspettate pe 'na pàgene popolare;\nPe piacere ha essere secure secure de avere capite le conzeguenze apprime de scè nnande.",
+       "movepagetext": "Ausanne 'u form aqquà sotte ste cange 'u nome d'a pàgene, spustanne tutte 'a storia soje sus a 'u nome nuéve.\nU' vecchie titole devènde 'nu ridirezionamende sus 'a pàgena nove.\nTu puè aggiornà 'u ridirezionamende ca apponde a 'u titole origgenale automaticamende.\nCe tu no ste scacchie, sta secure de condrollà [[Special:DoubleRedirects|doppie ridirezionaminde]] o [[Special:BrokenRedirects|ridirezionaminde scuasciate]].\nTu si 'u responsabbile de quidde ca cumbine, assicurate ca 'u collegamende condinue a appondà addò avessa scè.\n\nVide Bbuene ca 'a pàgene <strong>non</strong> g'avène spustate ce esiste n'otra pàgene cu 'u titole nuéve, a mene ca jè vacande o jè 'na pàgene de ridirezionamende senza storie.\nQuieste significhe ca tu puè fà turnà 'u vecchie nome 'a pàgene ce jedde ha state renomenate e t'è rese conde ca è fatte 'na studecarije sovrascrevènne 'na pàgene esistende.\n\n<strong>ATTENZIONE!</strong>\nQuiste pò essere 'nu cangiamende drastiche e inaspettate de 'na pàgene famose assaje;\npe piacere a essere secure-secure de le conseguenze apprime de condinuà.",
+       "movepagetext-noredirectfixer": "Ausanne 'u module aqquà sotte puè renomenà 'na pàgene, spustanne tutte 'a storia soje sotte a 'u nome nuève.\n'U titele vecchie addevende 'na pàgene de ridirezionamende a 'u titele nuève.\nMe raccomande condrolle le redirezionaminde [[Special:DoubleRedirects|a doppie]] o [[Special:BrokenRedirects|scuasciate]].\nTu si responsabbele de assicurarte ca le collagaminde appondene a 'u punde giuste.\n\nVide ca 'a pàgene <strong>non</strong> g'avene spustate ce già stè 'na pàgene cu 'u titele nuève, a meno che non g'è vacande o jè 'nu ridirezionamende e non ge tène 'na storie de cangiaminde.\nQuiste signifeche ca jè possibbele renominà 'na pàgene accume se chiamave apprime addò tu è fatte 'n'errore e non g'è possibbele sovrascirevere 'na pàgene esistende.\n\n<strong>Fà Attenziò!</strong>\nQuiste pò essere 'nu cangiamende inaspettate pe 'na pàgene popolare;\nPe piacere ha essere secure secure de avere capite le conzeguenze apprime de scè nnande.",
        "movepagetalktext": "Ce tu cazze sta buatte, 'A pàgene de le 'ngazzaminde associate avène spustate automaticamende, sembre ca non g'esisite 'n'otra pàgene de le 'ngazzaminde.\n\nJndr'à stu case, 'a pàgene non g'avène spustate e pò t'a cupià a màne 'u condenute sue.",
        "moveuserpage-warning": "'''Attenziò:''' Tu stè spuèste 'na pàgene utende. Vide bbuène ca sulamende 'a pàgene avène spustate ma l'utende ''non'' g'avene renomenate.",
        "movecategorypage-warning": "<strong>Attenziò:</strong> Tu vuè ccu spuéste 'na pàgene categorije. Vide ca sulamende 'a pàgene avène spustate ma tutte le pàggene ca stonne jndr'à categorije <em>non</em> g'avène spustate sotte a quedda nove.",
        "fileduplicatesearch-noresults": "Nisciune file chiamate \"$1\" ha state acchiate.",
        "specialpages": "Pàggene speciele",
        "specialpages-note-top": "Leggende",
-       "specialpages-note": "* Pàggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Pàggene speciale cu le restriziune.</span>",
        "specialpages-group-maintenance": "Report d'a manutenzione",
        "specialpages-group-other": "Otre pàggene speciele",
        "specialpages-group-login": "Tràse / Reggistrate",
index 86759b4..f712a14 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (см. также [[Special:NewPages|список новых страниц]])",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Показать",
+       "rcfilters-legend-heading": "<strong>Список сокращений:</strong>",
        "rcfilters-activefilters": "Активные фильтры",
        "rcfilters-advancedfilters": "Расширенные фильтры",
+       "rcfilters-limit-title": "Изменения для показа",
+       "rcfilters-days-title": "Последние дни",
+       "rcfilters-hours-title": "Последние часы",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|день|дня|дней}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|час|часа|часов}}",
        "rcfilters-quickfilters": "Сохранённые фильтры",
        "rcfilters-quickfilters-placeholder-title": "Сохраненных ссылок еще нет",
        "rcfilters-quickfilters-placeholder-description": "Чтобы сохранить настройки фильтра и повторно использовать их позже, щелкните значок закладки в области «Активный фильтр» ниже.",
        "rcfilters-invalid-filter": "Недопустимый фильтр",
        "rcfilters-empty-filter": "Нет активных фильтров. Показываются все правки.",
        "rcfilters-filterlist-title": "Фильтры",
-       "rcfilters-filterlist-whatsthis": "ЧÑ\82о Ñ\8dÑ\82о?",
+       "rcfilters-filterlist-whatsthis": "Ð\9aак Ñ\8dÑ\82о Ñ\80абоÑ\82аеÑ\82?",
        "rcfilters-filterlist-feedbacklink": "Оставить отзыв о новых (бета) фильтрах",
        "rcfilters-highlightbutton-title": "Выделить результаты",
        "rcfilters-highlightmenu-title": "Выберите цвет",
        "rcfilters-noresults-conflict": "Результатов не найдено из-за конфликта критериев поиска",
        "rcfilters-state-message-subset": "Фильтр не будет иметь эффекта, поскольку его результаты включены в {{PLURAL:$2|следующий более широкий фильтр|следующие более широкие фильтры}} (попробуйте подсвечивание для различения): $1",
        "rcfilters-state-message-fullcoverage": "Выбор всех фильтров в группе — это то же самое, что и отсутствие какого-либо выбора, поэтому этот фильтр не имеет никакого эффекта. Включённые группы: $1",
-       "rcfilters-filtergroup-registration": "Регистрация участников",
-       "rcfilters-filter-registered-label": "Зарегистрированные",
-       "rcfilters-filter-registered-description": "Вошедшие редакторы.",
-       "rcfilters-filter-unregistered-label": "Незарегистрированные",
-       "rcfilters-filter-unregistered-description": "Редакторы, которые не вошли в систему.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Этот фильтр конфликтует со {{PLURAL:$2|следующим фильтром|следующими фильтрами}} по опыту, {{PLURAL:$2|который ищет|которые ищут}} только зарегистрированных пользователей: $1",
        "rcfilters-filtergroup-authorship": "Редактировать авторство",
        "rcfilters-filter-editsbyself-label": "Внесённые вами изменения",
        "rcfilters-filter-editsbyself-description": "Ваш вклад.",
        "rcfilters-filter-editsbyother-label": "Изменения, внесённые другими участниками",
        "rcfilters-filter-editsbyother-description": "Все правки, кроме ваших собственных.",
-       "rcfilters-filtergroup-userExpLevel": "Уровня опыта (только для зарегистрированных участников)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Фильтры по опыту ищут только зарегистрированных участников, поэтому этот фильтр конфликтует с фильтром «Незарегистрированные».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фильтр «Незарегистрированные» конфликтует с одним или более фильтрами по опыту, которые ищут только незарегистрированных участников. Конфликтующие фильтры отмечены в поле активных фильтров выше.",
+       "rcfilters-filtergroup-userExpLevel": "Регистрация участника и его опыт",
+       "rcfilters-filter-user-experience-level-registered-label": "Зарегистрированные",
+       "rcfilters-filter-user-experience-level-registered-description": "Вошедшие редакторы.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Незарегистрированные",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Редакторы, которые не вошли в систему.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новички",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9cенее 10 Ð¿Ñ\80авок Ð¸ 4 Ð´Ð½ÐµÐ¹ работы.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b Ñ\81 Ð¼ÐµÐ½ÐµÐµ Ñ\87ем 10 Ð¿Ñ\80авками Ð¸ 4 Ð´Ð½Ñ\8fми работы.",
        "rcfilters-filter-user-experience-level-learner-label": "Учащиеся",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\91олÑ\8cÑ\88е Ð¾Ð¿Ñ\8bÑ\82а, Ñ\87ем Ñ\83 Â«Ð\9dовиÑ\87ков», Ð½Ð¾ Ð¼ÐµÐ½Ñ\8cÑ\88е, Ñ\87ем Ñ\83 Â«Ð\9eпÑ\8bÑ\82нÑ\8bÑ\85 Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елей».",
+       "rcfilters-filter-user-experience-level-learner-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b, Ñ\87ей Ð¾Ð¿Ñ\8bÑ\82 Ð½Ð°Ñ\85одиÑ\82Ñ\81Ñ\8f Ð³Ð´Ðµ-Ñ\82о Ð¼ÐµÐ¶Ð´Ñ\83 Ñ\83Ñ\80овнÑ\8fми Â«Ð\9dовиÑ\87ок» Ð¸ Â«Ð\9eпÑ\8bÑ\82нÑ\8bе Ð¿Ð¾Ð»Ñ\8cзоваÑ\82ели».",
        "rcfilters-filter-user-experience-level-experienced-label": "Опытные пользователи",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ð\91олее 30 Ð´Ð½ÐµÐ¹ Ð°ÐºÑ\82ивноÑ\81Ñ\82и Ð¸ 500 Ð¿Ñ\80авок.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b Ñ\81 Ð±Ð¾Ð»ÐµÐµ Ñ\87ем 500 Ð¿Ñ\80авок Ð¸ 30 Ð´Ð½Ñ\8fми Ð°ÐºÑ\82ивноÑ\81Ñ\82и.",
        "rcfilters-filtergroup-automated": "Автоматизированные вклады",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-bots-description": "Правки, сделанные с помощью автоматизированных инструментов.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Фильтр \"малые правки\" конфликтует с одним или несколькими фильтрами, поскольку некоторые типы правок не могут быть названы малыми. Конфликтные фильтры отмечены вверху, в области Активных фильтров.",
        "rcfilters-hideminor-conflicts-typeofchange": "Определённые типы правок не могут быть названы «малыми», поэтому этот фильтр конфликтует со следующим фильтром типа правок: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Этот фильтр типа правок конфликтует с фильтром малых правок. Определённые типы правок не могут быть отмечены «малыми».",
-       "rcfilters-filtergroup-lastRevision": "ТекÑ\83Ñ\89аÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f",
+       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледние Ð²ÐµÑ\80Ñ\81ии",
        "rcfilters-filter-lastrevision-label": "Текущая версия",
-       "rcfilters-filter-lastrevision-description": "Самое последнее изменение на странице.",
-       "rcfilters-filter-previousrevision-label": "Ð\91олее Ñ\80анние Ð²ÐµÑ\80Ñ\81ии",
-       "rcfilters-filter-previousrevision-description": "Все правки, не являющиеся самыми последними на странице.",
+       "rcfilters-filter-lastrevision-description": "ТолÑ\8cко Ñ\81амое последнее изменение на странице.",
+       "rcfilters-filter-previousrevision-label": "Ð\9dе Ð¿Ð¾Ñ\81леднÑ\8fÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f",
+       "rcfilters-filter-previousrevision-description": "Все правки, не являющиеся «последней версией».",
        "rcfilters-filter-excluded": "Исключено",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Исключить выбранное",
+       "rcfilters-exclude-button-on": "Исключение выбранного",
        "rcfilters-view-tags": "Тегированные правки",
        "rcfilters-view-namespaces-tooltip": "Результаты фильтра по пространствам имён",
        "rcfilters-view-tags-tooltip": "Результаты фильтра, использующего метки правок",
        "undelete-search-title": "Поиск удалённых страниц",
        "undelete-search-box": "Поиск удалённых страниц",
        "undelete-search-prefix": "Показать страницы, начинающиеся с:",
+       "undelete-search-full": "Показать названия страниц, содержащие:",
        "undelete-search-submit": "Найти",
        "undelete-no-results": "не найдено соответствующих страниц в архиве удалений.",
        "undelete-filename-mismatch": "Невозможно восстановить версию файла с отметкой времени $1: несоответствие имени файла",
        "fileduplicatesearch-noresults": "Не найден файл с именем «$1».",
        "specialpages": "Спецстраницы",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Обычные служебные страницы.\n* <span class=\"mw-specialpagerestricted\">Служебные страницы с ограниченным доступом.</span>",
        "specialpages-group-maintenance": "Отчёты технического обслуживания",
        "specialpages-group-other": "Другие служебные страницы",
        "specialpages-group-login": "Представиться / Зарегистрироваться",
index e7abb78..b1f3c2b 100644 (file)
        "recentchanges-legend-heading": "<strong>Легендата:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (өссө көр: [[Special:NewPages|Саҥа сирэйдэр тиһиктэрэ]])",
        "recentchanges-submit": "Көрдөр",
+       "rcfilters-legend-heading": "<strong>Кылгатыы испииһэгэ:</strong>",
        "rcfilters-activefilters": "Холбоммут сиидэлэр",
+       "rcfilters-advancedfilters": "Сиһилии сиидэлэр",
        "rcfilters-quickfilters": "Бигэргэммит сиидэлэр",
        "rcfilters-quickfilters-placeholder-title": "Бигэргэммит сигэ билигин суох",
        "rcfilters-quickfilters-placeholder-description": "Сиидэ туруорууларын кэлин туһанарга, \"Холбоммут сиидэ\" хонуутугар кыбытык ойуутун баттаа.",
        "rcfilters-savedqueries-unsetdefault": "Эбии этиллибэтэҕинэ талыллары суох гын",
        "rcfilters-savedqueries-remove": "Сот",
        "rcfilters-savedqueries-new-name-label": "Аата",
-       "rcfilters-savedqueries-apply-label": "Туруоруулары өйдөөн хаалыы",
+       "rcfilters-savedqueries-new-name-placeholder": "Сиидэ сорудаҕын ойуулаа",
+       "rcfilters-savedqueries-apply-label": "Сиидэни оҥоруу",
        "rcfilters-savedqueries-cancel-label": "Салҕаама",
        "rcfilters-savedqueries-add-new-title": "Сиидэлэ билиҥҥи туруорууларын хааллар",
        "rcfilters-restore-default-filters": "Анаан этиллибэтэҕинэ турар сиидэлэри холбоо",
        "rcfilters-noresults-conflict": "Көрдөөһүн мэктиэлэрин мөккүөрэ тахсан, туох да көстүбэтэ",
        "rcfilters-state-message-subset": "Сиидэ туһата суох, тоҕо диэтэххэ кини түмүгэ атын {{PLURAL:$2|сиидэ түмүгэр|сиидэлэр түмүктэригэр}} киирэр эбит (өҥүн уларытыыны туһанан сиһилии көрүөххүн сөп): $1",
        "rcfilters-state-message-fullcoverage": "Сиидэни барытын талыы тугу да талбатаҕыҥ тэҥэ, онон сиидэ туһата суох. Холбоммут бөлөхтөр: $1",
-       "rcfilters-filtergroup-registration": "Кыттааччылары бэлиэтээһин",
-       "rcfilters-filter-registered-label": "Бэлиэтэммиттэр",
-       "rcfilters-filter-registered-description": "Киирбит эрэдээктэрдэр.",
-       "rcfilters-filter-unregistered-label": "Бэлиэтэммэтэхтэр",
-       "rcfilters-filter-unregistered-description": "Киирбэтэх эрэдээктэрдэр.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Бу сиидэ бэлиэтэммит эрэ кыттааччылары көрөр {{PLURAL:$2|сиидэни|сиидэлэри}} кытта сөп түбэспэт: $1",
        "rcfilters-filtergroup-authorship": "Ааптарын уларытыы",
        "rcfilters-filter-editsbyself-label": "Эн тус бэйэҥ уларытыыларыҥ",
        "rcfilters-filter-editsbyself-description": "Бэйэҥ уларытыыларыҥ.",
        "rcfilters-filter-editsbyother-label": "Атыттар уларытыылара",
        "rcfilters-filter-editsbyother-description": "Атыттар уларытыылара (Эйиэниттэн ураты).",
        "rcfilters-filtergroup-userExpLevel": "Кыттааччы уопутун таһымынан (бэлиэтэммиттэргэ эрэ)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Уопуту көрөр сиидэ бэлиэтэммит эрэ кыттааччылары көрөр, онон \"Бэлиэтэммэтэх\" диэнинэн көрдүүр сиидэни кытта бииргэ сатаан туттуллубат.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "\"Бэлиэтэммэтэх\" диэнинэн көрдүүр сиидэ уопуту көрөр биир дуу хас да дуу сиидэни кытта сатаан туттуллубат, тоҕо диэтэххэ уоупут көрөр сиидэлэр бэлиэтэммит эрэ кыттааччылары көрөллөр. Сөп түбэспэт сиидэлэр үөһэ көстөллөр.",
+       "rcfilters-filter-user-experience-level-registered-label": "Бэлиэтэммиттэр",
+       "rcfilters-filter-user-experience-level-registered-description": "Киирбит эрэдээктэрдэр.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Бэлиэтэммэтэхтэр",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Киирбэтэх эрэдээктэрдэр.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Саҥа киирбиттэр",
        "rcfilters-filter-user-experience-level-newcomer-description": "Отой саҥалар: 10 көннөрүүттэн аҕыйаҕы оҥорбуттар уонна бэлиэтэмиттэрэ 4 хоно иликтэр.",
        "rcfilters-filter-user-experience-level-learner-label": "Үөрэнээччилэр",
        "rcfilters-filter-lastrevision-description": "Сирэй тиһэх уларыйыыта.",
        "rcfilters-filter-previousrevision-label": "Эрдэ оҥоһуллубут көннөрүүлэр",
        "rcfilters-filter-previousrevision-description": "Тиһэх көннөрүүттэн ураты бары уларытыылар.",
+       "rcfilters-filter-excluded": "Мантан ураты",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-view-tags": "Бэлиэтэммит (тиэктээх) уларытыылар",
        "rcnotefrom": "Манна {{PLURAL:$5|уларытыы көрдөрүлүннэ|уларытыылар көһүннүлэр}} баччаттан <strong>$3, $4</strong> (баччаттан элбэх көстүбэт <strong>$1</strong>).",
        "rclistfromreset": "Дьылын-күнүн сот",
        "rclistfrom": "Бу кэм $3 $2 кэнниттэн оҥоһуллубуттары көрдөр",
index 010223b..907e6c7 100644 (file)
        "anontalk": "بحث",
        "navigation": "رھنمائي",
        "and": "&#32؛۽",
-       "qbfind": "ڳوليو",
-       "qbbrowse": "جھانگيو",
-       "qbedit": "سنواريو",
-       "qbpageoptions": "هيءُ صفحو",
-       "qbmyoptions": "منهنجا صفحا",
        "faq": "ڪپس",
-       "faqpage": "Project:ڪپس",
        "actions": "ڪارگذاريون",
        "namespaces": "نانءُپولارَ",
        "variants": "بَدَلَ",
        "edit-local": "مقامي تشريح کي ترميميو",
        "create": "سرجيو",
        "create-local": "مقامي تشريح ڏيو",
-       "editthispage": "هيءُ صفحو سنواريو",
-       "create-this-page": "اهو صفحو نئين سر جوڙيو",
        "delete": "ڊاھيو",
-       "deletethispage": "هيءُ صفحو ڊاهيو",
-       "undeletethispage": "هيءُ صفحو اڻ ڊاهيو",
        "undelete_short": "اڻڊاهيو {{PLURAL:$1|هڪ ترميم|$1 ترميمون}}",
        "viewdeleted_short": "ڏسو {{PLURAL:$1|هڪ ڊاٺل ترميم|$1 ڊاٺل ترميمون}}",
        "protect": "تحفظيو",
        "protect_change": "تبديل ڪريو",
-       "protectthispage": "هيءُ صفحو تحفظيو",
        "unprotect": "تحفظ بدلايو",
-       "unprotectthispage": "هن صفحي جو تحفظ بدلايو",
        "newpage": "نئون صفحو",
-       "talkpage": "هن صفحي تي بحث ڪريو",
        "talkpagelinktext": "بحث",
        "specialpage": "خاص صفحو",
        "personaltools": "ذاتي اوزار",
-       "articlepage": "مسودو ڏسو",
        "talk": "بحث",
        "views": "ڏيٺون",
        "toolbox": "اوزارَ",
        "tool-link-userrights": "{{GENDER:$1|يوزر}} گروھ تبديل ڪريو",
        "tool-link-userrights-readonly": "{{GENDER:$1|يوزر}} گروھ ڏسو",
        "tool-link-emailuser": "ھن {{GENDER:$1|يوزر}} ڏانھن برقٽپال موڪليو",
-       "userpage": "يوزر صفحو ڏسو",
-       "projectpage": "رٿائي صفحو ڏسو",
        "imagepage": "ذريعاتي صفحو ڏسو",
        "mediawikipage": "نياپي جو صفحو ڏسو",
        "templatepage": "سانچي جو صفحو ڏسو",
        "rcfilters-search-placeholder": "تازيون تبديليون ڇاڻيو (جھانگيو يا لکڻ شروع ڪريو)",
        "rcfilters-empty-filter": "ڪي بہ سرگرم ڇاڻيون ناھن. سڀ ڀاڱيداريون ڏيکاريل آھن.",
        "rcfilters-filterlist-title": "ڇاڻيون",
-       "rcfilters-filterlist-whatsthis": "Ù\87Ù\8a Ú\87ا Ø¢Ù\87Ù\8a؟",
+       "rcfilters-filterlist-whatsthis": "Ù\87Ù\8a ÚªÙ\8aئÙ\86 ÚªÙ\85 ÚªÙ\86 Ù¿Ø§؟",
        "rcfilters-highlightbutton-title": "نتيجن کي نمايان (هاءِ لائيٽ) ڪيو",
        "rcfilters-highlightmenu-title": "رنگ چونڊيو",
-       "rcfilters-filter-registered-label": "رجسٽر ٿيل",
-       "rcfilters-filter-unregistered-label": "اڻ رجسٽر ٿيل",
        "rcfilters-filter-editsbyself-label": "مون پاران تبديليون",
        "rcfilters-filter-editsbyother-label": "ٻين پاران تبديليون",
+       "rcfilters-filter-user-experience-level-registered-label": "رجسٽر ٿيل",
+       "rcfilters-filter-user-experience-level-unregistered-label": "اڻرجسٽر ٿيل",
        "rcfilters-filter-user-experience-level-newcomer-label": "نوان ايندڙ",
        "rcfilters-filter-user-experience-level-learner-label": "سکندڙ",
        "rcfilters-filter-user-experience-level-experienced-label": "تجربيڪار واھپ",
        "tooltip-compareselectedversions": "هن صفحي جن ٻن چونڊيل پرتن درميان تفاوت ڏسو.",
        "tooltip-watch": "هيءُ صفحو پنهنجي نظر ۾ فھرست ۾ شامل ڪريو",
        "tooltip-rollback": "\"واپس ورايو\" ھن صفحي ۾ پوئين ڀاڱيدار جي ڪيل ترميم(ن) کي ھڪ ٽڙڪ سان اڻڪري ٿو",
+       "tooltip-preferences-save": "ترجيحون سانڍيو",
        "tooltip-summary": "ننڍو خلاصو ڏيو",
        "anonymous": "گمنام {{PLURAL:$1|يوزر|يوزرس}} جو {{SITENAME}}",
        "simpleantispam-label": "اينٽي-اسپام روڪ.\nھن کي <strong>نہ</strong> ڀريو!",
index 368e648..824851e 100644 (file)
        "retrievedfrom": "Yurrid z \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|{{GENDER:$3|ⴷⴰⵔⴽ|ⴷⴰⵔⵎ}}}} $1 ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|ⵜⵓⵣⵉⵏⵜ ⵜⴰⵎⴰⵢⵏⵓⵜ|ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ}}",
-       "newmessagesdifflinkplural": "{{PLURAL:$1|â´°âµ\99âµ\8fâ´¼âµ\8d âµ\89ⴳⴳⵯâµ\94â´°âµ\8f|âµ\89âµ\99âµ\8fâ´¼ⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
+       "newmessagesdifflinkplural": "{{PLURAL:$1|âµ\93âµ\99âµ\8fâ´¼âµ\8d âµ\89ⴳⴳⵯâµ\94â´°âµ\8f|âµ\89âµ\99âµ\8fâ´¼âµ\89ⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
        "youhavenewmessagesmulti": "{{GENDER:|ⴷⴰⵔⴽ|ⴷⴰⵔⵎ}} ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ ⴳ $1",
        "editsection": "ⵙⵏⴼⵍ",
        "editold": "ⵙⵏⴼⵍ",
        "notloggedin": "Ur tmlit mat git",
        "createaccount": "Murzm amidan nek (lkunt)..",
        "createaccountmail": "S tirawt taliktunant",
-       "createacct-benefit-body1": "{{PLURAL:$1|ⴰⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
+       "createacct-benefit-body1": "{{PLURAL:$1|â´°âµ\99âµ\8fâ´¼âµ\8d|âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "createacct-benefit-body3": "{{PLURAL:$1|ⴰⵏⴰⵎⵓ ⵉⴳⴳⵯⵔⴰⵏ|ⵉⵏⴰⵎⵓⵜⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
        "badretype": "Tasarut lin tgit ur dis tucka.",
        "action-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "nchanges": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
        "enhancedrc-history": "ⴰⵎⵣⵔⵓⵢ",
-       "recentchanges": "ⵉⵙⵏⴼⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ",
+       "recentchanges": "âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f â´³â´³âµ¯âµ\94â´°âµ\8fâµ\89âµ\8f",
        "recentchanges-legend": "Tixtiɣitin (options) n imbddl imaynutn",
        "recentchanges-summary": "Ml imbddln imaynutn  n wiki ɣ tasna yad",
        "recentchanges-feed-description": "ⴹⴼⵓⵔ ⵉⵙⵏⴼⵍⵏ ⴰⴽⴽⵯ ⵉⴳⴳⵯⵔⴰⵏ ⵏ ⵓⵡⵉⴽⵉ ⴳ ⵉⴼⵉⵍⵉ ⴰⴷ.",
        "recentchanges-label-minor": "ⵡⴰⴷ ⵉⴳⴰ ⴰⵙⵏⴼⵍ ⵓⵎⵥⵉⵢ",
        "recentchanges-label-bot": "ⴰⵙⵏⴼⵍ ⴰⴷ ⵉⵙⴽⵔ ⵜ ⵢⴰⵏ ⵓⵔⵓⴱⵓ",
        "recentchanges-label-unpatrolled": "Ambddl ad ura jju ittmẓra",
+       "recentchanges-label-plusminus": "ⵜⵏⴼⵍ ⵜⵉⴷⴷⵉ ⵏ ⵜⴰⵙⵏⴰ ⵙ ⵡⵓⵟⵟⵓⵏ ⴰⴷ ⵏ ⵉⴷ ⴱⴰⵢⵜ",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ⵥⵔ ⵓⵍⴰ [[Special:NewPages|ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ]])",
        "rcfilters-savedqueries-new-name-label": "ⵉⵙⵎ",
        "rcfilters-filterlist-whatsthis": "ⵎⴰⵜⵜⴰ ⵓⵢⴰ?",
        "rcfilters-filter-bots-label": "ⴱⵓⵜ",
        "rcnotefrom": "Had imbddln lli ittuyskarn z '''$2''' ('''$1''' ɣ uggar).",
        "rclistfrom": "Mel imbdeltn imaynutn z $3 $2",
-       "rcshowhideminor": "$1 ⵉⵙⵏⴼⵍⵏ ⵓⵎⵥⵉⵢⵏ",
+       "rcshowhideminor": "$1 âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f âµ\93âµ\8eâµ¥âµ\89âµ¢âµ\8f",
        "rcshowhideminor-hide": "ⵙⵙⵏⵜⵍ",
        "rcshowhidebots": "$1 ⵉⴷ ⴱⵓⵜ",
        "rcshowhidebots-hide": "ⵙⵙⵏⵜⵍ",
        "recentchangeslinked": "Imbddel zun ɣwid",
        "recentchangeslinked-feed": "Imbddeln zund ɣwid",
        "recentchangeslinked-toolbox": "Imbddeln zund ɣwid",
-       "recentchangeslinked-title": "ⵉⵙⵏⴼⵍⵏ ⵇⵇⵏⵏⵉⵏ ⵙ \"$1\"",
+       "recentchangeslinked-title": "âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f âµ\87âµ\87âµ\8fâµ\8fâµ\89âµ\8f âµ\99 \"$1\"",
        "recentchangeslinked-summary": "Ɣid umuɣ iymbddeln li ittyskarnin tigira yad ɣ tisniwin li ittuyzdayn d kra n tasna (ulla i igmamn n kra taggayt ittuyzlayn). Tisniwin  ɣ [[Special:Watchlist|Umuɣ n tisniwin li ttsaggat]].",
        "recentchangeslinked-page": "ⵉⵙⵎ ⵏ ⵜⴰⵙⵏⴰ:",
        "recentchangeslinked-to": "Afficher les changements vers les pages liées au lieu de la page donnée\nMel imbddeln z tisniwin li ittuyzdayni bla tasna li trit.",
        "listusers": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵎⵔⴰⵙⵏ",
        "usercreated": "{{GENDER:$3|ⵉⵙⵏⵓⵍⴼⴰ|ⵜⵙⵏⵓⵍⴼⴰ}} ⴳ $1 ⴳ $2",
        "newpages": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ",
-       "move": "âµ\99âµ\8eâ´°ⵜⵜⵉ",
+       "move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ",
        "movethispage": "ⵙⵎⴰⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "unusedcategoriestext": "Taggayin ad llant waxxa gis nt ur tlli kra n tasna wala kra n taggayin yaḍnin",
        "notargettitle": "F walu",
        "restriction-type": "ⵜⵓⵔⴰⴳⵜ:",
        "restriction-level": "Restriction level:",
        "restriction-edit": "ⵙⵏⴼⵍ",
-       "restriction-move": "âµ\99âµ\8eâ´°ⵜⵜⵉ",
+       "restriction-move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ",
        "undeletelink": "mel/rard",
        "undeleteviewlink": "Ẓṛ",
        "undelete-search-submit": "ⵙⵉⴳⴳⵍ",
        "tooltip-ca-unprotect": "ⵙⵏⴼⵍ ⴰⴼⵔⴰⴳ ⵏ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-undelete": "Rard imbddeln imzwura li ittyskarnin ɣ tasna yad",
-       "tooltip-ca-move": "âµ\99âµ\8eâ´°ⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "tooltip-ca-move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-watch": "ⵔⵏⵓ ⵜⴰⵙⵏⴰ ⴰⴷ ⵉ ⵜⵍⴳⴰⵎⵜ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tooltip-ca-unwatch": "ⵙⵉⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ ⵣⴳ ⵜⵍⴳⴰⵎⵜ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tooltip-search": "ⵙⵉⴳⴳⵍ ⴳ {{SITENAME}}",
        "tooltip-n-mainpage-description": "Kid tasna tamuqrant",
        "tooltip-n-portal": "f' usenfar, matzdart atitskrt, maniɣrattaft ɣayli trit",
        "tooltip-n-currentevents": "Tiɣri izrbn i kullu maɣid immusn",
-       "tooltip-n-recentchanges": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵏⴼⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ ⴳ ⵓⵡⵉⴽⵉ",
+       "tooltip-n-recentchanges": "âµ\9câ´°âµ\8dⴳⴰâµ\8eâµ\9c âµ\8f âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f â´³â´³âµ¯âµ\94â´°âµ\8fâµ\89âµ\8f â´³ âµ\93ⵡâµ\89â´½âµ\89",
        "tooltip-n-randompage": "Srbu yat tasna ɣik nna ka tga",
        "tooltip-n-help": "Adɣar n w-aws",
        "tooltip-t-whatlinkshere": "Umuɣ n kullu tisnatin n Wiki lid ilkkmn ɣid",
        "tags-active-no": "ⵓⵀⵓ",
        "tags-edit": "ⵙⵏⴼⵍ",
        "tags-delete": "ⴽⴽⵙ",
-       "tags-hitcount": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
+       "tags-hitcount": "$1 {{PLURAL:$1|âµ\93âµ\99âµ\8fâ´¼âµ\8d|âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f}}",
        "tags-create-submit": "ⵙⵏⵓⵍⴼⵓ",
        "comparepages": "ⵙⵎⵣⴰⵣⴰⵍ ⵜⴰⵙⵏⵉⵡⵉⵏ",
        "compare-page1": "ⵜⴰⵙⵏⴰ 1",
diff --git a/languages/i18n/skr-arab.json b/languages/i18n/skr-arab.json
new file mode 100644 (file)
index 0000000..575c764
--- /dev/null
@@ -0,0 +1,651 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Saraiki"
+               ]
+       },
+       "tog-underline": "لنک  ہیٹھ لکیر",
+       "tog-hideminor": "چھوٹیاں تبدیلیاں لُکاؤ",
+       "tog-numberheadings": "سرخیاں کوں خود کار نمبر ݙیوو",
+       "tog-showtoolbar": "آلات ترمیم ݙکھاؤ",
+       "underline-always": "ہمیشہ",
+       "underline-never": "کݙاہیں وی کائناں",
+       "underline-default": "سکن یا براؤزر دا طے شدہ",
+       "editfont-style": "خانہ ترمیم دا فونٹ",
+       "editfont-default": "براؤزر دا طے شدہ",
+       "editfont-monospace": "مونوسپیسڈ فونٹ",
+       "editfont-sansserif": "سنس سیرف فونٹ",
+       "editfont-serif": "سیرف فونٹ",
+       "sunday": "اتوار",
+       "monday": "سونوار",
+       "tuesday": "منگل",
+       "wednesday": "بدھ",
+       "thursday": "خمیس",
+       "friday": "جمعہ",
+       "saturday": "چھݨ چھݨ",
+       "sun": "اتوار",
+       "mon": "سونوار",
+       "tue": "منگل",
+       "wed": "بدھ",
+       "thu": "خمیس",
+       "fri": "جمعہ",
+       "sat": "چھݨ چھݨ",
+       "january": "جنوری",
+       "february": "فروری",
+       "march": "مارچ",
+       "april": "اپريل",
+       "may_long": "مئی",
+       "june": "جون",
+       "july": "جولائی",
+       "august": "اگست",
+       "september": "ستمبر",
+       "october": "اکتوبر",
+       "november": "نومبر",
+       "december": "دسمبر",
+       "january-gen": "جنوری",
+       "february-gen": "فروری",
+       "march-gen": "مارچ",
+       "april-gen": "اپريل",
+       "may-gen": "مئی",
+       "june-gen": "جون",
+       "july-gen": "جولائی",
+       "august-gen": "اگست",
+       "september-gen": "ستمبر",
+       "october-gen": "اکتوبر",
+       "november-gen": "نومبر",
+       "december-gen": "دسمبر",
+       "jan": "جنوری",
+       "feb": "فروری",
+       "mar": "مارچ",
+       "apr": "اپریل",
+       "may": "مئی",
+       "jun": "جون",
+       "jul": "جولائی",
+       "aug": "اگست",
+       "sep": "ستمبر",
+       "oct": "اکتوبر",
+       "nov": "نومبر",
+       "dec": "دسمبر",
+       "january-date": "$1 جنوری",
+       "february-date": "$1 فروری",
+       "march-date": "$1 مارچ",
+       "april-date": "$1 اپریل",
+       "may-date": "$1 مئی",
+       "june-date": "$1 جون",
+       "july-date": "$1 جولائی",
+       "august-date": "$1 اگست",
+       "september-date": "$1 ستمبر",
+       "october-date": "$1 اکتوبر",
+       "november-date": "$1 نومبر",
+       "december-date": "$1 دسمبر",
+       "period-am": "سویر",
+       "period-pm": "شام",
+       "pagecategories": "{{PLURAL:$1|زمرہ|زمرہ جات}}",
+       "category_header": "زمرہ \"$1\" وچ ورقے",
+       "subcategories": "ذیلی زمرہ جات",
+       "category-media-header": "زمرہ \"$1\" وچ میڈیا",
+       "hidden-categories": "{{PLURAL:$1|پوشیدہ زمرہ|پوشیدہ زمرہ جات}}",
+       "listingcontinuesabbrev": "جاری۔",
+       "noindex-category": "غیر فہرست شدہ صفحات",
+       "broken-file-category": "ٹٹے ہوۓ جوڑاں آلے صفحے",
+       "about": "تعارف",
+       "article": "مواد آلا ورقہ",
+       "newwindow": "(نویں ونڈو وچ کھولو)",
+       "cancel": "مکاؤ",
+       "moredotdotdot": "ٻئے",
+       "mypage": "ورقہ",
+       "mytalk": "ڳالھ مہاڑ",
+       "anontalk": "ڳالھ مہاڑ",
+       "navigation": "رہنمائی",
+       "and": "&#32;تے",
+       "faq": "عام طور تے پچھے ونڄݨ آلے سوال",
+       "actions": "کم",
+       "namespaces": "ناں دیاں جہاواں",
+       "variants": "قسماں",
+       "navigation-heading": "فہرست رہنمائی",
+       "errorpagetitle": "نقص",
+       "returnto": "واپس $1 چلو",
+       "tagline": " {{SITENAME}} توں",
+       "help": "مدد",
+       "search": "کھوج",
+       "searchbutton": "کھوج",
+       "go": "ڄلو",
+       "searcharticle": "ڄلو",
+       "history": "پچھلے کم",
+       "history_short": "تاریخچہ",
+       "history_small": "تاریخچہ",
+       "printableversion": "چھپݨ جوگا ورقہ",
+       "permalink": "پکا جوڑ",
+       "print": "چھاپو",
+       "view": "ݙکھالے",
+       "view-foreign": "$1 تے ݙیکھو",
+       "edit": "لکھو",
+       "create": "بݨاؤ",
+       "create-local": "آپنی لکھت رلاؤ",
+       "delete": "مٹاؤ",
+       "protect_change": "تبدیل کرو",
+       "newpage": "نواں ورقہ",
+       "talkpagelinktext": "ڳالھ مہاڑ",
+       "specialpage": "خاص ورقہ",
+       "personaltools": "ذاتی آوزار",
+       "talk": "ڳالھ مہاڑ",
+       "views": "ݙکھالے",
+       "toolbox": "آوزار",
+       "otherlanguages": "ٻنھاں زباناں وچ",
+       "redirectedfrom": "($1 کنوں ولدا رجوع )",
+       "redirectpagesub": "صفحہ ریڈائریکٹ کرو",
+       "redirectto": "اڳے کرو:",
+       "lastmodifiedat": "ایہ ورقہ چھیکڑی واری  $1 کوں $2 تے تبدیل تھیا ہائی۔",
+       "jumpto": "ٹپ مارو",
+       "jumptonavigation": "رہنمائی",
+       "jumptosearch": "ڳولو",
+       "aboutsite": "{{SITENAME}} دا تعارف",
+       "aboutpage": "Project:تعارف",
+       "copyrightpage": "{{ns:project}}:حقوق تصانیف",
+       "currentevents": "حالیہ واقعات",
+       "currentevents-url": "Project:حالیہ واقعات",
+       "disclaimers": "لاتعلقی اظہار",
+       "disclaimerpage": "Project:عام لاتعلقی اظہار",
+       "edithelp": "لکھݨ وچ مدد",
+       "helppage-top-gethelp": "مدد",
+       "mainpage": "وݙا ورقہ",
+       "mainpage-description": "پہلا ورقہ",
+       "portal": "بیٹھک",
+       "portal-url": "Project:دیوان عام",
+       "privacy": "پرائیویسی پالیسی",
+       "privacypage": "Project:پرائیویسی پالیسی",
+       "ok": "ٹھیک ہے",
+       "retrievedfrom": "\"$1\" توں گھدا",
+       "youhavenewmessages": "{{PLURAL:$3| تہاݙے کیتے}} $1 ($2).",
+       "newmessagesdifflinkplural": "چھیکڑی {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
+       "editsection": "لکھو",
+       "editold": "لکھو",
+       "viewsourceold": "ماخذ ݙیکھو",
+       "editlink": "لکھو",
+       "viewsourcelink": "ماخذ ݙیکھو",
+       "editsectionhint": "حصہ لکھو: $1",
+       "toc": "حصے",
+       "showtoc": "ݙیکھاؤ",
+       "hidetoc": "لُکاؤ",
+       "collapsible-collapse": "لکاؤ",
+       "collapsible-expand": "ودھاؤ",
+       "confirmable-yes": "ڄیا",
+       "confirmable-no": "کو",
+       "site-atom-feed": "$1 اٹوم فیڈ",
+       "page-atom-feed": "$1 اٹوم فیڈ",
+       "red-link-title": "$1 (ایہ ورقہ اڄݨ تائیں کائنی بݨیا)",
+       "nstab-main": "ورقہ",
+       "nstab-user": "صفحۂ صارف",
+       "nstab-special": "خاص ورقہ",
+       "nstab-project": "پروجیکٹ ورقہ",
+       "nstab-image": "فائل",
+       "nstab-mediawiki": "سنیہہ",
+       "nstab-template": "سانچہ",
+       "nstab-category": "زمرہ",
+       "mainpage-nstab": "وݙا ورقہ",
+       "nosuchspecialpage": "اینجھا کوئی خاص ورقہ کائنی",
+       "error": "نقص",
+       "databaseerror": "ڈیٹابیس دی غلطی",
+       "databaseerror-error": "نقص: $1",
+       "badtitle": "بھیڑا عنوان",
+       "viewsource": "ماخذ ݙیکھو",
+       "viewsource-title": "$1 دا مسودہ ݙیکھو",
+       "yourname": "صارف دا ناں",
+       "userlogin-yourname": "صارف ناں",
+       "userlogin-yourname-ph": "آپݨا ورتݨ ناں صارف درج کرو",
+       "createacct-another-username-ph": "آپݨا ورتݨ ناں صارف درج کرو",
+       "yourpassword": "پاس ورڈ",
+       "userlogin-yourpassword": "پاس ورڈ",
+       "userlogin-yourpassword-ph": "پاس ورڈ درج کرو",
+       "createacct-yourpassword-ph": "پاس ورڈ درج کرو",
+       "yourpasswordagain": "پاس ورڈ ولدا لکھو",
+       "createacct-yourpasswordagain": "پاس ورڈ دی تصدیق کرو",
+       "createacct-yourpasswordagain-ph": "پاس ورڈ ولدا درج کرو",
+       "userlogin-remembermypassword": "میکوں لاگ ان رکھو",
+       "userlogin-signwithsecure": "محفوظ رابطہ (کنکشن) استعمال کرو",
+       "cannotlogin-title": "لاگ ان نی تھی سڳدے",
+       "cannotlogin-text": "لاگ ان تھیوݨ ناممکن ہے",
+       "cannotloginnow-title": "ہݨ لاگ ان نہوے تھی سڳدے",
+       "login": "لاگ ان تھیوو",
+       "logout": "لاگ آؤٹ",
+       "userlogout": "لاگ آؤٹ",
+       "notloggedin": "لاگ ان نہوے تھئے",
+       "userlogin-noaccount": "تہاݙا کھاتہ کائنی؟",
+       "userlogin-joinproject": "جُڑ ونڄو {{SITENAME}} نال",
+       "createaccount": "کھاتہ کھولو",
+       "userlogin-resetpassword-link": "پاسورڈ بھل ڳئے ہو؟",
+       "userlogin-helplink2": "لاگ ان تھیوݨ کیتے مدد دی لوڑ ہے؟",
+       "createacct-emailrequired": "ای میل پتہ",
+       "createacct-emailoptional": "ای-میل پتہ، آپشنل",
+       "createacct-email-ph": "اپنا ای-میل پتہ لکھو",
+       "createacct-another-email-ph": "اپنا ای-میل پتہ لکھو",
+       "createacct-reason": "سبب",
+       "createacct-submit": "اپݨاں کھاتا کھولو",
+       "createacct-another-submit": "کھاتہ کھولو",
+       "createacct-benefit-heading": "{{SITENAME}} تہاݙے وانگوں علم دوست افراد دا مرہون منت ہے۔",
+       "createacct-benefit-body1": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
+       "createacct-benefit-body2": "\n$1 {{PLURAL:$1|ورقہ|ورقے}}",
+       "createacct-benefit-body3": "ہݨ دے {{PLURAL:$1|کم|کماں}}",
+       "loginerror": "لاگ ان وچ غلطی",
+       "createacct-error": "کھاتہ بݨاوݨ وچ غلطی",
+       "loginlanguagelabel": "زبان: $1",
+       "pt-login": "لاگ ان تھیوو",
+       "pt-login-button": "لاگ ان تھیوو",
+       "pt-createaccount": "کھاتہ کھولو",
+       "pt-userlogout": "لاگ آؤٹ",
+       "changepassword": "پاس ورڈ تبدیل کرو",
+       "oldpassword": "پراݨا پاس ورڈ",
+       "retypenew": "نواں پاس ورڈ ولدا لکھو",
+       "botpasswords-label-create": "بݨاؤ",
+       "botpasswords-label-update": "اپ ݙیٹ",
+       "botpasswords-label-cancel": "منسوخ",
+       "botpasswords-label-delete": "مٹاؤ",
+       "botpasswords-label-resetpassword": "پاس ورڈ تبدیل کرو",
+       "passwordreset": "نواں پاس ورڈ بݨاؤ",
+       "passwordreset-username": "صارف دا ناں",
+       "resettokens-tokens": "ٹوکن",
+       "resettokens-token-label": "$1 (موجودہ قدر: $2)",
+       "bold_sample": "موٹی لکھائی",
+       "bold_tip": "موٹی لکھائی",
+       "italic_sample": "ترچھا متن",
+       "italic_tip": "ترچھی لکھائی",
+       "link_sample": "جوڑ",
+       "link_tip": "اندرونی جوڑ",
+       "extlink_sample": "http://www.example.com جوڑ دا ناں",
+       "extlink_tip": "باہرلے جوڑ (remember http:// prefix)",
+       "headline_sample": "شہ سرخی",
+       "headline_tip": "ݙوجھے درجے دی سرخی",
+       "nowiki_sample": "فارمیٹ نہ تھئی ہوئی لکھائی اتھ درج کرو",
+       "nowiki_tip": "ویکی فارمیٹ کوں نظرانداز کرو",
+       "image_tip": "پیوستہ فائل",
+       "media_tip": "فائل دا جوڑ",
+       "sig_tip": "تہاݙے دستخط ویلے دے نال",
+       "hr_tip": "اُفقی لکیر (زیادہ استعمال نہ کریں)",
+       "summary": "خلاصہ",
+       "subject": "عنوان:",
+       "minoredit": "ایہ ہک چھوٹی تبدیلی ہے",
+       "watchthis": "ایں ورقے تے اکھ رکھو",
+       "savearticle": "محفوظ",
+       "savechanges": "تبدیلیاں محفوظ کرو",
+       "publishpage": "ورقہ شائع کرو",
+       "publishchanges": "تبدیلیاں شائع کرو",
+       "preview": "نمائش",
+       "showpreview": "نمائش",
+       "showdiff": "تبدیلیاں ݙکھاؤ",
+       "loginreqlink": "لاگ ان",
+       "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" رجسٹرڈ کائنی۔",
+       "continue-editing": "خانہ ترمیم وچ ونڄو",
+       "editing": "تساں \"$1\" لکھدے پئے ہو",
+       "creating": "زیر تخلیق $1",
+       "editingsection": "«$1» دے قطعہ دی ترمیم",
+       "templatesused": "ایں ورقے تے  ورتے ڳئے {{PLURAL:$1|سانچے|سانچہ}}:",
+       "template-protected": "(بچایا گیا)",
+       "template-semiprotected": "(نیم محفوظ)",
+       "hiddencategories": "ایہ ورقہ {{PLURAL:$1|1 لُکے زمریاں|$1 لکا زمرہ }} وچ شامل ہے:",
+       "permissionserrors": "خطائے اجازت",
+       "moveddeleted-notice": "ایہ ورقہ مٹایا ڳیا ہے۔ مٹاوݨ دا لاگ ہیٹھاں ݙتا ہویا ہے",
+       "content-model-wikitext": "ویکی متن",
+       "undo-failure": "متنازع تبدیلیاں پاروں ایہ تبدیلی واپس نی تھی سڳدی۔",
+       "viewpagelogs": "صفحے دے لاگ ݙیکھو",
+       "currentrev-asof": "حالیہ نسخہ بمطابق $1",
+       "revisionasof": "دی تبدیلیاں $1",
+       "previousrevision": "→ پراݨا نسخہ",
+       "nextrevision": "نویں تبدیلی →",
+       "currentrevisionlink": "موجودہ حالت",
+       "cur": " رائج",
+       "last": "پچھلا",
+       "history-fieldset-title": "دہرائی کیتے لبھت",
+       "histfirst": "قدیم ترین",
+       "histlast": "تازہ ترین",
+       "history-feed-title": "ریویژن رکارڈ",
+       "history-feed-item-nocomment": "$2 کوں $1",
+       "rev-delundel": "ݙکھاؤ/لکاؤ",
+       "mergelog": "لاگ رلاؤ",
+       "history-title": "\"$1\" دا ریکارڈ",
+       "difference-title": "\"$1\" دے نسخیاں دے درمیان فرق",
+       "lineno": "سطر $1:",
+       "compareselectedversions": "منتخب متـن دا موازنہ",
+       "editundo": "واپس",
+       "diff-empty": "(کوئی فرق کائنی)",
+       "searchresults": "کھوج دا نتارا",
+       "searchresults-title": "\"$1\" دے کھوج نتارے",
+       "prevn": "پچھلے {{PLURAL:$1|$1}}",
+       "nextn": "اگلے {{PLURAL:$1|$1}}",
+       "prevn-title": "پہلے $1 {{PLURAL:$1|نتیجے}}",
+       "nextn-title": "اگلے $1 {{PLURAL:$1|نتیجے}}",
+       "shown-title": "وکھاؤ $1 {{PLURAL:$1|نتیجے}}",
+       "viewprevnext": "($1 {{int:pipe-separator}} $2) ݙیکھو ($3)",
+       "searchprofile-articles": "لسٹ ورقے",
+       "searchprofile-images": "ملٹی میڈیا",
+       "searchprofile-everything": "سب کجھ",
+       "searchprofile-advanced": "اگلا",
+       "searchprofile-articles-tooltip": "$1 وچ ڳولو",
+       "searchprofile-images-tooltip": "فائلاں ڳولو",
+       "searchprofile-everything-tooltip": " سارا مواد ڳولو",
+       "searchprofile-advanced-tooltip": "کسٹم نانواں وچ ڳولو",
+       "search-result-size": "$1 ({{PLURAL:$2|1 لفظ|$2 الفاظ}})",
+       "search-redirect": "($1 کنوں ولدا رجوع )",
+       "search-section": "(قطعہ $1)",
+       "search-file-match": "فائل مواد نال ملدا ہے",
+       "search-suggest": "بھلا تہاݙا مطلب ہائی: $1",
+       "searchall": "یکے",
+       "search-nonefound": "سوال دے نال رلدے ملدے نتارے کائنی۔",
+       "mypreferences": "ترجیحات",
+       "group-bot": "بوٹ",
+       "group-sysop": "منتظمین",
+       "grouppage-bot": "{{ns:project}}:بوٹ",
+       "grouppage-sysop": "{{ns:project}}:ایڈمنسٹریٹر",
+       "right-writeapi": "اے پی آئی تحریر دا استعمال",
+       "newuserlogpage": "یوزر بنݨاوݨ آلی لاگ",
+       "rightslog": "ورتݨ والے دے حقاں دی لاگ",
+       "action-edit": "ایں ورقے تے لکھو",
+       "action-createaccount": "ایہ ورتݨ آلا کھاتہ کھولو",
+       "enhancedrc-history": "پچھلا کم",
+       "recentchanges": "نویاں تبدیلیاں",
+       "recentchanges-legend": "اِختیاراتِ حالیہ تبدیلیاں",
+       "recentchanges-feed-description": "ایں فیڈ وچ وکی تے تھیوݨ آلیاں نویاں نکور تبدیلیاں ݙیکھو۔",
+       "recentchanges-label-newpage": "ایں تبدیلی نواں ورقہ بݨایا ہے",
+       "recentchanges-label-minor": "ایہ ہک چھوٹی تبدیلی ہے",
+       "recentchanges-label-bot": "ایہ تبدیلی  بوٹ نے کیتی ہے۔",
+       "recentchanges-label-unpatrolled": "ایہ تبدیلی اڄݨ تائیں واپس کائنی ولی۔",
+       "recentchanges-label-plusminus": "ورقے دا تبدیل شدہ حجم بلحاظ تعداد بائٹ",
+       "recentchanges-legend-heading": "<strong>اختصارات:</strong>",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ایہ وی ݙیکھو [[Special:NewPages|نویں ورقیاں دی لسٹ]])",
+       "rclistfrom": "$3 $2 توں ہونے آلیاں نویاں تبدیلیاں ݙکھاؤ",
+       "rcshowhideminor": "$1 معمولی تبدیلیاں",
+       "rcshowhideminor-show": "ݙیکھاؤ",
+       "rcshowhideminor-hide": "لُکاؤ",
+       "rcshowhidebots": "$1 بوٹ",
+       "rcshowhidebots-show": "ݙیکھاؤ",
+       "rcshowhidebots-hide": "لُکاؤ",
+       "rcshowhideliu": "مندرج صارفین $1",
+       "rcshowhideliu-show": "ݙیکھاؤ",
+       "rcshowhideliu-hide": "لُکاؤ",
+       "rcshowhideanons": "گمنام صارف $1",
+       "rcshowhideanons-show": "ݙیکھاؤ",
+       "rcshowhideanons-hide": "لُکاؤ",
+       "rcshowhidepatr": "$1 مراجعت شدہ ترامیم",
+       "rcshowhidemine": "ذاتی ترامیم میݙے کم $1",
+       "rcshowhidemine-show": "ݙیکھاؤ",
+       "rcshowhidemine-hide": "لُکاؤ",
+       "rclinks": "آخری $2 ݙینہ دیاں $1 تبدیلیاں ݙکھاؤ",
+       "diff": "فرق",
+       "hist": "پچھلا کم",
+       "hide": "لُکاؤ",
+       "show": "ݙیکھاؤ",
+       "minoreditletter": "چھوٹا کم",
+       "newpageletter": "نواں",
+       "boteditletter": " خودکار",
+       "rc-change-size-new": "تبدیلی دے بعد $1 {{PLURAL:$1|بائٹ}}",
+       "rc-old-title": "اصلاً «$1» دے عنوان نال تخلیق شدہ",
+       "recentchangeslinked": "رلدیاں ملدیاں تبدیلیاں",
+       "recentchangeslinked-feed": "رلدیاں ملدیاں تبدیلیاں",
+       "recentchangeslinked-toolbox": "رلدیاں ملدیاں تبدیلیاں",
+       "recentchangeslinked-title": "\"$1\" دے متعلقہ تبدیلیاں",
+       "recentchangeslinked-page": "ورقے دا ناں",
+       "recentchangeslinked-to": "کھلے ہوئے ورقے دی بجائے ایندے نال جُڑے ہوئے ورقے دیاں تبدیلیاں ݙکھاؤ",
+       "upload": "فائل چڑھاؤ",
+       "uploadlogpage": "اپلوڈ لاگ",
+       "filedesc": "خلاصہ",
+       "license": "اجازت نامہ:",
+       "license-header": "اجازہ کاری",
+       "imgfile": "فائل",
+       "listfiles": "فائل لسٹ",
+       "file-anchor-link": "فائل",
+       "filehist": "فائل دا تاریخچہ",
+       "filehist-help": "کہیں خاص ویلے تے تاریخ کوں فائل کینویں  نظردی ہائی، ݙیکݨ کیتے اوں ویلے تے کلک کرو۔",
+       "filehist-revert": "واپس",
+       "filehist-current": "موجودہ",
+       "filehist-datetime": "تریخ/ویلہ",
+       "filehist-thumb": "تھمب نیل",
+       "filehist-thumbtext": "مورخہ $1 دا تھمب نیل",
+       "filehist-nothumb": "کوئی تھمبنیل کائنی۔",
+       "filehist-user": "ورتݨ والا",
+       "filehist-dimensions": "پاسے",
+       "filehist-comment": "رائے",
+       "imagelinks": "فائل ورتݨ",
+       "linkstoimage": "اِیں فائل نال ہیٹھاں درج  {{PLURAL:$1|ورقہ مربوط ہے|$1 صفحات مربوط ہن}}:",
+       "nolinkstoimage": "ایں فائل نال کوئی ورقہ کائنی ڄُڑیا ہویا۔",
+       "linkstoimage-redirect": "$1 (فائل وت رجوع) $2",
+       "sharedupload-desc-here": "ایہ فائل $1 توں ہے تے ݙوجھیاں منصوبیاں تے وی ورتی ویسی۔\nایندی وضاحت [$2 فائل دی وضاحت دا ورقہ]  تے تھلے ݙتی ڳئی۔",
+       "filepage-nofile": "ایں ناں دی کوئی فائل کائنی۔",
+       "upload-disallowed-here": "تساں ایں فائل تے لکھ نی سڳدے۔",
+       "randompage": "رلے ملے ورقے",
+       "statistics": "شماريات",
+       "double-redirect-fixer": "ریڈائرکٹ فکسر",
+       "nbytes": "$1 {{PLURAL:$1|بائٹ}}",
+       "nmembers": "{{PLURAL:$1|رکن|اراکین}}",
+       "prefixindex": "سارے ورقے بمع سابقہ",
+       "listusers": "ورتݨ والیاں دے ناں",
+       "newpages": "نویں ورقے",
+       "move": "ٹرو",
+       "pager-newer-n": "{{PLURAL:$1|newer 1|زیادہ نواں $1}}",
+       "pager-older-n": "{{PLURAL:$1|قدیم}} $1",
+       "booksources": "کتابی وسائل",
+       "booksources-search-legend": "ایں مضمون تے کتاباں لبھو",
+       "booksources-search": "ڳولو",
+       "specialloguserlabel": "کرݨ آلا :",
+       "log": "لاگز",
+       "all-logs-page": "سارےعوامی لاگ",
+       "logempty": "لاگ وچ رلدیاں ملدیاں چیزاں کائنی۔",
+       "allpages": "سارے مقالے",
+       "allarticles": "سارے مقالے",
+       "allpagessubmit": "ڄلو",
+       "allpages-hide-redirects": "رجوع مکررات لکاؤ",
+       "categories": "زمرہ",
+       "listgrouprights-members": "(رکناں دی لسٹ)",
+       "emailuser": "ایں ورتݨ والے کوں ای میل کرو",
+       "usermessage-editor": "نظامی پیغام رساں",
+       "watchlist": "زیرنظر فہرست",
+       "mywatchlist": "زیرنظر فہرست",
+       "watchlistfor2": "$1 تے $2 کیتے",
+       "watch": "اکھ تلے رکھو",
+       "unwatch": "اکھ ہیٹھوں ہٹاؤ",
+       "wlshowlast": "ݙیکھاؤ چھیکڑی $1 گھنٹے $2 ݙینہ",
+       "watchlist-options": "نظر تھلے رکھݨ دیاں راہواں",
+       "enotif_reset": "سارے ورقے ڈیکھ گھدن",
+       "dellogpage": "مٹاوݨ آلی لاگ",
+       "rollbacklink": "واپس",
+       "protectlogpage": "بچت لاگ",
+       "protectedarticle": "\"[[$1]]\" بچایا گیا اے",
+       "modifiedarticleprotection": "«[[$1]]» دا درجہ حفاظت تبدیل کیتا",
+       "protect-default": "تمام صارفین کوں اجازت ہے",
+       "restriction-edit": "لکھو",
+       "restriction-move": "ٹرو",
+       "namespace": "ناں دی جگہ:",
+       "invert": "انتخاب معکوس",
+       "namespace_association": "رلدے ناں دی تھاں",
+       "blanknamespace": "(مکھ)",
+       "contributions": " $1 ورتن آلے دا حصہ",
+       "contributions-title": "صارف $1 دی شراکتاں",
+       "mycontris": "شراکتاں",
+       "anoncontribs": "شراکتاں",
+       "contribsub2": "{{GENDER:$3|$1}} ($2)",
+       "nocontribs": "ایں معیار دے مطابق کوئی تبدیلی نی لبھی۔",
+       "uctop": "(موجودہ)",
+       "month": "مہینے توں (تے پہلاں):",
+       "year": "سال توں (تے پہلاں):",
+       "sp-contributions-newbies": "صرف نویں ورتݨ آلیاں دے کم ݙکھاؤ",
+       "sp-contributions-blocklog": "لاگ روکو",
+       "sp-contributions-uploads": "اپلوڈ کردہ",
+       "sp-contributions-logs": "لاگز",
+       "sp-contributions-talk": "ڳالھ مہاڑ",
+       "sp-contributions-search": "حصے پاؤݨ آلیاں دی تلاش",
+       "sp-contributions-username": "آئی پی پتہ یا ورتݨ آلا ناں:",
+       "sp-contributions-toponly": "صرف اوہ تبدیلیاں ݙکھاؤ جیہڑیاں ہُݨے ہُݨے تھیاں ہن۔",
+       "sp-contributions-newonly": "صرف نویں ورقیاں بݨݨ آلیاں لکھتاں ݙیکھاؤ",
+       "sp-contributions-submit": "ڳولو",
+       "whatlinkshere": "مربوط ورقے",
+       "whatlinkshere-title": "«$1» دے نال جُڑے ہوے ورقے",
+       "whatlinkshere-page": "ورقہ",
+       "linkshere": "<strong>[[:$1]]</strong> نال درج ذیل ورقے مربوط ہن:",
+       "nolinkshere": "<strong>[[:$1]]</strong> نال کوئی ورقہ مربوط کائنی۔",
+       "isredirect": "صفحہ ریڈائریکٹ کرو",
+       "istemplate": "شامل شدہ",
+       "isimage": "فائل دا ربط",
+       "whatlinkshere-prev": "{{PLURAL:$1|پچھلا|پچھلے $1}}",
+       "whatlinkshere-next": "{{PLURAL:$1|اگلا|اگلے $1}}",
+       "whatlinkshere-links": "→ روابط",
+       "whatlinkshere-hideredirs": "رجوع مکررات $1",
+       "whatlinkshere-hidetrans": "استعمالات $1",
+       "whatlinkshere-hidelinks": "روابط $1",
+       "whatlinkshere-hideimages": "تصویر دے روابط $1",
+       "whatlinkshere-filters": "نتارے",
+       "infiniteblock": "بے انت",
+       "blocklink": "پابندی لاؤ",
+       "contribslink": "حصے داری",
+       "blocklogpage": "لاگ روکو",
+       "blocklogentry": "«[[$1]]» تے $2 کیتے پابندی عائد کی ڳئی ہے $3",
+       "reblock-logentry": "[[$1]] دی ترتیبات پابندی کوں تبدیل کیتاڳئے، ہݨ میعاد $2 $3 تے مُکسی",
+       "block-log-flags-nocreate": "کھاتا کھولݨ تے پابندی ہے",
+       "proxyblocker": "پراکسی روکݨ آلا",
+       "movelogpage": "ناں تبدیل کرݨ دا لاگ",
+       "export": "ورقے ٻاہر بھیجو",
+       "thumbnail-more": "وݙا کرو",
+       "importlogpage": "لاگ گھن آؤ",
+       "tooltip-pt-userpage": "تہاݙا صارف ورقہ",
+       "tooltip-pt-mytalk": "{{GENDER:|Your}} گالھ مہاڑ",
+       "tooltip-pt-preferences": "تہاݙیاں ترجیحاں",
+       "tooltip-pt-watchlist": " انہاں ورقیاں دی لسٹ جنہاں وچ تساں تبدیلیاں کرݨ کیتے ݙیہدے پئے ہو۔",
+       "tooltip-pt-mycontris": "میݙے کم",
+       "tooltip-pt-login": "لاگ ان تھیوو تاں چنگا ہے، ضروری کائنی۔",
+       "tooltip-pt-logout": "لاگ آؤٹ",
+       "tooltip-pt-createaccount": "ایہ تہاݙے کیتے چنگا ہے جو کھاتہ کھولو تے لاگ ان تھیوو، پر ایہ لازمی کائنی۔",
+       "tooltip-ca-talk": "مضمون بارے بحث",
+       "tooltip-ca-edit": "ایں ورقے تے لکھو",
+       "tooltip-ca-addsection": "نواں حصہ شروع کرو",
+       "tooltip-ca-viewsource": "ایہ ورقہ محفوظ تھیا ہویا ہے۔ \nتساں صرف ایندا ماخذ ݙیکھ سڳدے ہو۔",
+       "tooltip-ca-history": "ایں ورقے دا پراݨا روپ۔",
+       "tooltip-ca-protect": "ایہ ورقہ محفوظ کرو",
+       "tooltip-ca-delete": "ایں ورقے کوں مٹاؤ",
+       "tooltip-ca-move": "ایں ورقے کوں گھن ڄلو",
+       "tooltip-ca-watch": "ایں ورقے کوں آپݨی دید آلے ورقیاں وچ رکھو",
+       "tooltip-ca-unwatch": "ایں ورقے کوں آپݨی دید آلے ورقیاں وچ رکھو",
+       "tooltip-search": "ڳولو {{SITENAME}}",
+       "tooltip-search-go": "جے ایں عنوان دا ورقہ ہے تاں اتھ ونڄو",
+       "tooltip-search-fulltext": "ایں عبارت کوں ورقیاں وچ ڳولو",
+       "tooltip-p-logo": "پہلا ورقہ ݙیکھو",
+       "tooltip-n-mainpage": "پہلا ورقہ ݙیکھو",
+       "tooltip-n-mainpage-description": "پہلے ورقے تے ونڄو",
+       "tooltip-n-portal": "ایں مںصوبے بارے، تساں کیا کر سڳدو، ، چیزاں کتھوں ڳولوں",
+       "tooltip-n-currentevents": "موجودہ حالات وچ پچھلیاں معلومات ݙیکھو",
+       "tooltip-n-recentchanges": "وکی تے نویاں تبدیلیاں۔",
+       "tooltip-n-randompage": "کوئی صفہ کھولو۔",
+       "tooltip-n-help": "لبھݨ دی جاہ",
+       "tooltip-t-whatlinkshere": "ایں نال جڑے سارے وکی ورقے۔",
+       "tooltip-t-recentchangeslinked": "ایں ورقے توں جڑے ورقیاں وچ نویاں تبدیلیاں",
+       "tooltip-feed-atom": "اِیں ورقے دا اٹوم فیڈ",
+       "tooltip-t-upload": "فائل چڑھاؤ",
+       "tooltip-t-specialpages": "سارے خاص ورقیاں دی تندیر",
+       "tooltip-t-print": "ایں ورقے دا چھپݨ آلا انگ ݙیکھو",
+       "tooltip-t-permalink": "اس صفے دے ایں روپ نال پکا جوڑ",
+       "tooltip-ca-nstab-main": "مواد آلا صفہ ݙیکھو",
+       "tooltip-ca-nstab-user": "صارف دا ورقہ ݙیکھو",
+       "tooltip-ca-nstab-special": "ایہ ہک خاص ورقہ ہے، اینکوں تبدیل نسے کرسڳدے",
+       "tooltip-ca-nstab-project": "منصبے آلا ورقہ ݙیکھو",
+       "tooltip-ca-nstab-image": "فائل دا ورقہ ݙیکھو",
+       "tooltip-ca-nstab-mediawiki": "نظامی سنیہہ ݙیکھو",
+       "tooltip-ca-nstab-template": "سانچہ ݙیکھو",
+       "tooltip-ca-nstab-category": "کیٹاگری آلا ورقہ ݙیکھو",
+       "tooltip-minoredit": "ایں کوں نکی ترممیم وچ ڳݨو",
+       "tooltip-save": "تبدیلیاں محفوظ کرو",
+       "tooltip-preview": "محفوظ کرݨ کنے پہلے تبدیلیاں ݙیکھو، مہربانی ہوسی۔",
+       "tooltip-diff": "ایں لکھت وچ کیتیاں ڳیاں تبدیلیاں ݙیکھاؤ",
+       "tooltip-watch": "ایں ورقے کوں آپݨی دید آلے ورقیاں وچ رکھو",
+       "tooltip-rollback": "رول بیک\" ہک کلک وچ ورقے کوں پچھلی حالت وچ گھن ویسی\"",
+       "tooltip-undo": "واپس تے کلک کرݨ نال  پچھلی ترمیم تے پُڄ ویسو، نمائشی انداز وچ ترمیم دا خانہ کھلسی۔ تساں مختصر سسب وی بیان کر سڳدے ہو۔",
+       "tooltip-summary": "مختصر خلاصہ درج کرو",
+       "simpleantispam-label": "سپام روک پھاٹک\nاینکوں <strong>نہ</strong>  بھرو!",
+       "pageinfo-title": "«$1» دی معلومات",
+       "pageinfo-header-basic": "بنیادی معلومات",
+       "pageinfo-header-edits": "تاریخچۂ ترمیم",
+       "pageinfo-header-restrictions": "ورقے دی حفاظت",
+       "pageinfo-header-properties": "صفحہ دی خاصیتاں",
+       "pageinfo-display-title": "عنوان",
+       "pageinfo-default-sort": "کلید برائے ابتدائی ترتیب",
+       "pageinfo-length": "ورقے دی لمناݨ (بائٹ وچ)",
+       "pageinfo-article-id": "ورقے دی شناخت",
+       "pageinfo-language": "زبان",
+       "pageinfo-content-model": "انداز متن",
+       "pageinfo-robot-policy": "روبوٹ دی فہرست سازی",
+       "pageinfo-robot-index": "مجاز",
+       "pageinfo-robot-noindex": "ممنوع",
+       "pageinfo-watchers": "تعداد ناظرین",
+       "pageinfo-few-watchers": "$1 کنوں گھٹ {{PLURAL:$1|ناظر|ناظرین}}",
+       "pageinfo-redirects-name": "رجوعاں  دی تعداد",
+       "pageinfo-subpages-name": "ایں ورقے دے ذیلی ورقیاں دی تعداد",
+       "pageinfo-firstuser": "صفحہ ساز",
+       "pageinfo-firsttime": "صفحہ سازی دی تاریخ",
+       "pageinfo-lastuser": "چھیکڑی ترمیم کنندہ",
+       "pageinfo-lasttime": "چھیکڑی ترمیم دی تاریخ",
+       "pageinfo-edits": "ترامیم دی مجموعی تعداد",
+       "pageinfo-authors": "مختلف مصنفین دی  تعداد",
+       "pageinfo-recent-authors": "مختلف مصنفین دی حالیہ تعداد",
+       "pageinfo-magic-words": "جادوئی {{PLURAL:$1|لفظ|الفاظ}} ($1)",
+       "pageinfo-hidden-categories": "پوشیدہ {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
+       "pageinfo-templates": "زیر استعمال {{PLURAL:$1|سانچہ|سانچے}} ($1)",
+       "pageinfo-toolboxlink": "معلومات صفحہ",
+       "pageinfo-contentpage": "شمار بطور ورقہ",
+       "pageinfo-contentpage-yes": "ڄیا",
+       "patrol-log-page": "گشت لاگ",
+       "previousdiff": "← پرانی لکھائی",
+       "nextdiff": "نویں لکھائی →",
+       "widthheightpage": "$1×$2، $3 {{PLURAL:$3|ورقہ|ورقے}}",
+       "file-info-size": "\n$1 × $2 پکسل، فائل دا حجم: $3، MIME قسم: $4",
+       "file-info-size-pages": "$1 × $2 پکسل، فائل دا حجم: $3، MIME قسم: $4، $5 {{PLURAL:$5|ورقہ|ورقے}}",
+       "file-nohires": "ایں توں زیادہ ریزولیوشن دستیاب کائنی۔",
+       "svg-long-desc": "ایس وی جی فائل، ابعاد $1 × $2 پکسل، فائل دا حجم: $3",
+       "show-big-image": "اصل فائل",
+       "show-big-image-preview": "ایں نمائش دا حجم:$1",
+       "show-big-image-other": "ٻیاں {{PLURAL:$2|قرارداد|قراردادیں}}: $1۔",
+       "show-big-image-size": "$1 × $2 پکسلز",
+       "metadata": "میٹا ڈیٹا",
+       "metadata-help": "ایں فائل وچ ٻیاں معلومات وی ہن۔ شاید او تہاݙے کیمرے یا سیکنر توں آیاں ہن، جیندے نال تساں ایہ فائل بݨائی ہائی۔\nجے ایہ فائل آپݨی اصل حالت وچ نہ ہووے تاں کجھ معلومات تبدیل تھئی ہوئی فائل دی پوری پوری عکاسی کائناں کریسی۔",
+       "metadata-fields": "تصویر دے میٹاڈیٹا دے او خانے جہڑے پیغام میں درج ہن او تصویر دے صفحے تے شامل ہوندے ہن۔ ایہ ااوں ویلے ظاہر تھیندن جڈݨ میٹاڈیٹا کوں ودھایا ونڄے۔\nٻئے خانے شروع وچ لُڳے ہوندن۔\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-orientation": "اورینٹیشن",
+       "exif-xresolution": "افقی ریزولوشن",
+       "exif-yresolution": "عمودی ریزولیشن",
+       "exif-datetime": "فائل بدلݨ دی تاریخ تے ویلا",
+       "exif-make": "کیمرہ ساز کمپنی",
+       "exif-model": "کیمرے دا ماڈل",
+       "exif-software": "مستعمل سافٹ ویئر",
+       "exif-exifversion": "اکزیف ورژن",
+       "exif-colorspace": "رنگ فضا",
+       "exif-datetimeoriginal": "ڈیٹا بݨاوݨ دی تاریخ تے ویلا",
+       "exif-datetimedigitized": "ڈجیٹائزنگ دا ویلہ تے تریخ",
+       "exif-orientation-1": "عام",
+       "namespacesall": "یکے",
+       "monthsall": "یکے",
+       "imgmultipagenext": "اگلا →",
+       "imgmultigo": "ونڄو!",
+       "imgmultigoto": "$1 تے ونڄو",
+       "watchlisttools-clear": "زیرنظر فہرست دی صفائی",
+       "watchlisttools-view": "متعلقہ تبدیلیاں ݙیکو",
+       "watchlisttools-edit": "زیرنظر فہرست  کوں ݙیکھو تے تبدیلی کرو",
+       "watchlisttools-raw": "کچی زیرِنظرفہرست وچ تبدیلی کرو",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|تبادلۂ خیال]])",
+       "redirect": "فائل، صارف، ورقہ،دہرائی یا آئی ڈی لاگ دے ذریعے ولدا واپس",
+       "redirect-submit": "ڄلو",
+       "redirect-lookup": "تلاش:",
+       "redirect-value": "قدر:",
+       "redirect-user": "صارف دی شناخت",
+       "redirect-page": "ورقے دی شناخت",
+       "redirect-revision": "ورقے دا رویژن",
+       "redirect-file": "فائل دا ناں",
+       "specialpages": "خاص ورقے",
+       "tag-filter": "[[Special:Tags|Tag]] نتارا:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٹیگ|ٹیگز}}]]: $2)",
+       "tags-active-yes": "ڄیا",
+       "tags-active-no": "کو",
+       "tags-hitcount": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
+       "logentry-delete-delete": "$1 {{GENDER:$2|مٹایا ڳیا}} ورقہ $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|بحال تھی ڳیوہے}} page $3 ($4)",
+       "revdelete-content-hid": "مواد لکیا",
+       "logentry-move-move": "$1 {{جنس:$2|پلٹی}} صفہ $3 توں $4",
+       "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|بݨایا ڳیا}}",
+       "logentry-newusers-autocreate": "صارف کھاتہ $1 خودکار طور  {{GENDER:$2|تخلیق تھیا}}",
+       "logentry-upload-upload": "$1 {{GENDER:$2|اپلوڈ}} $3",
+       "searchsuggest-search": "ڳولو",
+       "duration-days": "$1 {{PLURAL:$1|ݙینہ}}",
+       "randomrootpage": "بے ترتيب بنیادی صفحہ"
+}
index bd66bb1..b60523e 100644 (file)
        "rcfilters-legend-heading": "<strong>Seznam okrajšav:</strong>",
        "rcfilters-activefilters": "Dejavni filtri",
        "rcfilters-advancedfilters": "Napredni filtri",
+       "rcfilters-limit-title": "Spremembe za prikaz",
+       "rcfilters-limit-shownum": "Prikaži zadnjih $1 sprememb",
+       "rcfilters-days-title": "Pretekli dnevi",
+       "rcfilters-hours-title": "Pretekle ure",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dan|dneva|dnevi|dni}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ura|uri|ure|ur}}",
        "rcfilters-quickfilters": "Shranjeni filtri",
        "rcfilters-quickfilters-placeholder-title": "Shranjena ni še nobena povezava",
        "rcfilters-quickfilters-placeholder-description": "Da shranite svoje nastavitve filtrov in jih ponovno uporabite pozneje, kliknite na ikono za zaznamek v območju Dejavni filtri spodaj.",
        "rcfilters-invalid-filter": "Neveljaven filter",
        "rcfilters-empty-filter": "Ni dejavnih filtrov. Prikazani so vsi prispevki.",
        "rcfilters-filterlist-title": "Filtri",
-       "rcfilters-filterlist-whatsthis": "Kaj je to?",
+       "rcfilters-filterlist-whatsthis": "Kako to deluje?",
        "rcfilters-filterlist-feedbacklink": "Podajte povratne informacije o novih (preizkusnih) filtrih",
        "rcfilters-highlightbutton-title": "Označi rezultate",
        "rcfilters-highlightmenu-title": "Izberite barvo",
        "rcfilters-noresults-conflict": "Našli nismo nobenih rezultatov, ker si merila za iskanje nasprotujejo",
        "rcfilters-state-message-subset": "Ta filter nima učinka, saj njegove rezultate {{PLURAL:$2|vključuje naslednji, širši filter|vključujeta naslednja, širša filtra|vključujejo naslednji, širši filtri}} (označite jih, da razlikujete med njimi): $1",
        "rcfilters-state-message-fullcoverage": "Izbira vseh filtrov v skupini je enako kot izbira nobenega, zato ta filter nima učinka. Skupina vključuje: $1",
-       "rcfilters-filtergroup-registration": "Registracija uporabnika",
-       "rcfilters-filter-registered-label": "Registriran",
-       "rcfilters-filter-registered-description": "Prijavljeni uredniki.",
-       "rcfilters-filter-unregistered-label": "Neregistriran",
-       "rcfilters-filter-unregistered-description": "Uredniki, ki niso prijavljeni.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Filter je v sporu z {{PLURAL:$2|naslednjim filtrom|naslednjima filtroma|naslednjimi filtri}} Izkušnje, ki {{PLURAL:$2|najde|najdeta|najdejo}} samo registrirane uporabnike: $1",
        "rcfilters-filtergroup-authorship": "Avtorstvo prispevka",
        "rcfilters-filter-editsbyself-label": "Vaše spremembe",
        "rcfilters-filter-editsbyself-description": "Vaša lastna urejanja.",
        "rcfilters-filter-editsbyother-label": "Spremembe drugih",
        "rcfilters-filter-editsbyother-description": "Vse spremembe razen vaše.",
-       "rcfilters-filtergroup-userExpLevel": "Stopnja izkušenosti (samo za registrirane uporabnike)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Napredni filtri najdejo samo registrirane uporabnike, zato je ta filter v sporu s filtrom »Neregistrirani«.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filter »Neregistrirani« je v sporu z enim ali več filtri Izkušnje, ki najdejo samo registrirane uporabnike. Filtri v sporu so označeni zgoraj, v območju Dejavni filtri.",
+       "rcfilters-filtergroup-userExpLevel": "Registriranost in izkušenost uporabnika",
+       "rcfilters-filter-user-experience-level-registered-label": "Registriran",
+       "rcfilters-filter-user-experience-level-registered-description": "Prijavljeni uredniki.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neregistriran",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Uredniki, ki niso prijavljeni.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novinci",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Manj kot 10 urejanj in 4 dni dejavnosti.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registrirani uredniki z manj kot 10 urejanji in 4 dnevi dejavnosti.",
        "rcfilters-filter-user-experience-level-learner-label": "Učenci",
-       "rcfilters-filter-user-experience-level-learner-description": "Več izkušenj kot »Novinci«, vendar manj kot »Izkušeni uporabniki«.",
+       "rcfilters-filter-user-experience-level-learner-description": "Registrirani uredniki, katerih izkušenost se uvršča med »Novince« in »Izkušene uporabnike«.",
        "rcfilters-filter-user-experience-level-experienced-label": "Izkušeni uporabniki",
-       "rcfilters-filter-user-experience-level-experienced-description": "Več kot 30 dni dejavnosti in 500 urejanj.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registrirani uredniki z več kot 500 urejanji in 30 dnevi dejavnosti.",
        "rcfilters-filtergroup-automated": "Samodejni prispevki",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Urejanja, narejena s samodejnimi orodji.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filter »Manjša urejanja« je v sporu z enim ali več filtri Vrsta spremembe, ker nekaterih vrst urejanj ni možno označiti kot »manjša«. Filtri v sporu so označeni v območju Dejavni filtri zgoraj.",
        "rcfilters-hideminor-conflicts-typeofchange": "Nekaterih vrst sprememb ni možno označiti kot »manjše«, zato je ta filter v sporu z naslednjimi filtri Vrsta spremembe: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Ta filter Vrsta spremembe je v sporu s filtrom »Manjše urejanje«. Nekaterih vrst sprememb ni možno označiti kot »manjše«.",
-       "rcfilters-filtergroup-lastRevision": "Zadnja redakcija",
-       "rcfilters-filter-lastrevision-label": "Zadnja redakcija",
-       "rcfilters-filter-lastrevision-description": "Najnovejša sprememba strani.",
-       "rcfilters-filter-previousrevision-label": "Zgodnejše redakcije",
-       "rcfilters-filter-previousrevision-description": "Vse spremembe, ki niso najnovejša sprememba strani.",
+       "rcfilters-filtergroup-lastRevision": "Najnovejše redakcije",
+       "rcfilters-filter-lastrevision-label": "Najnovejša redakcija",
+       "rcfilters-filter-lastrevision-description": "Samo najnovejša sprememba strani.",
+       "rcfilters-filter-previousrevision-label": "Nenajnovejša redakcija",
+       "rcfilters-filter-previousrevision-description": "Vse spremembe, ki niso »najnovejša redakcija«.",
        "rcfilters-filter-excluded": "Izključeno",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:ne</strong> $1",
+       "rcfilters-exclude-button-off": "Izključi izbrane",
+       "rcfilters-exclude-button-on": "Izključitev izbranih",
        "rcfilters-view-tags": "Označena urejanja",
        "rcfilters-view-namespaces-tooltip": "Filtriraj rezultate po imenskem prostoru",
        "rcfilters-view-tags-tooltip": "Filtriraj rezultate z uporabo oznak urejanj",
        "delete-warning-toobig": "Ta stran ima obsežno zgodovino urejanja, tj. čez $1 {{PLURAL:$1|redakcijo|redakciji|redakcije|redakcij}}.\nNjeno brisanje lahko zmoti obratovanje zbirke podatkov {{GRAMMAR:dative|{{SITENAME}}}};\nnadaljujte s previdnostjo.",
        "deleteprotected": "Strani ne morete izbrisati, ker jo je nekdo zaščitil.",
        "deleting-backlinks-warning": "<strong>Opozorilo:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Druge strani]] se povezujejo na ali vključujejo stran, ki jo nameravate izbrisati.",
+       "deleting-subpages-warning": "<strong>Opozorilo:</strong> Stran, ki jo nameravate izbrisati, ima [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|podstran|$1 podstrani|51=več kot 50 podstrani}}]].",
        "rollback": "Vrni spremembe",
        "rollbacklink": "vrni",
        "rollbacklinkcount": "vrni $1 {{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}",
        "fileduplicatesearch-noresults": "Datoteke imenovane »$1« ni mogoče najti.",
        "specialpages": "Posebne strani",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Navadne posebne strani.\n* <span class=\"mw-specialpagerestricted\">Omejene posebne strani.</span>",
+       "specialpages-note-restricted": "* Navadne posebne strani.\n* <span class=\"mw-specialpagerestricted\">Omejene posebne strani.</span>",
        "specialpages-group-maintenance": "Vzdrževalna poročila",
        "specialpages-group-other": "Ostale posebne strani",
        "specialpages-group-login": "Prijavite se / ustvarite račun",
index 8eadb75..1ee5c3b 100644 (file)
        "actionthrottled": "Радња је успорена",
        "actionthrottledtext": "У циљу борбе против непожељних порука, ограничене су вам измене у одређеном времену, а управо сте прешли то ограничење. Покушајте поново за неколико минута.",
        "protectedpagetext": "Ова страница је закључана за измене и друге радње.",
-       "viewsourcetext": "Можете читати и копирати садржај ове странице.",
+       "viewsourcetext": "Можете читати и копирати изворник ове странице.",
        "viewyourtext": "Можете да погледате и копирате изворни текст <strong>ваших измена</strong> на овој страници.",
-       "protectedinterface": "Ð\9eва Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81адÑ\80жи Ñ\82екÑ\81Ñ\82 ÐºÐ¾Ñ\80иÑ\81ниÑ\87ког Ð¾ÐºÑ\80Ñ\83жеÑ\9aа Ð·Ð° Ñ\81оÑ\84Ñ\82веÑ\80 Ð½Ð° Ð¾Ð²Ð¾Ð¼ Ð²Ð¸ÐºÐ¸Ñ\98Ñ\83 Ð¸ Ð·Ð°Ñ\88Ñ\82иÑ\9bена Ñ\98е Ñ\80ади Ñ\81пÑ\80еÑ\87аваÑ\9aа Ð·Ð»Ð¾Ñ\83поÑ\82Ñ\80ебе.\nÐ\94а Ð±Ð¸Ñ\81Ñ\82е Ð´Ð¾Ð´Ð°Ð»Ð¸ Ð¸Ð»Ð¸ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ð»Ð¸ Ð¿Ñ\80еводе Ñ\81виÑ\85 Ð²Ð¸ÐºÐ¸Ñ\98а, Ð¿Ð¾Ñ\81еÑ\82иÑ\82е [https://translatewiki.net/ Ð¢Ñ\80анÑ\81леÑ\98Ñ\82вики], пројекат за локализацију Медијавикија.",
+       "protectedinterface": "Ð\9eва Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\81адÑ\80жи Ñ\82екÑ\81Ñ\82 Ð¸Ð½Ñ\82еÑ\80Ñ\84еÑ\98Ñ\81а Ð·Ð° Ñ\81оÑ\84Ñ\82веÑ\80 Ð½Ð° Ð¾Ð²Ð¾Ð¼ Ð²Ð¸ÐºÐ¸Ñ\98Ñ\83 Ð¸ Ð·Ð°Ñ\88Ñ\82иÑ\9bена Ñ\98е Ñ\80ади Ñ\81пÑ\80еÑ\87аваÑ\9aа Ð·Ð»Ð¾Ñ\83поÑ\82Ñ\80ебе.\nÐ\94а Ð±Ð¸Ñ\81Ñ\82е Ð´Ð¾Ð´Ð°Ð»Ð¸ Ð¸Ð»Ð¸ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ð»Ð¸ Ð¿Ñ\80еводе Ð±Ð¸Ð»Ð¾ ÐºÐ¾Ñ\98ег Ð²Ð¸ÐºÐ¸Ñ\98а, Ð¿Ð¾Ñ\81еÑ\82иÑ\82е [https://translatewiki.net/ translatewiki.net], пројекат за локализацију Медијавикија.",
        "editinginterface": "<strong>Упозорење:</strong> уређујете страницу која се користи за приказивање текста корисничког окружења.\nИзмене на овој страници ће утицати на све кориснике овог викија.",
        "translateinterface": "Да додате или промените преводе за све викије, посетите [https://translatewiki.net/ Транслејтвики], пројекат за локализацију Медијавикија.",
        "cascadeprotected": "Ова страница је закључана јер садржи {{PLURAL:$1|следећу страницу која је заштићена|следеће странице које су заштићене}} „преносивом“ заштитом:\n$2",
        "page_last": "последња",
        "histlegend": "Избор разлика: изаберите кутијице измена за упоређивање и притисните ентер или дугме на дну.<br />\nОбјашњење: <strong>({{int:cur}})</strong> = разлика с тренутном изменом, <strong>({{int:last}})</strong> = разлика с претходном изменом, <strong>{{int:minoreditletter}}</strong> = мала измена",
        "history-fieldset-title": "Преглед измена",
-       "history-show-deleted": "Само Ð¾Ð±Ñ\80иÑ\81ано",
+       "history-show-deleted": "Само Ð¾Ð±Ñ\80иÑ\81ане Ð¸Ð·Ð¼Ñ\98ене",
        "histfirst": "најстарије",
        "histlast": "најновије",
        "historysize": "({{PLURAL:$1|1 бајт|$1 бајта|$1 бајтова}})",
        "username": "{{GENDER:$1|Корисничко име}}:",
        "prefs-memberingroups": "{{PLURAL:$1|Група|Групе}}:",
        "prefs-memberingroups-type": "$1",
+       "group-membership-link-with-expiry": "$1 (до $2)",
        "prefs-registration": "Време регистрације:",
        "prefs-registration-date-time": "$1",
        "yourrealname": "Право име:",
        "prefs-help-prefershttps": "Ова подешавања ће ступити на снагу при следећој пријави.",
        "prefswarning-warning": "Променили сте ваша подешавања али нисте их још сачували.\nАко не притиснете „$1“ ваша подешавања ће бити изгубљена.",
        "prefs-tabs-navigation-hint": "Савет: можете користити типке са левом и десном стрелицом за кретање кроз картице.",
-       "userrights": "УпÑ\80авÑ\99аÑ\9aе ÐºÐ¾Ñ\80иÑ\81ниÑ\87ким Ð¿Ñ\80авима",
+       "userrights": "Ð\9aоÑ\80иÑ\81ниÑ\87ка Ð¿Ñ\80ава",
        "userrights-lookup-user": "Изабери корисника",
        "userrights-user-editname": "Корисничко име:",
        "editusergroup": "Учитај корисничке групе",
        "userrights-expiry-current": "Истиче $1",
        "userrights-expiry-none": "Не истиче",
        "userrights-expiry": "Истиче:",
+       "userrights-expiry-existing": "Постојеће вријеме истека: $3, $2",
+       "userrights-expiry-othertime": "Друго вријеме:",
        "userrights-cannot-shorten-expiry": "Не можете убрзати истек чланства у групи „$1”. Само корисници са дозволом да додају или уклоне ову групу могу да убрзају рок истека.",
        "userrights-conflict": "Сукоб промена корисничких права! Молимо проверите ваше измене.",
        "group": "Група:",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|списак нових страница]])",
        "recentchanges-submit": "Прикажи",
+       "rcfilters-legend-heading": "<strong>Списак скраћеница:</strong>",
        "rcfilters-activefilters": "Активни филтери",
-       "rcfilters-quickfilters": "Ð\91Ñ\80зе Ð²ÐµÐ·Ðµ",
+       "rcfilters-quickfilters": "СаÑ\87Ñ\83вани Ñ\84илÑ\82еÑ\80и",
        "rcfilters-savedqueries-defaultlabel": "Сачувани филтери",
        "rcfilters-savedqueries-rename": "Преименуј",
        "rcfilters-savedqueries-setdefault": "Постави као подразумевано",
        "rcfilters-savedqueries-unsetdefault": "Уклони као подразумевано",
        "rcfilters-savedqueries-remove": "Уклони",
        "rcfilters-savedqueries-new-name-label": "Име",
-       "rcfilters-savedqueries-apply-label": "Направи брзу везу",
+       "rcfilters-savedqueries-apply-label": "Направи филтер",
        "rcfilters-savedqueries-cancel-label": "Откажи",
-       "rcfilters-savedqueries-add-new-title": "СаÑ\87Ñ\83ваÑ\98 Ñ\84илÑ\82еÑ\80е ÐºÐ°Ð¾ Ð±Ñ\80зÑ\83 Ð²ÐµÐ·Ñ\83",
+       "rcfilters-savedqueries-add-new-title": "СаÑ\87Ñ\83ваÑ\98 Ñ\82Ñ\80енÑ\83Ñ\82не Ð¿Ð¾Ñ\81Ñ\82авке Ñ\84илÑ\82еÑ\80а",
        "rcfilters-restore-default-filters": "Враћање подразумеваних филтера",
        "rcfilters-clear-all-filters": "Уклони све филтере",
        "rcfilters-empty-filter": "Нема активних филтера. Сви доприноси су приказани.",
        "rcfilters-noresults-conflict": "Није пронађен ниједан резултат јер су критеријуми претраге сукобљени",
        "rcfilters-state-message-subset": "Овај филтер нема ефекта јер су његови резултати укључени са онима {{PLURAL:$2|следећег, ширег филтера|следећих, ширих филтера}} (покушајте са означавањем да бисте их распознали): $1",
        "rcfilters-state-message-fullcoverage": "Одабир свих филтера у групи је исто као и одабир ниједног, тако да овај филтер нема ефекта. Група укључује: $1",
-       "rcfilters-filtergroup-registration": "Регистрација корисника",
-       "rcfilters-filter-registered-label": "Регистровани",
-       "rcfilters-filter-registered-description": "Пријављени уредници.",
-       "rcfilters-filter-unregistered-label": "Нерегистровани",
-       "rcfilters-filter-unregistered-description": "Уредници који нису пријављени.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Овај филтер је у сукобу са следећим {{PLURAL:$2|филтером|филтерима}} за искуство, који {{PLURAL:$2|проналази|проналазе}} само регистроване кориснике: $1",
        "rcfilters-filtergroup-authorship": "Ауторство доприноса",
        "rcfilters-filter-editsbyself-label": "Ваше измене",
        "rcfilters-filter-editsbyself-description": "Ваши доприноси.",
        "rcfilters-filter-editsbyother-label": "Измене других",
        "rcfilters-filter-editsbyother-description": "Све измене осим Ваших.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Филтери за искуство проналазе само регистроване кориснике, тако да је овај филтер у сукобу са филтером „Unregistered”.",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Филтер „Unregistered” је у сукобу са једним или више филтера за искуство, који проналазе само регистроване кориснике. Сукобљени филтери су означени у подручју Активни филтери, изнад.",
+       "rcfilters-filter-user-experience-level-registered-label": "Регистровани",
+       "rcfilters-filter-user-experience-level-registered-description": "Пријављени уредници.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Нерегистровани",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Уредници који нису пријављени.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новајлије",
        "rcfilters-filter-user-experience-level-newcomer-description": "Мање од 10 измјена и 4 дана активности.",
        "rcfilters-filter-user-experience-level-learner-label": "Ученици",
        "lonelypagestext": "Следеће странице нису повезане с другим страницама, нити су укључене трансклузијом у друге странице.",
        "uncategorizedpages": "Некатегорисане странице",
        "uncategorizedcategories": "Некатегорисане категорије",
-       "uncategorizedimages": "Ð\94аÑ\82оÑ\82еке Ð±ÐµÐ· ÐºÐ°Ñ\82егоÑ\80иÑ\98а",
+       "uncategorizedimages": "Ð\9dекаÑ\82егоÑ\80иÑ\81ане Ð´Ð°Ñ\82оÑ\82еке",
        "uncategorizedtemplates": "Некатегорисани шаблони",
        "unusedcategories": "Некоришћене категорије",
        "unusedimages": "Некоришћене датотеке",
        "lockedbyandtime": "(од $1 дана $2 у $3)",
        "move-page": "Премештање „$1“",
        "move-page-legend": "Премештање странице",
-       "movepagetext": "Доњи образац ће преименовати страницу, премештајући целу историју на ново име.\nСтари наслов постаће преусмерење на нови наслов.\nМожете аутоматски изменити преусмерење до изворног наслова.\nПогледајте [[Special:DoubleRedirects|двострука]] или [[Special:BrokenRedirects|покварена]] преусмерења.\nНа вама је одговорност да везе и даље иду тамо где би требало да иду.\n\nСтраница <strong>неће</strong> бити премештена ако већ постоји страница с тим именом, осим ако је она празна, садржи преусмерење или нема историју измена.\nТо значи да можете вратити страницу на претходно место ако погрешите, али не можете заменити постојећу страницу.\n\n<strong>Пажња!</strong>\nОво може представљати драстичну и неочекивану измену за популарну страницу;\nдобро размислите о последицама пре него што наставите.",
+       "movepagetext": "Доњи образац ће преименовати страницу, премештајући целу историју на ново име.\nСтари наслов постаће преусмерење на нови наслов.\nМожете ажурирати преусмерења која воде до изворног наслова;\nпогледајте [[Special:DoubleRedirects|двострука]] или [[Special:BrokenRedirects|покварена]] преусмерења.\nНа вама је одговорност да везе и даље иду тамо где треба.\n\nСтраница <strong>неће</strong> бити премештена ако већ постоји страница с тим именом, осим ако је празна, садржи преусмерење или нема историју измена.\nТо значи да можете вратити страницу на претходно име ако погрешите, али не можете ''преписати'' постојећу.\n\n<strong>Напомена:</strong>\nОво може представљати драстичну и неочекивану измену за популарну страницу;\nдобро размислите о последицама пре него што наставите.",
        "movepagetext-noredirectfixer": "Доњи образац ће преименовати страницу, премештајући целу историју на ново име.\nСтари наслов постаће преусмерење на нови наслов.\nПогледајте [[Special:DoubleRedirects|двострука]] или [[Special:BrokenRedirects|покварена]] преусмерења.\nНа вама је одговорност да везе и даље иду тамо где би требало да иду.\n\nСтраница <strong>неће</strong> бити премештена ако већ постоји страница с тим именом, осим ако је она празна, садржи преусмерење или нема историју измена.\nТо значи да можете вратити страницу на претходно место ако погрешите, али не можете заменити постојећу страницу.\n\n<strong>Пажња!</strong>\nОво може представљати драстичну и неочекивану измену за популарну страницу;\nдобро размислите о последицама пре него што наставите.",
        "movepagetalktext": "Ако сте означили овај квадратић, одговарајућа страница за разговор биће аутоматски премештена на нови наслов, осим ако већ постоји страница за разговор са истим насловом.\n\nУ том случају, мораћете ручно да је преместите или спојите, ако има потребе за тим.",
        "moveuserpage-warning": "'''Упозорење:''' на путу сте да преместите корисничку страницу. Имајте у виду да ће само страница бити премештена, а сам корисник ''неће'' бити преименован.",
        "tooltip-ca-nstab-template": "Погледајте шаблон",
        "tooltip-ca-nstab-help": "Погледајте страницу за помоћ",
        "tooltip-ca-nstab-category": "Погледајте страницу категорија",
-       "tooltip-minoredit": "Означите измену као мању",
-       "tooltip-save": "Сачувајте измене које сте направили",
+       "tooltip-minoredit": "Ð\9eзнаÑ\87иÑ\82е Ð¾Ð²Ñ\83 Ð¸Ð·Ð¼ÐµÐ½Ñ\83 ÐºÐ°Ð¾ Ð¼Ð°Ñ\9aÑ\83",
+       "tooltip-save": "Сачувајте своје измене",
        "tooltip-publish": "Објави своје измене",
-       "tooltip-preview": "Ð\9fÑ\80егледаÑ\98Ñ\82е Ñ\81воÑ\98е Ð¸Ð·Ð¼ÐµÐ½Ðµ. Ð\9fожеÑ\99но Ñ\98е Ð´Ð° ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82е Ð¾Ð²Ð¾ Ð´Ñ\83гме Ð¿Ñ\80е Ñ\87Ñ\83ваÑ\9aа",
-       "tooltip-diff": "Погледајте све измене које сте направили на тексту",
+       "tooltip-preview": "Ð\9fÑ\80егледаÑ\98Ñ\82е Ñ\81воÑ\98е Ð¸Ð·Ð¼ÐµÐ½Ðµ. Ð\9aоÑ\80иÑ\81Ñ\82иÑ\82е Ð¾Ð²Ð¾ Ð´Ñ\83гме Ð¿Ñ\80е Ñ\87Ñ\83ваÑ\9aа.",
+       "tooltip-diff": "Погледајте које измене сте направили на тексту",
        "tooltip-compareselectedversions": "Погледаjте разлике између две изабране измене ове странице.",
-       "tooltip-watch": "Додајте ову страницу на списак надгледања",
+       "tooltip-watch": "Ð\94одаÑ\98Ñ\82е Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð½Ð° Ñ\81воÑ\98 Ñ\81пиÑ\81ак Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aа",
        "tooltip-watchlistedit-normal-submit": "Уклони наслове",
        "tooltip-watchlistedit-raw-submit": "Ажурирај списак",
        "tooltip-recreate": "Поново направите страницу иако је обрисана",
        "fileduplicatesearch-noresults": "Датотека под називом „$1“ није пронађена.",
        "specialpages": "Посебне странице",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Нормалне посебне странице\n* <span class=\"mw-specialpagerestricted\">Ограничене посебне странице</span>",
        "specialpages-group-maintenance": "Извештаји одржавања",
        "specialpages-group-other": "Остале посебне странице",
        "specialpages-group-login": "Пријава / регистрација",
index 8de9d64..7bf39b3 100644 (file)
        "actionthrottled": "Radnja je usporena",
        "actionthrottledtext": "Kao meru protiv zloupotrebe, ograničeno vam je izvođenje ove radnje previše puta u kratkom vremenskom periodu, a vi ste premašili ovo ograničenje.\nMolimo pokušajte ponovo za nekoliko minuta.",
        "protectedpagetext": "Ova stranica je zaključana za izmene i druge radnje.",
-       "viewsourcetext": "Možete čitati i kopirati sadržaj ove stranice.",
+       "viewsourcetext": "Možete čitati i kopirati izvornik ove stranice.",
        "viewyourtext": "Možete da pogledate i kopirate izvorni tekst <strong>vaših izmena</strong> na ovoj stranici.",
-       "protectedinterface": "Ova stranica sadrži tekst korisničkog okruženja za softver na ovom vikiju i zaštićena je radi sprečavanja zloupotrebe.\nDa biste dodali ili izmenili prevode svih vikija, posetite [https://translatewiki.net/ Translejtviki], projekat za lokalizaciju Medijavikija.",
+       "protectedinterface": "Ova stranica sadrži tekst interfejsa za softver na ovom vikiju i zaštićena je radi sprečavanja zloupotrebe.\nDa biste dodali ili izmenili prevode bilo kojeg vikija, posetite [https://translatewiki.net/ translatewiki.net], projekat za lokalizaciju Medijavikija.",
        "editinginterface": "<strong>Upozorenje:</strong> uređujete stranicu koja se koristi za prikazivanje teksta korisničkog okruženja.\nIzmene na ovoj stranici će uticati na sve korisnike ovog vikija.",
        "translateinterface": "Da dodate ili promenite prevode za sve vikije, posetite [https://translatewiki.net/ Translejtviki], projekat za lokalizaciju Medijavikija.",
        "cascadeprotected": "Ova stranica je zaključana jer sadrži {{PLURAL:$1|sledeću stranicu koja je zaštićena|sledeće stranice koje su zaštićene}} „prenosivom“ zaštitom:\n$2",
        "username": "{{GENDER:$1|Korisničko ime}}:",
        "prefs-memberingroups": "{{PLURAL:$1|Grupa|Grupe}}:",
        "prefs-memberingroups-type": "$1",
+       "group-membership-link-with-expiry": "$1 (do $2)",
        "prefs-registration": "Vreme registracije:",
        "prefs-registration-date-time": "$1",
        "yourrealname": "Pravo ime:",
        "userrights-changeable-col": "Grupe koje možete da promenite",
        "userrights-unchangeable-col": "Grupe koje ne možete da promenite",
        "userrights-irreversible-marker": "$1*",
+       "userrights-expiry-existing": "Postojeće vrijeme isteka: $3, $2",
+       "userrights-expiry-othertime": "Drugo vrijeme:",
        "userrights-conflict": "Sukob promena korisničkih prava! Molimo proverite vaše izmene.",
        "group": "Grupa:",
        "group-user": "Korisnici",
        "recentchanges-legend-heading": "<strong>Legenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|spisak novih stranica]])",
        "recentchanges-submit": "Prikaži",
+       "rcfilters-legend-heading": "<strong>Spisak skraćenica:</strong>",
        "rcfilters-activefilters": "Aktivni filteri",
-       "rcfilters-filtergroup-registration": "Registracija korisnika",
-       "rcfilters-filter-registered-label": "Registrovani",
-       "rcfilters-filter-registered-description": "Prijavljeni urednici.",
-       "rcfilters-filter-unregistered-label": "Neregistrovani",
-       "rcfilters-filter-unregistered-description": "Urednici koji nisu prijavljeni.",
        "rcfilters-filtergroup-authorship": "Autorstvo doprinosa",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrovani",
+       "rcfilters-filter-user-experience-level-registered-description": "Prijavljeni urednici.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neregistrovani",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Urednici koji nisu prijavljeni.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novajlije",
        "rcfilters-filter-user-experience-level-newcomer-description": "Manje od 10 izmjena i 4 dana aktivnosti.",
        "rcfilters-filter-user-experience-level-learner-label": "Učenici",
        "lonelypagestext": "Sledeće stranice nisu povezane s drugim stranicama, niti su uključene transkluzijom u druge stranice.",
        "uncategorizedpages": "Nekategorisane stranice",
        "uncategorizedcategories": "Nekategorisane kategorije",
-       "uncategorizedimages": "Datoteke bez kategorija",
+       "uncategorizedimages": "Nekategorisane datoteke",
        "uncategorizedtemplates": "Nekategorisani šabloni",
        "unusedcategories": "Nekorišćene kategorije",
        "unusedimages": "Nekorišćene datoteke",
        "tooltip-ca-nstab-template": "Pogledajte šablon",
        "tooltip-ca-nstab-help": "Pogledajte stranicu za pomoć",
        "tooltip-ca-nstab-category": "Pogledajte stranicu kategorija",
-       "tooltip-minoredit": "Označite izmenu kao manju",
-       "tooltip-save": "Sačuvajte izmene koje ste napravili",
+       "tooltip-minoredit": "Označite ovu izmenu kao manju",
+       "tooltip-save": "Sačuvajte svoje izmene",
        "tooltip-publish": "Objavi svoje izmene",
-       "tooltip-preview": "Pregledajte svoje izmene. Poželjno je da koristite ovo dugme pre čuvanja",
-       "tooltip-diff": "Pogledajte sve izmene koje ste napravili na tekstu",
+       "tooltip-preview": "Pregledajte svoje izmene. Koristite ovo dugme pre čuvanja.",
+       "tooltip-diff": "Pogledajte koje izmene ste napravili na tekstu",
        "tooltip-compareselectedversions": "Pogledajte razlike između dve izabrane izmene ove stranice.",
-       "tooltip-watch": "Dodajte ovu stranicu na spisak nadgledanja",
+       "tooltip-watch": "Dodajte ovu stranicu na svoj spisak nadgledanja",
        "tooltip-watchlistedit-normal-submit": "Ukloni naslove",
        "tooltip-watchlistedit-raw-submit": "Ažuriraj spisak",
        "tooltip-recreate": "Ponovo napravite stranicu iako je obrisana",
        "fileduplicatesearch-noresults": "Datoteka pod nazivom „$1“ nije pronađena.",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Ograničene posebne stranice</span>",
        "specialpages-group-maintenance": "Izveštaji održavanja",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava / registracija",
index 7e2cd24..c53b13a 100644 (file)
        "createacct-realname": "Ngaran asli (teu wajib)",
        "createacct-reason": "Alesan",
        "createacct-reason-ph": "Naha bet nyieun akun séjén",
+       "createacct-reason-help": "Talatah ditémbongkeun dina log panyieunan akun",
        "createacct-submit": "Jieun akun anjeun",
        "createacct-another-submit": "Jieun akun",
        "createacct-continue-submit": "Tuluykeun panyieunan akun",
        "createacct-benefit-body2": "{{PLURAL:$1|kaca|kaca}}",
        "createacct-benefit-body3": "{{PLURAL:$1|kontributor|kontributor}} panungtung",
        "badretype": "Sandi nu diasupkeun teu cocog.",
+       "usernameinprogress": "Panyieunan akun pikeun ngaran pamaké ieu keur dijalankeun. Mangga tungguan.",
        "userexists": "Sandiasma nu diasupkeun geus aya nu maké.\nMangga pilih sandiasma nu séjén.",
        "loginerror": "Kasalahan asup log",
        "createacct-error": "Nyieun akun éror",
        "botpasswords-label-grants": "Aksés nu dibikeun:",
        "botpasswords-label-grants-column": "Diwidian",
        "botpasswords-bad-appid": "Ngaran bot \"$1\" teu sah.",
+       "botpasswords-update-failed": "Gagal nganyarkeun ngaran bot \"$1\". Baheula pernah dipupus kitu?",
        "botpasswords-created-title": "Kecap sandi bot dijieun",
+       "botpasswords-created-body": "Kecap sandi pikeun ngaran bot \"$1\" ti pamaké \"$2\" geus dijieun.",
        "botpasswords-updated-title": "Kecap sandi bot dianyarkeun",
+       "botpasswords-updated-body": "Kecap sandi pikeun ngaran bot \"$1\" ti pamaké \"$2\" geus dianyarkeun.",
        "botpasswords-deleted-title": "Kecap sandi bot dihapus",
+       "botpasswords-deleted-body": "Kecap sandi pikeun ngaran bot \"$1\" ti pamaké \"$2\" geus dipupus.",
        "resetpass_forbidden": "Sandi henteu bisa dirobah",
        "resetpass_forbidden-reason": "Kecap sandi teu bisa diganti: $1",
        "resetpass-no-info": "Anjeun kudu asup log pkeun bisa muka ieu kaca sacara langsung.",
        "passwordreset-nocaller": "Panggero kudu dibikeun",
        "passwordreset-nosuchcaller": "Panggero can aya: $1",
        "passwordreset-invalidemail": "Alamat surélék teu sah",
-       "changeemail": "Ganti alamat surélék",
-       "changeemail-header": "Ganti alamat surélék akun",
+       "passwordreset-nodata": "Boh ngaran pamaké, boh alamat surélék teu dibéré",
+       "changeemail": "Ganti atawa pupus alamat surélék",
+       "changeemail-header": "Lengkepan ieu formulir pikeun ngarobah alamat surélék anjeun. Mun anjeun hayang mupus sakumna alamat surélék nu patalian jeung akun anjeun, kosongkeun alamat surélék nalika ngirim formulir.",
        "changeemail-no-info": "Anjeun kudu asup log pikeun bisa muka ieu kaca sacara langsung.",
        "changeemail-oldemail": "Alamat surélék ayeuna:",
        "changeemail-newemail": "Alamat surélék anyar:",
        "nocreate-loggedin": "Anjeun teu diwenangkeun pikeun nyieun kaca anyar.",
        "sectioneditnotsupported-title": "Teu bisa ngarobah dina sub-judul",
        "sectioneditnotsupported-text": "Eusi subjudul di ieu kaca teu bisa diédit.",
-       "permissionserrors": "Kasalahan Hak Aksés",
+       "permissionserrors": "Kasalahan widi",
        "permissionserrorstext": "Anjeung teu boga kawenangan pikeun peta kitu, kalawan {{PLURAL:$1|alesan|alesan}} di handap ieu:",
        "permissionserrorstext-withaction": "Anjeun teu ngabogaan hak keur $2, kusabab {{PLURAL:$1|alesan|alesan}} katut:",
        "recreate-moveddeleted-warn": "'''Awas: anjeun keur nyieun deui kaca nu geus kungsi dihapus.'''\n\nMangga émutan deui perlu/henteuna nyieun deui ieu artikel.\nPikeun leuwih écés, di handap dibéréndélkeun logna:",
        "postedit-confirmation-saved": "Éditan anjeun tos disimpen.",
        "edit-already-exists": "Teu bisa nyieun kaca anyar.\nArtikelna geus aya.",
        "defaultmessagetext": "Téks ti dituna",
+       "content-failed-to-parse": "Gagal ngarecah kontén $2 pikeun modél $1:$3",
        "invalid-content-data": "Data eusi henteu valid",
        "content-not-allowed-here": "Eusi \"$1\" teu diijinan di kaca [[$2]]",
        "editwarning-warning": "Ninggalkeun ieu kaca bakal ngaleungitkeun parobahan anu tas dijieun.\nUpama anjeun geus asup log, anjeun bisa numpurkeun ieu talatah dina bagian \"{{int:prefs-editing}}\" préferénsi anjeun.",
        "post-expand-template-argument-warning": "'''Awas''': Ieu kaca sahenteuna ngandung hiji argumén citakan anu ukuran ékspansina badag teuing. Éta argumén geus teu dipaliré.",
        "post-expand-template-argument-category": "Kaca kalawan argumén citakan anu teu dipaliré",
        "parser-template-loop-warning": "Katangén ayana citakan anu patumpang tindih: [[$1]]",
+       "template-loop-category": "Kaca kawalan citakan bulak-balik",
        "parser-template-recursion-depth-warning": "Citakan patumpang tindih ngaleuwihan wates($1)",
        "language-converter-depth-warning": "Parobah basa ngaleuwihan wates jerona ($1)",
        "expansion-depth-exceeded-category": "Kaca-kaca anu jero ékspansina leuwih ti wates",
        "revdelete-selected-text": "{{PLURAL:$1|Révisi dipilih}} ti [[:$2]]:",
        "revdelete-selected-file": "{{PLURAL:$1|Vérsi berkas dipilih}} ti [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Kajadian log dipilih}}:",
+       "revdelete-text-file": "Vérsi berkas nu dipupus bakal muncul kénéh di jujutan berkas, ngan bagéan tina konténna bakal moal bisa diaksés pikeun umum.",
        "revdelete-confirm": "Mangga geura konfirmasi yen Anjeun gaduh maksad pikeun ngalakukeun hal ieu, paham kana konsekwensina, tur nu dilakukeun ieu teh luyu sareng [[{{MediaWiki:Policy-url}}|kawijakanana]]",
        "revdelete-suppress-text": "Nyumputkeun revisi '''ukur''' bisa digunakeun keur kasus-kasus di handap ieu:\n* Informasi nu boga potensi mitenah\n* Informasi pribadi nu teu pantes\n*: ''alamat imah katut nomer telepon, nomer kartu identitas, jeung lian-liana.''",
        "revdelete-legend": "Setél réstriksi révisi:",
        "rcfilters-highlightmenu-title": "Pilih warna",
        "rcfilters-highlightmenu-help": "Pilih warna pikeun nyorot ieu properti",
        "rcfilters-filterlist-noresults": "Taya saringan nu kapanggih",
-       "rcfilters-filtergroup-registration": "Padaptaran pamaké",
-       "rcfilters-filter-registered-label": "Kadaptar",
-       "rcfilters-filter-registered-description": "Éditor asup log",
-       "rcfilters-filter-unregistered-label": "Teu kadaptar",
-       "rcfilters-filter-unregistered-description": "Éditor nu teu asup log.",
        "rcfilters-filtergroup-authorship": "Kontribusi kapangarangan",
        "rcfilters-filter-editsbyself-label": "Éditan meunang anjeun",
        "rcfilters-filter-editsbyself-description": "Kontribusi anjeun.",
        "rcfilters-filter-editsbyother-label": "Éditan ku batur",
        "rcfilters-filter-editsbyother-description": "Sakumna éditan iwal nu kuring.",
        "rcfilters-filtergroup-userExpLevel": "Undakan mahér (pikeun pamaké kadaptar hungkul)",
+       "rcfilters-filter-user-experience-level-registered-label": "Kadaptar",
+       "rcfilters-filter-user-experience-level-registered-description": "Éditor asup log",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Teu kadaptar",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Éditor nu teu asup log.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Padatang anyar",
        "rcfilters-filter-user-experience-level-learner-label": "Palajar",
        "rcfilters-filter-user-experience-level-experienced-label": "Pamaké pangpangalaman",
        "pageinfo-toolboxlink": "Émbaran kaca",
        "pageinfo-redirectsto": "Alihkeun ka",
        "pageinfo-redirectsto-info": "info",
-       "pageinfo-contentpage": "Diitung salaku kaca eusi",
+       "pageinfo-contentpage": "Diitung minangka kaca eusi",
        "pageinfo-contentpage-yes": "Enya",
        "pageinfo-protect-cascading-yes": "Enya",
        "pageinfo-category-info": "Info kategori",
index 0945600..961ba5a 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista över förkortningar:</strong>",
        "rcfilters-activefilters": "Aktiva filter",
        "rcfilters-advancedfilters": "Avancerade filter",
+       "rcfilters-limit-title": "Ändringar att visa",
+       "rcfilters-limit-shownum": "Visa de senaste $1 ändringarna",
+       "rcfilters-days-title": "Senaste dagarna",
+       "rcfilters-hours-title": "Senaste timmarna",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dagar}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|timme|timmar}}",
        "rcfilters-quickfilters": "Sparade filter",
        "rcfilters-quickfilters-placeholder-title": "Inga länkar har sparats ännu",
        "rcfilters-quickfilters-placeholder-description": "För att spara dina filterinställningar och återanvända dem senare, klicka på bokmärkesikonen under \"Aktiva filter\" nedan.",
        "rcfilters-invalid-filter": "Ogiltigt filter",
        "rcfilters-empty-filter": "Inga aktiva filter. Alla bidrag visas.",
        "rcfilters-filterlist-title": "Filter",
-       "rcfilters-filterlist-whatsthis": "Vad är detta?",
+       "rcfilters-filterlist-whatsthis": "Hur fungerar desse?",
        "rcfilters-filterlist-feedbacklink": "Ge återkoppling på nya (beta)filter",
        "rcfilters-highlightbutton-title": "Markera resultat",
        "rcfilters-highlightmenu-title": "Välj en färg",
        "rcfilters-noresults-conflict": "Inga resultat hittades eftersom sökkriterierna är i konflikt",
        "rcfilters-state-message-subset": "Detta filter har ingen effekt, eftersom resultaten inkluderar följande, bredare {{PLURAL:$2|filter}} (försök att markera för att särskilja det): $1",
        "rcfilters-state-message-fullcoverage": "Att markera alla filter i en grupp är detsamma som att markera inget alls, så detta filter har igen effekt. Grupper som inkluderas: $1",
-       "rcfilters-filtergroup-registration": "Användarregistrering",
-       "rcfilters-filter-registered-label": "Registrerade",
-       "rcfilters-filter-registered-description": "Inloggade redigerare.",
-       "rcfilters-filter-unregistered-label": "Oregistrerade",
-       "rcfilters-filter-unregistered-description": "Redigerare som inte är inloggade.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Detta filter är i konflikt med följande {{PLURAL:$2|upplevelsefilter}}, som endast {{PLURAL:$2|hittar}} registrerade användare: $1",
        "rcfilters-filtergroup-authorship": "Författarskap av bidrag",
        "rcfilters-filter-editsbyself-label": "Redigeringar av dig",
        "rcfilters-filter-editsbyself-description": "Dina egna bidrag.",
        "rcfilters-filter-editsbyother-label": "Ändringar av andra",
        "rcfilters-filter-editsbyother-description": "Alla ändringar förutom dina egna.",
-       "rcfilters-filtergroup-userExpLevel": "Erfarenhetsnivå (endast för registrerade användare)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Upplevelsefilter hittar endast registrerade användare, så detta filter är i konflikt med filtret \"Oregistrerade\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filtret \"Oregistrerade\" är i konflikt med en eller flera upplevelsefilter, som endast hittar registrerade användare. Filtren som är i konflikt är markerade i området med aktiva filter ovan.",
+       "rcfilters-filtergroup-userExpLevel": "Användarregistrering och -erfarenhet",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrerade",
+       "rcfilters-filter-user-experience-level-registered-description": "Inloggade redigerare.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Oregistrerade",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Redigerare som inte är inloggade.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nykomlingar",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Färre än 10 redigeringar och 4 dagars aktivitet.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registrerade redigerare med färre än 10 redigeringar och 4 dagars aktivitet.",
        "rcfilters-filter-user-experience-level-learner-label": "Nybörjare",
-       "rcfilters-filter-user-experience-level-learner-description": "Mer erfarenhet än \"Nybörjare\" men mindre än \"Erfarna användare\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Registrerade redigerare vars erfarenhet hamnar mellan \"Nybörjare\" och \"Erfarna användare\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Erfarna användare",
-       "rcfilters-filter-user-experience-level-experienced-description": "Fler än 30 dagars aktivitet och 500 redigeringar.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registrerade redigerare med fler än 500 redigeringar och 30 dagars aktivitet.",
        "rcfilters-filtergroup-automated": "Automatiserade bidrag",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Redigeringar gjorda av automatiserade verktyg.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filtret \"Mindre redigering\" är i konflikt med en eller flera ändringstypfilter, eftersom vissa ändringstyper inte kan betecknas som \"mindre\". Filtren som är i konflikt är markerade i området med aktiva filter ovan.",
        "rcfilters-hideminor-conflicts-typeofchange": "Vissa ändringstyper kan inte betecknas som \"mindre\", så detta filter är i konflikt med följande ändringstypfilter: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Detta ändringstypfilter är i konflikt med filtret \"Mindre ändringar\". Vissa ändringstyper kan inte betecknas som \"mindre\".",
-       "rcfilters-filtergroup-lastRevision": "Senaste version",
+       "rcfilters-filtergroup-lastRevision": "Senaste versioner",
        "rcfilters-filter-lastrevision-label": "Senaste version",
-       "rcfilters-filter-lastrevision-description": "Den senaste ändringen av en sida.",
-       "rcfilters-filter-previousrevision-label": "Tidigare versioner",
-       "rcfilters-filter-previousrevision-description": "Alla ändringar som inte är den senaste ändringen av en sida.",
+       "rcfilters-filter-lastrevision-description": "Endast senaste ändringen av en sida.",
+       "rcfilters-filter-previousrevision-label": "Inte den senaste versionen",
+       "rcfilters-filter-previousrevision-description": "Alla ändringar som inte är den \"senaste versionen\".",
        "rcfilters-filter-excluded": "Exkluderad",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Exkludera markerade",
+       "rcfilters-exclude-button-on": "Exkluderar markerade",
        "rcfilters-view-tags": "Märkta redigeringar",
        "rcfilters-view-namespaces-tooltip": "Filtrera resultat efter namnrymder",
        "rcfilters-view-tags-tooltip": "Filtrera resultat med redigeringsmärken",
        "delete-warning-toobig": "Denna sida har en lång redigeringshistorik med mer än $1 {{PLURAL:$1|sidversion|sidversioner}}. Att radera sidan kan skapa problem med hanteringen av databasen på {{SITENAME}}; var försiktig.",
        "deleteprotected": "Du kan inte radera denna sida eftersom den är skyddad.",
        "deleting-backlinks-warning": "<strong>Varning:</strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Andra sidor]] länkar till eller inkluderar sidan som du är på väg att radera.",
+       "deleting-subpages-warning": "<strong>Varning:</strong> Sidan du håller på att radera har [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|en undersida|$1 undersidor|51=över 50 undersidor}}]].",
        "rollback": "Rulla tillbaka ändringar",
        "rollbacklink": "rulla tillbaka",
        "rollbacklinkcount": "rulla tillbaka $1 {{PLURAL:$1|redigering|redigeringar}}",
        "undelete-search-title": "Sök efter raderade sidor",
        "undelete-search-box": "Sök efter raderade sidor",
        "undelete-search-prefix": "Sidor som börjar med:",
+       "undelete-search-full": "Visa sidtitlar som innehåller:",
        "undelete-search-submit": "Sök",
        "undelete-no-results": "Inga sidor med sådan titel hittades i arkivet över raderade sidor.",
        "undelete-filename-mismatch": "Filversionen med tidsstämpeln $1 kan inte återställas: Filnamnet stämmer inte.",
        "fileduplicatesearch-noresults": "Ingen fil med namnet \"$1\" hittades.",
        "specialpages": "Specialsidor",
        "specialpages-note-top": "Teckenförklaring",
-       "specialpages-note": "* Normala specialsidor.\n* <span class=\"mw-specialpagerestricted\">Specialsidor med begränsad åtkomst.</span>",
        "specialpages-group-maintenance": "Underhållsrapporter",
        "specialpages-group-other": "Övriga specialsidor",
        "specialpages-group-login": "Logga in / skapa konto",
index f54c390..493d2e6 100644 (file)
        "anontalk": "చర్చ",
        "navigation": "మార్గదర్శకం",
        "and": "&#32;మరియు",
-       "qbfind": "వెతుకు",
-       "qbbrowse": "విహరించు",
-       "qbedit": "సవరించు",
-       "qbpageoptions": "ఈ పేజీ",
-       "qbmyoptions": "నా పేజీలు",
        "faq": "తరచూ అడిగే ప్రశ్నలు",
-       "faqpage": "Project:తరచూ అడిగే ప్రశ్నలు",
        "actions": "పనులు",
        "namespaces": "పేరుబరులు",
        "variants": "వివిధ రూపాలు",
        "edit-local": "స్థానిక వివరణని మార్చు",
        "create": "సృష్టించు",
        "create-local": "స్థానిక వివరణను చేర్చు",
-       "editthispage": "ఈ పేజీని సవరించండి",
-       "create-this-page": "ఈ పేజీని సృష్టించండి",
        "delete": "తొలగించు",
-       "deletethispage": "ఈ పేజీని తొలగించండి",
-       "undeletethispage": "ఈ పేజీ తొలగింపును ఆపు",
        "undelete_short": "{{PLURAL:$1|ఒక మార్పు|$1 మార్పుల}} తొలగింపును రద్దుచెయ్యి",
        "viewdeleted_short": "{{PLURAL:$1|తొలగించిన ఒక మార్పు|$1 తొలగించిన మార్పుల}}ను చూడండి",
        "protect": "సంరక్షించు",
        "protect_change": "మార్చు",
-       "protectthispage": "ఈ పేజీని సంరక్షించు",
        "unprotect": "సంరక్షణ మార్పు",
-       "unprotectthispage": "ఈ పుట సంరక్షణను మార్చండి",
        "newpage": "కొత్త పేజీ",
-       "talkpage": "ఈ పేజీని చర్చించండి",
        "talkpagelinktext": "చర్చ",
        "specialpage": "ప్రత్యేక పేజీ",
        "personaltools": "వ్యక్తిగత పనిముట్లు",
-       "articlepage": "విషయపు పేజీని చూడండి",
        "talk": "చర్చ",
        "views": "చూపులు",
        "toolbox": "పనిముట్లు",
        "tool-link-userrights": "{{GENDER:$1|వాడుకరి}} గుంపులను మార్చు",
        "tool-link-userrights-readonly": "{{GENDER:$1|వాడుకరి}} గుంపులను చూడండి",
        "tool-link-emailuser": "ఈ {{GENDER:$1|వాడుకరికి}} ఈమెయిలు పంపు",
-       "userpage": "వాడుకరి పేజీని చూడండి",
-       "projectpage": "ప్రాజెక్టు పేజీని చూడండి",
        "imagepage": "ఫైలు పేజీని చూడండి",
        "mediawikipage": "సందేశం పేజీని చూడండి",
        "templatepage": "మూస పేజీని చూడండి",
        "userlogin-remembermypassword": "నన్ను లాగిన్ చేసే ఉంచు",
        "userlogin-signwithsecure": "సురక్షిత కనెక్షను వాడు",
        "cannotlogin-title": "లాగిన్ చెయ్యలేకున్నాం",
+       "cannotlogin-text": "లాగినవడం సాధ్యపడడం లేదు.",
        "cannotloginnow-title": "ఇప్పుడు లాగిన్ అవలేరు",
        "cannotloginnow-text": "$1 ను వాడుతూండగా లాగౌట్ అవలేరు.",
        "cannotcreateaccount-title": "ఖాతాలను సృష్టించలేము",
        "page_first": "మొదటి",
        "page_last": "చివరి",
        "histlegend": "తేడా ఎంపిక: సంచికల యొక్క రేడియో బాక్సులను ఎంచుకొని ఎంటర్‌ నొక్కండి, లేదా పైన/ కింద ఉన్న మీటను నొక్కండి.<br />\nసూచిక: <strong>({{int:cur}})</strong> = ప్రస్తుత సంచికతో కల తేడాలు, <strong>({{int:last}})</strong> = ఇంతకు ముందరి సంచికతో గల తేడాలు, <strong>{{int:minoreditletter}}</strong> = చిన్న మార్పు",
-       "history-fieldset-title": "à°\9aà°°à°¿à°¤à±\8dà°° à°\9aà±\82à°¡ండి",
+       "history-fieldset-title": "à°\95à±\82à°°à±\8dà°ªà±\81à°² à°\95à±\8bà°¸à°\82 à°µà±\86à°¤à°\95ండి",
        "history-show-deleted": "తొలగించినవి మాత్రమే",
        "histfirst": "అతి పాతవి",
        "histlast": "సరికొత్త",
        "recentchanges-legend-heading": "<strong>సూచిక :</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|కొత్త పేజీల జాబితా]]ను కూడా చూడండి)",
        "recentchanges-submit": "చూపించు",
+       "rcfilters-savedqueries-remove": "తొలగించు",
+       "rcfilters-savedqueries-new-name-label": "పేరు",
        "rcfilters-filterlist-title": "వడపోతలు",
        "rcfilters-highlightmenu-title": "ఒక రంగును ఎంచుకోండి",
-       "rcfilters-filtergroup-registration": "వాడుకరి నమోదు",
-       "rcfilters-filter-editsbyself-label": "మీ స్వంత దిద్దుబాట్లు",
-       "rcfilters-filter-editsbyself-description": "మీ దిద్దుబాట్లు.",
+       "rcfilters-filter-editsbyself-label": "మీ దిద్దుబాట్లు",
+       "rcfilters-filter-editsbyself-description": "మీ స్వంత దిద్దుబాట్లు.",
        "rcfilters-filter-editsbyother-label": "ఇతరుల దిద్దుబాట్లు",
-       "rcfilters-filter-editsbyother-description": "à°\87తరà±\81à°²à±\81 (à°®à±\80à°°à±\81 à°\95ాదà±\81) à°\9aà±\87సిన à°¦à°¿à°¦à±\8dà°¦à±\81బాà°\9fà±\8dà°²à±\81.",
+       "rcfilters-filter-editsbyother-description": "à°®à±\80à°°à±\81 à°\9aà±\87సినవి à°\95à°¾à°\95à±\81à°\82à°¡à°¾ à°®à°¿à°\97ిలిన à°¦à°¿à°¦à±\8dà°¦à±\81బాà°\9fà±\8dలనà±\8dà°¨à±\80.",
        "rcfilters-filtergroup-userExpLevel": "అనుభవ స్థాయి (నమోదైన వాడుకరులకు మాత్రమే)",
+       "rcfilters-filter-user-experience-level-registered-label": "నమోదైనది",
        "rcfilters-filter-user-experience-level-newcomer-label": "కొత్తవారు",
        "rcfilters-filter-user-experience-level-newcomer-description": "10 కంటే తక్కువ దిద్దుబాట్లు, 4 రోజుల కంటే తక్కువ పని.",
        "rcfilters-filter-user-experience-level-learner-label": "నేర్చుకుంటున్నవారు",
-       "rcfilters-filter-user-experience-level-learner-description": "\"à°\95à±\8aà°¤à±\8dతవారి\" à°\95à°\82à°\9fà±\87 à°\8eà°\95à±\8dà°\95à±\81à°µ, \"à°\85à°¨à±\81à°­à°µà°\9cà±\8dà°\9eà±\81à°²à±\88à°¨ à°µà°¾à°¡à±\81à°\95à°°à±\81à°²\" à°\95à°\82à°\9fà±\87 à°¤à°\95à±\8dà°\95à±\81à°µ à°ªà°¨à°¿à°°à±\8bà°\9cà±\81à°²à±\81.",
+       "rcfilters-filter-user-experience-level-learner-description": "\"à°\95à±\8aà°¤à±\8dతవారి\" à°\95à°\82à°\9fà±\87 à°\8eà°\95à±\8dà°\95à±\81à°µ, \"à°\85à°¨à±\81à°­à°µà°\9cà±\8dà°\9eà±\81à°²à±\88à°¨ à°µà°¾à°¡à±\81à°\95à°°à±\81à°²\" à°\95à°\82à°\9fà±\87 à°¤à°\95à±\8dà°\95à±\81à°µ à°\85à°¨à±\81à°­à°µà°\82.",
        "rcfilters-filter-user-experience-level-experienced-label": "అనుభవజ్ఞులైన వాడుకరులు",
        "rcfilters-filter-user-experience-level-experienced-description": "30 రోజుల కంటే ఎక్కువ పని, 500 పైచిలుకు దిద్దుబాట్లు.",
+       "rcfilters-filter-bots-label": "బాట్",
        "rcfilters-filtergroup-significance": "ప్రాముఖ్యం",
        "rcfilters-filter-minor-label": "చిన్న మార్పులు",
        "rcfilters-filter-minor-description": "రచయిత చిన్నవిగా గుర్తు పెట్టిన దిద్దుబాట్లు.",
        "fileduplicatesearch-noresults": "\"$1\" అనే పేరుగల దస్త్రమేమీ కనబడలేదు.",
        "specialpages": "ప్రత్యేక పేజీలు",
        "specialpages-note-top": "సూచిక",
-       "specialpages-note": "* మామూలు ప్రత్యేక పుటలు.\n* <span class=\"mw-specialpagerestricted\">నియంత్రిత ప్రత్యేక పుటలు.</span>",
        "specialpages-group-maintenance": "నిర్వహణా నివేదికలు",
        "specialpages-group-other": "ఇతర ప్రత్యేక పేజీలు",
        "specialpages-group-login": "ప్రవేశించండి / ఖాతాను సృష్టించుకోండి",
index 05cf315..3d72f11 100644 (file)
        "page_first": "uluk",
        "page_last": "ikus",
        "histfirst": "sedu liu hotu",
-       "histlast": "Foun liu hotu",
+       "histlast": "foun liu hotu",
        "historyempty": "(mamuk)",
        "history-feed-item-nocomment": "$1 iha $2",
        "rev-delundel": "hatudu/subar",
index 32fbc7f..25c0457 100644 (file)
@@ -12,7 +12,9 @@
                        "לערי ריינהארט",
                        "아라",
                        "Macofe",
-                       "AryanSogd"
+                       "AryanSogd",
+                       "ToJack",
+                       "Vashgird"
                ]
        },
        "tog-underline": "Пайвандҳо хаткашида:",
        "tog-watchlisthideliu": "Пинҳон кардани вироишоти корбарони вурудшуда аз феҳристи пайгириҳо",
        "tog-watchlisthideanons": "Пинҳон кардани вироишоти корбарони гумном аз феҳристи пайгириҳо",
        "tog-watchlisthidepatrolled": "Пинҳони вироишҳои гаштхӯрда аз феҳристи пайгириҳо",
+       "tog-watchlisthidecategorization": "Пинҳон кардани гурӯҳбандии саҳифаҳо",
        "tog-ccmeonemails": "Нусхаҳои хатҳоро ба ман рои кунед, ман онҳоро ба корбарон рои мекунам",
        "tog-diffonly": "Муҳтавиёти саҳифаи зерин намоиш дода нашавад",
        "tog-showhiddencats": "Гурӯҳҳои пинҳонро намоиш бидеҳ",
-       "tog-norollbackdiff": "Баъд аз вогардони тафовутро нишон надеҳ",
+       "tog-norollbackdiff": "Баъд аз вогардонӣ тафовутро нишон надеҳ",
        "tog-useeditwarning": "Дар ҳолати тарки саҳифа вироиши тағйироти захиранашуда манро огаҳ кун",
-       "tog-prefershttps": "ҲамеÑ\88а Ð¿Ð°Ð¹Ð²Ð°Ñ\81Ñ\82Ñ\88авии Ð°Ð¼Ð½Ñ\80о Ð´Ð°Ñ\80 Ò³Ð¾Ð»Ð¸ Ð²Ñ\83Ñ\80Ñ\83д Ð¸Ñ\81Ñ\82иÑ\84ода Ð±Ð°Ñ\80",
+       "tog-prefershttps": "ҲамеÑ\88а Ð¸Ñ\81Ñ\82иÑ\84ода Ð±Ñ\83Ñ\80дани Ð¿Ð°Ð¹Ð²Ð°Ñ\81Ñ\82Ñ\88авии Ð°Ð¼Ð½ Ð´Ð°Ñ\80 Ò³Ð¾Ð»Ð¸ Ð²Ñ\83Ñ\80Ñ\83д",
        "underline-always": "Доимо",
        "underline-never": "Ҳеҷгоҳ",
        "underline-default": "Пӯст ё мурургари пешфарз",
@@ -77,8 +80,8 @@
        "sat": "Шн",
        "january": "Январ",
        "february": "Феврал",
-       "march": "Ð\9cарт",
-       "april": "Ð\90прел",
+       "march": "март",
+       "april": "апрел",
        "may_long": "май",
        "june": "Июн",
        "july": "Июл",
        "october": "Октябр",
        "november": "Ноябр",
        "december": "Декабр",
-       "january-gen": "Январ",
-       "february-gen": "Феврал",
-       "march-gen": "Ð\9cаÑ\80Ñ\82",
-       "april-gen": "Ð\90пÑ\80ел",
-       "may-gen": "Ð\9cай",
-       "june-gen": "Ð\98Ñ\8eн",
-       "july-gen": "Ð\98Ñ\8eл",
-       "august-gen": "Ð\90вгÑ\83Ñ\81Ñ\82",
-       "september-gen": "Сентябр",
-       "october-gen": "Ð\9eкÑ\82Ñ\8fбÑ\80",
-       "november-gen": "Ð\9dоÑ\8fбÑ\80",
-       "december-gen": "Ð\94екабÑ\80",
+       "january-gen": "январи",
+       "february-gen": "феврали",
+       "march-gen": "маÑ\80Ñ\82и",
+       "april-gen": "апÑ\80ели",
+       "may-gen": "майи",
+       "june-gen": "иÑ\8eни",
+       "july-gen": "иÑ\8eли",
+       "august-gen": "авгÑ\83Ñ\81Ñ\82и",
+       "september-gen": "сентябри",
+       "october-gen": "окÑ\82Ñ\8fбÑ\80и",
+       "november-gen": "ноÑ\8fбÑ\80и",
+       "december-gen": "декабÑ\80и",
        "jan": "Ян",
        "feb": "Фев",
        "mar": "Мар",
        "october-date": "$1 октябр",
        "november-date": "$1 ноябр",
        "december-date": "$1 декабр",
+       "period-am": "АМ",
+       "period-pm": "РМ",
        "pagecategories": "{{PLURAL:$1|Гурӯҳ|Гурӯҳҳо}}",
        "category_header": "Мақолаҳо дар гурӯҳи \"$1\"",
        "subcategories": "Зергурӯҳҳо",
        "morenotlisted": "Ин феҳрист комил нест.",
        "mypage": "Саҳифа",
        "mytalk": "Баҳс",
-       "anontalk": "Баҳс бо ин IP",
+       "anontalk": "Баҳс",
        "navigation": "Гаштан",
        "and": "&#32;ва",
-       "qbfind": "Ёфтан",
-       "qbbrowse": "Мурур",
-       "qbedit": "Вироиш",
-       "qbpageoptions": "Ин саҳифа",
-       "qbmyoptions": "Саҳифаҳои ман",
        "faq": "Саволҳои тез-тез пурсидашуда",
-       "faqpage": "Project:Саволҳои тез-тез пурсидашуда",
        "actions": "Амалкардҳо",
        "namespaces": "Фазоҳои ном",
        "variants": "Вариантҳо",
        "edit-local": "Вироиши тавзеҳи маҳаллӣ",
        "create": "Эҷод",
        "create-local": "Иловаи тавзеҳи маҳаллӣ",
-       "editthispage": "Вироиши ин саҳифа",
-       "create-this-page": "Эҷод кардани ин саҳифа",
        "delete": "Ҳазф",
-       "deletethispage": "Ин саҳифаро ҳазф кунед",
-       "undeletethispage": "Барқарор кардани ин саҳифа",
        "undelete_short": "Эҳёи {{PLURAL:$1|вироиш|$1 вироишот}}",
        "viewdeleted_short": "{{PLURAL:$1|вироиши ҳазфшуда|$1 вироишҳои ҳазфшуда}}",
        "protect": "Ҳифз кардан",
        "protect_change": "тағйир",
-       "protectthispage": "Ҳифз намудани ин саҳифа",
        "unprotect": "Тағйири сатҳи муҳофизат",
-       "unprotectthispage": "Тағйири муҳофизати ин саҳифа",
        "newpage": "Саҳифаи нав",
-       "talkpage": "Ин саҳифаро муҳокима кунед",
        "talkpagelinktext": "Баҳс",
        "specialpage": "Саҳифаи вижа",
        "personaltools": "Абзорҳои шахсӣ",
-       "articlepage": "Намоиши мақола",
        "talk": "Баҳс",
        "views": "Назарот",
        "toolbox": "Абзорҳо",
-       "userpage": "Саҳифаи корбарро бинед",
-       "projectpage": "Дидани саҳифаи лоиҳа",
+       "tool-link-userrights-readonly": "Дидани гурӯҳҳои корбар",
        "imagepage": "Намоиши саҳифаи парванда",
        "mediawikipage": "Намоиши саҳифаи акс",
        "templatepage": "Нигаристани саҳифаи шаблон",
        "retypenew": "Калимаи нави убурро такроран нависед:",
        "resetpass_submit": "Калимаи убурро танзим карда ба систем вуруд кунед",
        "changepassword-success": "Гузарвожаи шумо бо муваффақият тағйир дода шуд!",
+       "botpasswords-label-cancel": "Пӯшидан",
        "resetpass_forbidden": "Гузарвожаҳоро наметавон тағйир дод",
        "resetpass-no-info": "Барои дастрасии мустақим ба ин саҳифа шумо бояд ба систем ворид шуда бошед.",
        "resetpass-submit-loggedin": "Тағйири гузарвожа",
        "session_fail_preview_html": "'''Бубахшед! Имкони сабти вироиши шумо ба хотир аз даст рафтани иттилоот нишаст.'''\n\n''Бо таваҷҷӯҳ ба ин ки дар {{SITENAME}} имкони HTML хом фаъол аст, пешнамоиши саҳифа пинҳон шуда то имкони зидди ҳамалоти ҶаваСкрипт вуҷуд ндошта бошад.''\n\n'''Агар боварӣ доред, ки ин пешнамоиш як вироиши миҷоз ас, онро такрор кунед. Агар пешнамоиш натиҷае надод, аз систем хориҷ шавед ва дубора ворид шавед'''",
        "token_suffix_mismatch": "'''Вироишҳои шумо захира нашуд, зеро мурургари шумо навиштаҳои нуқтагузориро аз ҳам пошида аст. Вироиши шумо захира нашуд то аз вайрон шудани матни саҳифа пешгирӣ кунад.\nБаъзан ин хато вақте пайдо мешавад, ки шумо аз хизмати (web-based proxy) истифода мекунед.'''",
        "edit_form_incomplete": "<strong>Бархе аз қисмати форми вироиш ба пойгоҳ нарасид; дубора бисанҷед, ки вироишоти шумо дуруст аст ва дубора саъй кунед.</strong>",
-       "editing": "Дар ҳоли вироиш $1",
+       "editing": "Дар ҳоли вироиши $1",
        "creating": "Дар ҳоли эҷоди $1",
        "editingsection": "Дар ҳоли вироиши $1 (қисмат)",
        "editingcomment": "Дар ҳоли вироиши $1 (қисми нав)",
        "postedit-confirmation-saved": "Вироиши шумо захира шуд.",
        "edit-already-exists": "Саҳифаи нав эҷод нашуд.\nҚаблан ин саҳифа вуҷуд дорад.",
        "defaultmessagetext": "Матни пайёми пешфарз",
-       "content-model-wikitext": "викиматн",
+       "content-model-wikitext": "вики-матн",
        "content-model-text": "матни содда",
        "content-model-javascript": "ҶаваСкрипт",
        "post-expand-template-inclusion-warning": "<strong>Огоҳӣ:</strong> Шаблони дар баргирифта хеле калон аст. Баъзе аз шаблонҳо дар бар гирифта намешавад.",
        "revdelete-hide-user": "Номи корбар/нишонаи IP",
        "revdelete-hide-restricted": "Фурунишонии иттилоот барои мудирон ба ҳамроҳи дигарон",
        "revdelete-radio-same": "(тағйир надиҳед)",
-       "revdelete-radio-set": "Пинҳон",
+       "revdelete-radio-set": "Пинҳонкарда",
        "revdelete-radio-unset": "Намоён",
        "revdelete-suppress": "Аз дастрасии мудирон ба додаҳо низ монанди сайри корбарон ҷилавгирӣ ба амал ояд",
        "revdelete-unsuppress": "Хотимаи маҳдудиятҳо дар мавриди нусхаҳои интихобшуда",
        "titlematches": "Унвони саҳифа татбиқ мекунад",
        "textmatches": "Матни мақола татбиқ мекунад",
        "notextmatches": "Матни ҳеҷ мақолае рост намеояд",
-       "prevn": "қаблӣ {{PLURAL:$1|$1}}",
-       "nextn": "баъдӣ {{PLURAL:$1|$1}}",
+       "prevn": "{{PLURAL:$1|$1-тои}} қаблӣ",
+       "nextn": "{{PLURAL:$1|$1-тои}} навбатӣ",
+       "next-page": "саҳифаи навбатӣ",
        "prevn-title": "Қаблӣ $1 {{PLURAL:$1|натиҷа|натоиҷ}}",
        "nextn-title": "Баъдӣ $1 {{PLURAL:$1|натиҷа|натоиҷ}}",
        "shown-title": "Намоиши $1 {{PLURAL:$1|натиҷа|натоиҷ}} дар ҳар саҳифа",
-       "viewprevnext": "Намоиш ($1 {{int:pipe-separator}} $2) ($3)",
+       "viewprevnext": "Намоиши ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Саҳифае бо номи \"[[:$1]]\" дар ин вики вуҷуд дорад.'''",
        "searchmenu-new": "'''Эҷоди саҳифаи \"[[:$1]]\" дар ин вики!'''",
        "searchprofile-articles": "Саҳифаҳои мӯҳтаво",
        "searchprofile-images-tooltip": "Ҷустуҷӯи парвандаҳо",
        "searchprofile-everything-tooltip": "Ҷустуҷӯи ҳамаи мӯҳтаво (бо ҳисоби саҳифаҳои баҳс)",
        "searchprofile-advanced-tooltip": "Ҷустуҷӯ дар фазоҳои номи дилхоҳ",
-       "search-result-size": "$1 ({{PLURAL:$2|1 калима|$2 калимаҳо}})",
+       "search-result-size": "$1 ({{PLURAL:$2|1 калима|$2 калима}})",
        "search-redirect": "(тағйири масир $1)",
        "search-section": "(қисмат $1)",
+       "search-category": "(гурӯҳи $1)",
        "search-suggest": "Оё шумо инро дар назар доред: $1",
        "search-interwiki-caption": "Лоиҳаҳои хоҳарӣ",
        "search-interwiki-default": "Натоиҷ аз $1:",
        "editusergroup": "Гуруҳҳои корбарро вироиш кунед",
        "editinguser": "Дар ҳоли вироиши корбар '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]]{{int:pipe-separator}}[[Special:Contributions/$1|{{int:contribslink}}]])",
        "userrights-editusergroup": "Вироиши гурӯҳҳои корбарӣ",
+       "userrights-viewusergroup": "Дидани гурӯҳҳои корбар",
        "saveusergroups": "Сабти гурӯҳҳои корбарӣ",
        "userrights-groupsmember": "Аъзои:",
        "userrights-groups-help": "Шумо метавонед гурӯҳҳое, ки корбар дар он қарор доштаро тағйир диҳед.\n* Қуттии аломатхӯрда ба ин маънӣ аст, ки корбар дар он гурӯҳ қарор дорад.\n* Қуттии аломатнахӯрда ба ин маънӣ аст, ки корбар дар он гурӯҳ қарор надорад.\n* \"*\" маънои онро дорад, ки шумо қобили ҳазф кардани гурӯҳе нестед дар ҳоле ки шумо онро илова карда бошед, ин ё он.",
        "right-editinterface": "Вироиши намоиши корбарӣ",
        "right-editusercss": "Вироиши парвандаҳои CSS дигар корбарон",
        "right-edituserjs": "Вироиши парвандаҳои JS дигар корбарон",
-       "right-rollback": "Ð\92огаÑ\80донии Ñ\82ези Ð²Ð¸Ñ\80оиÑ\88ҳои Ð¾Ñ\85иÑ\80ин ÐºÐ¾Ñ\80баÑ\80е, ÐºÐ¸ Ñ\8fк Ñ\81аҳиÑ\84аÑ\80о Ð²Ð¸Ñ\80оиÑ\88 ÐºÐ°Ñ\80дааÑ\81Ñ\82",
+       "right-rollback": "зÑ\83д Ð²Ð¾Ð³Ð°Ñ\80донидани Ð²Ð¸Ñ\80оиÑ\88оÑ\82и ÐºÐ¾Ñ\80баÑ\80и Ð¾Ñ\85иÑ\80ин",
        "right-markbotedits": "Аломат задани вироишҳои вогардонишуда ба унвони вироиши бот",
        "right-noratelimit": "Таъсирнопазир аз маҳдудияти суръат",
        "right-import": "Ворид кардани саҳифаҳо аз дигар викиҳо",
        "right-userrights": "Вироиши ҳамаи ихтиёроти корбарон",
        "right-userrights-interwiki": "Вироиши ихтиёроти корбарии корбарони дигар викиҳо",
        "right-siteadmin": "Бастн ва боз кардани пойгоҳи дода",
+       "grant-rollback": "Вогардонидани тағйироти саҳифа",
        "newuserlogpage": "Гузориши эҷоди корбар",
        "newuserlogpagetext": "Ин гузориш аз номҳои корбарии тозасохташуда аст.",
        "rightslog": "Гузориши ихтиёроти корбар",
        "action-deleterevision": "ҳазви ин нусха",
        "action-browsearchive": "ҷустуҷӯи саҳифаҳои ҳазвшуда",
        "action-undelete": "барқарор кардани ин саҳифа",
+       "action-rollback": "вогардонии зуди тағйирот",
        "action-sendemail": "фиристодани пайёмҳо",
        "nchanges": "$1 {{PLURAL:$1|тағйир|тағйирот}}",
        "enhancedrc-history": "таърих",
        "recentchanges-label-minor": "Ин вироиши ҷузъи аст",
        "recentchanges-label-bot": "Ин вироишро робот анҷом додааст",
        "recentchanges-label-unpatrolled": "Ин вироиш ҳанӯз гаштзанӣ нашудааст",
+       "rcfilters-savedqueries-cancel-label": "Лағв",
        "rcnotefrom": "Дар зер тағйиротҳои охирин аз <b>$2</b> (то <b>$1</b> нишон дода шудааст).",
        "rclistfrom": "Нишон додани тағйиротҳои нав сар карда аз $3 $2",
        "rcshowhideminor": "$1 вироишҳои хурд",
-       "rcshowhideminor-show": "Намоиш",
-       "rcshowhideminor-hide": "Пинҳон",
+       "rcshowhideminor-show": "Намоиш додани",
+       "rcshowhideminor-hide": "Пинҳон кардани",
        "rcshowhidebots": "$1 ботҳо",
        "rcshowhidebots-show": "Намоиш",
-       "rcshowhidebots-hide": "Пинҳон",
+       "rcshowhidebots-hide": "Пинҳон кардани",
        "rcshowhideliu": "$1 корбарони сабтиномшуда",
        "rcshowhideliu-show": "Намоиш",
-       "rcshowhideliu-hide": "Пинҳон",
+       "rcshowhideliu-hide": "Пинҳон кардани",
        "rcshowhideanons": "$1 корбарони вуруднашуда",
        "rcshowhideanons-show": "Намоиш",
-       "rcshowhideanons-hide": "Пинҳон",
+       "rcshowhideanons-hide": "Пинҳон кардани",
        "rcshowhidepatr": "$1 вироишҳои гаштӣ",
        "rcshowhidepatr-show": "Намоиш",
-       "rcshowhidepatr-hide": "Пинҳон",
+       "rcshowhidepatr-hide": "Пинҳон кардани",
        "rcshowhidemine": "$1 вироишҳои ман",
        "rcshowhidemine-show": "Намоиш",
-       "rcshowhidemine-hide": "Пинҳон",
+       "rcshowhidemine-hide": "Пинҳон кардани",
        "rclinks": "Нишон додани $1 тағйироти охирин дар $2 рӯзи охир",
        "diff": "фарқият",
        "hist": "таърих",
        "upload-file-error-text": "Ҳангоми талош барои эчоди як парвандаи муваққатӣ дар коргузор, як хатои дохилӣ рух дод. Лутфан бо мудири систем тамос бигиред.",
        "upload-misc-error": "Хатои номаълум дар боргузорӣ",
        "upload-misc-error-text": "Ҳангоми боргузорӣ, хатои номаълуме рух дод.  Лутфан итминнон ҳосил кунед, ки нишонаи URL мӯътабар ва қобили дастрасӣ аст, ва баъд дубора талош кунед. Агар мушкили пофишорӣ кунад, бо яке аз мудирони систем  тамос бигиред.",
+       "upload-form-label-infoform-categories": "Гурӯҳҳо",
        "backend-fail-opentemp": "Парвандаи муваққатӣ боз нашуд.",
        "backend-fail-writetemp": "Ба парвандаи муваққатӣ сабт нашуд.",
        "backend-fail-closetemp": "Парвандаи муваққатиро пӯшида нашуд.",
        "license": "Иҷозатнома:",
        "license-header": "Иҷозатнома",
        "nolicense": "Ҳеҷ яке интихоб нашудааст",
+       "licenses-edit": "Тағйири имконоти иҷозатнома",
        "license-nopreview": "(Пешнамоиш вуҷуд надорад)",
        "upload_source_url": "(як нишони интернетии мӯътабар ва оммавӣ)",
        "upload_source_file": " (парвандае дар компютери шумо)",
        "randompage": "Саҳифаи тасодуфӣ",
        "randompage-nopages": "Ҳеҷ саҳифае дар ин фазои ном мавҷуд нест.",
        "randomincategory": "Саҳифаи тасодуфӣ дар гурӯҳ",
+       "randomincategory-category": "Гурӯҳ:",
        "randomincategory-submit": "Бирав",
        "randomredirect": "Масири тасодуфӣ",
        "randomredirect-nopages": "Ҳеҷ саҳифаи тағйири масире дар ин фазои ном мавҷуд нест.",
-       "statistics": "Омор\\Статистика",
+       "statistics": "Омор/Статистика",
        "statistics-header-pages": "Саҳифаи омор",
        "statistics-header-edits": "Вироиши омор",
        "statistics-header-users": "Омори корбарон",
        "statistics-header-hooks": "Дигар омор",
        "statistics-articles": "Саҳифаҳои мӯҳтаво",
        "statistics-pages": "Саҳифаҳо",
+       "statistics-pages-desc": "Тамоми саҳифаҳо дар ин вики-сомона (саҳифаҳои равонакунӣ, баҳсҳо ва ғ.)",
        "statistics-files": "Парвандаҳои боршуда",
+       "statistics-edits": "Шумораи вироишҳо аз замони эҷоди ин {{SITENAME}}",
        "statistics-users": "[[Special:ListUsers|Корбарони]] сабтиномшуда",
        "statistics-users-active": "Корбарони фаъол",
        "pageswithprop-submit": "Бирав",
        "withoutinterwiki": "Саҳифаҳои бидуни пайвандҳои забонӣ",
        "withoutinterwiki-summary": "Саҳифаҳои зерин пайванде ба забони дигар надоранд:",
        "withoutinterwiki-legend": "Пешванд",
-       "withoutinterwiki-submit": "Намоиш",
+       "withoutinterwiki-submit": "Намоиши",
        "fewestrevisions": "Саҳифаҳое, ки шумораи ками нусхаҳо доранд",
-       "nbytes": "$1 {{PLURAL:$1|байт|байтҳо}}",
+       "nbytes": "$1 {{PLURAL:$1|байт}}",
        "ncategories": "$1 {{PLURAL:$1|гурӯҳ|гурӯҳҳо}}",
        "nlinks": "$1 {{PLURAL:$1|пайванд|пайвандҳо}}",
        "nmembers": "$1 {{PLURAL:$1|узв}}",
        "deadendpagestext": "Саҳифаҳои зерин ба ҳеҷ дигар саҳифае дар {{SITENAME}} пайванд нестанд.",
        "protectedpages": "Саҳифаҳои ҳифзшуда",
        "protectedpages-indef": "Фақат ҳифзшудаҳои номуайян",
+       "protectedpages-noredirect": "Пинҳони саҳифаҳои равонакунӣ",
        "protectedpagesempty": "Дар ҳоли ҳозир ҳеҷ саҳифае муҳофизат нашудааст.",
        "protectedpages-page": "Саҳифа",
        "protectedpages-expiry": "Замони саромадан",
        "notargettitle": "Мақсаде нест",
        "notargettext": "Шумо ягон саҳифа ё корбареро барои иҷрои ин амал рӯи он мушаххас накардаед.",
        "nopagetitle": "Чунин саҳифае вуҷуд надорад",
-       "pager-newer-n": "{{PLURAL:$1|навтар 1|навтар $1}}",
-       "pager-older-n": "{{PLURAL:$1|кӯҳнатар 1|кӯҳнатар $1}}",
+       "pager-newer-n": "{{PLURAL:$1|1-тои навтар|$1-тои навтар}}",
+       "pager-older-n": "{{PLURAL:$1|1-тои кӯҳнатар|$1-тои кӯҳнатар}}",
        "suppress": "Назорат",
        "booksources": "Манбаҳои китобҳо",
        "booksources-search-legend": "Ҷустуҷӯи сарчашмаҳои китоб",
        "logempty": "Мавриди мутобиқ ба манзури шумо дар гузориш пайдо нашуд.",
        "log-title-wildcard": "Саҳифаҳоеро ҷустуҷӯ кунед, ки унвонашон бо ин матн оғоз мешаванд",
        "allpages": "Ҳамаи саҳифаҳо",
-       "nextpage": "СаҳиÑ\84аи Ð±Ð°Ñ\8aдина ($1)",
+       "nextpage": "СаҳиÑ\84аи Ð½Ð°Ð²Ð±Ð°Ñ\82Ó£ ($1)",
        "prevpage": "Саҳифаи пешина ($1)",
        "allpagesfrom": "Намоиши саҳифаҳо бо шурӯъ аз:",
        "allpagesto": "Намоиши саҳифаҳо бо поёни дар:",
        "allpagesprefix": "Намоиши саҳифаҳои дорои пешванд:",
        "allpagesbadtitle": "Унвони саҳифаи дода шуда номӯътабар аст, ё инки дорои пешванди байнизабонӣ ё байнивикиӣ аст. Мумкин аст аломатҳоеро дорад, ки наметавон онҳоро дар унвонҳо истифода бурд.",
        "allpages-bad-ns": "{{SITENAME}} дорои фазоином \"$1\" нест.",
-       "allpages-hide-redirects": "Ð\9fинҳони Ñ\82аÒ\93йиÑ\80оÑ\82и Ð¼Ð°Ñ\81иÑ\80",
+       "allpages-hide-redirects": "Ð\9fинҳони Ñ\81аҳиÑ\84аҳои Ñ\80авонакÑ\83нӣ",
        "cachedspecial-refresh-now": "Дидани охирин.",
        "categories": "Гурӯҳҳо",
        "categoriespagetext": "Гурӯҳҳои зерин дар вики вуҷуд доранд.\n[[Special:UnusedCategories|Unused categories]] are not shown here.\nAlso see [[Special:WantedCategories|wanted categories]].",
        "mailnologin": "Нишонае аз фиристанда вуҷуд надорад",
        "mailnologintext": "Барои фиристодани почтаи электронӣ барои корбарони дигар бояд [[Special:UserLogin|ба систем ворид шавед]] ва нишонаи почтаи электронии мӯътабар дар [[Special:Preferences|тарҷиҳоти]] худ дошта бошед.",
        "emailuser": "Фиристодани email ба ин корбар",
+       "emailuser-title-target": "Навиштани мактуб ба email-и ин корбар",
        "emailuser-title-notarget": "Фиристодани пайём ба корбар",
        "emailpagetext": "Агар ин корбар нишонаи почтаи электронии мӯътабаре дар тарҷиҳоти ворид карда бошад, форми зерин як пайғоме мефиристад.\nНишонаи почтаи электроние, ки шумо дар тарҷиҳоти корбариатон ворид кардаед, дар нишони фиристандаи нома \"From\" хоҳад омад, то ки гиранда тавонад ба шумо посух диҳад.",
        "defemailsubject": "Пайёми {{SITENAME}} аз корбар \"$1\"",
        "delete-edit-reasonlist": "Вироиш ҳазф далелҳо",
        "delete-toobig": "Ин саҳифа таърихчаи бузурге дорад, ки шомили беш аз $1 вироиш аст. Ҳазфи ин гуна саҳифаҳо барои пешгири аз шикастани тасодуфӣ дар {{SITENAME}} маҳдуд шудааст.",
        "delete-warning-toobig": "Ин саҳифа таърихи бузурге дорад, ки шомили беш аз $1 вироиш аст. Ҳазфи ин саҳифа метавонад ихтилол ба амалгари пойгоҳи додаи {{SITENAME}} бишавад; лутфан бо эҳтиёт иқдом кунед.",
-       "rollback": "Вогардонии вироишҳо",
-       "rollbacklink": "вогардони",
+       "rollback": "Вогардонидани вироишот",
+       "rollbacklink": "вогардонидан",
+       "rollbacklinkcount": "вогардонидани $1 {{PLURAL:$1|вироиш}}",
+       "rollbacklinkcount-morethan": "вогардонидани беш аз $1 {{PLURAL:$1|вироиш}}",
        "rollbackfailed": "Вогардони нашуд",
        "cantrollback": "Наметавон вироишро вогардонд; охирин ҳиссагузор танҳо муаллифи ин мақола аст.",
-       "alreadyrolled": "Ð\9eÑ\85иÑ\80ин Ð²Ð¸Ñ\80оиÑ\88и [[:$1]] Ð°Ð· Ñ\82аÑ\80аÑ\84и [[User:$2|$2]] ([[User talk:$2|Ð\91аҳÑ\81]]) Ð½Ð°Ð¼ÐµÑ\82авон Ð²Ð¾Ð³Ð°Ñ\80донӣ ÐºÐ°Ñ\80д; Ð¿ÐµÑ\88 Ð°Ð· Ð¸Ð½ Ñ\88аÑ\85Ñ\81и Ð´Ð¸Ð³Ð°Ñ\80е Ð¼Ð°Ò\9bолаÑ\80о Ð²Ð¸Ñ\80оиÑ\88 Ñ\91 Ð²Ð¾Ð³Ð°Ñ\80донӣ ÐºÐ°Ñ\80дааÑ\81Ñ\82.\n\nÐ\9eÑ\85иÑ\80ин Ð²Ð¸Ñ\80оиÑ\88 Ð°Ð· Ñ\82аÑ\80аÑ\84и [[User:$3|$3]] ([[User talk:$3|Ð\91аҳÑ\81]]) Ð±Ñ\83д.",
+       "alreadyrolled": "ТаÒ\93йиÑ\80оÑ\82и Ð¾Ñ\85иÑ\80ини Ñ\81аҳиÑ\84аи Â«[[:$1]]»-Ñ\80о Ð²Ð¾Ð³Ð°Ñ\80донӣ ÐºÐ°Ñ\80дан Ò\93айÑ\80иимкон Ð°Ñ\81Ñ\82, ÐºÐ¸ Ð°Ð· Ñ\82аÑ\80аÑ\84 [[User:$2|$2]] ([[User talk:$2|баҳÑ\81]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) Ð¸Ò·Ñ\80о Ñ\88Ñ\83даанд, Ñ\87Ñ\83нки Ñ\88аÑ\85Ñ\81е Ð°Ð»Ð»Ð°ÐºÐ°Ð¹ Ð²Ð¾Ð³Ð°Ñ\80донӣ Ð²Ð° Ñ\91 Ñ\82аҳÑ\80иÑ\80аÑ\88 ÐºÐ°Ñ\80д.\n\nÐ\9eÑ\85иÑ\80ин Ð±Ð¾Ñ\80 {{GENDER:$3|коÑ\80баÑ\80}} [[User:$3|$3]] ([[User talk:$3|баҳÑ\81]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) Ñ\81аҳиÑ\84аÑ\80о Ñ\82аҳÑ\80иÑ\80 ÐºÐ°Ñ\80д.",
        "editcomment": "Хулосаи вироиш ин буд: <em>$1</em>.",
        "revertpage": "Вироиши [[Special:Contributions/$2|$2]] ([[User talk:$2|Баҳс]]) вогардонида шуд ба охирин тағйире, ки [[User:$1|$1]] анҷом дода буд",
        "rollback-success": "Вироишҳои $1 вогардонӣ шуд; саҳифа ба вироиши $2 баргардонида шуд.",
+       "rollback-success-notify": "Вироишоти $1 вогардонида шуд ба охирин вироише, ки  $2 анҷом дода буд. [$3 Намоиши тавофут]",
        "sessionfailure": "Ба назар мерасад, мушкилие дар мавриди нишасти корбарии шумо вуҷуд дорад; амали дархостшуда ба унвони иқдоми пешгирона дар баробари рабуда шудани иттилооти нишасти корбарӣ, лағв шуд. Лутфан тугмаи \"бозгашт\"-ро дар мурургари худ пахш кунед ва саҳифае, ки аз он инҷо расидаед муҷаддадан фарохонӣ кунед, сипас муҷаддадан боз саъй кунед.",
        "protectlogpage": "Гузориши муҳофизат",
        "protectlogtext": "Дар зер феҳристи қуфл карданҳо ва аз қуфл озод шуданҳо омада аст. Барои иттилооти бештар ба [[Special:ProtectedPages|феҳристи саҳифаҳои муҳофизатшуда]] нигаред.",
        "isredirect": "саҳифаи тағйири масир",
        "istemplate": "истифодашуда дар саҳифа",
        "isimage": "пайванд ба парванда",
-       "whatlinkshere-prev": "{{PLURAL:$1|қаблӣ|қаблӣ $1}}",
+       "whatlinkshere-prev": "{{PLURAL:$1|қаблӣ|$1-тои қаблӣ}}",
        "whatlinkshere-next": "{{PLURAL:$1|баъдӣ|баъдӣ $1}}",
        "whatlinkshere-links": "← пайвандҳо",
        "whatlinkshere-hideredirs": "$1 тағйири масир",
        "unblocked": "Дастрасии [[User:$1|$1]] боз карда шуд",
        "unblocked-id": "Қатъи дастрасии шумораи $1 хотима ёфт",
        "blocklist": "Корбарони басташуда",
+       "autoblocklist-submit": "Ҷустуҷӯ",
        "ipblocklist": "Корбарони басташуда",
        "ipblocklist-legend": "Ҷустуҷӯи корбари баста шуда",
        "blocklist-target": "Ҳадаф",
        "tooltip-pt-preferences": "Тарҷиҳоти ман",
        "tooltip-pt-watchlist": "Рӯйхати саҳифаҳое, ки тағйиротҳояшонро Шумо назорат мекунед",
        "tooltip-pt-mycontris": "Феҳристи ҳиссагузориҳои шумо",
-       "tooltip-pt-login": "Тавсия мешавад ки ба систем ворид шавад, лекин иҷборӣ нест.",
+       "tooltip-pt-login": "Тавсия мешавад ки ба система ворид шавед, лекин маҷбурӣ нест.",
        "tooltip-pt-logout": "Хуруҷ аз систем",
        "tooltip-ca-talk": "Баҳси матни таркибии ин саҳифа",
        "tooltip-ca-edit": "Шумо ин саҳифаро вироиш карда метавонед. Пеш аз захира кардани саҳифа пешнамоишро истифода баред.",
        "spambot_username": "Спамтозакуни МедиаВики",
        "spam_reverting": "Вогардони ба охирин нусхае, ки пайванде ба $1 надорад",
        "spam_blanking": "Ҳамаи нусхаҳои пайвандҳо $1 доштан, дар ҳоли холӣ кардан",
+       "pageinfo-display-title": "Сарлавҳаи намоишӣ",
+       "pageinfo-default-sort": "Тартиб кардан ба унвони калидӣ (пешфарз)",
+       "pageinfo-length": "Дарозии саҳифа (дар байт)",
+       "pageinfo-article-id": "Рамзи саҳифа",
+       "pageinfo-language": "Забони саҳифа",
+       "pageinfo-content-model": "Навъи таркибии саҳифа",
+       "pageinfo-redirects-name": "Шумораи равонакуниҳо ба ин саҳифа",
+       "pageinfo-redirects-value": "$1-то",
        "pageinfo-toolboxlink": "Иттилооти саҳифа",
        "pageinfo-contentpage-yes": "Бале",
        "pageinfo-protect-cascading-yes": "Бале",
        "markedaspatrolledtext": "Нусхаи интихобшуда ба унвони баррасишуда аломат зада шуд.",
        "rcpatroldisabled": "Баррасии Тағйироти Охир фаъол нест",
        "rcpatroldisabledtext": "Имкони Баррасии Тағйироти Охир дар ҳоли ҳозир ғайрифаъол аст.",
-       "markedaspatrollederror": "Аломати баррасӣ зада нашуд",
+       "markedaspatrollederror": "Аломати тафтишшавӣ зада нашуд.",
        "markedaspatrollederrortext": "Бояд як нусхаро мушаххас кунед то барчасб ба унвони баррасишуда аломат бихӯрад.",
        "markedaspatrollederror-noautopatrol": "Шумо наметавонед ба тағйироти анҷом шуда тавассути худатон аломати баррасӣ бизанед.",
        "patrol-log-page": "Гузоришҳои нигаҳбонӣ",
        "file-nohires": "Нусхаи ҳаҷман ва сифатан баландтар дастрас нест.",
        "svg-long-desc": "SVG парванда, исмӣ $1 × $2 пиксел, андозаи парванда: $3",
        "show-big-image": "Акси аслӣ",
+       "show-big-image-preview-differ": "Ҳаҷми пешнамоиши $3 барои файли кунунии $2: $1.",
+       "show-big-image-other": "Дигар {{PLURAL:$1|нусхаи|нусхаҳои}} андозавӣ: $1.",
        "newimages": "Намоишгоҳи парвандаҳои ҷадид",
        "imagelisttext": "Дар зер феҳристи '''$1''' {{PLURAL:$1|парвандаи|парвандаҳои}} ба тартиб оварда шуда, омадааст $2.",
        "newimages-summary": "Ин саҳифаи вижа охирин парвандаҳои боршударо намоиш медиҳад.",
        "exif-gpsdatestamp": "Таърихи ҶПС",
        "exif-gpsdifferential": "Тасҳеҳи ҷузъии ҶПС",
        "exif-keywords": "Калидвожаҳо",
+       "exif-languagecode": "Забон",
+       "exif-iimcategory": "Гурӯҳ",
        "exif-compression-1": "Ғайрифишурда",
        "exif-unknowndate": "Таърихи номаълум",
        "exif-orientation-1": "Оддӣ",
        "recreate": "Аз нав созед",
        "confirm_purge_button": "Таъйид",
        "confirm-purge-top": "Пок карадни нусхаи ҳофизаи ниҳонии (Cache) ин саҳифаро таъйид мекунед?",
-       "imgmultipageprev": "← саҳифаи пешин",
-       "imgmultipagenext": "саҳифаи баъд →",
+       "imgmultipageprev": "← саҳифаи пешина",
+       "imgmultipagenext": "саҳифаи баъдӣ →",
        "imgmultigo": "Бирав!",
        "imgmultigoto": "Ба саҳифаи $1 равед",
        "ascending_abbrev": "афзуншаванда",
        "autosumm-replace": "Ивазкунии саҳифа бо '$1'",
        "autoredircomment": "Тағйири масир ба [[$1]]",
        "autosumm-new": "Саҳифаи нав бо \"$1\" эҷод шуд",
+       "autosumm-newblank": "Саҳифаи холӣ эҷод шуд",
+       "size-bytes": "$1 {{PLURAL:$1|байт}}",
+       "size-pixel": "$1 {{PLURAL:$1|пиксел}}",
        "lag-warn-normal": "Мумкин аст тағйироти тозатар аз $1 сония дар ин феҳрист нишон дода нашавад.",
        "lag-warn-high": "Мумкин аст, ба хотири пасафтодагии зиёд коргузори додаҳо, тағйироти тоза аз $1 сония дар ин феҳрист нишон нашуда бошанд.",
        "watchlistedit-normal-title": "Вироиши феҳристи пайгириҳо",
        "version-software": "Нусхаи насбшуда",
        "version-software-product": "Маҳсул",
        "version-software-version": "Нусха",
+       "redirect-page": "Рамзи саҳифа",
        "fileduplicatesearch": "Ҷустуҷӯ барои парвандаҳои такрорӣ",
        "fileduplicatesearch-summary": "Ҷустуҷӯ барои парвандаҳои такрорӣ бар асоси миқдори дар ҳам шудаи онҳо сурат мегирад.",
        "fileduplicatesearch-filename": "Номи парванда:",
        "expand_templates_remove_comments": "Ҳазфи тавзеҳот",
        "expand_templates_generate_xml": "Намоиши дарахти таҷзеҳи XML",
        "expand_templates_preview": "Пешнамоиш",
+       "mediastatistics-nbytes": "{{PLURAL:$1|$1 байт}} ($2; $3%)",
+       "special-characters-group-cyrillic": "Сириллик",
        "randomrootpage": "Саҳифаи решавии тасодуфӣ",
        "changecredentials-submit": "Тағйири ҳисоби корбарӣ",
        "removecredentials-submit": "Хориҷи эътиборнома"
index e771d78..bc512f1 100644 (file)
        "recentchanges-legend-heading": "<strong>Аңлатма:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шулай ук [[Special:NewPages|яңа битләр исемлеген]] карагыз)",
        "recentchanges-submit": "Күрсәт",
-       "rcfilters-quickfilters": "Сакланган филтр көйләнмәләре",
+       "rcfilters-activefilters": "Актив фильтрлар",
+       "rcfilters-quickfilters": "Сакланган фильтрлар",
        "rcfilters-clear-all-filters": "Барлык филтерләрне чистарту",
-       "rcfilters-filter-registered-label": "Теркәлгән",
-       "rcfilters-filter-registered-description": "Теркәлгән мөхәррирләр.",
+       "rcfilters-search-placeholder": "Фильтрланы соңгы үзгәртү (карау яисә кертүне башлау)",
+       "rcfilters-filter-user-experience-level-registered-label": "Теркәлгән",
+       "rcfilters-filter-user-experience-level-registered-description": "Теркәлгән мөхәррирләр.",
        "rcfilters-filter-user-experience-level-experienced-label": "Тәҗрибәле кулланучылар",
        "rcfilters-filter-humans-label": "Кеше (бот түгел)",
        "rcfilters-filter-minor-description": "«Кече үзгәртү» дип тамгаланган үзгәртүләр",
index 92f157d..146647e 100644 (file)
@@ -70,7 +70,8 @@
                        "Олександр",
                        "Similartothissimilartothat",
                        "Bunyk",
-                       "Choomaq"
+                       "Choomaq",
+                       "SimondR"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
        "rcfilters-invalid-filter": "Недійсний фільтр",
        "rcfilters-empty-filter": "Без фільтрів. Показано всі зміни.",
        "rcfilters-filterlist-title": "Фільтри",
-       "rcfilters-filterlist-whatsthis": "Що Ñ\86е?",
+       "rcfilters-filterlist-whatsthis": "Як Ñ\86е Ð¿Ñ\80аÑ\86Ñ\8eÑ\94?",
        "rcfilters-filterlist-feedbacklink": "Надайте відгук про нові (бета) фільтри",
        "rcfilters-highlightbutton-title": "Виділити результати",
        "rcfilters-highlightmenu-title": "Вибрати колір",
        "rcfilters-noresults-conflict": "Результатів не знайдено через конфлікт у пошукових критеріях",
        "rcfilters-state-message-subset": "Цей фільтр не має впливу, оскільки його результати включені в результати {{PLURAL:$2|цього, ширшого, фільтра|цих, ширших, фільтрів}} (спробуйте увімкнути виділення, щоб вирізнити їх): $1",
        "rcfilters-state-message-fullcoverage": "Вибір усіх фільтрів у групі — це все одно, що не вибирати жодного з них, тобто таке фільтрування не має впливу. Гупа містить: $1",
-       "rcfilters-filtergroup-registration": "Реєстрація користувача",
-       "rcfilters-filter-registered-label": "Зареєстровані",
-       "rcfilters-filter-registered-description": "Користувачі, що увійшли в систему.",
-       "rcfilters-filter-unregistered-label": "Незареєстровані",
-       "rcfilters-filter-unregistered-description": "Користувачі, які не ввійшли в систему.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Цей фільтр конфліктує з {{PLURAL:$2|таким фільтром|такими фільтрами}} досвіду, {{PLURAL:$2|який знаходить|які знаходять}} лише зареєстрованих користувачів: $1",
        "rcfilters-filtergroup-authorship": "Авторство внеску",
        "rcfilters-filter-editsbyself-label": "Зміни, здійснені Вами",
        "rcfilters-filter-editsbyself-description": "Ваш власний внесок.",
        "rcfilters-filter-editsbyother-label": "Зміни, здійснені іншими",
        "rcfilters-filter-editsbyother-description": "Усі зміни, за винятком Ваших власних.",
        "rcfilters-filtergroup-userExpLevel": "Рівень досвіду (тільки для зареєстрованих користувачів)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Фільтри досвіду знаходять лише зареєстрованих користувачів, тож цей фільтр конфліктує з фільтром «Незареєстровані».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фільтр «Незареєстровані» конфліктує з одним або більше фільтрами досвіду, які знаходять лише зареєстрованих користувачів. Конфліктні фільтри позначені вище в ділянці активних фільтрів.",
+       "rcfilters-filter-user-experience-level-registered-label": "Зареєстровані",
+       "rcfilters-filter-user-experience-level-registered-description": "Користувачі, що увійшли в систему.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Незареєстровані",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Користувачі, які не ввійшли в систему.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новачки",
        "rcfilters-filter-user-experience-level-newcomer-description": "Менше ніж 10 редагувань і 4 дні активності.",
        "rcfilters-filter-user-experience-level-learner-label": "Учні",
        "fileduplicatesearch-noresults": "Файл з назвою «$1» не знайдено.",
        "specialpages": "Спеціальні сторінки",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звичайні службові сторінки\n* <span class=\"mw-specialpagerestricted\">Сторінки з обмеженим доступом.</span>",
        "specialpages-group-maintenance": "Технічні звіти",
        "specialpages-group-other": "Інші",
        "specialpages-group-login": "Вхід до системи / реєстрація",
index d434d40..8dd0c53 100644 (file)
@@ -36,7 +36,8 @@
                        "Xð",
                        "Nguyên Lê",
                        "Asmen",
-                       "Stephanecbisson"
+                       "Stephanecbisson",
+                       "Quoclinh94"
                ]
        },
        "tog-underline": "Gạch chân liên kết:",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Xem",
        "rcfilters-activefilters": "Bộ lọc hiện hành",
+       "rcfilters-days-title": "Những ngày gần đây",
        "rcfilters-savedqueries-setdefault": "Đặt làm mặc định",
        "rcfilters-savedqueries-unsetdefault": "Loại bỏ mặc định",
        "rcfilters-restore-default-filters": "Mặc định lại các bộ lọc",
        "rcfilters-highlightmenu-help": "Chọn màu để làm nổi bật thuộc tính này",
        "rcfilters-filterlist-noresults": "Không tìm thấy bộ lọc",
        "rcfilters-noresults-conflict": "Không tìm thấy kết quả nào do tiêu chí tìm kiếm đang bị mâu thuẫn",
-       "rcfilters-filtergroup-registration": "Trạng thái đăng ký thành viên",
-       "rcfilters-filter-registered-label": "Đã đăng ký",
-       "rcfilters-filter-registered-description": "Người dùng đã đăng nhập.",
-       "rcfilters-filter-unregistered-label": "Vô danh",
-       "rcfilters-filter-unregistered-description": "Người dùng chưa đăng nhập.",
        "rcfilters-filtergroup-authorship": "Người sửa đổi",
        "rcfilters-filter-editsbyself-label": "Sửa đổi của bạn",
        "rcfilters-filter-editsbyself-description": "Các sửa đổi do bạn tạo ra.",
        "rcfilters-filter-editsbyother-label": "Sửa đổi của người khác",
        "rcfilters-filter-editsbyother-description": "Các sửa đổi của người khác.",
        "rcfilters-filtergroup-userExpLevel": "Trình độ (chỉ người dùng đã đăng ký)",
+       "rcfilters-filter-user-experience-level-registered-label": "Đã đăng ký",
+       "rcfilters-filter-user-experience-level-registered-description": "Người dùng đã đăng nhập.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Vô danh",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Người dùng chưa đăng nhập.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Người mới đến",
        "rcfilters-filter-user-experience-level-newcomer-description": "Chưa tới 10 sửa đổi và 4 ngày hoạt động.",
        "rcfilters-filter-user-experience-level-learner-label": "Người đang tập",
        "fileduplicatesearch-noresults": "Không tìm thấy tập tin nào tên “$1”.",
        "specialpages": "Các trang đặc biệt",
        "specialpages-note-top": "Chú giải",
-       "specialpages-note": "* Trang đặc biệt thông thường.\n* <strong class=\"mw-specialpagerestricted\">Trang đặc biệt được hạn chế.</strong>",
        "specialpages-group-maintenance": "Báo cáo bảo quản",
        "specialpages-group-other": "Trang đặc biệt khác",
        "specialpages-group-login": "Đăng nhập / Mở tài khoản",
index 4001765..da6046e 100644 (file)
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "ווייזן",
        "rcfilters-activefilters": "אַקטיווע פילטערס",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|טאג|טעג}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|שעה|שעה'ן}}",
        "rcfilters-quickfilters": "אויפֿגעהיטענע פֿילטערס",
        "rcfilters-quickfilters-placeholder-title": "קיין לינקען נאך נישט אויפֿגעהיטן",
        "rcfilters-savedqueries-defaultlabel": "אױפֿגעהיטענע פֿילטערס",
        "rcfilters-invalid-filter": "אומגילטיגער פֿילטער",
        "rcfilters-empty-filter": "קיין אַקטיווע פילטערס. אלע ביישטייערונגען געוויזן.",
        "rcfilters-filterlist-title": "פֿילטערס",
-       "rcfilters-filterlist-whatsthis": "וואס איז דאס?",
+       "rcfilters-filterlist-whatsthis": "ווי ארבעט דאס?",
+       "rcfilters-highlightbutton-title": "ארויסשטאַרצן רעזולטאַטן",
        "rcfilters-highlightmenu-title": "אויסקלויבן א קאליר",
        "rcfilters-filterlist-noresults": "קיין פֿילטערס נישט געטראפֿן",
-       "rcfilters-filtergroup-registration": "באניצער איינשרייבונג",
-       "rcfilters-filter-registered-label": "אײַנגעשריבן",
        "rcfilters-filter-editsbyself-label": "ענדערונגען פון אייך",
        "rcfilters-filter-editsbyself-description": "אייערע אייגענע בײשטײערונגען.",
        "rcfilters-filter-editsbyother-label": "ענדערונגען פֿון אנדערע",
        "rcfilters-filter-editsbyother-description": "אלע ענדערונגען אחוץ אייערע אייגענע.",
+       "rcfilters-filter-user-experience-level-registered-label": "אײַנגעשריבן",
        "rcfilters-filter-user-experience-level-learner-label": "לערנער",
        "rcfilters-filter-bots-label": "באט",
        "rcfilters-filter-humans-label": "מענטש (נישט קיין באט)",
        "rcfilters-filter-minor-label": "מינערדיקע רעדאַקטירונגען",
        "rcfilters-filter-pageedits-label": "בלאט רעדאקטירונגען",
        "rcfilters-filter-newpages-label": "בלאַט־שאַפֿונגען",
-       "rcfilters-filtergroup-lastRevision": "לעצטע ווערסיע",
+       "rcfilters-filtergroup-lastRevision": "לעצטע ווערסיעס",
        "rcfilters-filter-lastrevision-label": "לעצטע ווערסיע",
-       "rcfilters-filter-previousrevision-label": "פֿר×\99ער×\93×\99קע ווערסיעס",
+       "rcfilters-filter-previousrevision-label": "× ×\99ש×\98 ×\93×\99 ×\9cעצ×\98ע ווערסיעס",
        "rcfilters-filter-excluded": "אויסגעשלאסן",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:נישט</strong> $1",
        "rcnotefrom": "פֿאלגנד {{PLURAL:$5|איז די ענדערונג| זענען די ענדערונגען}} זײַט <strong>$3, $4</strong> (ביז <strong>$1</strong>).",
        "undeletecomment": "אורזאַך:",
        "cannotundelete": "טייל אדער גארע צוריקשטעלונג איז דורכגעפאלן: $1",
        "undeletedpage": "'''דער בלאט $1 איז געווארן צוריקגעשטעלט.'''\n\nזעט דעם [[Special:Log/delete| אויסמעקן לאג]] פֿאר א ליסטע פון די לעצטע אויסגעמעקטע און צוריקגעשטעלטע בלעטער.",
-       "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט recently deleted pages.",
+       "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט.",
        "undelete-search-title": "זוכן אויסגעמעקטע בלעטער",
        "undelete-search-box": "זוכן אויסגעמעקטע בלעטער",
        "undelete-search-prefix": "ווײַז בלעטער וואס הייבן אן מיט:",
        "version-entrypoints-header-url": "URL",
        "version-libraries-library": "ביבליאטעק",
        "version-libraries-version": "ווערסיע",
+       "redirect": "ווייטערפֿירן לויט טעקע, באַניצער, בלאַט, ווערסיע אדער לאגבוך אידענטיפֿצירער",
        "redirect-submit": "גייט",
        "redirect-lookup": "זוכן:",
        "redirect-value": "ווערט:",
        "fileduplicatesearch-noresults": "קיין טעקע מיטן נאמען \"$1\" נישט געטראפֿן.",
        "specialpages": "ספעציעלע בלעטער",
        "specialpages-note-top": "לעגענדע",
-       "specialpages-note": "* נארמאַלע באַזונדערע בלעטער.\n* <span class=\"mw-specialpagerestricted\">באַגרענעצטע באַזונדערע בלעטער.</span>",
        "specialpages-group-maintenance": "אויפֿהאַלטונג באַריכטן",
        "specialpages-group-other": "אַנדערע ספעציעלע בלעטער",
        "specialpages-group-login": "ארײַנלאגירן / שאַפֿן קאנטע",
index 8902585..2f02916 100644 (file)
        "grant-editmywatchlist": "改你嘅監視清單",
        "grant-editpage": "改已經有嘅版",
        "grant-editprotected": "改保護咗嘅版",
+       "grant-viewmywatchlist": "睇你嘅監視清單",
        "newuserlogpage": "使用者開戶記錄",
        "newuserlogpagetext": "呢個係一個使用者開戶嘅日誌",
        "rightslog": "用戶權限日誌",
        "recentchanges-legend-heading": "<strong>標記:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (睇埋[[Special:NewPages|新開版]])",
        "recentchanges-submit": "顯示",
-       "rcfilters-filter-editsbyself-description": "你改嘅嘢。",
+       "rcfilters-filter-editsbyself-description": "你嘅貢獻。",
        "rcfilters-filter-editsbyother-label": "其他人改嘅嘢",
-       "rcfilters-filter-editsbyother-description": "其他人(唔係你)改嘅嘢",
+       "rcfilters-filter-editsbyother-description": "所有改過嘅嘢(除咗你自己)",
        "rcfilters-filtergroup-userExpLevel": "經驗級別(只限簽咗到嘅用戶)",
        "rcfilters-filter-user-experience-level-newcomer-label": "新手",
-       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯",
+       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯嘅用戶",
        "rcfilters-filter-user-experience-level-learner-label": "學徒",
        "rcfilters-filter-user-experience-level-learner-description": "編輯數同經驗多過「新手」但少過「老手」。",
        "rcfilters-filter-user-experience-level-experienced-label": "老手",
-       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯",
+       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯嘅用戶",
        "rcfilters-filtergroup-automated": "自動貢獻",
        "rcfilters-filter-bots-label": "機械人",
        "rcfilters-filter-bots-description": "用自動工具做嘅貢獻",
        "fileduplicatesearch-result-n": "個檔案 \"$1\" 有$2項完全相同嘅重覆。",
        "fileduplicatesearch-noresults": "檔案名\"$1\"找不到",
        "specialpages": "特別頁",
-       "specialpages-note": "* 標準特別頁。\n* <span class=\"mw-specialpagerestricted\">有限制嘅特別頁。</span>",
        "specialpages-group-maintenance": "維護報告",
        "specialpages-group-other": "其它特別頁",
        "specialpages-group-login": "簽到/開新戶口",
index ac23388..c46ad3a 100644 (file)
@@ -96,7 +96,8 @@
                        "D41D8CD98F",
                        "Wmr",
                        "逆襲的天邪鬼",
-                       "WhitePhosphorus"
+                       "WhitePhosphorus",
+                       "A2093064"
                ]
        },
        "tog-underline": "链接下划线:",
        "tog-shownumberswatching": "显示监视用户数",
        "tog-oldsig": "您现有的签名:",
        "tog-fancysig": "将签名视为维基文本(不自动生成链接)",
-       "tog-uselivepreview": "使ç\94¨å®\9e时预览",
+       "tog-uselivepreview": "使ç\94¨å\8d³时预览",
        "tog-forceeditsummary": "未输入编辑摘要时提醒我",
        "tog-watchlisthideown": "隐藏监视列表中的我的编辑",
        "tog-watchlisthidebots": "隐藏监视列表中的机器人编辑",
        "tog-showhiddencats": "显示隐藏分类",
        "tog-norollbackdiff": "执行回退后不显示差异",
        "tog-useeditwarning": "当我离开编辑页面时,如果有尚未保存的更改,请提醒我",
-       "tog-prefershttps": "在登录总是使用安全连接",
+       "tog-prefershttps": "在登录总是使用安全连接",
        "underline-always": "始终",
        "underline-never": "从不",
        "underline-default": "皮肤或浏览器默认设置",
        "period-am": "AM",
        "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|分类}}",
-       "category_header": "å\88\86ç±»â\80\9c$1â\80\9dä¸\8b的页面",
+       "category_header": "å\88\86ç±»â\80\9c$1â\80\9d中的页面",
        "subcategories": "子分类",
        "category-media-header": "分类“$1”中的媒体文件",
        "category-empty": "<em>本分类目前不含有任何页面或媒体文件。</em>",
        "resetpass-submit-cancel": "取消",
        "resetpass-wrong-oldpass": "临时密码或当前密码无效。您可能已经更改了您的密码,或者请求了新的临时密码。",
        "resetpass-recycled": "请重置您的密码为与当前密码不同的密码。",
-       "resetpass-temp-emailed": "您通过一个暂时电子邮件发送的代码登录。要完成登录,您必须在此设置一个新密码:",
+       "resetpass-temp-emailed": "您使用了通过电子邮件发送的临时密码登录。要完成登录,您必须在此设置一个新密码:",
        "resetpass-temp-password": "临时密码:",
        "resetpass-abort-generic": "密码更改已经被扩展程序中止。",
        "resetpass-expired": "您的密码已经到期。请设置新登录密码。",
        "rcfilters-legend-heading": "<strong>缩写列表:</strong>",
        "rcfilters-activefilters": "激活的过滤器",
        "rcfilters-advancedfilters": "高级过滤器",
+       "rcfilters-limit-title": "要显示的更改",
+       "rcfilters-limit-shownum": "显示最近$1次更改",
+       "rcfilters-days-title": "最近几天",
+       "rcfilters-hours-title": "最近几个小时",
+       "rcfilters-days-show-days": "$1{{PLURAL:$1|天}}",
+       "rcfilters-days-show-hours": "$1{{PLURAL:$1|小时}}",
        "rcfilters-quickfilters": "已保存过滤器",
        "rcfilters-quickfilters-placeholder-title": "尚未保存链接",
        "rcfilters-quickfilters-placeholder-description": "要保存您的过滤器设置并供日后再利用,点击下方激活的过滤器区域内的书签图标。",
        "rcfilters-invalid-filter": "无效过滤器",
        "rcfilters-empty-filter": "没有激活的过滤器。已显示所有贡献。",
        "rcfilters-filterlist-title": "过滤器",
-       "rcfilters-filterlist-whatsthis": "这是什么?",
+       "rcfilters-filterlist-whatsthis": "这些是怎样工作的?",
        "rcfilters-filterlist-feedbacklink": "在新(测试版)过滤器中提供反馈",
        "rcfilters-highlightbutton-title": "高亮结果",
        "rcfilters-highlightmenu-title": "选择颜色",
        "rcfilters-noresults-conflict": "找不到结果,因为搜索条件不一致",
        "rcfilters-state-message-subset": "该过滤器没有影响,因为其结果已包含在以下影响更广的{{PLURAL:$2|过滤器}}中(尝试高亮来区别它):$1",
        "rcfilters-state-message-fullcoverage": "组中选择的所有过滤器与选定过滤器相同,因此该过滤器没有影响。组包含:$1",
-       "rcfilters-filtergroup-registration": "用户注册",
-       "rcfilters-filter-registered-label": "已注册",
-       "rcfilters-filter-registered-description": "登录编辑者。",
-       "rcfilters-filter-unregistered-label": "未注册",
-       "rcfilters-filter-unregistered-description": "未登录的编辑者。",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "该过滤器与以下体验{{PLURAL:$2|过滤器}}相冲突,而它只{{PLURAL:$2|寻找}}注册用户:$1",
        "rcfilters-filtergroup-authorship": "贡献的作者",
        "rcfilters-filter-editsbyself-label": "您的更改",
        "rcfilters-filter-editsbyself-description": "您自己的贡献。",
        "rcfilters-filter-editsbyother-label": "他人更改",
        "rcfilters-filter-editsbyother-description": "除了您的更改以外的所有更改。",
-       "rcfilters-filtergroup-userExpLevel": "体验水平(仅限注册用户)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "体验过滤器只寻找注册用户,因此该过滤器与“未注册”过滤器相冲突。",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "“未注册”过滤器与一个或更多体验过滤器相冲突,而它只寻找注册用户。冲突过滤器已在上方激活过滤器区域中被标记。",
+       "rcfilters-filtergroup-userExpLevel": "用户注册及体验",
+       "rcfilters-filter-user-experience-level-registered-label": "已注册",
+       "rcfilters-filter-user-experience-level-registered-description": "登录编辑者。",
+       "rcfilters-filter-user-experience-level-unregistered-label": "未注册",
+       "rcfilters-filter-user-experience-level-unregistered-description": "未登录的编辑者。",
        "rcfilters-filter-user-experience-level-newcomer-label": "新手",
        "rcfilters-filter-user-experience-level-newcomer-description": "少于10次编辑和4天活跃。",
        "rcfilters-filter-user-experience-level-learner-label": "初学者",
        "rcfilters-hideminor-conflicts-typeofchange-global": "“小编辑”过滤器与一个或多个更改类型过滤器冲突,因为其中某种更改类型不可指定为“小编辑”。冲突过滤器已在上方活跃过滤器中被标记。",
        "rcfilters-hideminor-conflicts-typeofchange": "某种更改类型不可指定为“小编辑”,因此该过滤器与以下更改类型过滤器相冲突:$1",
        "rcfilters-typeofchange-conflicts-hideminor": "这种更改类型过滤器与“小编辑”过滤器相冲突。某种更改类型不可指定为“小编辑”。",
-       "rcfilters-filtergroup-lastRevision": "最新版本",
-       "rcfilters-filter-lastrevision-label": "最新版本",
-       "rcfilters-filter-lastrevision-description": "对页面的最近更改。",
-       "rcfilters-filter-previousrevision-label": "早期版本",
-       "rcfilters-filter-previousrevision-description": "除最近更改外,所有对某一页面的更改。",
+       "rcfilters-filtergroup-lastRevision": "最新修订版本",
+       "rcfilters-filter-lastrevision-label": "最新修订版本",
+       "rcfilters-filter-lastrevision-description": "å\8fªå\8c\85æ\8b¬å¯¹é¡µé\9d¢ç\9a\84æ\9c\80è¿\91æ\9b´æ\94¹ã\80\82",
+       "rcfilters-filter-previousrevision-label": "不是最新修订版本",
+       "rcfilters-filter-previousrevision-description": "所有不是“最新修订版本”的更改。",
        "rcfilters-filter-excluded": "已排除",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:不是</strong>$1",
+       "rcfilters-exclude-button-off": "排除选项",
+       "rcfilters-exclude-button-on": "排除选项",
        "rcfilters-view-tags": "标记的编辑",
        "rcfilters-view-namespaces-tooltip": "按名字空间过滤结果",
        "rcfilters-view-tags-tooltip": "按编辑标签过滤结果",
        "rcfilters-view-return-to-default-tooltip": "返回主过滤菜单",
-       "rcfilters-liveupdates-button": "å\9c¨çº¿更新",
+       "rcfilters-liveupdates-button": "å®\9eæ\97更新",
        "rcnotefrom": "下面{{PLURAL:$5|是}}<strong>$3 $4</strong>之后的更改(最多显示<strong>$1</strong>个)。",
        "rclistfromreset": "重置时间选择",
        "rclistfrom": "显示$3 $2之后的新更改",
        "delete-warning-toobig": "此页面有大量的编辑历史,超过$1个版本。删除它可能会破坏{{SITENAME}}的数据库操作;请谨慎考虑是否执行。",
        "deleteprotected": "您不能删除此页面因为它被保护。",
        "deleting-backlinks-warning": "<strong>警告:</strong>有[[Special:WhatLinksHere/{{FULLPAGENAME}}|其他页面]]链接至或包含您要删除的页面。",
+       "deleting-subpages-warning": "<strong>警告:</strong>您要删除的页面有[[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|$1个子页面|51=超过50个子页面}}]]。",
        "rollback": "回退编辑",
        "rollbacklink": "回退",
        "rollbacklinkcount": "回退$1次编辑",
        "confirm-purge-title": "刷新本页",
        "confirm_purge_button": "确定",
        "confirm-purge-top": "要清除此页面的缓存吗?",
-       "confirm-purge-bottom": "清除页面数据会清除缓存并强制显示最近的版本。",
+       "confirm-purge-bottom": "刷新页面会清除缓存并强制显示最近的版本。",
        "confirm-watch-button": "确定",
        "confirm-watch-top": "将此页添加到您的监视列表吗?",
        "confirm-unwatch-button": "确定",
        "confirm-rollback-button": "确定",
        "confirm-rollback-top": "回退此页面的编辑么?",
        "semicolon-separator": ";",
-       "comma-separator": "",
+       "comma-separator": "",
        "colon-separator": ":",
        "pipe-separator": "&#32;|&#32;",
        "word-separator": "",
        "fileduplicatesearch-noresults": "没有文件命名为\"$1\"发现。",
        "specialpages": "特殊页面",
        "specialpages-note-top": "说明",
-       "specialpages-note": "*普通特殊页面。\n*<span class=\"mw-specialpagerestricted\">受限特殊页面。</span>",
+       "specialpages-note-restricted": "* 普通特殊页面。\n* <span class=\"mw-specialpagerestricted\">受限特殊页面。</span>",
        "specialpages-group-maintenance": "维护报告",
        "specialpages-group-other": "其它特殊页面",
        "specialpages-group-login": "登录/创建账户",
index 9415770..933567b 100644 (file)
        "resetpass-submit-cancel": "取消",
        "resetpass-wrong-oldpass": "無效的臨時或現有密碼。\n您可能已經變更了密碼,或者已經請求一個新的臨時密碼。",
        "resetpass-recycled": "請重設您的密碼為一個與目前不同的密碼。",
-       "resetpass-temp-emailed": "您使用臨時電子郵件傳送的碼登入。\n要完成登入,您必須在這裡設定一個新密碼:",
+       "resetpass-temp-emailed": "您使用臨時電子郵件傳送的碼登入。\n要完成登入,您必須在這裡設定一個新密碼:",
        "resetpass-temp-password": "臨時密碼:",
        "resetpass-abort-generic": "擴充套件已中止了變更密碼的操作。",
        "resetpass-expired": "您的密碼已過期。請設定一個新的密碼以登入。",
        "rcfilters-noresults-conflict": "因搜尋條件衝突,查無結果",
        "rcfilters-state-message-subset": "此過濾條件沒有效果,因其結果包含了以下範圍更廣的{{PLURAL:$2|過濾條件|過濾條件}}其中之一 (嘗試以明顯標示來區別它):$1",
        "rcfilters-state-message-fullcoverage": "選擇在群組中的所有過濾條件與沒選擇時相同,代表此過濾條件沒有效果。群組包含了:$1",
-       "rcfilters-filtergroup-registration": "使用者註冊",
-       "rcfilters-filter-registered-label": "已註冊",
-       "rcfilters-filter-registered-description": "已登入的編輯者。",
-       "rcfilters-filter-unregistered-label": "未註冊",
-       "rcfilters-filter-unregistered-description": "尚未登入的編輯者。",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "此過濾條件與以下經歷{{PLURAL:$2|過濾條件|過濾條件}}衝突,因其只會{{PLURAL:$2|尋找|尋找}}已註冊的使用者:$1",
        "rcfilters-filtergroup-authorship": "貢獻的作者",
        "rcfilters-filter-editsbyself-label": "您的編輯",
        "rcfilters-filter-editsbyself-description": "您的貢獻",
        "rcfilters-filter-editsbyother-label": "其他人的更改",
        "rcfilters-filter-editsbyother-description": "除了您以外的所有更改",
        "rcfilters-filtergroup-userExpLevel": "經驗等級 (僅限已註冊的使用者)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "經歷過濾條件只會尋找已註冊的使用者,所以此過濾條件會與 \"未註冊\" 的過濾條件衝突。",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "\"未註冊\" 的過濾條件與一個或多個經歷過濾條件衝突,該過濾條件只會尋找已註冊使用者。衝突的過濾條件已在上方使用的過濾條件區域中標示。",
+       "rcfilters-filter-user-experience-level-registered-label": "已註冊",
+       "rcfilters-filter-user-experience-level-registered-description": "已登入的編輯者。",
+       "rcfilters-filter-user-experience-level-unregistered-label": "未註冊",
+       "rcfilters-filter-user-experience-level-unregistered-description": "尚未登入的編輯者。",
        "rcfilters-filter-user-experience-level-newcomer-label": "新手",
        "rcfilters-filter-user-experience-level-newcomer-description": "編輯數低於 10 次,活躍低於 4 天。",
        "rcfilters-filter-user-experience-level-learner-label": "初學者",
        "rcfilters-filter-previousrevision-label": "早期版本",
        "rcfilters-filter-previousrevision-description": "所有除了頁面近期變更的變更。",
        "rcfilters-filter-excluded": "已排除",
-       "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:不是</strong>$1",
        "rcfilters-view-tags": "標記的編輯",
+       "rcfilters-liveupdates-button": "實時更新",
        "rcnotefrom": "以下{{PLURAL:$5|為}}自 <strong>$3 $4</strong> 以來的變更 (最多顯示 <strong>$1</strong> 筆)。",
        "rclistfromreset": "重設日期選擇",
        "rclistfrom": "顯示自 $3 $2 以來的新變更",
        "confirmrecreate": "在您編輯的同時,使用者 [[User:$1|$1]] ([[User talk:$1|對話]]) 刪除了此頁面,原因為:\n: <em>$2</em>\n請確認您是否真的要重新建立此頁面。",
        "confirmrecreate-noreason": "在您編輯的同時,使用者 [[User:$1|$1]] ([[User talk:$1|對話]]) 刪除了此頁面,請確認您是否真的要重新建立此頁面。",
        "recreate": "重新建立",
-       "confirm-purge-title": "刪除此頁",
+       "confirm-purge-title": "清除此頁快取",
        "confirm_purge_button": "確定",
        "confirm-purge-top": "要清除此頁面的快取嗎?",
-       "confirm-purge-bottom": "清除頁面會清空頁面的快取記錄並強制顯示最近的頁面修訂。",
+       "confirm-purge-bottom": "刷新頁面會清空頁面的快取記錄並強制顯示最近的頁面修訂。",
        "confirm-watch-button": "確定",
        "confirm-watch-top": "新增此頁面至您的監視清單?",
        "confirm-unwatch-button": "確定",
        "confirm-rollback-button": "確定",
        "confirm-rollback-top": "還原編輯到此頁面?",
        "semicolon-separator": ";",
-       "comma-separator": "",
+       "comma-separator": "",
        "colon-separator": ":",
        "word-separator": "",
        "ellipsis": "…",
        "fileduplicatesearch-noresults": "查無名稱為 \"$1\" 的檔案。",
        "specialpages": "特殊頁面",
        "specialpages-note-top": "說明",
-       "specialpages-note": "* 一般特殊頁面。\n* <span class=\"mw-specialpagerestricted\">受限制的特殊頁面。</span>",
        "specialpages-group-maintenance": "維護報表",
        "specialpages-group-other": "其它特殊頁面",
        "specialpages-group-login": "登入 / 建立帳號",
        "tags-create-reason": "原因:",
        "tags-create-submit": "建立",
        "tags-create-no-name": "您必須指定一個標籤名稱。",
-       "tags-create-invalid-chars": "標籤名稱不可包含逗號 (<code>,</code>)、線 (<code>|</code>) 或斜線 (<code>/</code>)。",
+       "tags-create-invalid-chars": "標籤名稱不可包含逗號 (<code>,</code>)、線 (<code>|</code>) 或斜線 (<code>/</code>)。",
        "tags-create-invalid-title-chars": "標籤名稱不能含有無法使用者頁面標題的字元。",
        "tags-create-already-exists": "標籤 \"$1\" 已存在。",
        "tags-create-warnings-above": "嘗試建立標籤 \"$1\" 時發生下列{{PLURAL:$2|警告}}:",
        "mw-widgets-usersmultiselect-placeholder": "加入更多...",
        "date-range-from": "開始日期:",
        "date-range-to": "結束日期:",
-       "sessionmanager-tie": "ç\84¡æ³\95å\90\88ä½µå¤\9aå\80\8bè«\8bæ±\82èª\8d証類型:$1。",
+       "sessionmanager-tie": "ç\84¡æ³\95å\90\88ä½µå¤\9aå\80\8bè«\8bæ±\82èª\8dè­\89類型:$1。",
        "sessionprovider-generic": "$1 連線階段",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "以 cookie 為基礎的連線階段",
        "sessionprovider-nocookies": "Cookie 功能可能已被關閉,請確認您改開啟 Cookie 功能並重新啟動。",
        "log-action-filter-suppress-reblock": "由重新封鎖禁止顯示使用者",
        "log-action-filter-upload-upload": "新上傳",
        "log-action-filter-upload-overwrite": "重新上傳",
-       "authmanager-authn-not-in-progress": "èª\8d証尚未進行或連線階段資料已遺失,請重頭再開始。",
+       "authmanager-authn-not-in-progress": "èª\8dè­\89尚未進行或連線階段資料已遺失,請重頭再開始。",
        "authmanager-authn-no-primary": "提供的憑證無法用來認証。",
        "authmanager-authn-no-local-user": "提供的憑證沒有與任何在此 wiki 上的使用者相關聯。",
        "authmanager-authn-no-local-user-link": "提供的憑證有效但沒有與任何在此 wiki 上的使用者相關聯。請採用其他方式登入,或建立新使用者,您將會有選項可以連結您先前的憑證到新帳號。",
        "authmanager-autocreate-exception": "自動帳號建立因發生錯誤臨時關閉。",
        "authmanager-userdoesnotexist": "使用者帳號 \"$1\" 尚未註冊。",
        "authmanager-userlogin-remembermypassword-help": "是否要記住密碼來取得更長的連線階段時間。",
-       "authmanager-username-help": "èª\8d証用的使用者名稱。",
-       "authmanager-password-help": "èª\8d証用的密碼。",
-       "authmanager-domain-help": "å¤\96é\83¨èª\8d証用的網域。",
+       "authmanager-username-help": "èª\8dè­\89用的使用者名稱。",
+       "authmanager-password-help": "èª\8dè­\89用的密碼。",
+       "authmanager-domain-help": "å¤\96é\83¨èª\8dè­\89用的網域。",
        "authmanager-retype-help": "再輸入一次密碼確認。",
        "authmanager-email-label": "電子郵件",
        "authmanager-email-help": "電子郵件地址",
        "authmanager-realname-label": "真實姓名",
        "authmanager-realname-help": "使用者的真實姓名",
-       "authmanager-provider-password": "Password-based èª\8d証",
-       "authmanager-provider-password-domain": "Password- å\8f\8a domain-based èª\8d証",
+       "authmanager-provider-password": "Password-based èª\8dè­\89",
+       "authmanager-provider-password-domain": "Password- å\8f\8a domain-based èª\8dè­\89",
        "authmanager-provider-temporarypassword": "臨時密碼",
        "authprovider-confirmlink-message": "根據您最近的登入嘗試次數,以下帳號可以被連結至您的 wiki 帳號。連結帳號可以讓您使用這些帳號登入。請選擇一個您想連結的帳號。",
        "authprovider-confirmlink-request-label": "應連結的帳號",
        "authprovider-confirmlink-ok-help": "顯示連結失敗訊息後繼續。",
        "authprovider-resetpass-skip-label": "略過",
        "authprovider-resetpass-skip-help": "略過重設密碼。",
-       "authform-nosession-login": "å·²æ\88\90å\8a\9fèª\8d証,但您的瀏覽器無法 \"記住\" 登入資訊。\n\n$1",
+       "authform-nosession-login": "å·²æ\88\90å\8a\9fèª\8dè­\89,但您的瀏覽器無法 \"記住\" 登入資訊。\n\n$1",
        "authform-nosession-signup": "已建立帳號,但您的瀏覽器無法 \"記住\" 登入資訊。\n\n$1",
        "authform-newtoken": "缺少密鑰。$1",
        "authform-notoken": "缺少密鑰",
        "authpage-cannot-link-continue": "無法繼續帳號連結,您的連線階段似乎已逾時。",
        "cannotauth-not-allowed-title": "權限不足",
        "cannotauth-not-allowed": "您不被允許使用此頁面",
-       "changecredentials": "è®\8aæ\9b´æ\86\91証",
+       "changecredentials": "è®\8aæ\9b´æ\86\91è­\89",
        "changecredentials-submit": "變更認證",
        "changecredentials-invalidsubpage": "$1 不是有效的憑証類型。",
        "changecredentials-success": "已變更您的認證。",
-       "removecredentials": "移é\99¤æ\86\91証",
+       "removecredentials": "移é\99¤æ\86\91è­\89",
        "removecredentials-submit": "移除認證",
        "removecredentials-invalidsubpage": "$1 不是有效的憑証類型。",
        "removecredentials-success": "已移除您的憑證。",
-       "credentialsform-provider": "æ\86\91証類型:",
+       "credentialsform-provider": "æ\86\91è­\89類型:",
        "credentialsform-account": "帳號名稱:",
        "cannotlink-no-provider-title": "沒有可連結的帳號",
        "cannotlink-no-provider": "沒有可連結的帳號。",
        "linkaccounts-submit": "連結帳號",
        "unlinkaccounts": "取消連結帳號",
        "unlinkaccounts-success": "已取消連結帳號。",
-       "authenticationdatachange-ignored": "èª\8d証資料變更未被處理,可能未設定提供者?",
+       "authenticationdatachange-ignored": "èª\8dè­\89資料變更未被處理,可能未設定提供者?",
        "userjsispublic": "請注意:JavaScript 子頁面可被其他使用者檢視,不應包含機密資料。",
        "usercssispublic": "請注意:CSS 子頁面可被其他使用者檢視,不應包含機密資料。",
        "restrictionsfield-badip": "無效的 IP 位址或範圍:$1",
diff --git a/languages/messages/MessagesSkr.php b/languages/messages/MessagesSkr.php
new file mode 100644 (file)
index 0000000..2072b8d
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+/** Saraiki (multiple scripts)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'skr-arab';
diff --git a/languages/messages/MessagesSkr_arab.php b/languages/messages/MessagesSkr_arab.php
new file mode 100644 (file)
index 0000000..901e2aa
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/** Saraiki (Arabic script) (سرائیکی)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'ur, pnb';
+
+$rtl = true;
index 478a0c4..04565f2 100644 (file)
@@ -344,7 +344,7 @@ abstract class Maintenance {
         * @return mixed
         */
        protected function getStdin( $len = null ) {
-               if ( $len == Maintenance::STDIN_ALL ) {
+               if ( $len == self::STDIN_ALL ) {
                        return file_get_contents( 'php://stdin' );
                }
                $f = fopen( 'php://stdin', 'rt' );
@@ -457,7 +457,7 @@ abstract class Maintenance {
         * @return int
         */
        public function getDbType() {
-               return Maintenance::DB_STD;
+               return self::DB_STD;
        }
 
        /**
index 2262338..b21bfbb 100644 (file)
@@ -39,7 +39,7 @@ class AddRFCAndPMIDInterwiki extends LoggedUpdateMaintenance {
        }
 
        protected function updateSkippedMessage() {
-               return 'RFC and PMID already added to interwiki database table';
+               return 'RFC and PMID already added to interwiki database table.';
        }
 
        protected function doDBUpdates() {
index 69f4f89..ba8662a 100644 (file)
@@ -35,6 +35,7 @@ class DeleteDefaultMessages extends Maintenance {
                parent::__construct();
                $this->addDescription( 'Deletes all pages in the MediaWiki namespace' .
                        ' which were last edited by "MediaWiki default"' );
+               $this->addOption( 'dry-run', 'Perform a dry run, delete nothing' );
        }
 
        public function execute() {
@@ -52,14 +53,23 @@ class DeleteDefaultMessages extends Maintenance {
                );
 
                if ( $dbr->numRows( $res ) == 0 ) {
-                       # No more messages left
+                       // No more messages left
                        $this->output( "done.\n" );
+                       return;
+               }
 
+               $dryrun = $this->hasOption( 'dry-run' );
+               if ( $dryrun ) {
+                       foreach ( $res as $row ) {
+                               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $this->output( "\n* [[$title]]" );
+                       }
+                       $this->output( "\n\nRun again without --dry-run to delete these pages.\n" );
                        return;
                }
 
-               # Deletions will be made by $user temporarly added to the bot group
-               # in order to hide it in RecentChanges.
+               // Deletions will be made by $user temporarly added to the bot group
+               // in order to hide it in RecentChanges.
                $user = User::newFromName( 'MediaWiki default' );
                if ( !$user ) {
                        $this->error( "Invalid username", true );
@@ -67,7 +77,7 @@ class DeleteDefaultMessages extends Maintenance {
                $user->addGroup( 'bot' );
                $wgUser = $user;
 
-               # Handle deletion
+               // Handle deletion
                $this->output( "\n...deleting old default messages (this may take a long time!)...", 'msg' );
                $dbw = $this->getDB( DB_MASTER );
 
index 79f75ef..8c9faca 100644 (file)
@@ -42,8 +42,8 @@ class FixDoubleRedirects extends Maintenance {
        }
 
        public function execute() {
-               $async = $this->getOption( 'async', false );
-               $dryrun = $this->getOption( 'dry-run', false );
+               $async = $this->hasOption( 'async' );
+               $dryrun = $this->hasOption( 'dry-run' );
 
                if ( $this->hasOption( 'title' ) ) {
                        $title = Title::newFromText( $this->getOption( 'title' ) );
index fb00bed..d273a6a 100644 (file)
@@ -140,9 +140,6 @@ class GenerateSitemap extends Maintenance {
         */
        private $identifier;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Creates a sitemap for the site' );
@@ -195,7 +192,7 @@ class GenerateSitemap extends Maintenance {
                }
                $this->identifier = $this->getOption( 'identifier', wfWikiID() );
                $this->compress = $this->getOption( 'compress', 'yes' ) !== 'no';
-               $this->skipRedirects = $this->getOption( 'skip-redirects', false ) !== false;
+               $this->skipRedirects = $this->hasOption( 'skip-redirects' );
                $this->dbr = $this->getDB( DB_REPLICA );
                $this->generateNamespaces();
                $this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
index cf0acde..9e9fd3e 100644 (file)
@@ -40,7 +40,6 @@ class CheckLanguageCLI {
        private $includeExif = false;
 
        /**
-        * Constructor.
         * @param array $options Options for script.
         */
        public function __construct( array $options ) {
@@ -557,7 +556,6 @@ class CheckExtensionsCLI extends CheckLanguageCLI {
        private $extensions;
 
        /**
-        * Constructor.
         * @param array $options Options for script.
         * @param string $extension The extension name (or names).
         */
index 3c29689..d578a49 100644 (file)
@@ -64,7 +64,7 @@ class MoveBatch extends Maintenance {
                $user = $this->getOption( 'u', false );
                $reason = $this->getOption( 'r', '' );
                $interval = $this->getOption( 'i', 0 );
-               $noredirects = $this->getOption( 'noredirects', false );
+               $noredirects = $this->hasOption( 'noredirects' );
                if ( $this->hasArg() ) {
                        $file = fopen( $this->getArg(), 'r' );
                } else {
index 0326e02..e735aed 100644 (file)
@@ -52,8 +52,8 @@ class NukeNS extends Maintenance {
 
        public function execute() {
                $ns = $this->getOption( 'ns', NS_MEDIAWIKI );
-               $delete = $this->getOption( 'delete', false );
-               $all = $this->getOption( 'all', false );
+               $delete = $this->hasOption( 'delete' );
+               $all = $this->hasOption( 'all' );
                $dbw = $this->getDB( DB_MASTER );
                $this->beginTransaction( $dbw, __METHOD__ );
 
index ff821cc..e27324a 100644 (file)
@@ -40,7 +40,7 @@ class NukePage extends Maintenance {
 
        public function execute() {
                $name = $this->getArg();
-               $delete = $this->getOption( 'delete', false );
+               $delete = $this->hasOption( 'delete' );
 
                $dbw = $this->getDB( DB_MASTER );
                $this->beginTransaction( $dbw, __METHOD__ );
index 87a4699..5dccdd6 100644 (file)
@@ -71,7 +71,7 @@ TEXT
        public function execute() {
                $begin = $this->getOption( 'begin', '' );
                $throttle = $this->getOption( 'throttle', 0 );
-               $force = $this->getOption( 'force', false );
+               $force = $this->hasOption( 'force' );
 
                $dbw = $this->getDB( DB_MASTER );
 
index 8f7a918..1b05e1e 100644 (file)
@@ -56,7 +56,7 @@ TEXT
        }
 
        public function execute() {
-               $force = $this->getOption( 'force', false );
+               $force = $this->hasOption( 'force' );
                $this->source = $this->getOption( 'source', 'https://en.wikipedia.org/w/api.php' );
 
                $data = $this->fetchLinks();
diff --git a/maintenance/populatePPSortKey.php b/maintenance/populatePPSortKey.php
new file mode 100644 (file)
index 0000000..7e3c2c3
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Populate the pp_sortkey fields in the page_props table
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Usage:
+ *  populatePPSortKey.php
+ */
+class PopulatePPSortKey extends LoggedUpdateMaintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Populate the pp_sortkey field' );
+               $this->setBatchSize( 100 );
+       }
+
+       protected function doDBUpdates() {
+               $dbw = $this->getDB( DB_MASTER );
+
+               $lastProp = null;
+               $lastPageValue = 0;
+               $editedRowCount = 0;
+
+               $this->output( "Populating page_props.pp_sortkey...\n" );
+               while ( true ) {
+                       $conditions = [ 'pp_sortkey IS NULL' ];
+                       if ( $lastPageValue !== 0 ) {
+                               $conditions[] = 'pp_page > ' . $dbw->addQuotes( $lastPageValue ) . ' OR ' .
+                                       '( pp_page = ' . $dbw->addQuotes( $lastPageValue ) .
+                                       ' AND pp_propname > ' . $dbw->addQuotes( $lastProp ) . ' )';
+                       }
+
+                       $res = $dbw->select(
+                               'page_props',
+                               [ 'pp_propname', 'pp_page', 'pp_sortkey', 'pp_value' ],
+                               $conditions,
+                               __METHOD__,
+                               [
+                                       'ORDER BY' => 'pp_page, pp_propname',
+                                       'LIMIT' => $this->mBatchSize
+                               ]
+                       );
+
+                       if ( $res->numRows() === 0 ) {
+                               break;
+                       }
+
+                       $this->beginTransaction( $dbw, __METHOD__ );
+
+                       foreach ( $res as $row ) {
+                               if ( !is_numeric( $row->pp_value ) ) {
+                                       continue;
+                               }
+                               $dbw->update(
+                                       'page_props',
+                                       [ 'pp_sortkey' => $row->pp_value ],
+                                       [
+                                               'pp_page' => $row->pp_page,
+                                               'pp_propname' => $row->pp_propname
+                                       ],
+                                       __METHOD__
+                               );
+                               $editedRowCount++;
+                       }
+
+                       $this->output( "Updated " . $editedRowCount . " rows\n" );
+                       $this->commitTransaction( $dbw, __METHOD__ );
+
+                       // We need to get the last element's page ID
+                       $lastPageValue = $row->pp_page;
+                       // And the propname...
+                       $lastProp = $row->pp_propname;
+               }
+
+               $this->output( "Populating page_props.pp_sortkey complete.\n" );
+       }
+
+       protected function getUpdateKey() {
+               return 'populate pp_sortkey';
+       }
+}
+
+$maintClass = 'PopulatePPSortKey';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 04ac967..fe3944c 100644 (file)
@@ -78,7 +78,7 @@ class RebuildFileCache extends Maintenance {
                $this->output( "Building content page file cache from page {$start}!\n" );
 
                $dbr = $this->getDB( DB_REPLICA );
-               $overwrite = $this->getOption( 'overwrite', false );
+               $overwrite = $this->hasOption( 'overwrite' );
                $start = ( $start > 0 )
                        ? $start
                        : $dbr->selectField( 'page', 'MIN(page_id)', false, __METHOD__ );
diff --git a/maintenance/recountCategories.php b/maintenance/recountCategories.php
new file mode 100644 (file)
index 0000000..a4bfa98
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Refreshes category counts.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Maintenance script that refreshes category membership counts in the category
+ * table.
+ *
+ * (The populateCategory.php script will also recalculate counts, but
+ * recountCategories only updates rows that need to be updated, making it more
+ * efficient.)
+ *
+ * @ingroup Maintenance
+ */
+class RecountCategories extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( <<<'TEXT'
+This script refreshes the category membership counts stored in the category
+table. As time passes, these counts often drift from the actual number of
+category members. The script identifies rows where the value in the category
+table does not match the number of categorylinks rows for that category, and
+updates the category table accordingly.
+
+To fully refresh the data in the category table, you need to run this script
+three times: once in each mode. Alternatively, just one mode can be run if
+required.
+TEXT
+               );
+               $this->addOption(
+                       'mode',
+                       '(REQUIRED) Which category count column to recompute: "pages", "subcats" or "files".',
+                       true,
+                       true
+               );
+               $this->addOption(
+                       'begin',
+                       'Only recount categories with cat_id greater than the given value',
+                       false,
+                       true
+               );
+               $this->addOption(
+                       'throttle',
+                       'Wait this many milliseconds after each batch. Default: 0',
+                       false,
+                       true
+               );
+
+               $this->setBatchSize( 500 );
+       }
+
+       public function execute() {
+               $this->mode = $this->getOption( 'mode' );
+               if ( !in_array( $this->mode, [ 'pages', 'subcats', 'files' ] ) ) {
+                       $this->error( 'Please specify a valid mode: one of "pages", "subcats" or "files".', 1 );
+               }
+
+               $this->minimumId = intval( $this->getOption( 'begin', 0 ) );
+
+               // do the work, batch by batch
+               $affectedRows = 0;
+               while ( ( $result = $this->doWork() ) !== false ) {
+                       $affectedRows += $result;
+                       usleep( $this->getOption( 'throttle', 0 ) * 1000 );
+               }
+
+               $this->output( "Done! Updated the {$this->mode} counts of $affectedRows categories.\n" .
+                       "Now run the script using the other --mode options if you haven't already.\n" );
+               if ( $this->mode === 'pages' ) {
+                       $this->output(
+                               "Also run 'php cleanupEmptyCategories.php --mode remove' to remove empty,\n" .
+                               "nonexistent categories from the category table.\n\n" );
+               }
+       }
+
+       protected function doWork() {
+               $this->output( "Finding up to {$this->mBatchSize} drifted rows " .
+                       "starting at cat_id {$this->minimumId}...\n" );
+
+               $countingConds = [ 'cl_to = cat_title' ];
+               if ( $this->mode === 'subcats' ) {
+                       $countingConds['cl_type'] = 'subcat';
+               } elseif ( $this->mode === 'files' ) {
+                       $countingConds['cl_type'] = 'file';
+               }
+
+               $dbr = $this->getDB( DB_REPLICA, 'vslow' );
+               $countingSubquery = $dbr->selectSQLText( 'categorylinks',
+                       'COUNT(*)',
+                       $countingConds,
+                       __METHOD__ );
+
+               // First, let's find out which categories have drifted and need to be updated.
+               // The query counts the categorylinks for each category on the replica DB,
+               // but this data can't be used for updating the master, so we don't include it
+               // in the results.
+               $idsToUpdate = $dbr->selectFieldValues( 'category',
+                       'cat_id',
+                       [
+                               'cat_id > ' . $this->minimumId,
+                               "cat_{$this->mode} != ($countingSubquery)"
+                       ],
+                       __METHOD__,
+                       [ 'LIMIT' => $this->mBatchSize ]
+               );
+               if ( !$idsToUpdate ) {
+                       return false;
+               }
+               $this->output( "Updating cat_{$this->mode} field on " .
+                       count( $idsToUpdate ) . " rows...\n" );
+
+               // In the next batch, start where this query left off. The rows selected
+               // in this iteration shouldn't be selected again after being updated, but
+               // we still keep track of where we are up to, as extra protection against
+               // infinite loops.
+               $this->minimumId = end( $idsToUpdate );
+
+               // Now, on master, find the correct counts for these categories.
+               $dbw = $this->getDB( DB_MASTER );
+               $res = $dbw->select( 'category',
+                       [ 'cat_id', 'count' => "($countingSubquery)" ],
+                       [ 'cat_id' => $idsToUpdate ],
+                       __METHOD__ );
+
+               // Update the category counts on the rows we just identified.
+               // This logic is equivalent to Category::refreshCounts, except here, we
+               // don't remove rows when cat_pages is zero and the category description page
+               // doesn't exist - instead we print a suggestion to run
+               // cleanupEmptyCategories.php.
+               $affectedRows = 0;
+               foreach ( $res as $row ) {
+                       $dbw->update( 'category',
+                               [ "cat_{$this->mode}" => $row->count ],
+                               [
+                                       'cat_id' => $row->cat_id,
+                                       "cat_{$this->mode} != {$row->count}",
+                               ],
+                               __METHOD__ );
+                       $affectedRows += $dbw->affectedRows();
+               }
+
+               MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
+
+               return $affectedRows;
+       }
+}
+
+$maintClass = 'RecountCategories';
+require_once RUN_MAINTENANCE_IF_MAIN;
index facc506..3e1654a 100644 (file)
@@ -76,9 +76,9 @@ class RefreshLinks extends Maintenance {
                } elseif ( ( $category = $this->getOption( 'tracking-category', false ) ) !== false ) {
                        $this->refreshTrackingCategory( $category );
                } elseif ( !$this->hasOption( 'dfn-only' ) ) {
-                       $new = $this->getOption( 'new-only', false );
-                       $redir = $this->getOption( 'redirects-only', false );
-                       $oldRedir = $this->getOption( 'old-redirects-only', false );
+                       $new = $this->hasOption( 'new-only' );
+                       $redir = $this->hasOption( 'redirects-only' );
+                       $oldRedir = $this->hasOption( 'old-redirects-only' );
                        $this->doRefreshLinks( $start, $new, $end, $redir, $oldRedir );
                        $this->deleteLinksFromNonexistent( null, null, $this->mBatchSize, $dfnChunkSize );
                } else {
index 117e9cc..f14856a 100644 (file)
@@ -48,7 +48,7 @@ class Sqlite {
         * @return bool True if no error or error string in case of errors
         */
        public static function checkSqlSyntax( $files ) {
-               if ( !Sqlite::isPresent() ) {
+               if ( !self::isPresent() ) {
                        throw new MWException( "Can't check SQL syntax: SQLite not found" );
                }
                if ( !is_array( $files ) ) {
index 02b4405..2f3fc36 100644 (file)
@@ -36,7 +36,7 @@ class UpdateRestrictions extends Maintenance {
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Updates page_restrictions table from old page_restriction column' );
-               $this->setBatchSize( 100 );
+               $this->setBatchSize( 1000 );
        }
 
        public function execute() {
index 5d773d1..01cf3c3 100644 (file)
@@ -47,7 +47,7 @@ class UserOptions {
         */
        function __construct( $opts, $args ) {
                if ( !$this->checkOpts( $opts, $args ) ) {
-                       UserOptions::showUsageAndExit();
+                       self::showUsageAndExit();
                } else {
                        $this->mReady = $this->initializeOpts( $opts, $args );
                }
index 8b2d6e5..c745ce4 100644 (file)
@@ -4,7 +4,7 @@
 
                function syncText() {
                        var value = $( this ).val()
-                               .replace( /[\[\]\{\}|#<>%+? ]/g, '_' )
+                               .replace( /[\[\]{}|#<>%+? ]/g, '_' ) // eslint-disable-line no-useless-escape
                                .replace( /&/, '&amp;' )
                                .replace( /__+/g, '_' )
                                .replace( /^_+/, '' )
                }
 
                // Set up the help system
-               $( '.config-help-field-data' )
-                       .hide()
-                       .closest( '.config-help-field-container' )
-                               .find( '.config-help-field-hint' )
-                                       .show()
-                                       .click( function () {
-                                               $( this )
-                                                       .closest( '.config-help-field-container' )
-                                                               .find( '.config-help-field-data' )
-                                                                       .slideToggle( 'fast' );
-                                       } );
+               $( '.config-help-field-data' ).hide()
+                       .closest( '.config-help-field-container' ).find( '.config-help-field-hint' )
+                       .show()
+                       .click( function () {
+                               $( this ).closest( '.config-help-field-container' ).find( '.config-help-field-data' )
+                                       .slideToggle( 'fast' );
+                       } );
 
                // Show/hide code for DB-specific options
                // FIXME: Do we want slow, fast, or even non-animated (instantaneous) showing/hiding here?
index fe3c910..8507238 100644 (file)
     "grunt-banana-checker": "0.6.0",
     "grunt-contrib-copy": "1.0.0",
     "grunt-contrib-watch": "1.0.0",
-    "grunt-eslint": "19.0.0",
+    "grunt-eslint": "20.0.0",
     "grunt-jsonlint": "1.1.0",
     "grunt-karma": "2.0.0",
-    "grunt-stylelint": "0.7.0",
+    "grunt-stylelint": "0.8.0",
     "grunt-webdriver": "2.0.3",
     "karma": "1.5.0",
     "karma-chrome-launcher": "2.0.0",
@@ -26,6 +26,7 @@
     "karma-qunit": "1.0.0",
     "nodemw": "0.10.1",
     "qunitjs": "1.23.1",
+    "stylelint": "7.8.0",
     "stylelint-config-wikimedia": "0.4.1",
     "wdio-junit-reporter": "0.2.0",
     "wdio-mocha-framework": "0.5.8",
index 92a218a..d2e4fcc 100644 (file)
--- a/phpcs.xml
+++ b/phpcs.xml
        <arg name="bootstrap" value="vendor/mediawiki/mediawiki-codesniffer/utils/bootstrap-ci.php"/>
        <arg name="encoding" value="UTF-8"/>
        <arg name="extensions" value="php,php5,inc,sample"/>
-       <exclude-pattern>node_modules/</exclude-pattern>
-       <exclude-pattern>vendor/</exclude-pattern>
        <exclude-pattern type="relative">^extensions/</exclude-pattern>
        <exclude-pattern type="relative">^skins/</exclude-pattern>
-       <exclude-pattern>.git</exclude-pattern>
-       <exclude-pattern>AdminSettings.php</exclude-pattern>
-       <exclude-pattern>LocalSettings.php</exclude-pattern>
-       <exclude-pattern>StartProfiler.php</exclude-pattern>
+       <exclude-pattern>AdminSettings\.php</exclude-pattern>
+       <exclude-pattern>LocalSettings\.php</exclude-pattern>
+       <exclude-pattern>StartProfiler\.php</exclude-pattern>
 </ruleset>
index a8cf91d..f725efe 100644 (file)
@@ -1060,6 +1060,9 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.hlist' => [
+               'styles' => [
+                       'resources/src/mediawiki/mediawiki.hlist-allskins.less',
+               ],
                'skinStyles' => [
                        'default' => 'resources/src/mediawiki/mediawiki.hlist.css',
                ],
@@ -1781,6 +1784,11 @@ return [
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ViewSwitchWidget.js',
+                       'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.js',
+                       'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitPopupWidget.js',
+                       'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js',
+                       'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js',
+                       'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DatePopupWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SavedLinksListWidget.js',
@@ -1806,6 +1814,8 @@ return [
                        'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterMenuHeaderWidget.less',
                        'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.MenuSelectWidget.less',
                        'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ViewSwitchWidget.less',
+                       'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ValuePickerWidget.less',
+                       'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.DatePopupWidget.less',
                        'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less',
                        'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less',
                        'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.HighlightColorPickerWidget.less',
@@ -1825,6 +1835,12 @@ return [
                'messages' => [
                        'rcfilters-activefilters',
                        'rcfilters-advancedfilters',
+                       'rcfilters-limit-title',
+                       'rcfilters-limit-shownum',
+                       'rcfilters-days-title',
+                       'rcfilters-hours-title',
+                       'rcfilters-days-show-days',
+                       'rcfilters-days-show-hours',
                        'rcfilters-quickfilters',
                        'rcfilters-quickfilters-placeholder-title',
                        'rcfilters-quickfilters-placeholder-description',
@@ -1857,6 +1873,8 @@ return [
                        'rcfilters-tag-prefix-namespace',
                        'rcfilters-tag-prefix-namespace-inverted',
                        'rcfilters-tag-prefix-tags',
+                       'rcfilters-exclude-button-off',
+                       'rcfilters-exclude-button-on',
                        'rcfilters-view-tags',
                        'rcfilters-view-namespaces-tooltip',
                        'rcfilters-view-tags-tooltip',
@@ -2544,9 +2562,9 @@ return [
                ],
                'dependencies' => [
                        'mediawiki.searchSuggest',
-                       // FIXME: Needs TitleWidget only
+                       'oojs-ui.styles.icons-interactions',
+                       // FIXME: Needs TitleInputWidget only
                        'mediawiki.widgets',
-                       'oojs-ui-widgets',
                ],
        ],
        'mediawiki.widgets.SearchInputWidget.styles' => [
index ae68fc4..4749222 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * QUnit 1.23.1
+ * QUnit 2.4.0
  * https://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2017-07-08T15:20Z
  */
 
 /** Font Family and Sizes */
@@ -27,7 +27,7 @@
 }
 
 
-/** Header */
+/** Header (excluding toolbar) */
 
 #qunit-header {
        padding: 0.5em 0 0.5em 1em;
        color: #FFF;
 }
 
-#qunit-testrunner-toolbar label {
-       display: inline-block;
-       padding: 0 0.5em 0 0.1em;
-}
-
 #qunit-banner {
        height: 5px;
 }
 
-#qunit-testrunner-toolbar {
-       padding: 0.5em 1em 0.5em 1em;
-       color: #5E740B;
-       background-color: #EEE;
-       overflow: hidden;
-}
-
 #qunit-filteredTest {
        padding: 0.5em 1em 0.5em 1em;
-       background-color: #F4FF77;
        color: #366097;
+       background-color: #F4FF77;
 }
 
 #qunit-userAgent {
        padding: 0.5em 1em 0.5em 1em;
-       background-color: #2B81AF;
        color: #FFF;
+       background-color: #2B81AF;
        text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
 }
 
-#qunit-modulefilter-container {
-       float: right;
-       padding: 0.2em;
+
+/** Toolbar */
+
+#qunit-testrunner-toolbar {
+       padding: 0.5em 1em 0.5em 1em;
+       color: #5E740B;
+       background-color: #EEE;
+}
+
+#qunit-testrunner-toolbar .clearfix {
+       height: 0;
+       clear: both;
 }
 
-.qunit-url-config {
+#qunit-testrunner-toolbar label {
        display: inline-block;
-       padding: 0.1em;
 }
 
-.qunit-filter {
-       display: block;
+#qunit-testrunner-toolbar input[type=checkbox],
+#qunit-testrunner-toolbar input[type=radio] {
+       margin: 3px;
+       vertical-align: -2px;
+}
+
+#qunit-testrunner-toolbar input[type=text] {
+       box-sizing: border-box;
+       height: 1.6em;
+}
+
+.qunit-url-config,
+.qunit-filter,
+#qunit-modulefilter {
+       display: inline-block;
+       line-height: 2.1em;
+}
+
+.qunit-filter,
+#qunit-modulefilter {
        float: right;
+       position: relative;
        margin-left: 1em;
 }
 
+.qunit-url-config label {
+       margin-right: 0.5em;
+}
+
+#qunit-modulefilter-search {
+       box-sizing: border-box;
+       width: 400px;
+}
+
+#qunit-modulefilter-search-container:after {
+       position: absolute;
+       right: 0.3em;
+       content: "\25bc";
+       color: black;
+}
+
+#qunit-modulefilter-dropdown {
+       /* align with #qunit-modulefilter-search */
+       box-sizing: border-box;
+       width: 400px;
+       position: absolute;
+       right: 0;
+       top: 50%;
+       margin-top: 0.8em;
+
+       border: 1px solid #D3D3D3;
+       border-top: none;
+       border-radius: 0 0 .25em .25em;
+       color: #000;
+       background-color: #F5F5F5;
+       z-index: 99;
+}
+
+#qunit-modulefilter-dropdown a {
+       color: inherit;
+       text-decoration: none;
+}
+
+#qunit-modulefilter-dropdown .clickable.checked {
+       font-weight: bold;
+       color: #000;
+       background-color: #D2E0E6;
+}
+
+#qunit-modulefilter-dropdown .clickable:hover {
+       color: #FFF;
+       background-color: #0D3349;
+}
+
+#qunit-modulefilter-actions {
+       display: block;
+       overflow: auto;
+
+       /* align with #qunit-modulefilter-dropdown-list */
+       font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
+       box-sizing: border-box;
+       max-height: 2.8em;
+       display: block;
+       padding: 0.4em;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
+       float: right;
+       font: inherit;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
+       /* insert padding to align with checkbox margins */
+       padding-left: 3px;
+}
+
+#qunit-modulefilter-dropdown-list {
+       max-height: 200px;
+       overflow-y: auto;
+       margin: 0;
+       border-top: 2px groove threedhighlight;
+       padding: 0.4em 0 0;
+       font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown-list li {
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+
+#qunit-modulefilter-dropdown-list .clickable {
+       display: block;
+       padding-left: 0.15em;
+}
+
+
 /** Tests: Pass/Fail */
 
 #qunit-tests {
 #qunit-tests li.running,
 #qunit-tests li.pass,
 #qunit-tests li.fail,
-#qunit-tests li.skipped {
+#qunit-tests li.skipped,
+#qunit-tests li.aborted {
        display: list-item;
 }
 
 }
 
 #qunit-tests.hidepass li.running,
-#qunit-tests.hidepass li.pass {
+#qunit-tests.hidepass li.pass:not(.todo) {
        visibility: hidden;
        position: absolute;
        width:   0;
 }
 
 #qunit-tests del {
-       background-color: #E0F2BE;
        color: #374E0C;
+       background-color: #E0F2BE;
        text-decoration: none;
 }
 
 #qunit-tests ins {
-       background-color: #FFCACA;
        color: #500;
+       background-color: #FFCACA;
        text-decoration: none;
 }
 
 
 #qunit-banner.qunit-fail                    { background-color: #EE5757; }
 
+
+/*** Aborted tests */
+#qunit-tests .aborted { color: #000; background-color: orange; }
 /*** Skipped tests */
 
 #qunit-tests .skipped {
        background-color: #EBECE9;
 }
 
+#qunit-tests .qunit-todo-label,
 #qunit-tests .qunit-skipped-label {
        background-color: #F4FF77;
        display: inline-block;
        margin: -0.4em 0.4em -0.4em 0;
 }
 
+#qunit-tests .qunit-todo-label {
+       background-color: #EEE;
+}
+
 /** Result */
 
 #qunit-testresult {
-       padding: 0.5em 1em 0.5em 1em;
-
        color: #2B81AF;
        background-color: #D2E0E6;
 
        border-bottom: 1px solid #FFF;
 }
+#qunit-testresult .clearfix {
+       height: 0;
+       clear: both;
+}
 #qunit-testresult .module-name {
        font-weight: 700;
 }
+#qunit-testresult-display {
+       padding: 0.5em 1em 0.5em 1em;
+       width: 85%;
+       float:left;
+}
+#qunit-testresult-controls {
+       padding: 0.5em 1em 0.5em 1em;
+  width: 10%;
+       float:left;
+}
 
 /** Fixture */
 
index 5df0822..bb8f31d 100644 (file)
 /*!
- * QUnit 1.23.1
+ * QUnit 2.4.0
  * https://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2017-07-08T15:20Z
  */
+(function (global$1) {
+  'use strict';
 
-( function( global ) {
-
-var QUnit = {};
-
-var Date = global.Date;
-var now = Date.now || function() {
-       return new Date().getTime();
-};
-
-var setTimeout = global.setTimeout;
-var clearTimeout = global.clearTimeout;
-
-// Store a local window from the global to allow direct references.
-var window = global.window;
-
-var defined = {
-       document: window && window.document !== undefined,
-       setTimeout: setTimeout !== undefined,
-       sessionStorage: ( function() {
-               var x = "qunit-test-string";
-               try {
-                       sessionStorage.setItem( x, x );
-                       sessionStorage.removeItem( x );
-                       return true;
-               } catch ( e ) {
-                       return false;
-               }
-       }() )
-};
-
-var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" );
-var globalStartCalled = false;
-var runStarted = false;
-
-var toString = Object.prototype.toString,
-       hasOwn = Object.prototype.hasOwnProperty;
-
-// Returns a new Array with the elements that are in a but not in b
-function diff( a, b ) {
-       var i, j,
-               result = a.slice();
-
-       for ( i = 0; i < result.length; i++ ) {
-               for ( j = 0; j < b.length; j++ ) {
-                       if ( result[ i ] === b[ j ] ) {
-                               result.splice( i, 1 );
-                               i--;
-                               break;
-                       }
-               }
-       }
-       return result;
-}
-
-// From jquery.js
-function inArray( elem, array ) {
-       if ( array.indexOf ) {
-               return array.indexOf( elem );
-       }
-
-       for ( var i = 0, length = array.length; i < length; i++ ) {
-               if ( array[ i ] === elem ) {
-                       return i;
-               }
-       }
-
-       return -1;
-}
-
-/**
- * Makes a clone of an object using only Array or Object as base,
- * and copies over the own enumerable properties.
- *
- * @param {Object} obj
- * @return {Object} New object with only the own properties (recursively).
- */
-function objectValues ( obj ) {
-       var key, val,
-               vals = QUnit.is( "array", obj ) ? [] : {};
-       for ( key in obj ) {
-               if ( hasOwn.call( obj, key ) ) {
-                       val = obj[ key ];
-                       vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
-               }
-       }
-       return vals;
-}
-
-function extend( a, b, undefOnly ) {
-       for ( var prop in b ) {
-               if ( hasOwn.call( b, prop ) ) {
-
-                       // Avoid "Member not found" error in IE8 caused by messing with window.constructor
-                       // This block runs on every environment, so `global` is being used instead of `window`
-                       // to avoid errors on node.
-                       if ( prop !== "constructor" || a !== global ) {
-                               if ( b[ prop ] === undefined ) {
-                                       delete a[ prop ];
-                               } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
-                                       a[ prop ] = b[ prop ];
-                               }
-                       }
-               }
-       }
-
-       return a;
-}
-
-function objectType( obj ) {
-       if ( typeof obj === "undefined" ) {
-               return "undefined";
-       }
-
-       // Consider: typeof null === object
-       if ( obj === null ) {
-               return "null";
-       }
-
-       var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
-               type = match && match[ 1 ];
-
-       switch ( type ) {
-               case "Number":
-                       if ( isNaN( obj ) ) {
-                               return "nan";
-                       }
-                       return "number";
-               case "String":
-               case "Boolean":
-               case "Array":
-               case "Set":
-               case "Map":
-               case "Date":
-               case "RegExp":
-               case "Function":
-               case "Symbol":
-                       return type.toLowerCase();
-       }
-       if ( typeof obj === "object" ) {
-               return "object";
-       }
-}
-
-// Safe object type checking
-function is( type, obj ) {
-       return QUnit.objectType( obj ) === type;
-}
-
-// Doesn't support IE6 to IE9, it will return undefined on these browsers
-// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
-function extractStacktrace( e, offset ) {
-       offset = offset === undefined ? 4 : offset;
-
-       var stack, include, i;
-
-       if ( e.stack ) {
-               stack = e.stack.split( "\n" );
-               if ( /^error$/i.test( stack[ 0 ] ) ) {
-                       stack.shift();
-               }
-               if ( fileName ) {
-                       include = [];
-                       for ( i = offset; i < stack.length; i++ ) {
-                               if ( stack[ i ].indexOf( fileName ) !== -1 ) {
-                                       break;
-                               }
-                               include.push( stack[ i ] );
-                       }
-                       if ( include.length ) {
-                               return include.join( "\n" );
-                       }
-               }
-               return stack[ offset ];
-
-       // Support: Safari <=6 only
-       } else if ( e.sourceURL ) {
-
-               // Exclude useless self-reference for generated Error objects
-               if ( /qunit.js$/.test( e.sourceURL ) ) {
-                       return;
-               }
-
-               // For actual exceptions, this is useful
-               return e.sourceURL + ":" + e.line;
-       }
-}
-
-function sourceFromStacktrace( offset ) {
-       var error = new Error();
-
-       // Support: Safari <=7 only, IE <=10 - 11 only
-       // Not all browsers generate the `stack` property for `new Error()`, see also #636
-       if ( !error.stack ) {
-               try {
-                       throw error;
-               } catch ( err ) {
-                       error = err;
-               }
-       }
-
-       return extractStacktrace( error, offset );
-}
-
-/**
- * Config object: Maintain internal state
- * Later exposed as QUnit.config
- * `config` initialized at top of scope
- */
-var config = {
+  global$1 = global$1 && 'default' in global$1 ? global$1['default'] : global$1;
 
-       // The queue of tests to run
-       queue: [],
+  var window = global$1.window;
+  var self$1 = global$1.self;
+  var console = global$1.console;
+  var setTimeout = global$1.setTimeout;
+  var clearTimeout = global$1.clearTimeout;
 
-       // Block until document ready
-       blocking: true,
+  var document = window && window.document;
+  var navigator = window && window.navigator;
 
-       // By default, run previously failed tests first
-       // very useful in combination with "Hide passed tests" checked
-       reorder: true,
+  var localSessionStorage = function () {
+       var x = "qunit-test-string";
+       try {
+               global$1.sessionStorage.setItem(x, x);
+               global$1.sessionStorage.removeItem(x);
+               return global$1.sessionStorage;
+       } catch (e) {
+               return undefined;
+       }
+  }();
 
-       // By default, modify document.title when suite is done
-       altertitle: true,
+  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+    return typeof obj;
+  } : function (obj) {
+    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  };
 
-       // HTML Reporter: collapse every test except the first failing test
-       // If false, all failing tests will be expanded
-       collapse: true,
 
-       // By default, scroll to top of the page when suite is done
-       scrolltop: true,
 
-       // Depth up-to which object will be dumped
-       maxDepth: 5,
 
-       // When enabled, all tests must call expect()
-       requireExpects: false,
 
-       // Placeholder for user-configurable form-exposed URL parameters
-       urlConfig: [],
 
-       // Set of all modules.
-       modules: [],
 
-       // Stack of nested modules
-       moduleStack: [],
 
-       // The first unnamed module
-       currentModule: {
-               name: "",
-               tests: []
-       },
 
-       callbacks: {}
-};
-
-// Push a loose unnamed module to the modules collection
-config.modules.push( config.currentModule );
-
-var loggingCallbacks = {};
-
-// Register logging callbacks
-function registerLoggingCallbacks( obj ) {
-       var i, l, key,
-               callbackNames = [ "begin", "done", "log", "testStart", "testDone",
-                       "moduleStart", "moduleDone" ];
-
-       function registerLoggingCallback( key ) {
-               var loggingCallback = function( callback ) {
-                       if ( objectType( callback ) !== "function" ) {
-                               throw new Error(
-                                       "QUnit logging methods require a callback function as their first parameters."
-                               );
-                       }
-
-                       config.callbacks[ key ].push( callback );
-               };
-
-               // DEPRECATED: This will be removed on QUnit 2.0.0+
-               // Stores the registered functions allowing restoring
-               // at verifyLoggingCallbacks() if modified
-               loggingCallbacks[ key ] = loggingCallback;
-
-               return loggingCallback;
-       }
-
-       for ( i = 0, l = callbackNames.length; i < l; i++ ) {
-               key = callbackNames[ i ];
-
-               // Initialize key collection of logging callback
-               if ( objectType( config.callbacks[ key ] ) === "undefined" ) {
-                       config.callbacks[ key ] = [];
-               }
-
-               obj[ key ] = registerLoggingCallback( key );
-       }
-}
-
-function runLoggingCallbacks( key, args ) {
-       var i, l, callbacks;
-
-       callbacks = config.callbacks[ key ];
-       for ( i = 0, l = callbacks.length; i < l; i++ ) {
-               callbacks[ i ]( args );
-       }
-}
-
-// DEPRECATED: This will be removed on 2.0.0+
-// This function verifies if the loggingCallbacks were modified by the user
-// If so, it will restore it, assign the given callback and print a console warning
-function verifyLoggingCallbacks() {
-       var loggingCallback, userCallback;
-
-       for ( loggingCallback in loggingCallbacks ) {
-               if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
-
-                       userCallback = QUnit[ loggingCallback ];
-
-                       // Restore the callback function
-                       QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
-
-                       // Assign the deprecated given callback
-                       QUnit[ loggingCallback ]( userCallback );
-
-                       if ( global.console && global.console.warn ) {
-                               global.console.warn(
-                                       "QUnit." + loggingCallback + " was replaced with a new value.\n" +
-                                       "Please, check out the documentation on how to apply logging callbacks.\n" +
-                                       "Reference: https://api.qunitjs.com/category/callbacks/"
-                               );
-                       }
-               }
-       }
-}
-
-( function() {
-       if ( !defined.document ) {
-               return;
-       }
-
-       // `onErrorFnPrev` initialized at top of scope
-       // Preserve other handlers
-       var onErrorFnPrev = window.onerror;
-
-       // Cover uncaught exceptions
-       // Returning true will suppress the default browser handler,
-       // returning false will let it run.
-       window.onerror = function( error, filePath, linerNr ) {
-               var ret = false;
-               if ( onErrorFnPrev ) {
-                       ret = onErrorFnPrev( error, filePath, linerNr );
-               }
-
-               // Treat return value as window.onerror itself does,
-               // Only do our handling if not suppressed.
-               if ( ret !== true ) {
-                       if ( QUnit.config.current ) {
-                               if ( QUnit.config.current.ignoreGlobalErrors ) {
-                                       return true;
-                               }
-                               QUnit.pushFailure( error, filePath + ":" + linerNr );
-                       } else {
-                               QUnit.test( "global failure", extend( function() {
-                                       QUnit.pushFailure( error, filePath + ":" + linerNr );
-                               }, { validTest: true } ) );
-                       }
-                       return false;
-               }
-
-               return ret;
-       };
-}() );
-
-// Figure out if we're running the tests from a server or not
-QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" );
-
-// Expose the current QUnit version
-QUnit.version = "1.23.1";
-
-extend( QUnit, {
-
-       // Call on start of module test to prepend name to all tests
-       module: function( name, testEnvironment, executeNow ) {
-               var module, moduleFns;
-               var currentModule = config.currentModule;
-
-               if ( arguments.length === 2 ) {
-                       if ( objectType( testEnvironment ) === "function" ) {
-                               executeNow = testEnvironment;
-                               testEnvironment = undefined;
-                       }
-               }
-
-               // DEPRECATED: handles setup/teardown functions,
-               // beforeEach and afterEach should be used instead
-               if ( testEnvironment && testEnvironment.setup ) {
-                       testEnvironment.beforeEach = testEnvironment.setup;
-                       delete testEnvironment.setup;
-               }
-               if ( testEnvironment && testEnvironment.teardown ) {
-                       testEnvironment.afterEach = testEnvironment.teardown;
-                       delete testEnvironment.teardown;
-               }
-
-               module = createModule();
-
-               moduleFns = {
-                       beforeEach: setHook( module, "beforeEach" ),
-                       afterEach: setHook( module, "afterEach" )
-               };
-
-               if ( objectType( executeNow ) === "function" ) {
-                       config.moduleStack.push( module );
-                       setCurrentModule( module );
-                       executeNow.call( module.testEnvironment, moduleFns );
-                       config.moduleStack.pop();
-                       module = module.parentModule || currentModule;
-               }
-
-               setCurrentModule( module );
-
-               function createModule() {
-                       var parentModule = config.moduleStack.length ?
-                               config.moduleStack.slice( -1 )[ 0 ] : null;
-                       var moduleName = parentModule !== null ?
-                               [ parentModule.name, name ].join( " > " ) : name;
-                       var module = {
-                               name: moduleName,
-                               parentModule: parentModule,
-                               tests: [],
-                               moduleId: generateHash( moduleName )
-                       };
-
-                       var env = {};
-                       if ( parentModule ) {
-                               extend( env, parentModule.testEnvironment );
-                               delete env.beforeEach;
-                               delete env.afterEach;
-                       }
-                       extend( env, testEnvironment );
-                       module.testEnvironment = env;
-
-                       config.modules.push( module );
-                       return module;
-               }
-
-               function setCurrentModule( module ) {
-                       config.currentModule = module;
-               }
-
-       },
-
-       // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
-       asyncTest: asyncTest,
-
-       test: test,
-
-       skip: skip,
-
-       only: only,
-
-       // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
-       // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
-       start: function( count ) {
-               var globalStartAlreadyCalled = globalStartCalled;
-
-               if ( !config.current ) {
-                       globalStartCalled = true;
-
-                       if ( runStarted ) {
-                               throw new Error( "Called start() outside of a test context while already started" );
-                       } else if ( globalStartAlreadyCalled || count > 1 ) {
-                               throw new Error( "Called start() outside of a test context too many times" );
-                       } else if ( config.autostart ) {
-                               throw new Error( "Called start() outside of a test context when " +
-                                       "QUnit.config.autostart was true" );
-                       } else if ( !config.pageLoaded ) {
-
-                               // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
-                               config.autostart = true;
-                               return;
-                       }
-               } else {
-
-                       // If a test is running, adjust its semaphore
-                       config.current.semaphore -= count || 1;
-
-                       // If semaphore is non-numeric, throw error
-                       if ( isNaN( config.current.semaphore ) ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() with a non-numeric decrement.",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-
-                       // Don't start until equal number of stop-calls
-                       if ( config.current.semaphore > 0 ) {
-                               return;
-                       }
-
-                       // Throw an Error if start is called more often than stop
-                       if ( config.current.semaphore < 0 ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() while already started (test's semaphore was 0 already)",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-               }
-
-               resumeProcessing();
-       },
-
-       // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
-       stop: function( count ) {
-
-               // If there isn't a test running, don't allow QUnit.stop() to be called
-               if ( !config.current ) {
-                       throw new Error( "Called stop() outside of a test context" );
-               }
-
-               // If a test is running, adjust its semaphore
-               config.current.semaphore += count || 1;
-
-               pauseProcessing();
-       },
-
-       config: config,
-
-       is: is,
-
-       objectType: objectType,
-
-       extend: extend,
-
-       load: function() {
-               config.pageLoaded = true;
-
-               // Initialize the configuration options
-               extend( config, {
-                       stats: { all: 0, bad: 0 },
-                       moduleStats: { all: 0, bad: 0 },
-                       started: 0,
-                       updateRate: 1000,
-                       autostart: true,
-                       filter: ""
-               }, true );
-
-               config.blocking = false;
-
-               if ( config.autostart ) {
-                       resumeProcessing();
-               }
-       },
-
-       stack: function( offset ) {
-               offset = ( offset || 0 ) + 2;
-               return sourceFromStacktrace( offset );
-       }
-} );
-
-registerLoggingCallbacks( QUnit );
-
-function begin() {
-       var i, l,
-               modulesLog = [];
-
-       // If the test run hasn't officially begun yet
-       if ( !config.started ) {
-
-               // Record the time of the test run's beginning
-               config.started = now();
-
-               verifyLoggingCallbacks();
-
-               // Delete the loose unnamed module if unused.
-               if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
-                       config.modules.shift();
-               }
-
-               // Avoid unnecessary information by not logging modules' test environments
-               for ( i = 0, l = config.modules.length; i < l; i++ ) {
-                       modulesLog.push( {
-                               name: config.modules[ i ].name,
-                               tests: config.modules[ i ].tests
-                       } );
-               }
-
-               // The test run is officially beginning now
-               runLoggingCallbacks( "begin", {
-                       totalTests: Test.count,
-                       modules: modulesLog
-               } );
-       }
-
-       config.blocking = false;
-       process( true );
-}
-
-function process( last ) {
-       function next() {
-               process( last );
-       }
-       var start = now();
-       config.depth = ( config.depth || 0 ) + 1;
-
-       while ( config.queue.length && !config.blocking ) {
-               if ( !defined.setTimeout || config.updateRate <= 0 ||
-                               ( ( now() - start ) < config.updateRate ) ) {
-                       if ( config.current ) {
-
-                               // Reset async tracking for each phase of the Test lifecycle
-                               config.current.usedAsync = false;
-                       }
-                       config.queue.shift()();
-               } else {
-                       setTimeout( next, 13 );
-                       break;
-               }
-       }
-       config.depth--;
-       if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
-               done();
-       }
-}
-
-function pauseProcessing() {
-       config.blocking = true;
-
-       if ( config.testTimeout && defined.setTimeout ) {
-               clearTimeout( config.timeout );
-               config.timeout = setTimeout( function() {
-                       if ( config.current ) {
-                               config.current.semaphore = 0;
-                               QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
-                       } else {
-                               throw new Error( "Test timed out" );
-                       }
-                       resumeProcessing();
-               }, config.testTimeout );
-       }
-}
-
-function resumeProcessing() {
-       runStarted = true;
-
-       // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
-       if ( defined.setTimeout ) {
-               setTimeout( function() {
-                       if ( config.current && config.current.semaphore > 0 ) {
-                               return;
-                       }
-                       if ( config.timeout ) {
-                               clearTimeout( config.timeout );
-                       }
-
-                       begin();
-               }, 13 );
-       } else {
-               begin();
-       }
-}
-
-function done() {
-       var runtime, passed;
-
-       config.autorun = true;
-
-       // Log the last module results
-       if ( config.previousModule ) {
-               runLoggingCallbacks( "moduleDone", {
-                       name: config.previousModule.name,
-                       tests: config.previousModule.tests,
-                       failed: config.moduleStats.bad,
-                       passed: config.moduleStats.all - config.moduleStats.bad,
-                       total: config.moduleStats.all,
-                       runtime: now() - config.moduleStats.started
-               } );
-       }
-       delete config.previousModule;
-
-       runtime = now() - config.started;
-       passed = config.stats.all - config.stats.bad;
-
-       runLoggingCallbacks( "done", {
-               failed: config.stats.bad,
-               passed: passed,
-               total: config.stats.all,
-               runtime: runtime
-       } );
-}
-
-function setHook( module, hookName ) {
-       if ( module.testEnvironment === undefined ) {
-               module.testEnvironment = {};
-       }
-
-       return function( callback ) {
-               module.testEnvironment[ hookName ] = callback;
-       };
-}
-
-var focused = false;
-var priorityCount = 0;
-var unitSampler;
-
-function Test( settings ) {
-       var i, l;
-
-       ++Test.count;
-
-       extend( this, settings );
-       this.assertions = [];
-       this.semaphore = 0;
-       this.usedAsync = false;
-       this.module = config.currentModule;
-       this.stack = sourceFromStacktrace( 3 );
-
-       // Register unique strings
-       for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
-               if ( this.module.tests[ i ].name === this.testName ) {
-                       this.testName += " ";
-               }
-       }
-
-       this.testId = generateHash( this.module.name, this.testName );
-
-       this.module.tests.push( {
-               name: this.testName,
-               testId: this.testId
-       } );
-
-       if ( settings.skip ) {
-
-               // Skipped tests will fully ignore any sent callback
-               this.callback = function() {};
-               this.async = false;
-               this.expected = 0;
-       } else {
-               this.assert = new Assert( this );
-       }
-}
-
-Test.count = 0;
-
-Test.prototype = {
-       before: function() {
-               if (
-
-                       // Emit moduleStart when we're switching from one module to another
-                       this.module !== config.previousModule ||
-
-                               // They could be equal (both undefined) but if the previousModule property doesn't
-                               // yet exist it means this is the first test in a suite that isn't wrapped in a
-                               // module, in which case we'll just emit a moduleStart event for 'undefined'.
-                               // Without this, reporters can get testStart before moduleStart  which is a problem.
-                               !hasOwn.call( config, "previousModule" )
-               ) {
-                       if ( hasOwn.call( config, "previousModule" ) ) {
-                               runLoggingCallbacks( "moduleDone", {
-                                       name: config.previousModule.name,
-                                       tests: config.previousModule.tests,
-                                       failed: config.moduleStats.bad,
-                                       passed: config.moduleStats.all - config.moduleStats.bad,
-                                       total: config.moduleStats.all,
-                                       runtime: now() - config.moduleStats.started
-                               } );
-                       }
-                       config.previousModule = this.module;
-                       config.moduleStats = { all: 0, bad: 0, started: now() };
-                       runLoggingCallbacks( "moduleStart", {
-                               name: this.module.name,
-                               tests: this.module.tests
-                       } );
-               }
-
-               config.current = this;
-
-               if ( this.module.testEnvironment ) {
-                       delete this.module.testEnvironment.beforeEach;
-                       delete this.module.testEnvironment.afterEach;
-               }
-               this.testEnvironment = extend( {}, this.module.testEnvironment );
-
-               this.started = now();
-               runLoggingCallbacks( "testStart", {
-                       name: this.testName,
-                       module: this.module.name,
-                       testId: this.testId
-               } );
-
-               if ( !config.pollution ) {
-                       saveGlobal();
-               }
-       },
-
-       run: function() {
-               var promise;
-
-               config.current = this;
-
-               if ( this.async ) {
-                       QUnit.stop();
-               }
-
-               this.callbackStarted = now();
-
-               if ( config.notrycatch ) {
-                       runTest( this );
-                       return;
-               }
-
-               try {
-                       runTest( this );
-               } catch ( e ) {
-                       this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
-                               this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
-
-                       // Else next test will carry the responsibility
-                       saveGlobal();
-
-                       // Restart the tests if they're blocking
-                       if ( config.blocking ) {
-                               QUnit.start();
-                       }
-               }
-
-               function runTest( test ) {
-                       promise = test.callback.call( test.testEnvironment, test.assert );
-                       test.resolvePromise( promise );
-               }
-       },
-
-       after: function() {
-               checkPollution();
-       },
-
-       queueHook: function( hook, hookName ) {
-               var promise,
-                       test = this;
-               return function runHook() {
-                       config.current = test;
-                       if ( config.notrycatch ) {
-                               callHook();
-                               return;
-                       }
-                       try {
-                               callHook();
-                       } catch ( error ) {
-                               test.pushFailure( hookName + " failed on " + test.testName + ": " +
-                               ( error.message || error ), extractStacktrace( error, 0 ) );
-                       }
-
-                       function callHook() {
-                               promise = hook.call( test.testEnvironment, test.assert );
-                               test.resolvePromise( promise, hookName );
-                       }
-               };
-       },
-
-       // Currently only used for module level hooks, can be used to add global level ones
-       hooks: function( handler ) {
-               var hooks = [];
-
-               function processHooks( test, module ) {
-                       if ( module.parentModule ) {
-                               processHooks( test, module.parentModule );
-                       }
-                       if ( module.testEnvironment &&
-                               QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) {
-                               hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) );
-                       }
-               }
-
-               // Hooks are ignored on skipped tests
-               if ( !this.skip ) {
-                       processHooks( this, this.module );
-               }
-               return hooks;
-       },
-
-       finish: function() {
-               config.current = this;
-               if ( config.requireExpects && this.expected === null ) {
-                       this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
-                               "not called.", this.stack );
-               } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
-                       this.pushFailure( "Expected " + this.expected + " assertions, but " +
-                               this.assertions.length + " were run", this.stack );
-               } else if ( this.expected === null && !this.assertions.length ) {
-                       this.pushFailure( "Expected at least one assertion, but none were run - call " +
-                               "expect(0) to accept zero assertions.", this.stack );
-               }
-
-               var i,
-                       bad = 0;
-
-               this.runtime = now() - this.started;
-               config.stats.all += this.assertions.length;
-               config.moduleStats.all += this.assertions.length;
-
-               for ( i = 0; i < this.assertions.length; i++ ) {
-                       if ( !this.assertions[ i ].result ) {
-                               bad++;
-                               config.stats.bad++;
-                               config.moduleStats.bad++;
-                       }
-               }
-
-               runLoggingCallbacks( "testDone", {
-                       name: this.testName,
-                       module: this.module.name,
-                       skipped: !!this.skip,
-                       failed: bad,
-                       passed: this.assertions.length - bad,
-                       total: this.assertions.length,
-                       runtime: this.runtime,
-
-                       // HTML Reporter use
-                       assertions: this.assertions,
-                       testId: this.testId,
-
-                       // Source of Test
-                       source: this.stack,
-
-                       // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
-                       duration: this.runtime
-               } );
-
-               // QUnit.reset() is deprecated and will be replaced for a new
-               // fixture reset function on QUnit 2.0/2.1.
-               // It's still called here for backwards compatibility handling
-               QUnit.reset();
-
-               config.current = undefined;
-       },
-
-       queue: function() {
-               var priority,
-                       test = this;
-
-               if ( !this.valid() ) {
-                       return;
-               }
-
-               function run() {
-
-                       // Each of these can by async
-                       synchronize( [
-                               function() {
-                                       test.before();
-                               },
-
-                               test.hooks( "beforeEach" ),
-                               function() {
-                                       test.run();
-                               },
-
-                               test.hooks( "afterEach" ).reverse(),
-
-                               function() {
-                                       test.after();
-                               },
-                               function() {
-                                       test.finish();
-                               }
-                       ] );
-               }
-
-               // Prioritize previously failed tests, detected from sessionStorage
-               priority = QUnit.config.reorder && defined.sessionStorage &&
-                               +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
-
-               return synchronize( run, priority, config.seed );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
-               var source,
-                       details = {
-                               module: this.module.name,
-                               name: this.testName,
-                               result: resultInfo.result,
-                               message: resultInfo.message,
-                               actual: resultInfo.actual,
-                               expected: resultInfo.expected,
-                               testId: this.testId,
-                               negative: resultInfo.negative || false,
-                               runtime: now() - this.started
-                       };
-
-               if ( !resultInfo.result ) {
-                       source = sourceFromStacktrace();
-
-                       if ( source ) {
-                               details.source = source;
-                       }
-               }
-
-               runLoggingCallbacks( "log", details );
-
-               this.assertions.push( {
-                       result: !!resultInfo.result,
-                       message: resultInfo.message
-               } );
-       },
-
-       pushFailure: function( message, source, actual ) {
-               if ( !( this instanceof Test ) ) {
-                       throw new Error( "pushFailure() assertion outside test context, was " +
-                               sourceFromStacktrace( 2 ) );
-               }
-
-               var details = {
-                               module: this.module.name,
-                               name: this.testName,
-                               result: false,
-                               message: message || "error",
-                               actual: actual || null,
-                               testId: this.testId,
-                               runtime: now() - this.started
-                       };
-
-               if ( source ) {
-                       details.source = source;
-               }
-
-               runLoggingCallbacks( "log", details );
-
-               this.assertions.push( {
-                       result: false,
-                       message: message
-               } );
-       },
-
-       resolvePromise: function( promise, phase ) {
-               var then, message,
-                       test = this;
-               if ( promise != null ) {
-                       then = promise.then;
-                       if ( QUnit.objectType( then ) === "function" ) {
-                               QUnit.stop();
-                               then.call(
-                                       promise,
-                                       function() { QUnit.start(); },
-                                       function( error ) {
-                                               message = "Promise rejected " +
-                                                       ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
-                                                       " " + test.testName + ": " + ( error.message || error );
-                                               test.pushFailure( message, extractStacktrace( error, 0 ) );
-
-                                               // Else next test will carry the responsibility
-                                               saveGlobal();
-
-                                               // Unblock
-                                               QUnit.start();
-                                       }
-                               );
-                       }
-               }
-       },
-
-       valid: function() {
-               var filter = config.filter,
-                       regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ),
-                       module = config.module && config.module.toLowerCase(),
-                       fullName = ( this.module.name + ": " + this.testName );
-
-               function moduleChainNameMatch( testModule ) {
-                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
-                       if ( testModuleName === module ) {
-                               return true;
-                       } else if ( testModule.parentModule ) {
-                               return moduleChainNameMatch( testModule.parentModule );
-                       } else {
-                               return false;
-                       }
-               }
-
-               function moduleChainIdMatch( testModule ) {
-                       return inArray( testModule.moduleId, config.moduleId ) > -1 ||
-                               testModule.parentModule && moduleChainIdMatch( testModule.parentModule );
-               }
-
-               // Internally-generated tests are always valid
-               if ( this.callback && this.callback.validTest ) {
-                       return true;
-               }
-
-               if ( config.moduleId && config.moduleId.length > 0 &&
-                       !moduleChainIdMatch( this.module ) ) {
-
-                       return false;
-               }
-
-               if ( config.testId && config.testId.length > 0 &&
-                       inArray( this.testId, config.testId ) < 0 ) {
-
-                       return false;
-               }
-
-               if ( module && !moduleChainNameMatch( this.module ) ) {
-                       return false;
-               }
-
-               if ( !filter ) {
-                       return true;
-               }
-
-               return regexFilter ?
-                       this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) :
-                       this.stringFilter( filter, fullName );
-       },
-
-       regexFilter: function( exclude, pattern, flags, fullName ) {
-               var regex = new RegExp( pattern, flags );
-               var match = regex.test( fullName );
-
-               return match !== exclude;
-       },
-
-       stringFilter: function( filter, fullName ) {
-               filter = filter.toLowerCase();
-               fullName = fullName.toLowerCase();
-
-               var include = filter.charAt( 0 ) !== "!";
-               if ( !include ) {
-                       filter = filter.slice( 1 );
-               }
-
-               // If the filter matches, we need to honour include
-               if ( fullName.indexOf( filter ) !== -1 ) {
-                       return include;
-               }
-
-               // Otherwise, do the opposite
-               return !include;
-       }
-};
-
-// Resets the test setup. Useful for tests that modify the DOM.
-/*
-DEPRECATED: Use multiple tests instead of resetting inside a test.
-Use testStart or testDone for custom cleanup.
-This method will throw an error in 2.0, and will be removed in 2.1
-*/
-QUnit.reset = function() {
-
-       // Return on non-browser environments
-       // This is necessary to not break on node tests
-       if ( !defined.document ) {
-               return;
-       }
-
-       var fixture = defined.document && document.getElementById &&
-                       document.getElementById( "qunit-fixture" );
-
-       if ( fixture ) {
-               fixture.innerHTML = config.fixture;
-       }
-};
-
-QUnit.pushFailure = function() {
-       if ( !QUnit.config.current ) {
-               throw new Error( "pushFailure() assertion outside test context, in " +
-                       sourceFromStacktrace( 2 ) );
-       }
-
-       // Gets current test obj
-       var currentTest = QUnit.config.current;
-
-       return currentTest.pushFailure.apply( currentTest, arguments );
-};
-
-// Based on Java's String.hashCode, a simple but not
-// rigorously collision resistant hashing function
-function generateHash( module, testName ) {
-       var hex,
-               i = 0,
-               hash = 0,
-               str = module + "\x1C" + testName,
-               len = str.length;
-
-       for ( ; i < len; i++ ) {
-               hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
-               hash |= 0;
-       }
-
-       // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
-       // strictly necessary but increases user understanding that the id is a SHA-like hash
-       hex = ( 0x100000000 + hash ).toString( 16 );
-       if ( hex.length < 8 ) {
-               hex = "0000000" + hex;
-       }
-
-       return hex.slice( -8 );
-}
-
-function synchronize( callback, priority, seed ) {
-       var last = !priority,
-               index;
-
-       if ( QUnit.objectType( callback ) === "array" ) {
-               while ( callback.length ) {
-                       synchronize( callback.shift() );
-               }
-               return;
-       }
-
-       if ( priority ) {
-               config.queue.splice( priorityCount++, 0, callback );
-       } else if ( seed ) {
-               if ( !unitSampler ) {
-                       unitSampler = unitSamplerGenerator( seed );
-               }
-
-               // Insert into a random position after all priority items
-               index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) );
-               config.queue.splice( priorityCount + index, 0, callback );
-       } else {
-               config.queue.push( callback );
-       }
-
-       if ( config.autorun && !config.blocking ) {
-               process( last );
-       }
-}
-
-function unitSamplerGenerator( seed ) {
-
-       // 32-bit xorshift, requires only a nonzero seed
-       // http://excamera.com/sphinx/article-xorshift.html
-       var sample = parseInt( generateHash( seed ), 16 ) || -1;
-       return function() {
-               sample ^= sample << 13;
-               sample ^= sample >>> 17;
-               sample ^= sample << 5;
-
-               // ECMAScript has no unsigned number type
-               if ( sample < 0 ) {
-                       sample += 0x100000000;
-               }
-
-               return sample / 0x100000000;
-       };
-}
-
-function saveGlobal() {
-       config.pollution = [];
-
-       if ( config.noglobals ) {
-               for ( var key in global ) {
-                       if ( hasOwn.call( global, key ) ) {
-
-                               // In Opera sometimes DOM element ids show up here, ignore them
-                               if ( /^qunit-test-output/.test( key ) ) {
-                                       continue;
-                               }
-                               config.pollution.push( key );
-                       }
-               }
-       }
-}
-
-function checkPollution() {
-       var newGlobals,
-               deletedGlobals,
-               old = config.pollution;
-
-       saveGlobal();
-
-       newGlobals = diff( config.pollution, old );
-       if ( newGlobals.length > 0 ) {
-               QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
-       }
-
-       deletedGlobals = diff( old, config.pollution );
-       if ( deletedGlobals.length > 0 ) {
-               QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
-       }
-}
-
-// Will be exposed as QUnit.asyncTest
-function asyncTest( testName, expected, callback ) {
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       QUnit.test( testName, expected, callback, true );
-}
-
-// Will be exposed as QUnit.test
-function test( testName, expected, callback, async ) {
-       if ( focused )  { return; }
-
-       var newTest;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-// Will be exposed as QUnit.skip
-function skip( testName ) {
-       if ( focused )  { return; }
-
-       var test = new Test( {
-               testName: testName,
-               skip: true
-       } );
-
-       test.queue();
-}
-
-// Will be exposed as QUnit.only
-function only( testName, expected, callback, async ) {
-       var newTest;
-
-       if ( focused )  { return; }
-
-       QUnit.config.queue.length = 0;
-       focused = true;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-function Assert( testContext ) {
-       this.test = testContext;
-}
-
-// Assert helpers
-QUnit.assert = Assert.prototype = {
-
-       // Specify the number of expected assertions to guarantee that failed test
-       // (no assertions are run at all) don't slip through.
-       expect: function( asserts ) {
-               if ( arguments.length === 1 ) {
-                       this.test.expected = asserts;
-               } else {
-                       return this.test.expected;
-               }
-       },
-
-       // Increment this Test's semaphore counter, then return a function that
-       // decrements that counter a maximum of once.
-       async: function( count ) {
-               var test = this.test,
-                       popped = false,
-                       acceptCallCount = count;
-
-               if ( typeof acceptCallCount === "undefined" ) {
-                       acceptCallCount = 1;
-               }
-
-               test.semaphore += 1;
-               test.usedAsync = true;
-               pauseProcessing();
-
-               return function done() {
-
-                       if ( popped ) {
-                               test.pushFailure( "Too many calls to the `assert.async` callback",
-                                       sourceFromStacktrace( 2 ) );
-                               return;
-                       }
-                       acceptCallCount -= 1;
-                       if ( acceptCallCount > 0 ) {
-                               return;
-                       }
-
-                       test.semaphore -= 1;
-                       popped = true;
-                       resumeProcessing();
-               };
-       },
-
-       // Exports test.push() to the user API
-       // Alias of pushResult.
-       push: function( result, actual, expected, message, negative ) {
-               var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert;
-               return currentAssert.pushResult( {
-                       result: result,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: negative
-               } );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
-               var assert = this,
-                       currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
-
-               // Backwards compatibility fix.
-               // Allows the direct use of global exported assertions and QUnit.assert.*
-               // Although, it's use is not recommended as it can leak assertions
-               // to other tests from async tests, because we only get a reference to the current test,
-               // not exactly the test where assertion were intended to be called.
-               if ( !currentTest ) {
-                       throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
-               }
-
-               if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
-                       currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
-                               sourceFromStacktrace( 2 ) );
-
-                       // Allow this assertion to continue running anyway...
-               }
-
-               if ( !( assert instanceof Assert ) ) {
-                       assert = currentTest.assert;
-               }
-
-               return assert.test.pushResult( resultInfo );
-       },
-
-       ok: function( result, message ) {
-               message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !!result,
-                       actual: result,
-                       expected: true,
-                       message: message
-               } );
-       },
-
-       notOk: function( result, message ) {
-               message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !result,
-                       actual: result,
-                       expected: false,
-                       message: message
-               } );
-       },
-
-       equal: function( actual, expected, message ) {
-               /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected == actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notEqual: function( actual, expected, message ) {
-               /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected != actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       propEqual: function( actual, expected, message ) {
-               actual = objectValues( actual );
-               expected = objectValues( expected );
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notPropEqual: function( actual, expected, message ) {
-               actual = objectValues( actual );
-               expected = objectValues( expected );
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       deepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notDeepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       strictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected === actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notStrictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected !== actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       "throws": function( block, expected, message ) {
-               var actual, expectedType,
-                       expectedOutput = expected,
-                       ok = false,
-                       currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
-
-               // 'expected' is optional unless doing string comparison
-               if ( message == null && typeof expected === "string" ) {
-                       message = expected;
-                       expected = null;
-               }
-
-               currentTest.ignoreGlobalErrors = true;
-               try {
-                       block.call( currentTest.testEnvironment );
-               } catch ( e ) {
-                       actual = e;
-               }
-               currentTest.ignoreGlobalErrors = false;
-
-               if ( actual ) {
-                       expectedType = QUnit.objectType( expected );
-
-                       // We don't want to validate thrown error
-                       if ( !expected ) {
-                               ok = true;
-                               expectedOutput = null;
-
-                       // Expected is a regexp
-                       } else if ( expectedType === "regexp" ) {
-                               ok = expected.test( errorString( actual ) );
-
-                       // Expected is a string
-                       } else if ( expectedType === "string" ) {
-                               ok = expected === errorString( actual );
-
-                       // Expected is a constructor, maybe an Error constructor
-                       } else if ( expectedType === "function" && actual instanceof expected ) {
-                               ok = true;
-
-                       // Expected is an Error object
-                       } else if ( expectedType === "object" ) {
-                               ok = actual instanceof expected.constructor &&
-                                       actual.name === expected.name &&
-                                       actual.message === expected.message;
-
-                       // Expected is a validation function which returns true if validation passed
-                       } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
-                               expectedOutput = null;
-                               ok = true;
-                       }
-               }
-
-               currentTest.assert.pushResult( {
-                       result: ok,
-                       actual: actual,
-                       expected: expectedOutput,
-                       message: message
-               } );
-       }
-};
-
-// Provide an alternative to assert.throws(), for environments that consider throws a reserved word
-// Known to us are: Closure Compiler, Narwhal
-( function() {
-       /*jshint sub:true */
-       Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation
-}() );
-
-function errorString( error ) {
-       var name, message,
-               resultErrorString = error.toString();
-       if ( resultErrorString.substring( 0, 7 ) === "[object" ) {
-               name = error.name ? error.name.toString() : "Error";
-               message = error.message ? error.message.toString() : "";
-               if ( name && message ) {
-                       return name + ": " + message;
-               } else if ( name ) {
-                       return name;
-               } else if ( message ) {
-                       return message;
-               } else {
-                       return "Error";
-               }
-       } else {
-               return resultErrorString;
-       }
-}
-
-// Test for equality any JavaScript type.
-// Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = ( function() {
-
-       // Stack to decide between skip/abort functions
-       var callers = [];
-
-       // Stack to avoiding loops from circular referencing
-       var parents = [];
-       var parentsB = [];
-
-       var getProto = Object.getPrototypeOf || function( obj ) {
-
-               /*jshint proto: true */
-               return obj.__proto__;
-       };
-
-       function useStrictEquality( b, a ) {
-
-               // To catch short annotation VS 'new' annotation of a declaration. e.g.:
-               // `var i = 1;`
-               // `var j = new Number(1);`
-               if ( typeof a === "object" ) {
-                       a = a.valueOf();
-               }
-               if ( typeof b === "object" ) {
-                       b = b.valueOf();
-               }
-
-               return a === b;
-       }
-
-       function compareConstructors( a, b ) {
-               var protoA = getProto( a );
-               var protoB = getProto( b );
-
-               // Comparing constructors is more strict than using `instanceof`
-               if ( a.constructor === b.constructor ) {
-                       return true;
-               }
-
-               // Ref #851
-               // If the obj prototype descends from a null constructor, treat it
-               // as a null prototype.
-               if ( protoA && protoA.constructor === null ) {
-                       protoA = null;
-               }
-               if ( protoB && protoB.constructor === null ) {
-                       protoB = null;
-               }
-
-               // Allow objects with no prototype to be equivalent to
-               // objects with Object as their constructor.
-               if ( ( protoA === null && protoB === Object.prototype ) ||
-                               ( protoB === null && protoA === Object.prototype ) ) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       function getRegExpFlags( regexp ) {
-               return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
-       }
-
-       var callbacks = {
-               "string": useStrictEquality,
-               "boolean": useStrictEquality,
-               "number": useStrictEquality,
-               "null": useStrictEquality,
-               "undefined": useStrictEquality,
-               "symbol": useStrictEquality,
-               "date": useStrictEquality,
-
-               "nan": function() {
-                       return true;
-               },
-
-               "regexp": function( b, a ) {
-                       return a.source === b.source &&
-
-                               // Include flags in the comparison
-                               getRegExpFlags( a ) === getRegExpFlags( b );
-               },
-
-               // - skip when the property is a method of an instance (OOP)
-               // - abort otherwise,
-               // initial === would have catch identical references anyway
-               "function": function() {
-                       var caller = callers[ callers.length - 1 ];
-                       return caller !== Object && typeof caller !== "undefined";
-               },
-
-               "array": function( b, a ) {
-                       var i, j, len, loop, aCircular, bCircular;
-
-                       len = a.length;
-                       if ( len !== b.length ) {
-
-                               // Safe and faster
-                               return false;
-                       }
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-                       for ( i = 0; i < len; i++ ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
-                                                       parents.pop();
-                                                       parentsB.pop();
-                                                       return false;
-                                               }
-                                       }
-                               }
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       parents.pop();
-                                       parentsB.pop();
-                                       return false;
-                               }
-                       }
-                       parents.pop();
-                       parentsB.pop();
-                       return true;
-               },
-
-               "set": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal ) {
-                                       if ( innerEquiv( bVal, aVal ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "map": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal, aKey ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal, bKey ) {
-                                       if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "object": function( b, a ) {
-                       var i, j, loop, aCircular, bCircular;
-
-                       // Default to true
-                       var eq = true;
-                       var aProperties = [];
-                       var bProperties = [];
-
-                       if ( compareConstructors( a, b ) === false ) {
-                               return false;
-                       }
-
-                       // Stack constructor before traversing properties
-                       callers.push( a.constructor );
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-
-                       // Be strict: don't ensure hasOwnProperty and go deep
-                       for ( i in a ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
-                                                       eq = false;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               aProperties.push( i );
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       eq = false;
-                                       break;
-                               }
-                       }
-
-                       parents.pop();
-                       parentsB.pop();
-
-                       // Unstack, we are done
-                       callers.pop();
-
-                       for ( i in b ) {
-
-                               // Collect b's properties
-                               bProperties.push( i );
-                       }
-
-                       // Ensures identical properties name
-                       return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
-               }
-       };
-
-       function typeEquiv( a, b ) {
-               var type = QUnit.objectType( a );
-               return QUnit.objectType( b ) === type && callbacks[ type ]( b, a );
-       }
-
-       // The real equiv function
-       function innerEquiv( a, b ) {
-
-               // We're done when there's nothing more to compare
-               if ( arguments.length < 2 ) {
-                       return true;
-               }
-
-               // Require type-specific equality
-               return ( a === b || typeEquiv( a, b ) ) &&
-
-                       // ...across all consecutive argument pairs
-                       ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );
-       }
-
-       return innerEquiv;
-}() );
-
-// Based on jsDump by Ariel Flesler
-// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
-QUnit.dump = ( function() {
-       function quote( str ) {
-               return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\"";
-       }
-       function literal( o ) {
-               return o + "";
-       }
-       function join( pre, arr, post ) {
-               var s = dump.separator(),
-                       base = dump.indent(),
-                       inner = dump.indent( 1 );
-               if ( arr.join ) {
-                       arr = arr.join( "," + s + inner );
-               }
-               if ( !arr ) {
-                       return pre + post;
-               }
-               return [ pre, inner + arr, base + post ].join( s );
-       }
-       function array( arr, stack ) {
-               var i = arr.length,
-                       ret = new Array( i );
-
-               if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
-                       return "[object Array]";
-               }
-
-               this.up();
-               while ( i-- ) {
-                       ret[ i ] = this.parse( arr[ i ], undefined, stack );
-               }
-               this.down();
-               return join( "[", ret, "]" );
-       }
-
-       var reName = /^function (\w+)/,
-               dump = {
-
-                       // The objType is used mostly internally, you can fix a (custom) type in advance
-                       parse: function( obj, objType, stack ) {
-                               stack = stack || [];
-                               var res, parser, parserType,
-                                       inStack = inArray( obj, stack );
-
-                               if ( inStack !== -1 ) {
-                                       return "recursion(" + ( inStack - stack.length ) + ")";
-                               }
-
-                               objType = objType || this.typeOf( obj  );
-                               parser = this.parsers[ objType ];
-                               parserType = typeof parser;
-
-                               if ( parserType === "function" ) {
-                                       stack.push( obj );
-                                       res = parser.call( this, obj, stack );
-                                       stack.pop();
-                                       return res;
-                               }
-                               return ( parserType === "string" ) ? parser : this.parsers.error;
-                       },
-                       typeOf: function( obj ) {
-                               var type;
-                               if ( obj === null ) {
-                                       type = "null";
-                               } else if ( typeof obj === "undefined" ) {
-                                       type = "undefined";
-                               } else if ( QUnit.is( "regexp", obj ) ) {
-                                       type = "regexp";
-                               } else if ( QUnit.is( "date", obj ) ) {
-                                       type = "date";
-                               } else if ( QUnit.is( "function", obj ) ) {
-                                       type = "function";
-                               } else if ( obj.setInterval !== undefined &&
-                                               obj.document !== undefined &&
-                                               obj.nodeType === undefined ) {
-                                       type = "window";
-                               } else if ( obj.nodeType === 9 ) {
-                                       type = "document";
-                               } else if ( obj.nodeType ) {
-                                       type = "node";
-                               } else if (
-
-                                       // Native arrays
-                                       toString.call( obj ) === "[object Array]" ||
-
-                                       // NodeList objects
-                                       ( typeof obj.length === "number" && obj.item !== undefined &&
-                                       ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
-                                       obj[ 0 ] === undefined ) ) )
-                               ) {
-                                       type = "array";
-                               } else if ( obj.constructor === Error.prototype.constructor ) {
-                                       type = "error";
-                               } else {
-                                       type = typeof obj;
-                               }
-                               return type;
-                       },
-
-                       separator: function() {
-                               return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
-                       },
-
-                       // Extra can be a number, shortcut for increasing-calling-decreasing
-                       indent: function( extra ) {
-                               if ( !this.multiline ) {
-                                       return "";
-                               }
-                               var chr = this.indentChar;
-                               if ( this.HTML ) {
-                                       chr = chr.replace( /\t/g, "   " ).replace( / /g, "&#160;" );
-                               }
-                               return new Array( this.depth + ( extra || 0 ) ).join( chr );
-                       },
-                       up: function( a ) {
-                               this.depth += a || 1;
-                       },
-                       down: function( a ) {
-                               this.depth -= a || 1;
-                       },
-                       setParser: function( name, parser ) {
-                               this.parsers[ name ] = parser;
-                       },
-
-                       // The next 3 are exposed so you can use them
-                       quote: quote,
-                       literal: literal,
-                       join: join,
-                       depth: 1,
-                       maxDepth: QUnit.config.maxDepth,
-
-                       // This is the list of parsers, to modify them, use dump.setParser
-                       parsers: {
-                               window: "[Window]",
-                               document: "[Document]",
-                               error: function( error ) {
-                                       return "Error(\"" + error.message + "\")";
-                               },
-                               unknown: "[Unknown]",
-                               "null": "null",
-                               "undefined": "undefined",
-                               "function": function( fn ) {
-                                       var ret = "function",
-
-                                               // Functions never have name in IE
-                                               name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
-
-                                       if ( name ) {
-                                               ret += " " + name;
-                                       }
-                                       ret += "(";
-
-                                       ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
-                                       return join( ret, dump.parse( fn, "functionCode" ), "}" );
-                               },
-                               array: array,
-                               nodelist: array,
-                               "arguments": array,
-                               object: function( map, stack ) {
-                                       var keys, key, val, i, nonEnumerableProperties,
-                                               ret = [];
-
-                                       if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
-                                               return "[object Object]";
-                                       }
-
-                                       dump.up();
-                                       keys = [];
-                                       for ( key in map ) {
-                                               keys.push( key );
-                                       }
-
-                                       // Some properties are not always enumerable on Error objects.
-                                       nonEnumerableProperties = [ "message", "name" ];
-                                       for ( i in nonEnumerableProperties ) {
-                                               key = nonEnumerableProperties[ i ];
-                                               if ( key in map && inArray( key, keys ) < 0 ) {
-                                                       keys.push( key );
-                                               }
-                                       }
-                                       keys.sort();
-                                       for ( i = 0; i < keys.length; i++ ) {
-                                               key = keys[ i ];
-                                               val = map[ key ];
-                                               ret.push( dump.parse( key, "key" ) + ": " +
-                                                       dump.parse( val, undefined, stack ) );
-                                       }
-                                       dump.down();
-                                       return join( "{", ret, "}" );
-                               },
-                               node: function( node ) {
-                                       var len, i, val,
-                                               open = dump.HTML ? "&lt;" : "<",
-                                               close = dump.HTML ? "&gt;" : ">",
-                                               tag = node.nodeName.toLowerCase(),
-                                               ret = open + tag,
-                                               attrs = node.attributes;
-
-                                       if ( attrs ) {
-                                               for ( i = 0, len = attrs.length; i < len; i++ ) {
-                                                       val = attrs[ i ].nodeValue;
-
-                                                       // IE6 includes all attributes in .attributes, even ones not explicitly
-                                                       // set. Those have values like undefined, null, 0, false, "" or
-                                                       // "inherit".
-                                                       if ( val && val !== "inherit" ) {
-                                                               ret += " " + attrs[ i ].nodeName + "=" +
-                                                                       dump.parse( val, "attribute" );
-                                                       }
-                                               }
-                                       }
-                                       ret += close;
-
-                                       // Show content of TextNode or CDATASection
-                                       if ( node.nodeType === 3 || node.nodeType === 4 ) {
-                                               ret += node.nodeValue;
-                                       }
-
-                                       return ret + open + "/" + tag + close;
-                               },
-
-                               // Function calls it internally, it's the arguments part of the function
-                               functionArgs: function( fn ) {
-                                       var args,
-                                               l = fn.length;
-
-                                       if ( !l ) {
-                                               return "";
-                                       }
-
-                                       args = new Array( l );
-                                       while ( l-- ) {
-
-                                               // 97 is 'a'
-                                               args[ l ] = String.fromCharCode( 97 + l );
-                                       }
-                                       return " " + args.join( ", " ) + " ";
-                               },
-
-                               // Object calls it internally, the key part of an item in a map
-                               key: quote,
-
-                               // Function calls it internally, it's the content of the function
-                               functionCode: "[code]",
-
-                               // Node calls it internally, it's a html attribute value
-                               attribute: quote,
-                               string: quote,
-                               date: quote,
-                               regexp: literal,
-                               number: literal,
-                               "boolean": literal
-                       },
-
-                       // If true, entities are escaped ( <, >, \t, space and \n )
-                       HTML: false,
-
-                       // Indentation unit
-                       indentChar: "  ",
-
-                       // If true, items in a collection, are separated by a \n, else just a space.
-                       multiline: true
-               };
-
-       return dump;
-}() );
-
-// Back compat
-QUnit.jsDump = QUnit.dump;
-
-// Deprecated
-// Extend assert methods to QUnit for Backwards compatibility
-( function() {
-       var i,
-               assertions = Assert.prototype;
-
-       function applyCurrent( current ) {
-               return function() {
-                       var assert = new Assert( QUnit.config.current );
-                       current.apply( assert, arguments );
-               };
-       }
-
-       for ( i in assertions ) {
-               QUnit[ i ] = applyCurrent( assertions[ i ] );
-       }
-}() );
-
-// For browser, export only select globals
-if ( defined.document ) {
-
-       ( function() {
-               var i, l,
-                       keys = [
-                               "test",
-                               "module",
-                               "expect",
-                               "asyncTest",
-                               "start",
-                               "stop",
-                               "ok",
-                               "notOk",
-                               "equal",
-                               "notEqual",
-                               "propEqual",
-                               "notPropEqual",
-                               "deepEqual",
-                               "notDeepEqual",
-                               "strictEqual",
-                               "notStrictEqual",
-                               "throws",
-                               "raises"
-                       ];
-
-               for ( i = 0, l = keys.length; i < l; i++ ) {
-                       window[ keys[ i ] ] = QUnit[ keys[ i ] ];
-               }
-       }() );
-
-       window.QUnit = QUnit;
-}
-
-// For nodejs
-if ( typeof module !== "undefined" && module && module.exports ) {
-       module.exports = QUnit;
-
-       // For consistency with CommonJS environments' exports
-       module.exports.QUnit = QUnit;
-}
-
-// For CommonJS with exports, but without module.exports, like Rhino
-if ( typeof exports !== "undefined" && exports ) {
-       exports.QUnit = QUnit;
-}
-
-if ( typeof define === "function" && define.amd ) {
-       define( function() {
-               return QUnit;
-       } );
-       QUnit.config.autostart = false;
-}
-
-// Get a reference to the global object, like window in browsers
-}( ( function() {
-       return this;
-}() ) ) );
-
-( function() {
-
-// Only interact with URLs via window.location
-var location = typeof window !== "undefined" && window.location;
-if ( !location ) {
-       return;
-}
-
-var urlParams = getUrlParams();
-
-QUnit.urlParams = urlParams;
-
-// Match module/test by inclusion in an array
-QUnit.config.moduleId = [].concat( urlParams.moduleId || [] );
-QUnit.config.testId = [].concat( urlParams.testId || [] );
-
-// Exact case-insensitive match of the module name
-QUnit.config.module = urlParams.module;
-
-// Regular expression or case-insenstive substring match against "moduleName: testName"
-QUnit.config.filter = urlParams.filter;
-
-// Test order randomization
-if ( urlParams.seed === true ) {
-
-       // Generate a random seed if the option is specified without a value
-       QUnit.config.seed = Math.random().toString( 36 ).slice( 2 );
-} else if ( urlParams.seed ) {
-       QUnit.config.seed = urlParams.seed;
-}
-
-// Add URL-parameter-mapped config values with UI form rendering data
-QUnit.config.urlConfig.push(
-       {
-               id: "hidepassed",
-               label: "Hide passed tests",
-               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
-       },
-       {
-               id: "noglobals",
-               label: "Check for Globals",
-               tooltip: "Enabling this will test if any test introduces new properties on the " +
-                       "global object (`window` in Browsers). Stored as query-strings."
-       },
-       {
-               id: "notrycatch",
-               label: "No try-catch",
-               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
-                       "exceptions in IE reasonable. Stored as query-strings."
-       }
-);
-
-QUnit.begin( function() {
-       var i, option,
-               urlConfig = QUnit.config.urlConfig;
-
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
-               option = QUnit.config.urlConfig[ i ];
-               if ( typeof option !== "string" ) {
-                       option = option.id;
-               }
-
-               if ( QUnit.config[ option ] === undefined ) {
-                       QUnit.config[ option ] = urlParams[ option ];
-               }
-       }
-} );
-
-function getUrlParams() {
-       var i, param, name, value;
-       var urlParams = {};
-       var params = location.search.slice( 1 ).split( "&" );
-       var length = params.length;
-
-       for ( i = 0; i < length; i++ ) {
-               if ( params[ i ] ) {
-                       param = params[ i ].split( "=" );
-                       name = decodeURIComponent( param[ 0 ] );
-
-                       // Allow just a key to turn on a flag, e.g., test.html?noglobals
-                       value = param.length === 1 ||
-                               decodeURIComponent( param.slice( 1 ).join( "=" ) ) ;
-                       if ( urlParams[ name ] ) {
-                               urlParams[ name ] = [].concat( urlParams[ name ], value );
-                       } else {
-                               urlParams[ name ] = value;
-                       }
-               }
-       }
-
-       return urlParams;
-}
-
-// Don't load the HTML Reporter on non-browser environments
-if ( typeof window === "undefined" || !window.document ) {
-       return;
-}
-
-// Deprecated QUnit.init - Ref #530
-// Re-initialize the configuration options
-QUnit.init = function() {
-       var config = QUnit.config;
-
-       config.stats = { all: 0, bad: 0 };
-       config.moduleStats = { all: 0, bad: 0 };
-       config.started = 0;
-       config.updateRate = 1000;
-       config.blocking = false;
-       config.autostart = true;
-       config.autorun = false;
-       config.filter = "";
-       config.queue = [];
-
-       appendInterface();
-};
-
-var config = QUnit.config,
-       document = window.document,
-       collapseNext = false,
-       hasOwn = Object.prototype.hasOwnProperty,
-       unfilteredUrl = setUrl( { filter: undefined, module: undefined,
-               moduleId: undefined, testId: undefined } ),
-       defined = {
-               sessionStorage: ( function() {
-                       var x = "qunit-test-string";
-                       try {
-                               sessionStorage.setItem( x, x );
-                               sessionStorage.removeItem( x );
-                               return true;
-                       } catch ( e ) {
-                               return false;
-                       }
-               }() )
-       },
-       modulesList = [];
-
-/**
-* Escape text for attribute or text content.
-*/
-function escapeText( s ) {
-       if ( !s ) {
-               return "";
-       }
-       s = s + "";
-
-       // Both single quotes and double quotes (for attributes)
-       return s.replace( /['"<>&]/g, function( s ) {
-               switch ( s ) {
-               case "'":
-                       return "&#039;";
-               case "\"":
-                       return "&quot;";
-               case "<":
-                       return "&lt;";
-               case ">":
-                       return "&gt;";
-               case "&":
-                       return "&amp;";
-               }
-       } );
-}
-
-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
-       if ( elem.addEventListener ) {
-
-               // Standards-based browsers
-               elem.addEventListener( type, fn, false );
-       } else if ( elem.attachEvent ) {
-
-               // Support: IE <9
-               elem.attachEvent( "on" + type, function() {
-                       var event = window.event;
-                       if ( !event.target ) {
-                               event.target = event.srcElement || document;
-                       }
-
-                       fn.call( elem, event );
-               } );
-       }
-}
-
-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
-       var i = elems.length;
-       while ( i-- ) {
-               addEvent( elems[ i ], type, fn );
-       }
-}
-
-function hasClass( elem, name ) {
-       return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
-}
-
-function addClass( elem, name ) {
-       if ( !hasClass( elem, name ) ) {
-               elem.className += ( elem.className ? " " : "" ) + name;
-       }
-}
-
-function toggleClass( elem, name, force ) {
-       if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) {
-               addClass( elem, name );
-       } else {
-               removeClass( elem, name );
-       }
-}
-
-function removeClass( elem, name ) {
-       var set = " " + elem.className + " ";
-
-       // Class name may appear multiple times
-       while ( set.indexOf( " " + name + " " ) >= 0 ) {
-               set = set.replace( " " + name + " ", " " );
-       }
-
-       // Trim for prettiness
-       elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
-}
-
-function id( name ) {
-       return document.getElementById && document.getElementById( name );
-}
-
-function getUrlConfigHtml() {
-       var i, j, val,
-               escaped, escapedTooltip,
-               selection = false,
-               urlConfig = config.urlConfig,
-               urlConfigHtml = "";
-
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
-               val = config.urlConfig[ i ];
-               if ( typeof val === "string" ) {
-                       val = {
-                               id: val,
-                               label: val
-                       };
-               }
-
-               escaped = escapeText( val.id );
-               escapedTooltip = escapeText( val.tooltip );
-
-               if ( !val.value || typeof val.value === "string" ) {
-                       urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
-                               "' name='" + escaped + "' type='checkbox'" +
-                               ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
-                               ( config[ val.id ] ? " checked='checked'" : "" ) +
-                               " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
-                               "' title='" + escapedTooltip + "'>" + val.label + "</label>";
-               } else {
-                       urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
-                               "' title='" + escapedTooltip + "'>" + val.label +
-                               ": </label><select id='qunit-urlconfig-" + escaped +
-                               "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
-
-                       if ( QUnit.is( "array", val.value ) ) {
-                               for ( j = 0; j < val.value.length; j++ ) {
-                                       escaped = escapeText( val.value[ j ] );
-                                       urlConfigHtml += "<option value='" + escaped + "'" +
-                                               ( config[ val.id ] === val.value[ j ] ?
-                                                       ( selection = true ) && " selected='selected'" : "" ) +
-                                               ">" + escaped + "</option>";
-                               }
-                       } else {
-                               for ( j in val.value ) {
-                                       if ( hasOwn.call( val.value, j ) ) {
-                                               urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
-                                                       ( config[ val.id ] === j ?
-                                                               ( selection = true ) && " selected='selected'" : "" ) +
-                                                       ">" + escapeText( val.value[ j ] ) + "</option>";
-                                       }
-                               }
-                       }
-                       if ( config[ val.id ] && !selection ) {
-                               escaped = escapeText( config[ val.id ] );
-                               urlConfigHtml += "<option value='" + escaped +
-                                       "' selected='selected' disabled='disabled'>" + escaped + "</option>";
-                       }
-                       urlConfigHtml += "</select>";
-               }
-       }
-
-       return urlConfigHtml;
-}
-
-// Handle "click" events on toolbar checkboxes and "change" for select menus.
-// Updates the URL with the new state of `config.urlConfig` values.
-function toolbarChanged() {
-       var updatedUrl, value, tests,
-               field = this,
-               params = {};
-
-       // Detect if field is a select menu or a checkbox
-       if ( "selectedIndex" in field ) {
-               value = field.options[ field.selectedIndex ].value || undefined;
-       } else {
-               value = field.checked ? ( field.defaultValue || true ) : undefined;
-       }
-
-       params[ field.name ] = value;
-       updatedUrl = setUrl( params );
-
-       // Check if we can apply the change without a page refresh
-       if ( "hidepassed" === field.name && "replaceState" in window.history ) {
-               QUnit.urlParams[ field.name ] = value;
-               config[ field.name ] = value || false;
-               tests = id( "qunit-tests" );
-               if ( tests ) {
-                       toggleClass( tests, "hidepass", value || false );
-               }
-               window.history.replaceState( null, "", updatedUrl );
-       } else {
-               window.location = updatedUrl;
-       }
-}
-
-function setUrl( params ) {
-       var key, arrValue, i,
-               querystring = "?",
-               location = window.location;
-
-       params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
-
-       for ( key in params ) {
-
-               // Skip inherited or undefined properties
-               if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) {
-
-                       // Output a parameter for each value of this key (but usually just one)
-                       arrValue = [].concat( params[ key ] );
-                       for ( i = 0; i < arrValue.length; i++ ) {
-                               querystring += encodeURIComponent( key );
-                               if ( arrValue[ i ] !== true ) {
-                                       querystring += "=" + encodeURIComponent( arrValue[ i ] );
-                               }
-                               querystring += "&";
-                       }
-               }
-       }
-       return location.protocol + "//" + location.host +
-               location.pathname + querystring.slice( 0, -1 );
-}
-
-function applyUrlParams() {
-       var selectedModule,
-               modulesList = id( "qunit-modulefilter" ),
-               filter = id( "qunit-filter-input" ).value;
-
-       selectedModule = modulesList ?
-               decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
-               undefined;
-
-       window.location = setUrl( {
-               module: ( selectedModule === "" ) ? undefined : selectedModule,
-               filter: ( filter === "" ) ? undefined : filter,
-
-               // Remove moduleId and testId filters
-               moduleId: undefined,
-               testId: undefined
-       } );
-}
-
-function toolbarUrlConfigContainer() {
-       var urlConfigContainer = document.createElement( "span" );
-
-       urlConfigContainer.innerHTML = getUrlConfigHtml();
-       addClass( urlConfigContainer, "qunit-url-config" );
-
-       // For oldIE support:
-       // * Add handlers to the individual elements instead of the container
-       // * Use "click" instead of "change" for checkboxes
-       addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
-       addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
-
-       return urlConfigContainer;
-}
-
-function toolbarLooseFilter() {
-       var filter = document.createElement( "form" ),
-               label = document.createElement( "label" ),
-               input = document.createElement( "input" ),
-               button = document.createElement( "button" );
-
-       addClass( filter, "qunit-filter" );
-
-       label.innerHTML = "Filter: ";
-
-       input.type = "text";
-       input.value = config.filter || "";
-       input.name = "filter";
-       input.id = "qunit-filter-input";
-
-       button.innerHTML = "Go";
-
-       label.appendChild( input );
-
-       filter.appendChild( label );
-       filter.appendChild( button );
-       addEvent( filter, "submit", function( ev ) {
-               applyUrlParams();
-
-               if ( ev && ev.preventDefault ) {
-                       ev.preventDefault();
-               }
-
-               return false;
-       } );
-
-       return filter;
-}
-
-function toolbarModuleFilterHtml() {
-       var i,
-               moduleFilterHtml = "";
-
-       if ( !modulesList.length ) {
-               return false;
-       }
-
-       moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
-               "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
-               ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
-               ">< All Modules ></option>";
-
-       for ( i = 0; i < modulesList.length; i++ ) {
-               moduleFilterHtml += "<option value='" +
-                       escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
-                       ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
-                       ">" + escapeText( modulesList[ i ] ) + "</option>";
-       }
-       moduleFilterHtml += "</select>";
-
-       return moduleFilterHtml;
-}
-
-function toolbarModuleFilter() {
-       var toolbar = id( "qunit-testrunner-toolbar" ),
-               moduleFilter = document.createElement( "span" ),
-               moduleFilterHtml = toolbarModuleFilterHtml();
-
-       if ( !toolbar || !moduleFilterHtml ) {
-               return false;
-       }
-
-       moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
-       moduleFilter.innerHTML = moduleFilterHtml;
-
-       addEvent( moduleFilter.lastChild, "change", applyUrlParams );
-
-       toolbar.appendChild( moduleFilter );
-}
-
-function appendToolbar() {
-       var toolbar = id( "qunit-testrunner-toolbar" );
-
-       if ( toolbar ) {
-               toolbar.appendChild( toolbarUrlConfigContainer() );
-               toolbar.appendChild( toolbarLooseFilter() );
-               toolbarModuleFilter();
-       }
-}
-
-function appendHeader() {
-       var header = id( "qunit-header" );
-
-       if ( header ) {
-               header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +
-                       "</a> ";
-       }
-}
-
-function appendBanner() {
-       var banner = id( "qunit-banner" );
-
-       if ( banner ) {
-               banner.className = "";
-       }
-}
-
-function appendTestResults() {
-       var tests = id( "qunit-tests" ),
-               result = id( "qunit-testresult" );
-
-       if ( result ) {
-               result.parentNode.removeChild( result );
-       }
-
-       if ( tests ) {
-               tests.innerHTML = "";
-               result = document.createElement( "p" );
-               result.id = "qunit-testresult";
-               result.className = "result";
-               tests.parentNode.insertBefore( result, tests );
-               result.innerHTML = "Running...<br />&#160;";
-       }
-}
-
-function storeFixture() {
-       var fixture = id( "qunit-fixture" );
-       if ( fixture ) {
-               config.fixture = fixture.innerHTML;
-       }
-}
-
-function appendFilteredTest() {
-       var testId = QUnit.config.testId;
-       if ( !testId || testId.length <= 0 ) {
-               return "";
-       }
-       return "<div id='qunit-filteredTest'>Rerunning selected tests: " +
-               escapeText( testId.join( ", " ) ) +
-               " <a id='qunit-clearFilter' href='" +
-               escapeText( unfilteredUrl ) +
-               "'>Run all tests</a></div>";
-}
-
-function appendUserAgent() {
-       var userAgent = id( "qunit-userAgent" );
-
-       if ( userAgent ) {
-               userAgent.innerHTML = "";
-               userAgent.appendChild(
-                       document.createTextNode(
-                               "QUnit " + QUnit.version + "; " + navigator.userAgent
-                       )
-               );
-       }
-}
-
-function appendInterface() {
-       var qunit = id( "qunit" );
-
-       if ( qunit ) {
-               qunit.innerHTML =
-                       "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
-                       "<h2 id='qunit-banner'></h2>" +
-                       "<div id='qunit-testrunner-toolbar'></div>" +
-                       appendFilteredTest() +
-                       "<h2 id='qunit-userAgent'></h2>" +
-                       "<ol id='qunit-tests'></ol>";
-       }
-
-       appendHeader();
-       appendBanner();
-       appendTestResults();
-       appendUserAgent();
-       appendToolbar();
-}
-
-function appendTestsList( modules ) {
-       var i, l, x, z, test, moduleObj;
-
-       for ( i = 0, l = modules.length; i < l; i++ ) {
-               moduleObj = modules[ i ];
-
-               for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
-                       test = moduleObj.tests[ x ];
-
-                       appendTest( test.name, test.testId, moduleObj.name );
-               }
-       }
-}
-
-function appendTest( name, testId, moduleName ) {
-       var title, rerunTrigger, testBlock, assertList,
-               tests = id( "qunit-tests" );
-
-       if ( !tests ) {
-               return;
-       }
-
-       title = document.createElement( "strong" );
-       title.innerHTML = getNameHtml( name, moduleName );
-
-       rerunTrigger = document.createElement( "a" );
-       rerunTrigger.innerHTML = "Rerun";
-       rerunTrigger.href = setUrl( { testId: testId } );
-
-       testBlock = document.createElement( "li" );
-       testBlock.appendChild( title );
-       testBlock.appendChild( rerunTrigger );
-       testBlock.id = "qunit-test-output-" + testId;
-
-       assertList = document.createElement( "ol" );
-       assertList.className = "qunit-assert-list";
-
-       testBlock.appendChild( assertList );
-
-       tests.appendChild( testBlock );
-}
-
-// HTML Reporter initialization and load
-QUnit.begin( function( details ) {
-       var i, moduleObj, tests;
-
-       // Sort modules by name for the picker
-       for ( i = 0; i < details.modules.length; i++ ) {
-               moduleObj = details.modules[ i ];
-               if ( moduleObj.name ) {
-                       modulesList.push( moduleObj.name );
-               }
-       }
-       modulesList.sort( function( a, b ) {
-               return a.localeCompare( b );
-       } );
-
-       // Capture fixture HTML from the page
-       storeFixture();
-
-       // Initialize QUnit elements
-       appendInterface();
-       appendTestsList( details.modules );
-       tests = id( "qunit-tests" );
-       if ( tests && config.hidepassed ) {
-               addClass( tests, "hidepass" );
-       }
-} );
-
-QUnit.done( function( details ) {
-       var i, key,
-               banner = id( "qunit-banner" ),
-               tests = id( "qunit-tests" ),
-               html = [
-                       "Tests completed in ",
-                       details.runtime,
-                       " milliseconds.<br />",
-                       "<span class='passed'>",
-                       details.passed,
-                       "</span> assertions of <span class='total'>",
-                       details.total,
-                       "</span> passed, <span class='failed'>",
-                       details.failed,
-                       "</span> failed."
-               ].join( "" );
-
-       if ( banner ) {
-               banner.className = details.failed ? "qunit-fail" : "qunit-pass";
-       }
-
-       if ( tests ) {
-               id( "qunit-testresult" ).innerHTML = html;
-       }
-
-       if ( config.altertitle && document.title ) {
-
-               // Show ✖ for good, ✔ for bad suite result in title
-               // use escape sequences in case file gets loaded with non-utf-8-charset
-               document.title = [
-                       ( details.failed ? "\u2716" : "\u2714" ),
-                       document.title.replace( /^[\u2714\u2716] /i, "" )
-               ].join( " " );
-       }
-
-       // Clear own sessionStorage items if all tests passed
-       if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
-               for ( i = 0; i < sessionStorage.length; i++ ) {
-                       key = sessionStorage.key( i++ );
-                       if ( key.indexOf( "qunit-test-" ) === 0 ) {
-                               sessionStorage.removeItem( key );
-                       }
-               }
-       }
-
-       // Scroll back to top to show results
-       if ( config.scrolltop && window.scrollTo ) {
-               window.scrollTo( 0, 0 );
-       }
-} );
-
-function getNameHtml( name, module ) {
-       var nameHtml = "";
-
-       if ( module ) {
-               nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
-       }
-
-       nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
-
-       return nameHtml;
-}
-
-QUnit.testStart( function( details ) {
-       var running, testBlock, bad;
-
-       testBlock = id( "qunit-test-output-" + details.testId );
-       if ( testBlock ) {
-               testBlock.className = "running";
-       } else {
-
-               // Report later registered tests
-               appendTest( details.name, details.testId, details.module );
-       }
-
-       running = id( "qunit-testresult" );
-       if ( running ) {
-               bad = QUnit.config.reorder && defined.sessionStorage &&
-                       +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
-
-               running.innerHTML = ( bad ?
-                       "Rerunning previously failed test: <br />" :
-                       "Running: <br />" ) +
-                       getNameHtml( details.name, details.module );
-       }
-
-} );
-
-function stripHtml( string ) {
-
-       // Strip tags, html entity and whitespaces
-       return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\&quot;/g, "" ).replace( /\s+/g, "" );
-}
-
-QUnit.log( function( details ) {
-       var assertList, assertLi,
-               message, expected, actual, diff,
-               showDiff = false,
-               testItem = id( "qunit-test-output-" + details.testId );
-
-       if ( !testItem ) {
-               return;
-       }
-
-       message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
-       message = "<span class='test-message'>" + message + "</span>";
-       message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
-
-       // The pushFailure doesn't provide details.expected
-       // when it calls, it's implicit to also not show expected and diff stuff
-       // Also, we need to check details.expected existence, as it can exist and be undefined
-       if ( !details.result && hasOwn.call( details, "expected" ) ) {
-               if ( details.negative ) {
-                       expected = "NOT " + QUnit.dump.parse( details.expected );
-               } else {
-                       expected = QUnit.dump.parse( details.expected );
-               }
-
-               actual = QUnit.dump.parse( details.actual );
-               message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
-                       escapeText( expected ) +
-                       "</pre></td></tr>";
-
-               if ( actual !== expected ) {
-
-                       message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
-                               escapeText( actual ) + "</pre></td></tr>";
-
-                       // Don't show diff if actual or expected are booleans
-                       if ( !( /^(true|false)$/.test( actual ) ) &&
-                                       !( /^(true|false)$/.test( expected ) ) ) {
-                               diff = QUnit.diff( expected, actual );
-                               showDiff = stripHtml( diff ).length !==
-                                       stripHtml( expected ).length +
-                                       stripHtml( actual ).length;
-                       }
-
-                       // Don't show diff if expected and actual are totally different
-                       if ( showDiff ) {
-                               message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +
-                                       diff + "</pre></td></tr>";
-                       }
-               } else if ( expected.indexOf( "[object Array]" ) !== -1 ||
-                               expected.indexOf( "[object Object]" ) !== -1 ) {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the depth of object is more than current max depth (" +
-                               QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
-                               " run with a higher max depth or <a href='" +
-                               escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" +
-                               "Rerun</a> without max depth.</p></td></tr>";
-               } else {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the expected and actual results have an equivalent" +
-                               " serialization</td></tr>";
-               }
-
-               if ( details.source ) {
-                       message += "<tr class='test-source'><th>Source: </th><td><pre>" +
-                               escapeText( details.source ) + "</pre></td></tr>";
-               }
-
-               message += "</table>";
-
-       // This occurs when pushFailure is set and we have an extracted stack trace
-       } else if ( !details.result && details.source ) {
-               message += "<table>" +
-                       "<tr class='test-source'><th>Source: </th><td><pre>" +
-                       escapeText( details.source ) + "</pre></td></tr>" +
-                       "</table>";
-       }
-
-       assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
-       assertLi = document.createElement( "li" );
-       assertLi.className = details.result ? "pass" : "fail";
-       assertLi.innerHTML = message;
-       assertList.appendChild( assertLi );
-} );
-
-QUnit.testDone( function( details ) {
-       var testTitle, time, testItem, assertList,
-               good, bad, testCounts, skipped, sourceName,
-               tests = id( "qunit-tests" );
-
-       if ( !tests ) {
-               return;
-       }
-
-       testItem = id( "qunit-test-output-" + details.testId );
-
-       assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
-       good = details.passed;
-       bad = details.failed;
-
-       // Store result when possible
-       if ( config.reorder && defined.sessionStorage ) {
-               if ( bad ) {
-                       sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
-               } else {
-                       sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
-               }
-       }
-
-       if ( bad === 0 ) {
-
-               // Collapse the passing tests
-               addClass( assertList, "qunit-collapsed" );
-       } else if ( bad && config.collapse && !collapseNext ) {
-
-               // Skip collapsing the first failing test
-               collapseNext = true;
-       } else {
-
-               // Collapse remaining tests
-               addClass( assertList, "qunit-collapsed" );
-       }
-
-       // The testItem.firstChild is the test name
-       testTitle = testItem.firstChild;
-
-       testCounts = bad ?
-               "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
-               "";
-
-       testTitle.innerHTML += " <b class='counts'>(" + testCounts +
-               details.assertions.length + ")</b>";
-
-       if ( details.skipped ) {
-               testItem.className = "skipped";
-               skipped = document.createElement( "em" );
-               skipped.className = "qunit-skipped-label";
-               skipped.innerHTML = "skipped";
-               testItem.insertBefore( skipped, testTitle );
-       } else {
-               addEvent( testTitle, "click", function() {
-                       toggleClass( assertList, "qunit-collapsed" );
-               } );
-
-               testItem.className = bad ? "fail" : "pass";
-
-               time = document.createElement( "span" );
-               time.className = "runtime";
-               time.innerHTML = details.runtime + " ms";
-               testItem.insertBefore( time, assertList );
-       }
-
-       // Show the source of the test when showing assertions
-       if ( details.source ) {
-               sourceName = document.createElement( "p" );
-               sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
-               addClass( sourceName, "qunit-source" );
-               if ( bad === 0 ) {
-                       addClass( sourceName, "qunit-collapsed" );
-               }
-               addEvent( testTitle, "click", function() {
-                       toggleClass( sourceName, "qunit-collapsed" );
-               } );
-               testItem.appendChild( sourceName );
-       }
-} );
-
-// Avoid readyState issue with phantomjs
-// Ref: #818
-var notPhantom = ( function( p ) {
-       return !( p && p.version && p.version.major > 0 );
-} )( window.phantom );
-
-if ( notPhantom && document.readyState === "complete" ) {
-       QUnit.load();
-} else {
-       addEvent( window, "load", QUnit.load );
-}
-
-/*
- * This file is a modified version of google-diff-match-patch's JavaScript implementation
- * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
- * modifications are licensed as more fully set forth in LICENSE.txt.
- *
- * The original source of google-diff-match-patch is attributable and licensed as follows:
- *
- * Copyright 2006 Google Inc.
- * https://code.google.com/p/google-diff-match-patch/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * More Info:
- *  https://code.google.com/p/google-diff-match-patch/
- *
- * Usage: QUnit.diff(expected, actual)
- *
- */
-QUnit.diff = ( function() {
-       function DiffMatchPatch() {
-       }
-
-       //  DIFF FUNCTIONS
-
-       /**
-        * The data structure representing a diff is an array of tuples:
-        * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
-        * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
-        */
-       var DIFF_DELETE = -1,
-               DIFF_INSERT = 1,
-               DIFF_EQUAL = 0;
-
-       /**
-        * Find the differences between two texts.  Simplifies the problem by stripping
-        * any common prefix or suffix off the texts before diffing.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean=} optChecklines Optional speedup flag. If present and false,
-        *     then don't run a line-level diff first to identify the changed areas.
-        *     Defaults to true, which does a faster, slightly less optimal diff.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) {
-               var deadline, checklines, commonlength,
-                       commonprefix, commonsuffix, diffs;
-
-               // The diff must be complete in up to 1 second.
-               deadline = ( new Date() ).getTime() + 1000;
-
-               // Check for null inputs.
-               if ( text1 === null || text2 === null ) {
-                       throw new Error( "Null input. (DiffMain)" );
-               }
-
-               // Check for equality (speedup).
-               if ( text1 === text2 ) {
-                       if ( text1 ) {
-                               return [
-                                       [ DIFF_EQUAL, text1 ]
-                               ];
-                       }
-                       return [];
-               }
-
-               if ( typeof optChecklines === "undefined" ) {
-                       optChecklines = true;
-               }
-
-               checklines = optChecklines;
-
-               // Trim off common prefix (speedup).
-               commonlength = this.diffCommonPrefix( text1, text2 );
-               commonprefix = text1.substring( 0, commonlength );
-               text1 = text1.substring( commonlength );
-               text2 = text2.substring( commonlength );
-
-               // Trim off common suffix (speedup).
-               commonlength = this.diffCommonSuffix( text1, text2 );
-               commonsuffix = text1.substring( text1.length - commonlength );
-               text1 = text1.substring( 0, text1.length - commonlength );
-               text2 = text2.substring( 0, text2.length - commonlength );
-
-               // Compute the diff on the middle block.
-               diffs = this.diffCompute( text1, text2, checklines, deadline );
-
-               // Restore the prefix and suffix.
-               if ( commonprefix ) {
-                       diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
-               }
-               if ( commonsuffix ) {
-                       diffs.push( [ DIFF_EQUAL, commonsuffix ] );
-               }
-               this.diffCleanupMerge( diffs );
-               return diffs;
-       };
-
-       /**
-        * Reduce the number of edits by eliminating operationally trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, preIns, preDel, postIns, postDel;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Is there an insertion operation before the last equality.
-               preIns = false;
-
-               // Is there a deletion operation before the last equality.
-               preDel = false;
-
-               // Is there an insertion operation after the last equality.
-               postIns = false;
-
-               // Is there a deletion operation after the last equality.
-               postDel = false;
-               while ( pointer < diffs.length ) {
-
-                       // Equality found.
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) {
-                               if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) {
-
-                                       // Candidate found.
-                                       equalities[ equalitiesLength++ ] = pointer;
-                                       preIns = postIns;
-                                       preDel = postDel;
-                                       lastequality = diffs[ pointer ][ 1 ];
-                               } else {
-
-                                       // Not a candidate, and can never become one.
-                                       equalitiesLength = 0;
-                                       lastequality = null;
-                               }
-                               postIns = postDel = false;
-
-                       // An insertion or deletion.
-                       } else {
-
-                               if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
-                                       postDel = true;
-                               } else {
-                                       postIns = true;
-                               }
-
-                               /*
-                                * Five types to be split:
-                                * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
-                                * <ins>A</ins>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<ins>C</ins>
-                                * <ins>A</del>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<del>C</del>
-                                */
-                               if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
-                                               ( ( lastequality.length < 2 ) &&
-                                               ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-                                       equalitiesLength--; // Throw away the equality we just deleted;
-                                       lastequality = null;
-                                       if ( preIns && preDel ) {
-
-                                               // No changes made which could affect previous entry, keep going.
-                                               postIns = postDel = true;
-                                               equalitiesLength = 0;
-                                       } else {
-                                               equalitiesLength--; // Throw away the previous equality.
-                                               pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-                                               postIns = postDel = false;
-                                       }
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       /**
-        * Convert a diff array into a pretty HTML report.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {integer} string to be beautified.
-        * @return {string} HTML representation.
-        */
-       DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
-               var op, data, x,
-                       html = [];
-               for ( x = 0; x < diffs.length; x++ ) {
-                       op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal)
-                       data = diffs[ x ][ 1 ]; // Text of change.
-                       switch ( op ) {
-                       case DIFF_INSERT:
-                               html[ x ] = "<ins>" + escapeText( data ) + "</ins>";
-                               break;
-                       case DIFF_DELETE:
-                               html[ x ] = "<del>" + escapeText( data ) + "</del>";
-                               break;
-                       case DIFF_EQUAL:
-                               html[ x ] = "<span>" + escapeText( data ) + "</span>";
-                               break;
-                       }
-               }
-               return html.join( "" );
-       };
-
-       /**
-        * Determine the common prefix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the start of each
-        *     string.
-        */
-       DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerstart;
-
-               // Quick check for common null cases.
-               if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerstart = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( pointerstart, pointermid ) ===
-                                       text2.substring( pointerstart, pointermid ) ) {
-                               pointermin = pointermid;
-                               pointerstart = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Determine the common suffix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of each string.
-        */
-       DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerend;
-
-               // Quick check for common null cases.
-               if ( !text1 ||
-                               !text2 ||
-                               text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerend = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
-                                       text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
-                               pointermin = pointermid;
-                               pointerend = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Find the differences between two texts.  Assumes that the texts do not
-        * have any common prefix or suffix.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean} checklines Speedup flag.  If false, then don't run a
-        *     line-level diff first to identify the changed areas.
-        *     If true, then run a faster, slightly less optimal diff.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
-               var diffs, longtext, shorttext, i, hm,
-                       text1A, text2A, text1B, text2B,
-                       midCommon, diffsA, diffsB;
-
-               if ( !text1 ) {
-
-                       // Just add some text (speedup).
-                       return [
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               if ( !text2 ) {
-
-                       // Just delete some text (speedup).
-                       return [
-                               [ DIFF_DELETE, text1 ]
-                       ];
-               }
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               i = longtext.indexOf( shorttext );
-               if ( i !== -1 ) {
-
-                       // Shorter text is inside the longer text (speedup).
-                       diffs = [
-                               [ DIFF_INSERT, longtext.substring( 0, i ) ],
-                               [ DIFF_EQUAL, shorttext ],
-                               [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
-                       ];
-
-                       // Swap insertions for deletions if diff is reversed.
-                       if ( text1.length > text2.length ) {
-                               diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE;
-                       }
-                       return diffs;
-               }
-
-               if ( shorttext.length === 1 ) {
-
-                       // Single character string.
-                       // After the previous speedup, the character can't be an equality.
-                       return [
-                               [ DIFF_DELETE, text1 ],
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               // Check to see if the problem can be split in two.
-               hm = this.diffHalfMatch( text1, text2 );
-               if ( hm ) {
-
-                       // A half-match was found, sort out the return data.
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-                       midCommon = hm[ 4 ];
-
-                       // Send both pairs off for separate processing.
-                       diffsA = this.DiffMain( text1A, text2A, checklines, deadline );
-                       diffsB = this.DiffMain( text1B, text2B, checklines, deadline );
-
-                       // Merge the results.
-                       return diffsA.concat( [
-                               [ DIFF_EQUAL, midCommon ]
-                       ], diffsB );
-               }
-
-               if ( checklines && text1.length > 100 && text2.length > 100 ) {
-                       return this.diffLineMode( text1, text2, deadline );
-               }
-
-               return this.diffBisect( text1, text2, deadline );
-       };
-
-       /**
-        * Do the two texts share a substring which is at least half the length of the
-        * longer text?
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {Array.<string>} Five element Array, containing the prefix of
-        *     text1, the suffix of text1, the prefix of text2, the suffix of
-        *     text2 and the common middle.  Or null if there was no match.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) {
-               var longtext, shorttext, dmp,
-                       text1A, text2B, text2A, text1B, midCommon,
-                       hm1, hm2, hm;
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) {
-                       return null; // Pointless.
-               }
-               dmp = this; // 'this' becomes 'window' in a closure.
-
-               /**
-                * Does a substring of shorttext exist within longtext such that the substring
-                * is at least half the length of longtext?
-                * Closure, but does not reference any external variables.
-                * @param {string} longtext Longer string.
-                * @param {string} shorttext Shorter string.
-                * @param {number} i Start index of quarter length substring within longtext.
-                * @return {Array.<string>} Five element Array, containing the prefix of
-                *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
-                *     of shorttext and the common middle.  Or null if there was no match.
-                * @private
-                */
-               function diffHalfMatchI( longtext, shorttext, i ) {
-                       var seed, j, bestCommon, prefixLength, suffixLength,
-                               bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
-
-                       // Start with a 1/4 length substring at position i as a seed.
-                       seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) );
-                       j = -1;
-                       bestCommon = "";
-                       while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) {
-                               prefixLength = dmp.diffCommonPrefix( longtext.substring( i ),
-                                       shorttext.substring( j ) );
-                               suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ),
-                                       shorttext.substring( 0, j ) );
-                               if ( bestCommon.length < suffixLength + prefixLength ) {
-                                       bestCommon = shorttext.substring( j - suffixLength, j ) +
-                                               shorttext.substring( j, j + prefixLength );
-                                       bestLongtextA = longtext.substring( 0, i - suffixLength );
-                                       bestLongtextB = longtext.substring( i + prefixLength );
-                                       bestShorttextA = shorttext.substring( 0, j - suffixLength );
-                                       bestShorttextB = shorttext.substring( j + prefixLength );
-                               }
-                       }
-                       if ( bestCommon.length * 2 >= longtext.length ) {
-                               return [ bestLongtextA, bestLongtextB,
-                                       bestShorttextA, bestShorttextB, bestCommon
-                               ];
-                       } else {
-                               return null;
-                       }
-               }
-
-               // First check if the second quarter is the seed for a half-match.
-               hm1 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 4 ) );
-
-               // Check again based on the third quarter.
-               hm2 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 2 ) );
-               if ( !hm1 && !hm2 ) {
-                       return null;
-               } else if ( !hm2 ) {
-                       hm = hm1;
-               } else if ( !hm1 ) {
-                       hm = hm2;
-               } else {
-
-                       // Both matched.  Select the longest.
-                       hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2;
-               }
-
-               // A half-match was found, sort out the return data.
-               text1A, text1B, text2A, text2B;
-               if ( text1.length > text2.length ) {
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-               } else {
-                       text2A = hm[ 0 ];
-                       text2B = hm[ 1 ];
-                       text1A = hm[ 2 ];
-                       text1B = hm[ 3 ];
-               }
-               midCommon = hm[ 4 ];
-               return [ text1A, text1B, text2A, text2B, midCommon ];
-       };
-
-       /**
-        * Do a quick line-level diff on both strings, then rediff the parts for
-        * greater accuracy.
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) {
-               var a, diffs, linearray, pointer, countInsert,
-                       countDelete, textInsert, textDelete, j;
-
-               // Scan the text on a line-by-line basis first.
-               a = this.diffLinesToChars( text1, text2 );
-               text1 = a.chars1;
-               text2 = a.chars2;
-               linearray = a.lineArray;
-
-               diffs = this.DiffMain( text1, text2, false, deadline );
-
-               // Convert the diff back to original text.
-               this.diffCharsToLines( diffs, linearray );
-
-               // Eliminate freak matches (e.g. blank lines)
-               this.diffCleanupSemantic( diffs );
-
-               // Rediff any replacement blocks, this time character-by-character.
-               // Add a dummy entry at the end.
-               diffs.push( [ DIFF_EQUAL, "" ] );
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete >= 1 && countInsert >= 1 ) {
-
-                                       // Delete the offending records and add the merged ones.
-                                       diffs.splice( pointer - countDelete - countInsert,
-                                               countDelete + countInsert );
-                                       pointer = pointer - countDelete - countInsert;
-                                       a = this.DiffMain( textDelete, textInsert, false, deadline );
-                                       for ( j = a.length - 1; j >= 0; j-- ) {
-                                               diffs.splice( pointer, 0, a[ j ] );
-                                       }
-                                       pointer = pointer + a.length;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-                       pointer++;
-               }
-               diffs.pop(); // Remove the dummy entry at the end.
-
-               return diffs;
-       };
-
-       /**
-        * Find the 'middle snake' of a diff, split the problem in two
-        * and return the recursively constructed diff.
-        * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) {
-               var text1Length, text2Length, maxD, vOffset, vLength,
-                       v1, v2, x, delta, front, k1start, k1end, k2start,
-                       k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-               maxD = Math.ceil( ( text1Length + text2Length ) / 2 );
-               vOffset = maxD;
-               vLength = 2 * maxD;
-               v1 = new Array( vLength );
-               v2 = new Array( vLength );
-
-               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
-               // integers and undefined.
-               for ( x = 0; x < vLength; x++ ) {
-                       v1[ x ] = -1;
-                       v2[ x ] = -1;
-               }
-               v1[ vOffset + 1 ] = 0;
-               v2[ vOffset + 1 ] = 0;
-               delta = text1Length - text2Length;
-
-               // If the total number of characters is odd, then the front path will collide
-               // with the reverse path.
-               front = ( delta % 2 !== 0 );
-
-               // Offsets for start and end of k loop.
-               // Prevents mapping of space beyond the grid.
-               k1start = 0;
-               k1end = 0;
-               k2start = 0;
-               k2end = 0;
-               for ( d = 0; d < maxD; d++ ) {
-
-                       // Bail out if deadline is reached.
-                       if ( ( new Date() ).getTime() > deadline ) {
-                               break;
-                       }
-
-                       // Walk the front path one step.
-                       for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) {
-                               k1Offset = vOffset + k1;
-                               if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
-                                       x1 = v1[ k1Offset + 1 ];
-                               } else {
-                                       x1 = v1[ k1Offset - 1 ] + 1;
-                               }
-                               y1 = x1 - k1;
-                               while ( x1 < text1Length && y1 < text2Length &&
-                                       text1.charAt( x1 ) === text2.charAt( y1 ) ) {
-                                       x1++;
-                                       y1++;
-                               }
-                               v1[ k1Offset ] = x1;
-                               if ( x1 > text1Length ) {
-
-                                       // Ran off the right of the graph.
-                                       k1end += 2;
-                               } else if ( y1 > text2Length ) {
-
-                                       // Ran off the bottom of the graph.
-                                       k1start += 2;
-                               } else if ( front ) {
-                                       k2Offset = vOffset + delta - k1;
-                                       if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) {
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - v2[ k2Offset ];
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-
-                       // Walk the reverse path one step.
-                       for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) {
-                               k2Offset = vOffset + k2;
-                               if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
-                                       x2 = v2[ k2Offset + 1 ];
-                               } else {
-                                       x2 = v2[ k2Offset - 1 ] + 1;
-                               }
-                               y2 = x2 - k2;
-                               while ( x2 < text1Length && y2 < text2Length &&
-                                       text1.charAt( text1Length - x2 - 1 ) ===
-                                       text2.charAt( text2Length - y2 - 1 ) ) {
-                                       x2++;
-                                       y2++;
-                               }
-                               v2[ k2Offset ] = x2;
-                               if ( x2 > text1Length ) {
-
-                                       // Ran off the left of the graph.
-                                       k2end += 2;
-                               } else if ( y2 > text2Length ) {
-
-                                       // Ran off the top of the graph.
-                                       k2start += 2;
-                               } else if ( !front ) {
-                                       k1Offset = vOffset + delta - k2;
-                                       if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) {
-                                               x1 = v1[ k1Offset ];
-                                               y1 = vOffset + x1 - k1Offset;
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - x2;
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // Diff took too long and hit the deadline or
-               // number of diffs equals number of characters, no commonality at all.
-               return [
-                       [ DIFF_DELETE, text1 ],
-                       [ DIFF_INSERT, text2 ]
-               ];
-       };
-
-       /**
-        * Given the location of the 'middle snake', split the diff in two parts
-        * and recurse.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} x Index of split point in text1.
-        * @param {number} y Index of split point in text2.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
-               var text1a, text1b, text2a, text2b, diffs, diffsb;
-               text1a = text1.substring( 0, x );
-               text2a = text2.substring( 0, y );
-               text1b = text1.substring( x );
-               text2b = text2.substring( y );
-
-               // Compute both diffs serially.
-               diffs = this.DiffMain( text1a, text2a, false, deadline );
-               diffsb = this.DiffMain( text1b, text2b, false, deadline );
-
-               return diffs.concat( diffsb );
-       };
-
-       /**
-        * Reduce the number of edits by eliminating semantically trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
-                       lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Number of characters that changed prior to the equality.
-               lengthInsertions1 = 0;
-               lengthDeletions1 = 0;
-
-               // Number of characters that changed after the equality.
-               lengthInsertions2 = 0;
-               lengthDeletions2 = 0;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
-                               equalities[ equalitiesLength++ ] = pointer;
-                               lengthInsertions1 = lengthInsertions2;
-                               lengthDeletions1 = lengthDeletions2;
-                               lengthInsertions2 = 0;
-                               lengthDeletions2 = 0;
-                               lastequality = diffs[ pointer ][ 1 ];
-                       } else { // An insertion or deletion.
-                               if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                                       lengthInsertions2 += diffs[ pointer ][ 1 ].length;
-                               } else {
-                                       lengthDeletions2 += diffs[ pointer ][ 1 ].length;
-                               }
-
-                               // Eliminate an equality that is smaller or equal to the edits on both
-                               // sides of it.
-                               if ( lastequality && ( lastequality.length <=
-                                               Math.max( lengthInsertions1, lengthDeletions1 ) ) &&
-                                               ( lastequality.length <= Math.max( lengthInsertions2,
-                                                       lengthDeletions2 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-
-                                       // Throw away the equality we just deleted.
-                                       equalitiesLength--;
-
-                                       // Throw away the previous equality (it needs to be reevaluated).
-                                       equalitiesLength--;
-                                       pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-
-                                       // Reset the counters.
-                                       lengthInsertions1 = 0;
-                                       lengthDeletions1 = 0;
-                                       lengthInsertions2 = 0;
-                                       lengthDeletions2 = 0;
-                                       lastequality = null;
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // Normalize the diff.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-
-               // Find any overlaps between deletions and insertions.
-               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
-               //   -> <del>abc</del>xxx<ins>def</ins>
-               // e.g: <del>xxxabc</del><ins>defxxx</ins>
-               //   -> <ins>def</ins>xxx<del>abc</del>
-               // Only extract an overlap if it is as big as the edit ahead or behind it.
-               pointer = 1;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE &&
-                                       diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                               deletion = diffs[ pointer - 1 ][ 1 ];
-                               insertion = diffs[ pointer ][ 1 ];
-                               overlapLength1 = this.diffCommonOverlap( deletion, insertion );
-                               overlapLength2 = this.diffCommonOverlap( insertion, deletion );
-                               if ( overlapLength1 >= overlapLength2 ) {
-                                       if ( overlapLength1 >= deletion.length / 2 ||
-                                                       overlapLength1 >= insertion.length / 2 ) {
-
-                                               // Overlap found.  Insert an equality and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ]
-                                               );
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       deletion.substring( 0, deletion.length - overlapLength1 );
-                                               diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 );
-                                               pointer++;
-                                       }
-                               } else {
-                                       if ( overlapLength2 >= deletion.length / 2 ||
-                                                       overlapLength2 >= insertion.length / 2 ) {
-
-                                               // Reverse overlap found.
-                                               // Insert an equality and swap and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ]
-                                               );
-
-                                               diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT;
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       insertion.substring( 0, insertion.length - overlapLength2 );
-                                               diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE;
-                                               diffs[ pointer + 1 ][ 1 ] =
-                                                       deletion.substring( overlapLength2 );
-                                               pointer++;
-                                       }
-                               }
-                               pointer++;
-                       }
-                       pointer++;
-               }
-       };
-
-       /**
-        * Determine if the suffix of one string is the prefix of another.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of the first
-        *     string and the start of the second string.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) {
-               var text1Length, text2Length, textLength,
-                       best, length, pattern, found;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-
-               // Eliminate the null case.
-               if ( text1Length === 0 || text2Length === 0 ) {
-                       return 0;
-               }
-
-               // Truncate the longer string.
-               if ( text1Length > text2Length ) {
-                       text1 = text1.substring( text1Length - text2Length );
-               } else if ( text1Length < text2Length ) {
-                       text2 = text2.substring( 0, text1Length );
-               }
-               textLength = Math.min( text1Length, text2Length );
-
-               // Quick check for the worst case.
-               if ( text1 === text2 ) {
-                       return textLength;
-               }
-
-               // Start by looking for a single character match
-               // and increase length until no match is found.
-               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
-               best = 0;
-               length = 1;
-               while ( true ) {
-                       pattern = text1.substring( textLength - length );
-                       found = text2.indexOf( pattern );
-                       if ( found === -1 ) {
-                               return best;
-                       }
-                       length += found;
-                       if ( found === 0 || text1.substring( textLength - length ) ===
-                                       text2.substring( 0, length ) ) {
-                               best = length;
-                               length++;
-                       }
-               }
-       };
-
-       /**
-        * Split two texts into an array of strings.  Reduce the texts to a string of
-        * hashes where each Unicode character represents one line.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
-        *     An object containing the encoded text1, the encoded text2 and
-        *     the array of unique strings.
-        *     The zeroth element of the array of unique strings is intentionally blank.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {
-               var lineArray, lineHash, chars1, chars2;
-               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
-               lineHash = {};  // E.g. lineHash['Hello\n'] === 4
-
-               // '\x00' is a valid character, but various debuggers don't like it.
-               // So we'll insert a junk entry to avoid generating a null character.
-               lineArray[ 0 ] = "";
-
-               /**
-                * Split a text into an array of strings.  Reduce the texts to a string of
-                * hashes where each Unicode character represents one line.
-                * Modifies linearray and linehash through being a closure.
-                * @param {string} text String to encode.
-                * @return {string} Encoded string.
-                * @private
-                */
-               function diffLinesToCharsMunge( text ) {
-                       var chars, lineStart, lineEnd, lineArrayLength, line;
-                       chars = "";
-
-                       // Walk the text, pulling out a substring for each line.
-                       // text.split('\n') would would temporarily double our memory footprint.
-                       // Modifying text would create many large strings to garbage collect.
-                       lineStart = 0;
-                       lineEnd = -1;
-
-                       // Keeping our own length variable is faster than looking it up.
-                       lineArrayLength = lineArray.length;
-                       while ( lineEnd < text.length - 1 ) {
-                               lineEnd = text.indexOf( "\n", lineStart );
-                               if ( lineEnd === -1 ) {
-                                       lineEnd = text.length - 1;
-                               }
-                               line = text.substring( lineStart, lineEnd + 1 );
-                               lineStart = lineEnd + 1;
-
-                               if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) :
-                                                       ( lineHash[ line ] !== undefined ) ) {
-                                       chars += String.fromCharCode( lineHash[ line ] );
-                               } else {
-                                       chars += String.fromCharCode( lineArrayLength );
-                                       lineHash[ line ] = lineArrayLength;
-                                       lineArray[ lineArrayLength++ ] = line;
-                               }
-                       }
-                       return chars;
-               }
-
-               chars1 = diffLinesToCharsMunge( text1 );
-               chars2 = diffLinesToCharsMunge( text2 );
-               return {
-                       chars1: chars1,
-                       chars2: chars2,
-                       lineArray: lineArray
-               };
-       };
-
-       /**
-        * Rehydrate the text in a diff from a string of line hashes to real lines of
-        * text.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {!Array.<string>} lineArray Array of unique strings.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
-               var x, chars, text, y;
-               for ( x = 0; x < diffs.length; x++ ) {
-                       chars = diffs[ x ][ 1 ];
-                       text = [];
-                       for ( y = 0; y < chars.length; y++ ) {
-                               text[ y ] = lineArray[ chars.charCodeAt( y ) ];
-                       }
-                       diffs[ x ][ 1 ] = text.join( "" );
-               }
-       };
-
-       /**
-        * Reorder and merge like edit sections.  Merge equalities.
-        * Any edit section can move as long as it doesn't cross an equality.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) {
-               var pointer, countDelete, countInsert, textInsert, textDelete,
-                       commonlength, changes, diffPointer, position;
-               diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               commonlength;
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete + countInsert > 1 ) {
-                                       if ( countDelete !== 0 && countInsert !== 0 ) {
-
-                                               // Factor out any common prefixes.
-                                               commonlength = this.diffCommonPrefix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       if ( ( pointer - countDelete - countInsert ) > 0 &&
-                                                                       diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] ===
-                                                                       DIFF_EQUAL ) {
-                                                               diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] +=
-                                                                       textInsert.substring( 0, commonlength );
-                                                       } else {
-                                                               diffs.splice( 0, 0, [ DIFF_EQUAL,
-                                                                       textInsert.substring( 0, commonlength )
-                                                               ] );
-                                                               pointer++;
-                                                       }
-                                                       textInsert = textInsert.substring( commonlength );
-                                                       textDelete = textDelete.substring( commonlength );
-                                               }
-
-                                               // Factor out any common suffixies.
-                                               commonlength = this.diffCommonSuffix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length -
-                                                                       commonlength ) + diffs[ pointer ][ 1 ];
-                                                       textInsert = textInsert.substring( 0, textInsert.length -
-                                                               commonlength );
-                                                       textDelete = textDelete.substring( 0, textDelete.length -
-                                                               commonlength );
-                                               }
-                                       }
-
-                                       // Delete the offending records and add the merged ones.
-                                       if ( countDelete === 0 ) {
-                                               diffs.splice( pointer - countInsert,
-                                                       countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
-                                       } else if ( countInsert === 0 ) {
-                                               diffs.splice( pointer - countDelete,
-                                                       countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
-                                       } else {
-                                               diffs.splice(
-                                                       pointer - countDelete - countInsert,
-                                                       countDelete + countInsert,
-                                                       [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ]
-                                               );
-                                       }
-                                       pointer = pointer - countDelete - countInsert +
-                                               ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1;
-                               } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                                       // Merge this equality with the previous one.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ];
-                                       diffs.splice( pointer, 1 );
-                               } else {
-                                       pointer++;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-               }
-               if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) {
-                       diffs.pop(); // Remove the dummy entry at the end.
-               }
-
-               // Second pass: look for single edits surrounded on both sides by equalities
-               // which can be shifted sideways to eliminate an equality.
-               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
-               changes = false;
-               pointer = 1;
-
-               // Intentionally ignore the first and last element (don't need checking).
-               while ( pointer < diffs.length - 1 ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL &&
-                                       diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                               diffPointer = diffs[ pointer ][ 1 ];
-                               position = diffPointer.substring(
-                                       diffPointer.length - diffs[ pointer - 1 ][ 1 ].length
-                               );
-
-                               // This is a single edit surrounded by equalities.
-                               if ( position === diffs[ pointer - 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the previous equality.
-                                       diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] +
-                                               diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length -
-                                                       diffs[ pointer - 1 ][ 1 ].length );
-                                       diffs[ pointer + 1 ][ 1 ] =
-                                               diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer - 1, 1 );
-                                       changes = true;
-                               } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
-                                               diffs[ pointer + 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the next equality.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ];
-                                       diffs[ pointer ][ 1 ] =
-                                               diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) +
-                                               diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer + 1, 1 );
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // If shifts were made, the diff needs reordering and another shift sweep.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       return function( o, n ) {
-               var diff, output, text;
-               diff = new DiffMatchPatch();
-               output = diff.DiffMain( o, n );
-               diff.diffCleanupEfficiency( output );
-               text = diff.diffPrettyHtml( output );
-
-               return text;
-       };
-}() );
-
-}() );
+
+
+  var classCallCheck = function (instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var createClass = function () {
+    function defineProperties(target, props) {
+      for (var i = 0; i < props.length; i++) {
+        var descriptor = props[i];
+        descriptor.enumerable = descriptor.enumerable || false;
+        descriptor.configurable = true;
+        if ("value" in descriptor) descriptor.writable = true;
+        Object.defineProperty(target, descriptor.key, descriptor);
+      }
+    }
+
+    return function (Constructor, protoProps, staticProps) {
+      if (protoProps) defineProperties(Constructor.prototype, protoProps);
+      if (staticProps) defineProperties(Constructor, staticProps);
+      return Constructor;
+    };
+  }();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  var toConsumableArray = function (arr) {
+    if (Array.isArray(arr)) {
+      for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
+
+      return arr2;
+    } else {
+      return Array.from(arr);
+    }
+  };
+
+  var toString = Object.prototype.toString;
+  var hasOwn = Object.prototype.hasOwnProperty;
+  var now = Date.now || function () {
+       return new Date().getTime();
+  };
+
+  var defined = {
+       document: window && window.document !== undefined,
+       setTimeout: setTimeout !== undefined
+  };
+
+  // Returns a new Array with the elements that are in a but not in b
+  function diff(a, b) {
+       var i,
+           j,
+           result = a.slice();
+
+       for (i = 0; i < result.length; i++) {
+               for (j = 0; j < b.length; j++) {
+                       if (result[i] === b[j]) {
+                               result.splice(i, 1);
+                               i--;
+                               break;
+                       }
+               }
+       }
+       return result;
+  }
+
+  /**
+   * Determines whether an element exists in a given array or not.
+   *
+   * @method inArray
+   * @param {Any} elem
+   * @param {Array} array
+   * @return {Boolean}
+   */
+  function inArray(elem, array) {
+       return array.indexOf(elem) !== -1;
+  }
+
+  /**
+   * Makes a clone of an object using only Array or Object as base,
+   * and copies over the own enumerable properties.
+   *
+   * @param {Object} obj
+   * @return {Object} New object with only the own properties (recursively).
+   */
+  function objectValues(obj) {
+       var key,
+           val,
+           vals = is("array", obj) ? [] : {};
+       for (key in obj) {
+               if (hasOwn.call(obj, key)) {
+                       val = obj[key];
+                       vals[key] = val === Object(val) ? objectValues(val) : val;
+               }
+       }
+       return vals;
+  }
+
+  function extend(a, b, undefOnly) {
+       for (var prop in b) {
+               if (hasOwn.call(b, prop)) {
+                       if (b[prop] === undefined) {
+                               delete a[prop];
+                       } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
+                               a[prop] = b[prop];
+                       }
+               }
+       }
+
+       return a;
+  }
+
+  function objectType(obj) {
+       if (typeof obj === "undefined") {
+               return "undefined";
+       }
+
+       // Consider: typeof null === object
+       if (obj === null) {
+               return "null";
+       }
+
+       var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
+           type = match && match[1];
+
+       switch (type) {
+               case "Number":
+                       if (isNaN(obj)) {
+                               return "nan";
+                       }
+                       return "number";
+               case "String":
+               case "Boolean":
+               case "Array":
+               case "Set":
+               case "Map":
+               case "Date":
+               case "RegExp":
+               case "Function":
+               case "Symbol":
+                       return type.toLowerCase();
+               default:
+                       return typeof obj === "undefined" ? "undefined" : _typeof(obj);
+       }
+  }
+
+  // Safe object type checking
+  function is(type, obj) {
+       return objectType(obj) === type;
+  }
+
+  // Based on Java's String.hashCode, a simple but not
+  // rigorously collision resistant hashing function
+  function generateHash(module, testName) {
+       var str = module + "\x1C" + testName;
+       var hash = 0;
+
+       for (var i = 0; i < str.length; i++) {
+               hash = (hash << 5) - hash + str.charCodeAt(i);
+               hash |= 0;
+       }
+
+       // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+       // strictly necessary but increases user understanding that the id is a SHA-like hash
+       var hex = (0x100000000 + hash).toString(16);
+       if (hex.length < 8) {
+               hex = "0000000" + hex;
+       }
+
+       return hex.slice(-8);
+  }
+
+  // Test for equality any JavaScript type.
+  // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
+  var equiv = (function () {
+
+       // Value pairs queued for comparison. Used for breadth-first processing order, recursion
+       // detection and avoiding repeated comparison (see below for details).
+       // Elements are { a: val, b: val }.
+       var pairs = [];
+
+       var getProto = Object.getPrototypeOf || function (obj) {
+               return obj.__proto__;
+       };
+
+       function useStrictEquality(a, b) {
+
+               // This only gets called if a and b are not strict equal, and is used to compare on
+               // the primitive values inside object wrappers. For example:
+               // `var i = 1;`
+               // `var j = new Number(1);`
+               // Neither a nor b can be null, as a !== b and they have the same type.
+               if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
+                       a = a.valueOf();
+               }
+               if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
+                       b = b.valueOf();
+               }
+
+               return a === b;
+       }
+
+       function compareConstructors(a, b) {
+               var protoA = getProto(a);
+               var protoB = getProto(b);
+
+               // Comparing constructors is more strict than using `instanceof`
+               if (a.constructor === b.constructor) {
+                       return true;
+               }
+
+               // Ref #851
+               // If the obj prototype descends from a null constructor, treat it
+               // as a null prototype.
+               if (protoA && protoA.constructor === null) {
+                       protoA = null;
+               }
+               if (protoB && protoB.constructor === null) {
+                       protoB = null;
+               }
+
+               // Allow objects with no prototype to be equivalent to
+               // objects with Object as their constructor.
+               if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       function getRegExpFlags(regexp) {
+               return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
+       }
+
+       function isContainer(val) {
+               return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
+       }
+
+       function breadthFirstCompareChild(a, b) {
+
+               // If a is a container not reference-equal to b, postpone the comparison to the
+               // end of the pairs queue -- unless (a, b) has been seen before, in which case skip
+               // over the pair.
+               if (a === b) {
+                       return true;
+               }
+               if (!isContainer(a)) {
+                       return typeEquiv(a, b);
+               }
+               if (pairs.every(function (pair) {
+                       return pair.a !== a || pair.b !== b;
+               })) {
+
+                       // Not yet started comparing this pair
+                       pairs.push({ a: a, b: b });
+               }
+               return true;
+       }
+
+       var callbacks = {
+               "string": useStrictEquality,
+               "boolean": useStrictEquality,
+               "number": useStrictEquality,
+               "null": useStrictEquality,
+               "undefined": useStrictEquality,
+               "symbol": useStrictEquality,
+               "date": useStrictEquality,
+
+               "nan": function nan() {
+                       return true;
+               },
+
+               "regexp": function regexp(a, b) {
+                       return a.source === b.source &&
+
+                       // Include flags in the comparison
+                       getRegExpFlags(a) === getRegExpFlags(b);
+               },
+
+               // abort (identical references / instance methods were skipped earlier)
+               "function": function _function() {
+                       return false;
+               },
+
+               "array": function array(a, b) {
+                       var i, len;
+
+                       len = a.length;
+                       if (len !== b.length) {
+
+                               // Safe and faster
+                               return false;
+                       }
+
+                       for (i = 0; i < len; i++) {
+
+                               // Compare non-containers; queue non-reference-equal containers
+                               if (!breadthFirstCompareChild(a[i], b[i])) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               },
+
+               // Define sets a and b to be equivalent if for each element aVal in a, there
+               // is some element bVal in b such that aVal and bVal are equivalent. Element
+               // repetitions are not counted, so these are equivalent:
+               // a = new Set( [ {}, [], [] ] );
+               // b = new Set( [ {}, {}, [] ] );
+               "set": function set$$1(a, b) {
+                       var innerEq,
+                           outerEq = true;
+
+                       if (a.size !== b.size) {
+
+                               // This optimization has certain quirks because of the lack of
+                               // repetition counting. For instance, adding the same
+                               // (reference-identical) element to two equivalent sets can
+                               // make them non-equivalent.
+                               return false;
+                       }
+
+                       a.forEach(function (aVal) {
+
+                               // Short-circuit if the result is already known. (Using for...of
+                               // with a break clause would be cleaner here, but it would cause
+                               // a syntax error on older Javascript implementations even if
+                               // Set is unused)
+                               if (!outerEq) {
+                                       return;
+                               }
+
+                               innerEq = false;
+
+                               b.forEach(function (bVal) {
+                                       var parentPairs;
+
+                                       // Likewise, short-circuit if the result is already known
+                                       if (innerEq) {
+                                               return;
+                                       }
+
+                                       // Swap out the global pairs list, as the nested call to
+                                       // innerEquiv will clobber its contents
+                                       parentPairs = pairs;
+                                       if (innerEquiv(bVal, aVal)) {
+                                               innerEq = true;
+                                       }
+
+                                       // Replace the global pairs list
+                                       pairs = parentPairs;
+                               });
+
+                               if (!innerEq) {
+                                       outerEq = false;
+                               }
+                       });
+
+                       return outerEq;
+               },
+
+               // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
+               // in a, there is some key-value pair (bKey, bVal) in b such that
+               // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
+               // counted, so these are equivalent:
+               // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
+               // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
+               "map": function map(a, b) {
+                       var innerEq,
+                           outerEq = true;
+
+                       if (a.size !== b.size) {
+
+                               // This optimization has certain quirks because of the lack of
+                               // repetition counting. For instance, adding the same
+                               // (reference-identical) key-value pair to two equivalent maps
+                               // can make them non-equivalent.
+                               return false;
+                       }
+
+                       a.forEach(function (aVal, aKey) {
+
+                               // Short-circuit if the result is already known. (Using for...of
+                               // with a break clause would be cleaner here, but it would cause
+                               // a syntax error on older Javascript implementations even if
+                               // Map is unused)
+                               if (!outerEq) {
+                                       return;
+                               }
+
+                               innerEq = false;
+
+                               b.forEach(function (bVal, bKey) {
+                                       var parentPairs;
+
+                                       // Likewise, short-circuit if the result is already known
+                                       if (innerEq) {
+                                               return;
+                                       }
+
+                                       // Swap out the global pairs list, as the nested call to
+                                       // innerEquiv will clobber its contents
+                                       parentPairs = pairs;
+                                       if (innerEquiv([bVal, bKey], [aVal, aKey])) {
+                                               innerEq = true;
+                                       }
+
+                                       // Replace the global pairs list
+                                       pairs = parentPairs;
+                               });
+
+                               if (!innerEq) {
+                                       outerEq = false;
+                               }
+                       });
+
+                       return outerEq;
+               },
+
+               "object": function object(a, b) {
+                       var i,
+                           aProperties = [],
+                           bProperties = [];
+
+                       if (compareConstructors(a, b) === false) {
+                               return false;
+                       }
+
+                       // Be strict: don't ensure hasOwnProperty and go deep
+                       for (i in a) {
+
+                               // Collect a's properties
+                               aProperties.push(i);
+
+                               // Skip OOP methods that look the same
+                               if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
+                                       continue;
+                               }
+
+                               // Compare non-containers; queue non-reference-equal containers
+                               if (!breadthFirstCompareChild(a[i], b[i])) {
+                                       return false;
+                               }
+                       }
+
+                       for (i in b) {
+
+                               // Collect b's properties
+                               bProperties.push(i);
+                       }
+
+                       // Ensures identical properties name
+                       return typeEquiv(aProperties.sort(), bProperties.sort());
+               }
+       };
+
+       function typeEquiv(a, b) {
+               var type = objectType(a);
+
+               // Callbacks for containers will append to the pairs queue to achieve breadth-first
+               // search order. The pairs queue is also used to avoid reprocessing any pair of
+               // containers that are reference-equal to a previously visited pair (a special case
+               // this being recursion detection).
+               //
+               // Because of this approach, once typeEquiv returns a false value, it should not be
+               // called again without clearing the pair queue else it may wrongly report a visited
+               // pair as being equivalent.
+               return objectType(b) === type && callbacks[type](a, b);
+       }
+
+       function innerEquiv(a, b) {
+               var i, pair;
+
+               // We're done when there's nothing more to compare
+               if (arguments.length < 2) {
+                       return true;
+               }
+
+               // Clear the global pair queue and add the top-level values being compared
+               pairs = [{ a: a, b: b }];
+
+               for (i = 0; i < pairs.length; i++) {
+                       pair = pairs[i];
+
+                       // Perform type-specific comparison on any pairs that are not strictly
+                       // equal. For container types, that comparison will postpone comparison
+                       // of any sub-container pair to the end of the pair queue. This gives
+                       // breadth-first search order. It also avoids the reprocessing of
+                       // reference-equal siblings, cousins etc, which can have a significant speed
+                       // impact when comparing a container of small objects each of which has a
+                       // reference to the same (singleton) large object.
+                       if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
+                               return false;
+                       }
+               }
+
+               // ...across all consecutive argument pairs
+               return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
+       }
+
+       return function () {
+               var result = innerEquiv.apply(undefined, arguments);
+
+               // Release any retained objects
+               pairs.length = 0;
+               return result;
+       };
+  })();
+
+  /**
+   * Config object: Maintain internal state
+   * Later exposed as QUnit.config
+   * `config` initialized at top of scope
+   */
+  var config = {
+
+       // The queue of tests to run
+       queue: [],
+
+       // Block until document ready
+       blocking: true,
+
+       // By default, run previously failed tests first
+       // very useful in combination with "Hide passed tests" checked
+       reorder: true,
+
+       // By default, modify document.title when suite is done
+       altertitle: true,
+
+       // HTML Reporter: collapse every test except the first failing test
+       // If false, all failing tests will be expanded
+       collapse: true,
+
+       // By default, scroll to top of the page when suite is done
+       scrolltop: true,
+
+       // Depth up-to which object will be dumped
+       maxDepth: 5,
+
+       // When enabled, all tests must call expect()
+       requireExpects: false,
+
+       // Placeholder for user-configurable form-exposed URL parameters
+       urlConfig: [],
+
+       // Set of all modules.
+       modules: [],
+
+       // The first unnamed module
+       currentModule: {
+               name: "",
+               tests: [],
+               childModules: [],
+               testsRun: 0,
+               unskippedTestsRun: 0,
+               hooks: {
+                       before: [],
+                       beforeEach: [],
+                       afterEach: [],
+                       after: []
+               }
+       },
+
+       callbacks: {},
+
+       // The storage module to use for reordering tests
+       storage: localSessionStorage
+  };
+
+  // take a predefined QUnit.config and extend the defaults
+  var globalConfig = window && window.QUnit && window.QUnit.config;
+
+  // only extend the global config if there is no QUnit overload
+  if (window && window.QUnit && !window.QUnit.version) {
+       extend(config, globalConfig);
+  }
+
+  // Push a loose unnamed module to the modules collection
+  config.modules.push(config.currentModule);
+
+  // Based on jsDump by Ariel Flesler
+  // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+  var dump = (function () {
+       function quote(str) {
+               return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
+       }
+       function literal(o) {
+               return o + "";
+       }
+       function join(pre, arr, post) {
+               var s = dump.separator(),
+                   base = dump.indent(),
+                   inner = dump.indent(1);
+               if (arr.join) {
+                       arr = arr.join("," + s + inner);
+               }
+               if (!arr) {
+                       return pre + post;
+               }
+               return [pre, inner + arr, base + post].join(s);
+       }
+       function array(arr, stack) {
+               var i = arr.length,
+                   ret = new Array(i);
+
+               if (dump.maxDepth && dump.depth > dump.maxDepth) {
+                       return "[object Array]";
+               }
+
+               this.up();
+               while (i--) {
+                       ret[i] = this.parse(arr[i], undefined, stack);
+               }
+               this.down();
+               return join("[", ret, "]");
+       }
+
+       function isArray(obj) {
+               return (
+
+                       //Native Arrays
+                       toString.call(obj) === "[object Array]" ||
+
+                       // NodeList objects
+                       typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
+               );
+       }
+
+       var reName = /^function (\w+)/,
+           dump = {
+
+               // The objType is used mostly internally, you can fix a (custom) type in advance
+               parse: function parse(obj, objType, stack) {
+                       stack = stack || [];
+                       var res,
+                           parser,
+                           parserType,
+                           objIndex = stack.indexOf(obj);
+
+                       if (objIndex !== -1) {
+                               return "recursion(" + (objIndex - stack.length) + ")";
+                       }
+
+                       objType = objType || this.typeOf(obj);
+                       parser = this.parsers[objType];
+                       parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
+
+                       if (parserType === "function") {
+                               stack.push(obj);
+                               res = parser.call(this, obj, stack);
+                               stack.pop();
+                               return res;
+                       }
+                       return parserType === "string" ? parser : this.parsers.error;
+               },
+               typeOf: function typeOf(obj) {
+                       var type;
+
+                       if (obj === null) {
+                               type = "null";
+                       } else if (typeof obj === "undefined") {
+                               type = "undefined";
+                       } else if (is("regexp", obj)) {
+                               type = "regexp";
+                       } else if (is("date", obj)) {
+                               type = "date";
+                       } else if (is("function", obj)) {
+                               type = "function";
+                       } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
+                               type = "window";
+                       } else if (obj.nodeType === 9) {
+                               type = "document";
+                       } else if (obj.nodeType) {
+                               type = "node";
+                       } else if (isArray(obj)) {
+                               type = "array";
+                       } else if (obj.constructor === Error.prototype.constructor) {
+                               type = "error";
+                       } else {
+                               type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
+                       }
+                       return type;
+               },
+
+               separator: function separator() {
+                       if (this.multiline) {
+                               return this.HTML ? "<br />" : "\n";
+                       } else {
+                               return this.HTML ? "&#160;" : " ";
+                       }
+               },
+
+               // Extra can be a number, shortcut for increasing-calling-decreasing
+               indent: function indent(extra) {
+                       if (!this.multiline) {
+                               return "";
+                       }
+                       var chr = this.indentChar;
+                       if (this.HTML) {
+                               chr = chr.replace(/\t/g, "   ").replace(/ /g, "&#160;");
+                       }
+                       return new Array(this.depth + (extra || 0)).join(chr);
+               },
+               up: function up(a) {
+                       this.depth += a || 1;
+               },
+               down: function down(a) {
+                       this.depth -= a || 1;
+               },
+               setParser: function setParser(name, parser) {
+                       this.parsers[name] = parser;
+               },
+
+               // The next 3 are exposed so you can use them
+               quote: quote,
+               literal: literal,
+               join: join,
+               depth: 1,
+               maxDepth: config.maxDepth,
+
+               // This is the list of parsers, to modify them, use dump.setParser
+               parsers: {
+                       window: "[Window]",
+                       document: "[Document]",
+                       error: function error(_error) {
+                               return "Error(\"" + _error.message + "\")";
+                       },
+                       unknown: "[Unknown]",
+                       "null": "null",
+                       "undefined": "undefined",
+                       "function": function _function(fn) {
+                               var ret = "function",
+
+
+                               // Functions never have name in IE
+                               name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+                               if (name) {
+                                       ret += " " + name;
+                               }
+                               ret += "(";
+
+                               ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
+                               return join(ret, dump.parse(fn, "functionCode"), "}");
+                       },
+                       array: array,
+                       nodelist: array,
+                       "arguments": array,
+                       object: function object(map, stack) {
+                               var keys,
+                                   key,
+                                   val,
+                                   i,
+                                   nonEnumerableProperties,
+                                   ret = [];
+
+                               if (dump.maxDepth && dump.depth > dump.maxDepth) {
+                                       return "[object Object]";
+                               }
+
+                               dump.up();
+                               keys = [];
+                               for (key in map) {
+                                       keys.push(key);
+                               }
+
+                               // Some properties are not always enumerable on Error objects.
+                               nonEnumerableProperties = ["message", "name"];
+                               for (i in nonEnumerableProperties) {
+                                       key = nonEnumerableProperties[i];
+                                       if (key in map && !inArray(key, keys)) {
+                                               keys.push(key);
+                                       }
+                               }
+                               keys.sort();
+                               for (i = 0; i < keys.length; i++) {
+                                       key = keys[i];
+                                       val = map[key];
+                                       ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
+                               }
+                               dump.down();
+                               return join("{", ret, "}");
+                       },
+                       node: function node(_node) {
+                               var len,
+                                   i,
+                                   val,
+                                   open = dump.HTML ? "&lt;" : "<",
+                                   close = dump.HTML ? "&gt;" : ">",
+                                   tag = _node.nodeName.toLowerCase(),
+                                   ret = open + tag,
+                                   attrs = _node.attributes;
+
+                               if (attrs) {
+                                       for (i = 0, len = attrs.length; i < len; i++) {
+                                               val = attrs[i].nodeValue;
+
+                                               // IE6 includes all attributes in .attributes, even ones not explicitly
+                                               // set. Those have values like undefined, null, 0, false, "" or
+                                               // "inherit".
+                                               if (val && val !== "inherit") {
+                                                       ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
+                                               }
+                                       }
+                               }
+                               ret += close;
+
+                               // Show content of TextNode or CDATASection
+                               if (_node.nodeType === 3 || _node.nodeType === 4) {
+                                       ret += _node.nodeValue;
+                               }
+
+                               return ret + open + "/" + tag + close;
+                       },
+
+                       // Function calls it internally, it's the arguments part of the function
+                       functionArgs: function functionArgs(fn) {
+                               var args,
+                                   l = fn.length;
+
+                               if (!l) {
+                                       return "";
+                               }
+
+                               args = new Array(l);
+                               while (l--) {
+
+                                       // 97 is 'a'
+                                       args[l] = String.fromCharCode(97 + l);
+                               }
+                               return " " + args.join(", ") + " ";
+                       },
+
+                       // Object calls it internally, the key part of an item in a map
+                       key: quote,
+
+                       // Function calls it internally, it's the content of the function
+                       functionCode: "[code]",
+
+                       // Node calls it internally, it's a html attribute value
+                       attribute: quote,
+                       string: quote,
+                       date: quote,
+                       regexp: literal,
+                       number: literal,
+                       "boolean": literal,
+                       symbol: function symbol(sym) {
+                               return sym.toString();
+                       }
+               },
+
+               // If true, entities are escaped ( <, >, \t, space and \n )
+               HTML: false,
+
+               // Indentation unit
+               indentChar: "  ",
+
+               // If true, items in a collection, are separated by a \n, else just a space.
+               multiline: true
+       };
+
+       return dump;
+  })();
+
+  var LISTENERS = Object.create(null);
+  var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
+
+  /**
+   * Emits an event with the specified data to all currently registered listeners.
+   * Callbacks will fire in the order in which they are registered (FIFO). This
+   * function is not exposed publicly; it is used by QUnit internals to emit
+   * logging events.
+   *
+   * @private
+   * @method emit
+   * @param {String} eventName
+   * @param {Object} data
+   * @return {Void}
+   */
+  function emit(eventName, data) {
+       if (objectType(eventName) !== "string") {
+               throw new TypeError("eventName must be a string when emitting an event");
+       }
+
+       // Clone the callbacks in case one of them registers a new callback
+       var originalCallbacks = LISTENERS[eventName];
+       var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
+
+       for (var i = 0; i < callbacks.length; i++) {
+               callbacks[i](data);
+       }
+  }
+
+  /**
+   * Registers a callback as a listener to the specified event.
+   *
+   * @public
+   * @method on
+   * @param {String} eventName
+   * @param {Function} callback
+   * @return {Void}
+   */
+  function on(eventName, callback) {
+       if (objectType(eventName) !== "string") {
+               throw new TypeError("eventName must be a string when registering a listener");
+       } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
+               var events = SUPPORTED_EVENTS.join(", ");
+               throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
+       } else if (objectType(callback) !== "function") {
+               throw new TypeError("callback must be a function when registering a listener");
+       }
+
+       if (!LISTENERS[eventName]) {
+               LISTENERS[eventName] = [];
+       }
+
+       // Don't register the same callback more than once
+       if (!inArray(callback, LISTENERS[eventName])) {
+               LISTENERS[eventName].push(callback);
+       }
+  }
+
+  // Register logging callbacks
+  function registerLoggingCallbacks(obj) {
+       var i,
+           l,
+           key,
+           callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
+
+       function registerLoggingCallback(key) {
+               var loggingCallback = function loggingCallback(callback) {
+                       if (objectType(callback) !== "function") {
+                               throw new Error("QUnit logging methods require a callback function as their first parameters.");
+                       }
+
+                       config.callbacks[key].push(callback);
+               };
+
+               return loggingCallback;
+       }
+
+       for (i = 0, l = callbackNames.length; i < l; i++) {
+               key = callbackNames[i];
+
+               // Initialize key collection of logging callback
+               if (objectType(config.callbacks[key]) === "undefined") {
+                       config.callbacks[key] = [];
+               }
+
+               obj[key] = registerLoggingCallback(key);
+       }
+  }
+
+  function runLoggingCallbacks(key, args) {
+       var i, l, callbacks;
+
+       callbacks = config.callbacks[key];
+       for (i = 0, l = callbacks.length; i < l; i++) {
+               callbacks[i](args);
+       }
+  }
+
+  // Doesn't support IE9, it will return undefined on these browsers
+  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+  var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
+
+  function extractStacktrace(e, offset) {
+       offset = offset === undefined ? 4 : offset;
+
+       var stack, include, i;
+
+       if (e && e.stack) {
+               stack = e.stack.split("\n");
+               if (/^error$/i.test(stack[0])) {
+                       stack.shift();
+               }
+               if (fileName) {
+                       include = [];
+                       for (i = offset; i < stack.length; i++) {
+                               if (stack[i].indexOf(fileName) !== -1) {
+                                       break;
+                               }
+                               include.push(stack[i]);
+                       }
+                       if (include.length) {
+                               return include.join("\n");
+                       }
+               }
+               return stack[offset];
+       }
+  }
+
+  function sourceFromStacktrace(offset) {
+       var error = new Error();
+
+       // Support: Safari <=7 only, IE <=10 - 11 only
+       // Not all browsers generate the `stack` property for `new Error()`, see also #636
+       if (!error.stack) {
+               try {
+                       throw error;
+               } catch (err) {
+                       error = err;
+               }
+       }
+
+       return extractStacktrace(error, offset);
+  }
+
+  var priorityCount = 0;
+  var unitSampler = void 0;
+
+  /**
+   * Advances the ProcessingQueue to the next item if it is ready.
+   * @param {Boolean} last
+   */
+  function advance() {
+       var start = now();
+       config.depth = (config.depth || 0) + 1;
+
+       while (config.queue.length && !config.blocking) {
+               var elapsedTime = now() - start;
+
+               if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
+                       if (priorityCount > 0) {
+                               priorityCount--;
+                       }
+
+                       config.queue.shift()();
+               } else {
+                       setTimeout(advance, 13);
+                       break;
+               }
+       }
+
+       config.depth--;
+
+       if (!config.blocking && !config.queue.length && config.depth === 0) {
+               done();
+       }
+  }
+
+  function addToQueueImmediate(callback) {
+       if (objectType(callback) === "array") {
+               while (callback.length) {
+                       addToQueueImmediate(callback.pop());
+               }
+
+               return;
+       }
+
+       config.queue.unshift(callback);
+       priorityCount++;
+  }
+
+  /**
+   * Adds a function to the ProcessingQueue for execution.
+   * @param {Function|Array} callback
+   * @param {Boolean} priority
+   * @param {String} seed
+   */
+  function addToQueue(callback, prioritize, seed) {
+       if (prioritize) {
+               config.queue.splice(priorityCount++, 0, callback);
+       } else if (seed) {
+               if (!unitSampler) {
+                       unitSampler = unitSamplerGenerator(seed);
+               }
+
+               // Insert into a random position after all prioritized items
+               var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
+               config.queue.splice(priorityCount + index, 0, callback);
+       } else {
+               config.queue.push(callback);
+       }
+  }
+
+  /**
+   * Creates a seeded "sample" generator which is used for randomizing tests.
+   */
+  function unitSamplerGenerator(seed) {
+
+       // 32-bit xorshift, requires only a nonzero seed
+       // http://excamera.com/sphinx/article-xorshift.html
+       var sample = parseInt(generateHash(seed), 16) || -1;
+       return function () {
+               sample ^= sample << 13;
+               sample ^= sample >>> 17;
+               sample ^= sample << 5;
+
+               // ECMAScript has no unsigned number type
+               if (sample < 0) {
+                       sample += 0x100000000;
+               }
+
+               return sample / 0x100000000;
+       };
+  }
+
+  /**
+   * This function is called when the ProcessingQueue is done processing all
+   * items. It handles emitting the final run events.
+   */
+  function done() {
+       var storage = config.storage;
+
+       ProcessingQueue.finished = true;
+
+       var runtime = now() - config.started;
+       var passed = config.stats.all - config.stats.bad;
+
+       emit("runEnd", globalSuite.end(true));
+       runLoggingCallbacks("done", {
+               passed: passed,
+               failed: config.stats.bad,
+               total: config.stats.all,
+               runtime: runtime
+       });
+
+       // Clear own storage items if all tests passed
+       if (storage && config.stats.bad === 0) {
+               for (var i = storage.length - 1; i >= 0; i--) {
+                       var key = storage.key(i);
+
+                       if (key.indexOf("qunit-test-") === 0) {
+                               storage.removeItem(key);
+                       }
+               }
+       }
+  }
+
+  var ProcessingQueue = {
+       finished: false,
+       add: addToQueue,
+       addImmediate: addToQueueImmediate,
+       advance: advance
+  };
+
+  var TestReport = function () {
+       function TestReport(name, suite, options) {
+               classCallCheck(this, TestReport);
+
+               this.name = name;
+               this.suiteName = suite.name;
+               this.fullName = suite.fullName.concat(name);
+               this.runtime = 0;
+               this.assertions = [];
+
+               this.skipped = !!options.skip;
+               this.todo = !!options.todo;
+
+               this.valid = options.valid;
+
+               this._startTime = 0;
+               this._endTime = 0;
+
+               suite.pushTest(this);
+       }
+
+       createClass(TestReport, [{
+               key: "start",
+               value: function start(recordTime) {
+                       if (recordTime) {
+                               this._startTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               suiteName: this.suiteName,
+                               fullName: this.fullName.slice()
+                       };
+               }
+       }, {
+               key: "end",
+               value: function end(recordTime) {
+                       if (recordTime) {
+                               this._endTime = Date.now();
+                       }
+
+                       return extend(this.start(), {
+                               runtime: this.getRuntime(),
+                               status: this.getStatus(),
+                               errors: this.getFailedAssertions(),
+                               assertions: this.getAssertions()
+                       });
+               }
+       }, {
+               key: "pushAssertion",
+               value: function pushAssertion(assertion) {
+                       this.assertions.push(assertion);
+               }
+       }, {
+               key: "getRuntime",
+               value: function getRuntime() {
+                       return this._endTime - this._startTime;
+               }
+       }, {
+               key: "getStatus",
+               value: function getStatus() {
+                       if (this.skipped) {
+                               return "skipped";
+                       }
+
+                       var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
+
+                       if (!testPassed) {
+                               return "failed";
+                       } else if (this.todo) {
+                               return "todo";
+                       } else {
+                               return "passed";
+                       }
+               }
+       }, {
+               key: "getFailedAssertions",
+               value: function getFailedAssertions() {
+                       return this.assertions.filter(function (assertion) {
+                               return !assertion.passed;
+                       });
+               }
+       }, {
+               key: "getAssertions",
+               value: function getAssertions() {
+                       return this.assertions.slice();
+               }
+
+               // Remove actual and expected values from assertions. This is to prevent
+               // leaking memory throughout a test suite.
+
+       }, {
+               key: "slimAssertions",
+               value: function slimAssertions() {
+                       this.assertions = this.assertions.map(function (assertion) {
+                               delete assertion.actual;
+                               delete assertion.expected;
+                               return assertion;
+                       });
+               }
+       }]);
+       return TestReport;
+  }();
+
+  var focused$1 = false;
+
+  function Test(settings) {
+       var i, l;
+
+       ++Test.count;
+
+       this.expected = null;
+       this.assertions = [];
+       this.semaphore = 0;
+       this.module = config.currentModule;
+       this.stack = sourceFromStacktrace(3);
+       this.steps = [];
+       this.timeout = undefined;
+
+       // If a module is skipped, all its tests and the tests of the child suites
+       // should be treated as skipped even if they are defined as `only` or `todo`.
+       // As for `todo` module, all its tests will be treated as `todo` except for
+       // tests defined as `skip` which will be left intact.
+       //
+       // So, if a test is defined as `todo` and is inside a skipped module, we should
+       // then treat that test as if was defined as `skip`.
+       if (this.module.skip) {
+               settings.skip = true;
+               settings.todo = false;
+
+               // Skipped tests should be left intact
+       } else if (this.module.todo && !settings.skip) {
+               settings.todo = true;
+       }
+
+       extend(this, settings);
+
+       this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
+               todo: settings.todo,
+               skip: settings.skip,
+               valid: this.valid()
+       });
+
+       // Register unique strings
+       for (i = 0, l = this.module.tests; i < l.length; i++) {
+               if (this.module.tests[i].name === this.testName) {
+                       this.testName += " ";
+               }
+       }
+
+       this.testId = generateHash(this.module.name, this.testName);
+
+       this.module.tests.push({
+               name: this.testName,
+               testId: this.testId,
+               skip: !!settings.skip
+       });
+
+       if (settings.skip) {
+
+               // Skipped tests will fully ignore any sent callback
+               this.callback = function () {};
+               this.async = false;
+               this.expected = 0;
+       } else {
+               this.assert = new Assert(this);
+       }
+  }
+
+  Test.count = 0;
+
+  function getNotStartedModules(startModule) {
+       var module = startModule,
+           modules = [];
+
+       while (module && module.testsRun === 0) {
+               modules.push(module);
+               module = module.parentModule;
+       }
+
+       return modules;
+  }
+
+  Test.prototype = {
+       before: function before() {
+               var i,
+                   startModule,
+                   module = this.module,
+                   notStartedModules = getNotStartedModules(module);
+
+               for (i = notStartedModules.length - 1; i >= 0; i--) {
+                       startModule = notStartedModules[i];
+                       startModule.stats = { all: 0, bad: 0, started: now() };
+                       emit("suiteStart", startModule.suiteReport.start(true));
+                       runLoggingCallbacks("moduleStart", {
+                               name: startModule.name,
+                               tests: startModule.tests
+                       });
+               }
+
+               config.current = this;
+
+               this.testEnvironment = extend({}, module.testEnvironment);
+
+               this.started = now();
+               emit("testStart", this.testReport.start(true));
+               runLoggingCallbacks("testStart", {
+                       name: this.testName,
+                       module: module.name,
+                       testId: this.testId,
+                       previousFailure: this.previousFailure
+               });
+
+               if (!config.pollution) {
+                       saveGlobal();
+               }
+       },
+
+       run: function run() {
+               var promise;
+
+               config.current = this;
+
+               this.callbackStarted = now();
+
+               if (config.notrycatch) {
+                       runTest(this);
+                       return;
+               }
+
+               try {
+                       runTest(this);
+               } catch (e) {
+                       this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
+
+                       // Else next test will carry the responsibility
+                       saveGlobal();
+
+                       // Restart the tests if they're blocking
+                       if (config.blocking) {
+                               internalRecover(this);
+                       }
+               }
+
+               function runTest(test) {
+                       promise = test.callback.call(test.testEnvironment, test.assert);
+                       test.resolvePromise(promise);
+
+                       // If the test has a "lock" on it, but the timeout is 0, then we push a
+                       // failure as the test should be synchronous.
+                       if (test.timeout === 0 && test.semaphore !== 0) {
+                               pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
+                       }
+               }
+       },
+
+       after: function after() {
+               checkPollution();
+       },
+
+       queueHook: function queueHook(hook, hookName, hookOwner) {
+               var _this = this;
+
+               var callHook = function callHook() {
+                       var promise = hook.call(_this.testEnvironment, _this.assert);
+                       _this.resolvePromise(promise, hookName);
+               };
+
+               var runHook = function runHook() {
+                       if (hookName === "before") {
+                               if (hookOwner.unskippedTestsRun !== 0) {
+                                       return;
+                               }
+
+                               _this.preserveEnvironment = true;
+                       }
+
+                       if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
+                               return;
+                       }
+
+                       config.current = _this;
+                       if (config.notrycatch) {
+                               callHook();
+                               return;
+                       }
+                       try {
+                               callHook();
+                       } catch (error) {
+                               _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
+                       }
+               };
+
+               return runHook;
+       },
+
+
+       // Currently only used for module level hooks, can be used to add global level ones
+       hooks: function hooks(handler) {
+               var hooks = [];
+
+               function processHooks(test, module) {
+                       if (module.parentModule) {
+                               processHooks(test, module.parentModule);
+                       }
+
+                       if (module.hooks[handler].length) {
+                               for (var i = 0; i < module.hooks[handler].length; i++) {
+                                       hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
+                               }
+                       }
+               }
+
+               // Hooks are ignored on skipped tests
+               if (!this.skip) {
+                       processHooks(this, this.module);
+               }
+
+               return hooks;
+       },
+
+
+       finish: function finish() {
+               config.current = this;
+               if (config.requireExpects && this.expected === null) {
+                       this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
+               } else if (this.expected !== null && this.expected !== this.assertions.length) {
+                       this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
+               } else if (this.expected === null && !this.assertions.length) {
+                       this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
+               }
+
+               var i,
+                   module = this.module,
+                   moduleName = module.name,
+                   testName = this.testName,
+                   skipped = !!this.skip,
+                   todo = !!this.todo,
+                   bad = 0,
+                   storage = config.storage;
+
+               this.runtime = now() - this.started;
+
+               config.stats.all += this.assertions.length;
+               module.stats.all += this.assertions.length;
+
+               for (i = 0; i < this.assertions.length; i++) {
+                       if (!this.assertions[i].result) {
+                               bad++;
+                               config.stats.bad++;
+                               module.stats.bad++;
+                       }
+               }
+
+               notifyTestsRan(module, skipped);
+
+               // Store result when possible
+               if (storage) {
+                       if (bad) {
+                               storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
+                       } else {
+                               storage.removeItem("qunit-test-" + moduleName + "-" + testName);
+                       }
+               }
+
+               // After emitting the js-reporters event we cleanup the assertion data to
+               // avoid leaking it. It is not used by the legacy testDone callbacks.
+               emit("testEnd", this.testReport.end(true));
+               this.testReport.slimAssertions();
+
+               runLoggingCallbacks("testDone", {
+                       name: testName,
+                       module: moduleName,
+                       skipped: skipped,
+                       todo: todo,
+                       failed: bad,
+                       passed: this.assertions.length - bad,
+                       total: this.assertions.length,
+                       runtime: skipped ? 0 : this.runtime,
+
+                       // HTML Reporter use
+                       assertions: this.assertions,
+                       testId: this.testId,
+
+                       // Source of Test
+                       source: this.stack
+               });
+
+               if (module.testsRun === numberOfTests(module)) {
+                       logSuiteEnd(module);
+
+                       // Check if the parent modules, iteratively, are done. If that the case,
+                       // we emit the `suiteEnd` event and trigger `moduleDone` callback.
+                       var parent = module.parentModule;
+                       while (parent && parent.testsRun === numberOfTests(parent)) {
+                               logSuiteEnd(parent);
+                               parent = parent.parentModule;
+                       }
+               }
+
+               config.current = undefined;
+
+               function logSuiteEnd(module) {
+                       emit("suiteEnd", module.suiteReport.end(true));
+                       runLoggingCallbacks("moduleDone", {
+                               name: module.name,
+                               tests: module.tests,
+                               failed: module.stats.bad,
+                               passed: module.stats.all - module.stats.bad,
+                               total: module.stats.all,
+                               runtime: now() - module.stats.started
+                       });
+               }
+       },
+
+       preserveTestEnvironment: function preserveTestEnvironment() {
+               if (this.preserveEnvironment) {
+                       this.module.testEnvironment = this.testEnvironment;
+                       this.testEnvironment = extend({}, this.module.testEnvironment);
+               }
+       },
+
+       queue: function queue() {
+               var test = this;
+
+               if (!this.valid()) {
+                       return;
+               }
+
+               function runTest() {
+
+                       // Each of these can by async
+                       ProcessingQueue.addImmediate([function () {
+                               test.before();
+                       }, test.hooks("before"), function () {
+                               test.preserveTestEnvironment();
+                       }, test.hooks("beforeEach"), function () {
+                               test.run();
+                       }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
+                               test.after();
+                       }, function () {
+                               test.finish();
+                       }]);
+               }
+
+               var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
+
+               // Prioritize previously failed tests, detected from storage
+               var prioritize = config.reorder && !!previousFailCount;
+
+               this.previousFailure = !!previousFailCount;
+
+               ProcessingQueue.add(runTest, prioritize, config.seed);
+
+               // If the queue has already finished, we manually process the new test
+               if (ProcessingQueue.finished) {
+                       ProcessingQueue.advance();
+               }
+       },
+
+
+       pushResult: function pushResult(resultInfo) {
+               if (this !== config.current) {
+                       throw new Error("Assertion occured after test had finished.");
+               }
+
+               // Destructure of resultInfo = { result, actual, expected, message, negative }
+               var source,
+                   details = {
+                       module: this.module.name,
+                       name: this.testName,
+                       result: resultInfo.result,
+                       message: resultInfo.message,
+                       actual: resultInfo.actual,
+                       expected: resultInfo.expected,
+                       testId: this.testId,
+                       negative: resultInfo.negative || false,
+                       runtime: now() - this.started,
+                       todo: !!this.todo
+               };
+
+               if (!resultInfo.result) {
+                       source = resultInfo.source || sourceFromStacktrace();
+
+                       if (source) {
+                               details.source = source;
+                       }
+               }
+
+               this.logAssertion(details);
+
+               this.assertions.push({
+                       result: !!resultInfo.result,
+                       message: resultInfo.message
+               });
+       },
+
+       pushFailure: function pushFailure(message, source, actual) {
+               if (!(this instanceof Test)) {
+                       throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
+               }
+
+               this.pushResult({
+                       result: false,
+                       message: message || "error",
+                       actual: actual || null,
+                       expected: null,
+                       source: source
+               });
+       },
+
+       /**
+    * Log assertion details using both the old QUnit.log interface and
+    * QUnit.on( "assertion" ) interface.
+    *
+    * @private
+    */
+       logAssertion: function logAssertion(details) {
+               runLoggingCallbacks("log", details);
+
+               var assertion = {
+                       passed: details.result,
+                       actual: details.actual,
+                       expected: details.expected,
+                       message: details.message,
+                       stack: details.source,
+                       todo: details.todo
+               };
+               this.testReport.pushAssertion(assertion);
+               emit("assertion", assertion);
+       },
+
+
+       resolvePromise: function resolvePromise(promise, phase) {
+               var then,
+                   resume,
+                   message,
+                   test = this;
+               if (promise != null) {
+                       then = promise.then;
+                       if (objectType(then) === "function") {
+                               resume = internalStop(test);
+                               then.call(promise, function () {
+                                       resume();
+                               }, function (error) {
+                                       message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
+                                       test.pushFailure(message, extractStacktrace(error, 0));
+
+                                       // Else next test will carry the responsibility
+                                       saveGlobal();
+
+                                       // Unblock
+                                       resume();
+                               });
+                       }
+               }
+       },
+
+       valid: function valid() {
+               var filter = config.filter,
+                   regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
+                   module = config.module && config.module.toLowerCase(),
+                   fullName = this.module.name + ": " + this.testName;
+
+               function moduleChainNameMatch(testModule) {
+                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
+                       if (testModuleName === module) {
+                               return true;
+                       } else if (testModule.parentModule) {
+                               return moduleChainNameMatch(testModule.parentModule);
+                       } else {
+                               return false;
+                       }
+               }
+
+               function moduleChainIdMatch(testModule) {
+                       return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
+               }
+
+               // Internally-generated tests are always valid
+               if (this.callback && this.callback.validTest) {
+                       return true;
+               }
+
+               if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
+
+                       return false;
+               }
+
+               if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
+
+                       return false;
+               }
+
+               if (module && !moduleChainNameMatch(this.module)) {
+                       return false;
+               }
+
+               if (!filter) {
+                       return true;
+               }
+
+               return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
+       },
+
+       regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
+               var regex = new RegExp(pattern, flags);
+               var match = regex.test(fullName);
+
+               return match !== exclude;
+       },
+
+       stringFilter: function stringFilter(filter, fullName) {
+               filter = filter.toLowerCase();
+               fullName = fullName.toLowerCase();
+
+               var include = filter.charAt(0) !== "!";
+               if (!include) {
+                       filter = filter.slice(1);
+               }
+
+               // If the filter matches, we need to honour include
+               if (fullName.indexOf(filter) !== -1) {
+                       return include;
+               }
+
+               // Otherwise, do the opposite
+               return !include;
+       }
+  };
+
+  function pushFailure() {
+       if (!config.current) {
+               throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
+       }
+
+       // Gets current test obj
+       var currentTest = config.current;
+
+       return currentTest.pushFailure.apply(currentTest, arguments);
+  }
+
+  function saveGlobal() {
+       config.pollution = [];
+
+       if (config.noglobals) {
+               for (var key in global$1) {
+                       if (hasOwn.call(global$1, key)) {
+
+                               // In Opera sometimes DOM element ids show up here, ignore them
+                               if (/^qunit-test-output/.test(key)) {
+                                       continue;
+                               }
+                               config.pollution.push(key);
+                       }
+               }
+       }
+  }
+
+  function checkPollution() {
+       var newGlobals,
+           deletedGlobals,
+           old = config.pollution;
+
+       saveGlobal();
+
+       newGlobals = diff(config.pollution, old);
+       if (newGlobals.length > 0) {
+               pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
+       }
+
+       deletedGlobals = diff(old, config.pollution);
+       if (deletedGlobals.length > 0) {
+               pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
+       }
+  }
+
+  // Will be exposed as QUnit.test
+  function test(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback
+       });
+
+       newTest.queue();
+  }
+
+  function todo(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback,
+               todo: true
+       });
+
+       newTest.queue();
+  }
+
+  // Will be exposed as QUnit.skip
+  function skip(testName) {
+       if (focused$1) {
+               return;
+       }
+
+       var test = new Test({
+               testName: testName,
+               skip: true
+       });
+
+       test.queue();
+  }
+
+  // Will be exposed as QUnit.only
+  function only(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       config.queue.length = 0;
+       focused$1 = true;
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback
+       });
+
+       newTest.queue();
+  }
+
+  // Put a hold on processing and return a function that will release it.
+  function internalStop(test) {
+       test.semaphore += 1;
+       config.blocking = true;
+
+       // Set a recovery timeout, if so configured.
+       if (defined.setTimeout) {
+               var timeoutDuration = void 0;
+
+               if (typeof test.timeout === "number") {
+                       timeoutDuration = test.timeout;
+               } else if (typeof config.testTimeout === "number") {
+                       timeoutDuration = config.testTimeout;
+               }
+
+               if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
+                       clearTimeout(config.timeout);
+                       config.timeout = setTimeout(function () {
+                               pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
+                               internalRecover(test);
+                       }, timeoutDuration);
+               }
+       }
+
+       var released = false;
+       return function resume() {
+               if (released) {
+                       return;
+               }
+
+               released = true;
+               test.semaphore -= 1;
+               internalStart(test);
+       };
+  }
+
+  // Forcefully release all processing holds.
+  function internalRecover(test) {
+       test.semaphore = 0;
+       internalStart(test);
+  }
+
+  // Release a processing hold, scheduling a resumption attempt if no holds remain.
+  function internalStart(test) {
+
+       // If semaphore is non-numeric, throw error
+       if (isNaN(test.semaphore)) {
+               test.semaphore = 0;
+
+               pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
+               return;
+       }
+
+       // Don't start until equal number of stop-calls
+       if (test.semaphore > 0) {
+               return;
+       }
+
+       // Throw an Error if start is called more often than stop
+       if (test.semaphore < 0) {
+               test.semaphore = 0;
+
+               pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
+               return;
+       }
+
+       // Add a slight delay to allow more assertions etc.
+       if (defined.setTimeout) {
+               if (config.timeout) {
+                       clearTimeout(config.timeout);
+               }
+               config.timeout = setTimeout(function () {
+                       if (test.semaphore > 0) {
+                               return;
+                       }
+
+                       if (config.timeout) {
+                               clearTimeout(config.timeout);
+                       }
+
+                       begin();
+               }, 13);
+       } else {
+               begin();
+       }
+  }
+
+  function collectTests(module) {
+       var tests = [].concat(module.tests);
+       var modules = [].concat(toConsumableArray(module.childModules));
+
+       // Do a breadth-first traversal of the child modules
+       while (modules.length) {
+               var nextModule = modules.shift();
+               tests.push.apply(tests, nextModule.tests);
+               modules.push.apply(modules, toConsumableArray(nextModule.childModules));
+       }
+
+       return tests;
+  }
+
+  function numberOfTests(module) {
+       return collectTests(module).length;
+  }
+
+  function numberOfUnskippedTests(module) {
+       return collectTests(module).filter(function (test) {
+               return !test.skip;
+       }).length;
+  }
+
+  function notifyTestsRan(module, skipped) {
+       module.testsRun++;
+       if (!skipped) {
+               module.unskippedTestsRun++;
+       }
+       while (module = module.parentModule) {
+               module.testsRun++;
+               if (!skipped) {
+                       module.unskippedTestsRun++;
+               }
+       }
+  }
+
+  /**
+   * Returns a function that proxies to the given method name on the globals
+   * console object. The proxy will also detect if the console doesn't exist and
+   * will appropriately no-op. This allows support for IE9, which doesn't have a
+   * console if the developer tools are not open.
+   */
+  function consoleProxy(method) {
+       return function () {
+               if (console) {
+                       console[method].apply(console, arguments);
+               }
+       };
+  }
+
+  var Logger = {
+       warn: consoleProxy("warn")
+  };
+
+  var Assert = function () {
+       function Assert(testContext) {
+               classCallCheck(this, Assert);
+
+               this.test = testContext;
+       }
+
+       // Assert helpers
+
+       createClass(Assert, [{
+               key: "timeout",
+               value: function timeout(duration) {
+                       if (typeof duration !== "number") {
+                               throw new Error("You must pass a number as the duration to assert.timeout");
+                       }
+
+                       this.test.timeout = duration;
+               }
+
+               // Documents a "step", which is a string value, in a test as a passing assertion
+
+       }, {
+               key: "step",
+               value: function step(message) {
+                       var result = !!message;
+
+                       this.test.steps.push(message);
+
+                       return this.pushResult({
+                               result: result,
+                               message: message || "You must provide a message to assert.step"
+                       });
+               }
+
+               // Verifies the steps in a test match a given array of string values
+
+       }, {
+               key: "verifySteps",
+               value: function verifySteps(steps, message) {
+                       this.deepEqual(this.test.steps, steps, message);
+               }
+
+               // Specify the number of expected assertions to guarantee that failed test
+               // (no assertions are run at all) don't slip through.
+
+       }, {
+               key: "expect",
+               value: function expect(asserts) {
+                       if (arguments.length === 1) {
+                               this.test.expected = asserts;
+                       } else {
+                               return this.test.expected;
+                       }
+               }
+
+               // Put a hold on processing and return a function that will release it a maximum of once.
+
+       }, {
+               key: "async",
+               value: function async(count) {
+                       var test$$1 = this.test;
+
+                       var popped = false,
+                           acceptCallCount = count;
+
+                       if (typeof acceptCallCount === "undefined") {
+                               acceptCallCount = 1;
+                       }
+
+                       var resume = internalStop(test$$1);
+
+                       return function done() {
+                               if (config.current !== test$$1) {
+                                       throw Error("assert.async callback called after test finished.");
+                               }
+
+                               if (popped) {
+                                       test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
+                                       return;
+                               }
+
+                               acceptCallCount -= 1;
+                               if (acceptCallCount > 0) {
+                                       return;
+                               }
+
+                               popped = true;
+                               resume();
+                       };
+               }
+
+               // Exports test.push() to the user API
+               // Alias of pushResult.
+
+       }, {
+               key: "push",
+               value: function push(result, actual, expected, message, negative) {
+                       Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
+
+                       var currentAssert = this instanceof Assert ? this : config.current.assert;
+                       return currentAssert.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: negative
+                       });
+               }
+       }, {
+               key: "pushResult",
+               value: function pushResult(resultInfo) {
+
+                       // Destructure of resultInfo = { result, actual, expected, message, negative }
+                       var assert = this;
+                       var currentTest = assert instanceof Assert && assert.test || config.current;
+
+                       // Backwards compatibility fix.
+                       // Allows the direct use of global exported assertions and QUnit.assert.*
+                       // Although, it's use is not recommended as it can leak assertions
+                       // to other tests from async tests, because we only get a reference to the current test,
+                       // not exactly the test where assertion were intended to be called.
+                       if (!currentTest) {
+                               throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
+                       }
+
+                       if (!(assert instanceof Assert)) {
+                               assert = currentTest.assert;
+                       }
+
+                       return assert.test.pushResult(resultInfo);
+               }
+       }, {
+               key: "ok",
+               value: function ok(result, message) {
+                       if (!message) {
+                               message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
+                       }
+
+                       this.pushResult({
+                               result: !!result,
+                               actual: result,
+                               expected: true,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notOk",
+               value: function notOk(result, message) {
+                       if (!message) {
+                               message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
+                       }
+
+                       this.pushResult({
+                               result: !result,
+                               actual: result,
+                               expected: false,
+                               message: message
+                       });
+               }
+       }, {
+               key: "equal",
+               value: function equal(actual, expected, message) {
+
+                       // eslint-disable-next-line eqeqeq
+                       var result = expected == actual;
+
+                       this.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notEqual",
+               value: function notEqual(actual, expected, message) {
+
+                       // eslint-disable-next-line eqeqeq
+                       var result = expected != actual;
+
+                       this.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "propEqual",
+               value: function propEqual(actual, expected, message) {
+                       actual = objectValues(actual);
+                       expected = objectValues(expected);
+
+                       this.pushResult({
+                               result: equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notPropEqual",
+               value: function notPropEqual(actual, expected, message) {
+                       actual = objectValues(actual);
+                       expected = objectValues(expected);
+
+                       this.pushResult({
+                               result: !equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "deepEqual",
+               value: function deepEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notDeepEqual",
+               value: function notDeepEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: !equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "strictEqual",
+               value: function strictEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: expected === actual,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notStrictEqual",
+               value: function notStrictEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: expected !== actual,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "throws",
+               value: function throws(block, expected, message) {
+                       var actual = void 0,
+                           result = false;
+
+                       var currentTest = this instanceof Assert && this.test || config.current;
+
+                       // 'expected' is optional unless doing string comparison
+                       if (objectType(expected) === "string") {
+                               if (message == null) {
+                                       message = expected;
+                                       expected = null;
+                               } else {
+                                       throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
+                               }
+                       }
+
+                       currentTest.ignoreGlobalErrors = true;
+                       try {
+                               block.call(currentTest.testEnvironment);
+                       } catch (e) {
+                               actual = e;
+                       }
+                       currentTest.ignoreGlobalErrors = false;
+
+                       if (actual) {
+                               var expectedType = objectType(expected);
+
+                               // We don't want to validate thrown error
+                               if (!expected) {
+                                       result = true;
+                                       expected = null;
+
+                                       // Expected is a regexp
+                               } else if (expectedType === "regexp") {
+                                       result = expected.test(errorString(actual));
+
+                                       // Expected is a constructor, maybe an Error constructor
+                               } else if (expectedType === "function" && actual instanceof expected) {
+                                       result = true;
+
+                                       // Expected is an Error object
+                               } else if (expectedType === "object") {
+                                       result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+
+                                       // Expected is a validation function which returns true if validation passed
+                               } else if (expectedType === "function" && expected.call({}, actual) === true) {
+                                       expected = null;
+                                       result = true;
+                               }
+                       }
+
+                       currentTest.assert.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }]);
+       return Assert;
+  }();
+
+  // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
+  // Known to us are: Closure Compiler, Narwhal
+  // eslint-disable-next-line dot-notation
+
+
+  Assert.prototype.raises = Assert.prototype["throws"];
+
+  /**
+   * Converts an error into a simple string for comparisons.
+   *
+   * @param {Error} error
+   * @return {String}
+   */
+  function errorString(error) {
+       var resultErrorString = error.toString();
+
+       if (resultErrorString.substring(0, 7) === "[object") {
+               var name = error.name ? error.name.toString() : "Error";
+               var message = error.message ? error.message.toString() : "";
+
+               if (name && message) {
+                       return name + ": " + message;
+               } else if (name) {
+                       return name;
+               } else if (message) {
+                       return message;
+               } else {
+                       return "Error";
+               }
+       } else {
+               return resultErrorString;
+       }
+  }
+
+  /* global module, exports, define */
+  function exportQUnit(QUnit) {
+
+       if (defined.document) {
+
+               // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
+               if (window.QUnit && window.QUnit.version) {
+                       throw new Error("QUnit has already been defined.");
+               }
+
+               window.QUnit = QUnit;
+       }
+
+       // For nodejs
+       if (typeof module !== "undefined" && module && module.exports) {
+               module.exports = QUnit;
+
+               // For consistency with CommonJS environments' exports
+               module.exports.QUnit = QUnit;
+       }
+
+       // For CommonJS with exports, but without module.exports, like Rhino
+       if (typeof exports !== "undefined" && exports) {
+               exports.QUnit = QUnit;
+       }
+
+       if (typeof define === "function" && define.amd) {
+               define(function () {
+                       return QUnit;
+               });
+               QUnit.config.autostart = false;
+       }
+
+       // For Web/Service Workers
+       if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
+               self$1.QUnit = QUnit;
+       }
+  }
+
+  var SuiteReport = function () {
+       function SuiteReport(name, parentSuite) {
+               classCallCheck(this, SuiteReport);
+
+               this.name = name;
+               this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
+
+               this.tests = [];
+               this.childSuites = [];
+
+               if (parentSuite) {
+                       parentSuite.pushChildSuite(this);
+               }
+       }
+
+       createClass(SuiteReport, [{
+               key: "start",
+               value: function start(recordTime) {
+                       if (recordTime) {
+                               this._startTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               fullName: this.fullName.slice(),
+                               tests: this.tests.map(function (test) {
+                                       return test.start();
+                               }),
+                               childSuites: this.childSuites.map(function (suite) {
+                                       return suite.start();
+                               }),
+                               testCounts: {
+                                       total: this.getTestCounts().total
+                               }
+                       };
+               }
+       }, {
+               key: "end",
+               value: function end(recordTime) {
+                       if (recordTime) {
+                               this._endTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               fullName: this.fullName.slice(),
+                               tests: this.tests.map(function (test) {
+                                       return test.end();
+                               }),
+                               childSuites: this.childSuites.map(function (suite) {
+                                       return suite.end();
+                               }),
+                               testCounts: this.getTestCounts(),
+                               runtime: this.getRuntime(),
+                               status: this.getStatus()
+                       };
+               }
+       }, {
+               key: "pushChildSuite",
+               value: function pushChildSuite(suite) {
+                       this.childSuites.push(suite);
+               }
+       }, {
+               key: "pushTest",
+               value: function pushTest(test) {
+                       this.tests.push(test);
+               }
+       }, {
+               key: "getRuntime",
+               value: function getRuntime() {
+                       return this._endTime - this._startTime;
+               }
+       }, {
+               key: "getTestCounts",
+               value: function getTestCounts() {
+                       var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
+
+                       counts = this.tests.reduce(function (counts, test) {
+                               if (test.valid) {
+                                       counts[test.getStatus()]++;
+                                       counts.total++;
+                               }
+
+                               return counts;
+                       }, counts);
+
+                       return this.childSuites.reduce(function (counts, suite) {
+                               return suite.getTestCounts(counts);
+                       }, counts);
+               }
+       }, {
+               key: "getStatus",
+               value: function getStatus() {
+                       var _getTestCounts = this.getTestCounts(),
+                           total = _getTestCounts.total,
+                           failed = _getTestCounts.failed,
+                           skipped = _getTestCounts.skipped,
+                           todo = _getTestCounts.todo;
+
+                       if (failed) {
+                               return "failed";
+                       } else {
+                               if (skipped === total) {
+                                       return "skipped";
+                               } else if (todo === total) {
+                                       return "todo";
+                               } else {
+                                       return "passed";
+                               }
+                       }
+               }
+       }]);
+       return SuiteReport;
+  }();
+
+  // Handle an unhandled exception. By convention, returns true if further
+  // error handling should be suppressed and false otherwise.
+  // In this case, we will only suppress further error handling if the
+  // "ignoreGlobalErrors" configuration option is enabled.
+  function onError(error) {
+       for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+               args[_key - 1] = arguments[_key];
+       }
+
+       if (config.current) {
+               if (config.current.ignoreGlobalErrors) {
+                       return true;
+               }
+               pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+       } else {
+               test("global failure", extend(function () {
+                       pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+               }, { validTest: true }));
+       }
+
+       return false;
+  }
+
+  var focused = false;
+  var QUnit = {};
+  var globalSuite = new SuiteReport();
+
+  // The initial "currentModule" represents the global (or top-level) module that
+  // is not explicitly defined by the user, therefore we add the "globalSuite" to
+  // it since each module has a suiteReport associated with it.
+  config.currentModule.suiteReport = globalSuite;
+
+  var moduleStack = [];
+  var globalStartCalled = false;
+  var runStarted = false;
+
+  // Figure out if we're running the tests from a server or not
+  QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
+
+  // Expose the current QUnit version
+  QUnit.version = "2.4.0";
+
+  function createModule(name, testEnvironment, modifiers) {
+       var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
+       var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
+       var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
+
+       var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
+       var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
+
+       var module = {
+               name: moduleName,
+               parentModule: parentModule,
+               tests: [],
+               moduleId: generateHash(moduleName),
+               testsRun: 0,
+               unskippedTestsRun: 0,
+               childModules: [],
+               suiteReport: new SuiteReport(name, parentSuite),
+
+               // Pass along `skip` and `todo` properties from parent module, in case
+               // there is one, to childs. And use own otherwise.
+               // This property will be used to mark own tests and tests of child suites
+               // as either `skipped` or `todo`.
+               skip: skip$$1,
+               todo: skip$$1 ? false : todo$$1
+       };
+
+       var env = {};
+       if (parentModule) {
+               parentModule.childModules.push(module);
+               extend(env, parentModule.testEnvironment);
+       }
+       extend(env, testEnvironment);
+       module.testEnvironment = env;
+
+       config.modules.push(module);
+       return module;
+  }
+
+  function processModule(name, options, executeNow) {
+       var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+       var module = createModule(name, options, modifiers);
+
+       // Move any hooks to a 'hooks' object
+       var testEnvironment = module.testEnvironment;
+       var hooks = module.hooks = {};
+
+       setHookFromEnvironment(hooks, testEnvironment, "before");
+       setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
+       setHookFromEnvironment(hooks, testEnvironment, "afterEach");
+       setHookFromEnvironment(hooks, testEnvironment, "after");
+
+       function setHookFromEnvironment(hooks, environment, name) {
+               var potentialHook = environment[name];
+               hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
+               delete environment[name];
+       }
+
+       var moduleFns = {
+               before: setHookFunction(module, "before"),
+               beforeEach: setHookFunction(module, "beforeEach"),
+               afterEach: setHookFunction(module, "afterEach"),
+               after: setHookFunction(module, "after")
+       };
+
+       var currentModule = config.currentModule;
+       if (objectType(executeNow) === "function") {
+               moduleStack.push(module);
+               config.currentModule = module;
+               executeNow.call(module.testEnvironment, moduleFns);
+               moduleStack.pop();
+               module = module.parentModule || currentModule;
+       }
+
+       config.currentModule = module;
+  }
+
+  // TODO: extract this to a new file alongside its related functions
+  function module$1(name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow);
+  }
+
+  module$1.only = function () {
+       if (focused) {
+               return;
+       }
+
+       config.modules.length = 0;
+       config.queue.length = 0;
+
+       module$1.apply(undefined, arguments);
+
+       focused = true;
+  };
+
+  module$1.skip = function (name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow, { skip: true });
+  };
+
+  module$1.todo = function (name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow, { todo: true });
+  };
+
+  extend(QUnit, {
+       on: on,
+
+       module: module$1,
+
+       test: test,
+
+       todo: todo,
+
+       skip: skip,
+
+       only: only,
+
+       start: function start(count) {
+               var globalStartAlreadyCalled = globalStartCalled;
+
+               if (!config.current) {
+                       globalStartCalled = true;
+
+                       if (runStarted) {
+                               throw new Error("Called start() while test already started running");
+                       } else if (globalStartAlreadyCalled || count > 1) {
+                               throw new Error("Called start() outside of a test context too many times");
+                       } else if (config.autostart) {
+                               throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
+                       } else if (!config.pageLoaded) {
+
+                               // The page isn't completely loaded yet, so we set autostart and then
+                               // load if we're in Node or wait for the browser's load event.
+                               config.autostart = true;
+
+                               // Starts from Node even if .load was not previously called. We still return
+                               // early otherwise we'll wind up "beginning" twice.
+                               if (!defined.document) {
+                                       QUnit.load();
+                               }
+
+                               return;
+                       }
+               } else {
+                       throw new Error("QUnit.start cannot be called inside a test context.");
+               }
+
+               scheduleBegin();
+       },
+
+       config: config,
+
+       is: is,
+
+       objectType: objectType,
+
+       extend: extend,
+
+       load: function load() {
+               config.pageLoaded = true;
+
+               // Initialize the configuration options
+               extend(config, {
+                       stats: { all: 0, bad: 0 },
+                       started: 0,
+                       updateRate: 1000,
+                       autostart: true,
+                       filter: ""
+               }, true);
+
+               if (!runStarted) {
+                       config.blocking = false;
+
+                       if (config.autostart) {
+                               scheduleBegin();
+                       }
+               }
+       },
+
+       stack: function stack(offset) {
+               offset = (offset || 0) + 2;
+               return sourceFromStacktrace(offset);
+       },
+
+       onError: onError
+  });
+
+  QUnit.pushFailure = pushFailure;
+  QUnit.assert = Assert.prototype;
+  QUnit.equiv = equiv;
+  QUnit.dump = dump;
+
+  registerLoggingCallbacks(QUnit);
+
+  function scheduleBegin() {
+
+       runStarted = true;
+
+       // Add a slight delay to allow definition of more modules and tests.
+       if (defined.setTimeout) {
+               setTimeout(function () {
+                       begin();
+               }, 13);
+       } else {
+               begin();
+       }
+  }
+
+  function begin() {
+       var i,
+           l,
+           modulesLog = [];
+
+       // If the test run hasn't officially begun yet
+       if (!config.started) {
+
+               // Record the time of the test run's beginning
+               config.started = now();
+
+               // Delete the loose unnamed module if unused.
+               if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
+                       config.modules.shift();
+               }
+
+               // Avoid unnecessary information by not logging modules' test environments
+               for (i = 0, l = config.modules.length; i < l; i++) {
+                       modulesLog.push({
+                               name: config.modules[i].name,
+                               tests: config.modules[i].tests
+                       });
+               }
+
+               // The test run is officially beginning now
+               emit("runStart", globalSuite.start(true));
+               runLoggingCallbacks("begin", {
+                       totalTests: Test.count,
+                       modules: modulesLog
+               });
+       }
+
+       config.blocking = false;
+       ProcessingQueue.advance();
+  }
+
+  function setHookFunction(module, hookName) {
+       return function setHook(callback) {
+               module.hooks[hookName].push(callback);
+       };
+  }
+
+  exportQUnit(QUnit);
+
+  (function () {
+
+       if (typeof window === "undefined" || typeof document === "undefined") {
+               return;
+       }
+
+       var config = QUnit.config,
+           hasOwn = Object.prototype.hasOwnProperty;
+
+       // Stores fixture HTML for resetting later
+       function storeFixture() {
+
+               // Avoid overwriting user-defined values
+               if (hasOwn.call(config, "fixture")) {
+                       return;
+               }
+
+               var fixture = document.getElementById("qunit-fixture");
+               if (fixture) {
+                       config.fixture = fixture.innerHTML;
+               }
+       }
+
+       QUnit.begin(storeFixture);
+
+       // Resets the fixture DOM element if available.
+       function resetFixture() {
+               if (config.fixture == null) {
+                       return;
+               }
+
+               var fixture = document.getElementById("qunit-fixture");
+               if (fixture) {
+                       fixture.innerHTML = config.fixture;
+               }
+       }
+
+       QUnit.testStart(resetFixture);
+  })();
+
+  (function () {
+
+       // Only interact with URLs via window.location
+       var location = typeof window !== "undefined" && window.location;
+       if (!location) {
+               return;
+       }
+
+       var urlParams = getUrlParams();
+
+       QUnit.urlParams = urlParams;
+
+       // Match module/test by inclusion in an array
+       QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
+       QUnit.config.testId = [].concat(urlParams.testId || []);
+
+       // Exact case-insensitive match of the module name
+       QUnit.config.module = urlParams.module;
+
+       // Regular expression or case-insenstive substring match against "moduleName: testName"
+       QUnit.config.filter = urlParams.filter;
+
+       // Test order randomization
+       if (urlParams.seed === true) {
+
+               // Generate a random seed if the option is specified without a value
+               QUnit.config.seed = Math.random().toString(36).slice(2);
+       } else if (urlParams.seed) {
+               QUnit.config.seed = urlParams.seed;
+       }
+
+       // Add URL-parameter-mapped config values with UI form rendering data
+       QUnit.config.urlConfig.push({
+               id: "hidepassed",
+               label: "Hide passed tests",
+               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+       }, {
+               id: "noglobals",
+               label: "Check for Globals",
+               tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
+       }, {
+               id: "notrycatch",
+               label: "No try-catch",
+               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
+       });
+
+       QUnit.begin(function () {
+               var i,
+                   option,
+                   urlConfig = QUnit.config.urlConfig;
+
+               for (i = 0; i < urlConfig.length; i++) {
+
+                       // Options can be either strings or objects with nonempty "id" properties
+                       option = QUnit.config.urlConfig[i];
+                       if (typeof option !== "string") {
+                               option = option.id;
+                       }
+
+                       if (QUnit.config[option] === undefined) {
+                               QUnit.config[option] = urlParams[option];
+                       }
+               }
+       });
+
+       function getUrlParams() {
+               var i, param, name, value;
+               var urlParams = Object.create(null);
+               var params = location.search.slice(1).split("&");
+               var length = params.length;
+
+               for (i = 0; i < length; i++) {
+                       if (params[i]) {
+                               param = params[i].split("=");
+                               name = decodeQueryParam(param[0]);
+
+                               // Allow just a key to turn on a flag, e.g., test.html?noglobals
+                               value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
+                               if (name in urlParams) {
+                                       urlParams[name] = [].concat(urlParams[name], value);
+                               } else {
+                                       urlParams[name] = value;
+                               }
+                       }
+               }
+
+               return urlParams;
+       }
+
+       function decodeQueryParam(param) {
+               return decodeURIComponent(param.replace(/\+/g, "%20"));
+       }
+  })();
+
+  var stats = {
+       passedTests: 0,
+       failedTests: 0,
+       skippedTests: 0,
+       todoTests: 0
+  };
+
+  // Escape text for attribute or text content.
+  function escapeText(s) {
+       if (!s) {
+               return "";
+       }
+       s = s + "";
+
+       // Both single quotes and double quotes (for attributes)
+       return s.replace(/['"<>&]/g, function (s) {
+               switch (s) {
+                       case "'":
+                               return "&#039;";
+                       case "\"":
+                               return "&quot;";
+                       case "<":
+                               return "&lt;";
+                       case ">":
+                               return "&gt;";
+                       case "&":
+                               return "&amp;";
+               }
+       });
+  }
+
+  (function () {
+
+       // Don't load the HTML Reporter on non-browser environments
+       if (typeof window === "undefined" || !window.document) {
+               return;
+       }
+
+       var config = QUnit.config,
+           document$$1 = window.document,
+           collapseNext = false,
+           hasOwn = Object.prototype.hasOwnProperty,
+           unfilteredUrl = setUrl({ filter: undefined, module: undefined,
+               moduleId: undefined, testId: undefined }),
+           modulesList = [];
+
+       function addEvent(elem, type, fn) {
+               elem.addEventListener(type, fn, false);
+       }
+
+       function removeEvent(elem, type, fn) {
+               elem.removeEventListener(type, fn, false);
+       }
+
+       function addEvents(elems, type, fn) {
+               var i = elems.length;
+               while (i--) {
+                       addEvent(elems[i], type, fn);
+               }
+       }
+
+       function hasClass(elem, name) {
+               return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
+       }
+
+       function addClass(elem, name) {
+               if (!hasClass(elem, name)) {
+                       elem.className += (elem.className ? " " : "") + name;
+               }
+       }
+
+       function toggleClass(elem, name, force) {
+               if (force || typeof force === "undefined" && !hasClass(elem, name)) {
+                       addClass(elem, name);
+               } else {
+                       removeClass(elem, name);
+               }
+       }
+
+       function removeClass(elem, name) {
+               var set = " " + elem.className + " ";
+
+               // Class name may appear multiple times
+               while (set.indexOf(" " + name + " ") >= 0) {
+                       set = set.replace(" " + name + " ", " ");
+               }
+
+               // Trim for prettiness
+               elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+       }
+
+       function id(name) {
+               return document$$1.getElementById && document$$1.getElementById(name);
+       }
+
+       function abortTests() {
+               var abortButton = id("qunit-abort-tests-button");
+               if (abortButton) {
+                       abortButton.disabled = true;
+                       abortButton.innerHTML = "Aborting...";
+               }
+               QUnit.config.queue.length = 0;
+               return false;
+       }
+
+       function interceptNavigation(ev) {
+               applyUrlParams();
+
+               if (ev && ev.preventDefault) {
+                       ev.preventDefault();
+               }
+
+               return false;
+       }
+
+       function getUrlConfigHtml() {
+               var i,
+                   j,
+                   val,
+                   escaped,
+                   escapedTooltip,
+                   selection = false,
+                   urlConfig = config.urlConfig,
+                   urlConfigHtml = "";
+
+               for (i = 0; i < urlConfig.length; i++) {
+
+                       // Options can be either strings or objects with nonempty "id" properties
+                       val = config.urlConfig[i];
+                       if (typeof val === "string") {
+                               val = {
+                                       id: val,
+                                       label: val
+                               };
+                       }
+
+                       escaped = escapeText(val.id);
+                       escapedTooltip = escapeText(val.tooltip);
+
+                       if (!val.value || typeof val.value === "string") {
+                               urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
+                       } else {
+                               urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+                               if (QUnit.is("array", val.value)) {
+                                       for (j = 0; j < val.value.length; j++) {
+                                               escaped = escapeText(val.value[j]);
+                                               urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
+                                       }
+                               } else {
+                                       for (j in val.value) {
+                                               if (hasOwn.call(val.value, j)) {
+                                                       urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
+                                               }
+                                       }
+                               }
+                               if (config[val.id] && !selection) {
+                                       escaped = escapeText(config[val.id]);
+                                       urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+                               }
+                               urlConfigHtml += "</select>";
+                       }
+               }
+
+               return urlConfigHtml;
+       }
+
+       // Handle "click" events on toolbar checkboxes and "change" for select menus.
+       // Updates the URL with the new state of `config.urlConfig` values.
+       function toolbarChanged() {
+               var updatedUrl,
+                   value,
+                   tests,
+                   field = this,
+                   params = {};
+
+               // Detect if field is a select menu or a checkbox
+               if ("selectedIndex" in field) {
+                       value = field.options[field.selectedIndex].value || undefined;
+               } else {
+                       value = field.checked ? field.defaultValue || true : undefined;
+               }
+
+               params[field.name] = value;
+               updatedUrl = setUrl(params);
+
+               // Check if we can apply the change without a page refresh
+               if ("hidepassed" === field.name && "replaceState" in window.history) {
+                       QUnit.urlParams[field.name] = value;
+                       config[field.name] = value || false;
+                       tests = id("qunit-tests");
+                       if (tests) {
+                               toggleClass(tests, "hidepass", value || false);
+                       }
+                       window.history.replaceState(null, "", updatedUrl);
+               } else {
+                       window.location = updatedUrl;
+               }
+       }
+
+       function setUrl(params) {
+               var key,
+                   arrValue,
+                   i,
+                   querystring = "?",
+                   location = window.location;
+
+               params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
+
+               for (key in params) {
+
+                       // Skip inherited or undefined properties
+                       if (hasOwn.call(params, key) && params[key] !== undefined) {
+
+                               // Output a parameter for each value of this key (but usually just one)
+                               arrValue = [].concat(params[key]);
+                               for (i = 0; i < arrValue.length; i++) {
+                                       querystring += encodeURIComponent(key);
+                                       if (arrValue[i] !== true) {
+                                               querystring += "=" + encodeURIComponent(arrValue[i]);
+                                       }
+                                       querystring += "&";
+                               }
+                       }
+               }
+               return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
+       }
+
+       function applyUrlParams() {
+               var i,
+                   selectedModules = [],
+                   modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
+                   filter = id("qunit-filter-input").value;
+
+               for (i = 0; i < modulesList.length; i++) {
+                       if (modulesList[i].checked) {
+                               selectedModules.push(modulesList[i].value);
+                       }
+               }
+
+               window.location = setUrl({
+                       filter: filter === "" ? undefined : filter,
+                       moduleId: selectedModules.length === 0 ? undefined : selectedModules,
+
+                       // Remove module and testId filter
+                       module: undefined,
+                       testId: undefined
+               });
+       }
+
+       function toolbarUrlConfigContainer() {
+               var urlConfigContainer = document$$1.createElement("span");
+
+               urlConfigContainer.innerHTML = getUrlConfigHtml();
+               addClass(urlConfigContainer, "qunit-url-config");
+
+               addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
+               addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
+
+               return urlConfigContainer;
+       }
+
+       function abortTestsButton() {
+               var button = document$$1.createElement("button");
+               button.id = "qunit-abort-tests-button";
+               button.innerHTML = "Abort";
+               addEvent(button, "click", abortTests);
+               return button;
+       }
+
+       function toolbarLooseFilter() {
+               var filter = document$$1.createElement("form"),
+                   label = document$$1.createElement("label"),
+                   input = document$$1.createElement("input"),
+                   button = document$$1.createElement("button");
+
+               addClass(filter, "qunit-filter");
+
+               label.innerHTML = "Filter: ";
+
+               input.type = "text";
+               input.value = config.filter || "";
+               input.name = "filter";
+               input.id = "qunit-filter-input";
+
+               button.innerHTML = "Go";
+
+               label.appendChild(input);
+
+               filter.appendChild(label);
+               filter.appendChild(document$$1.createTextNode(" "));
+               filter.appendChild(button);
+               addEvent(filter, "submit", interceptNavigation);
+
+               return filter;
+       }
+
+       function moduleListHtml() {
+               var i,
+                   checked,
+                   html = "";
+
+               for (i = 0; i < config.modules.length; i++) {
+                       if (config.modules[i].name !== "") {
+                               checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
+                               html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
+                       }
+               }
+
+               return html;
+       }
+
+       function toolbarModuleFilter() {
+               var allCheckbox,
+                   commit,
+                   reset,
+                   moduleFilter = document$$1.createElement("form"),
+                   label = document$$1.createElement("label"),
+                   moduleSearch = document$$1.createElement("input"),
+                   dropDown = document$$1.createElement("div"),
+                   actions = document$$1.createElement("span"),
+                   dropDownList = document$$1.createElement("ul"),
+                   dirty = false;
+
+               moduleSearch.id = "qunit-modulefilter-search";
+               addEvent(moduleSearch, "input", searchInput);
+               addEvent(moduleSearch, "input", searchFocus);
+               addEvent(moduleSearch, "focus", searchFocus);
+               addEvent(moduleSearch, "click", searchFocus);
+
+               label.id = "qunit-modulefilter-search-container";
+               label.innerHTML = "Module: ";
+               label.appendChild(moduleSearch);
+
+               actions.id = "qunit-modulefilter-actions";
+               actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
+               allCheckbox = actions.lastChild.firstChild;
+               commit = actions.firstChild;
+               reset = commit.nextSibling;
+               addEvent(commit, "click", applyUrlParams);
+
+               dropDownList.id = "qunit-modulefilter-dropdown-list";
+               dropDownList.innerHTML = moduleListHtml();
+
+               dropDown.id = "qunit-modulefilter-dropdown";
+               dropDown.style.display = "none";
+               dropDown.appendChild(actions);
+               dropDown.appendChild(dropDownList);
+               addEvent(dropDown, "change", selectionChange);
+               selectionChange();
+
+               moduleFilter.id = "qunit-modulefilter";
+               moduleFilter.appendChild(label);
+               moduleFilter.appendChild(dropDown);
+               addEvent(moduleFilter, "submit", interceptNavigation);
+               addEvent(moduleFilter, "reset", function () {
+
+                       // Let the reset happen, then update styles
+                       window.setTimeout(selectionChange);
+               });
+
+               // Enables show/hide for the dropdown
+               function searchFocus() {
+                       if (dropDown.style.display !== "none") {
+                               return;
+                       }
+
+                       dropDown.style.display = "block";
+                       addEvent(document$$1, "click", hideHandler);
+                       addEvent(document$$1, "keydown", hideHandler);
+
+                       // Hide on Escape keydown or outside-container click
+                       function hideHandler(e) {
+                               var inContainer = moduleFilter.contains(e.target);
+
+                               if (e.keyCode === 27 || !inContainer) {
+                                       if (e.keyCode === 27 && inContainer) {
+                                               moduleSearch.focus();
+                                       }
+                                       dropDown.style.display = "none";
+                                       removeEvent(document$$1, "click", hideHandler);
+                                       removeEvent(document$$1, "keydown", hideHandler);
+                                       moduleSearch.value = "";
+                                       searchInput();
+                               }
+                       }
+               }
+
+               // Processes module search box input
+               function searchInput() {
+                       var i,
+                           item,
+                           searchText = moduleSearch.value.toLowerCase(),
+                           listItems = dropDownList.children;
+
+                       for (i = 0; i < listItems.length; i++) {
+                               item = listItems[i];
+                               if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
+                                       item.style.display = "";
+                               } else {
+                                       item.style.display = "none";
+                               }
+                       }
+               }
+
+               // Processes selection changes
+               function selectionChange(evt) {
+                       var i,
+                           item,
+                           checkbox = evt && evt.target || allCheckbox,
+                           modulesList = dropDownList.getElementsByTagName("input"),
+                           selectedNames = [];
+
+                       toggleClass(checkbox.parentNode, "checked", checkbox.checked);
+
+                       dirty = false;
+                       if (checkbox.checked && checkbox !== allCheckbox) {
+                               allCheckbox.checked = false;
+                               removeClass(allCheckbox.parentNode, "checked");
+                       }
+                       for (i = 0; i < modulesList.length; i++) {
+                               item = modulesList[i];
+                               if (!evt) {
+                                       toggleClass(item.parentNode, "checked", item.checked);
+                               } else if (checkbox === allCheckbox && checkbox.checked) {
+                                       item.checked = false;
+                                       removeClass(item.parentNode, "checked");
+                               }
+                               dirty = dirty || item.checked !== item.defaultChecked;
+                               if (item.checked) {
+                                       selectedNames.push(item.parentNode.textContent);
+                               }
+                       }
+
+                       commit.style.display = reset.style.display = dirty ? "" : "none";
+                       moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
+                       moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
+               }
+
+               return moduleFilter;
+       }
+
+       function appendToolbar() {
+               var toolbar = id("qunit-testrunner-toolbar");
+
+               if (toolbar) {
+                       toolbar.appendChild(toolbarUrlConfigContainer());
+                       toolbar.appendChild(toolbarModuleFilter());
+                       toolbar.appendChild(toolbarLooseFilter());
+                       toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
+               }
+       }
+
+       function appendHeader() {
+               var header = id("qunit-header");
+
+               if (header) {
+                       header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
+               }
+       }
+
+       function appendBanner() {
+               var banner = id("qunit-banner");
+
+               if (banner) {
+                       banner.className = "";
+               }
+       }
+
+       function appendTestResults() {
+               var tests = id("qunit-tests"),
+                   result = id("qunit-testresult"),
+                   controls;
+
+               if (result) {
+                       result.parentNode.removeChild(result);
+               }
+
+               if (tests) {
+                       tests.innerHTML = "";
+                       result = document$$1.createElement("p");
+                       result.id = "qunit-testresult";
+                       result.className = "result";
+                       tests.parentNode.insertBefore(result, tests);
+                       result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
+                       controls = id("qunit-testresult-controls");
+               }
+
+               if (controls) {
+                       controls.appendChild(abortTestsButton());
+               }
+       }
+
+       function appendFilteredTest() {
+               var testId = QUnit.config.testId;
+               if (!testId || testId.length <= 0) {
+                       return "";
+               }
+               return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
+       }
+
+       function appendUserAgent() {
+               var userAgent = id("qunit-userAgent");
+
+               if (userAgent) {
+                       userAgent.innerHTML = "";
+                       userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
+               }
+       }
+
+       function appendInterface() {
+               var qunit = id("qunit");
+
+               if (qunit) {
+                       qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
+               }
+
+               appendHeader();
+               appendBanner();
+               appendTestResults();
+               appendUserAgent();
+               appendToolbar();
+       }
+
+       function appendTestsList(modules) {
+               var i, l, x, z, test, moduleObj;
+
+               for (i = 0, l = modules.length; i < l; i++) {
+                       moduleObj = modules[i];
+
+                       for (x = 0, z = moduleObj.tests.length; x < z; x++) {
+                               test = moduleObj.tests[x];
+
+                               appendTest(test.name, test.testId, moduleObj.name);
+                       }
+               }
+       }
+
+       function appendTest(name, testId, moduleName) {
+               var title,
+                   rerunTrigger,
+                   testBlock,
+                   assertList,
+                   tests = id("qunit-tests");
+
+               if (!tests) {
+                       return;
+               }
+
+               title = document$$1.createElement("strong");
+               title.innerHTML = getNameHtml(name, moduleName);
+
+               rerunTrigger = document$$1.createElement("a");
+               rerunTrigger.innerHTML = "Rerun";
+               rerunTrigger.href = setUrl({ testId: testId });
+
+               testBlock = document$$1.createElement("li");
+               testBlock.appendChild(title);
+               testBlock.appendChild(rerunTrigger);
+               testBlock.id = "qunit-test-output-" + testId;
+
+               assertList = document$$1.createElement("ol");
+               assertList.className = "qunit-assert-list";
+
+               testBlock.appendChild(assertList);
+
+               tests.appendChild(testBlock);
+       }
+
+       // HTML Reporter initialization and load
+       QUnit.begin(function (details) {
+               var i, moduleObj, tests;
+
+               // Sort modules by name for the picker
+               for (i = 0; i < details.modules.length; i++) {
+                       moduleObj = details.modules[i];
+                       if (moduleObj.name) {
+                               modulesList.push(moduleObj.name);
+                       }
+               }
+               modulesList.sort(function (a, b) {
+                       return a.localeCompare(b);
+               });
+
+               // Initialize QUnit elements
+               appendInterface();
+               appendTestsList(details.modules);
+               tests = id("qunit-tests");
+               if (tests && config.hidepassed) {
+                       addClass(tests, "hidepass");
+               }
+       });
+
+       QUnit.done(function (details) {
+               var banner = id("qunit-banner"),
+                   tests = id("qunit-tests"),
+                   abortButton = id("qunit-abort-tests-button"),
+                   totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
+                   html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
+                   test,
+                   assertLi,
+                   assertList;
+
+               // Update remaing tests to aborted
+               if (abortButton && abortButton.disabled) {
+                       html = "Tests aborted after " + details.runtime + " milliseconds.";
+
+                       for (var i = 0; i < tests.children.length; i++) {
+                               test = tests.children[i];
+                               if (test.className === "" || test.className === "running") {
+                                       test.className = "aborted";
+                                       assertList = test.getElementsByTagName("ol")[0];
+                                       assertLi = document$$1.createElement("li");
+                                       assertLi.className = "fail";
+                                       assertLi.innerHTML = "Test aborted.";
+                                       assertList.appendChild(assertLi);
+                               }
+                       }
+               }
+
+               if (banner && (!abortButton || abortButton.disabled === false)) {
+                       banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
+               }
+
+               if (abortButton) {
+                       abortButton.parentNode.removeChild(abortButton);
+               }
+
+               if (tests) {
+                       id("qunit-testresult-display").innerHTML = html;
+               }
+
+               if (config.altertitle && document$$1.title) {
+
+                       // Show ✖ for good, ✔ for bad suite result in title
+                       // use escape sequences in case file gets loaded with non-utf-8-charset
+                       document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
+               }
+
+               // Scroll back to top to show results
+               if (config.scrolltop && window.scrollTo) {
+                       window.scrollTo(0, 0);
+               }
+       });
+
+       function getNameHtml(name, module) {
+               var nameHtml = "";
+
+               if (module) {
+                       nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
+               }
+
+               nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
+
+               return nameHtml;
+       }
+
+       QUnit.testStart(function (details) {
+               var running, testBlock, bad;
+
+               testBlock = id("qunit-test-output-" + details.testId);
+               if (testBlock) {
+                       testBlock.className = "running";
+               } else {
+
+                       // Report later registered tests
+                       appendTest(details.name, details.testId, details.module);
+               }
+
+               running = id("qunit-testresult-display");
+               if (running) {
+                       bad = QUnit.config.reorder && details.previousFailure;
+
+                       running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
+               }
+       });
+
+       function stripHtml(string) {
+
+               // Strip tags, html entity and whitespaces
+               return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
+       }
+
+       QUnit.log(function (details) {
+               var assertList,
+                   assertLi,
+                   message,
+                   expected,
+                   actual,
+                   diff,
+                   showDiff = false,
+                   testItem = id("qunit-test-output-" + details.testId);
+
+               if (!testItem) {
+                       return;
+               }
+
+               message = escapeText(details.message) || (details.result ? "okay" : "failed");
+               message = "<span class='test-message'>" + message + "</span>";
+               message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+               // The pushFailure doesn't provide details.expected
+               // when it calls, it's implicit to also not show expected and diff stuff
+               // Also, we need to check details.expected existence, as it can exist and be undefined
+               if (!details.result && hasOwn.call(details, "expected")) {
+                       if (details.negative) {
+                               expected = "NOT " + QUnit.dump.parse(details.expected);
+                       } else {
+                               expected = QUnit.dump.parse(details.expected);
+                       }
+
+                       actual = QUnit.dump.parse(details.actual);
+                       message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
+
+                       if (actual !== expected) {
+
+                               message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
+
+                               if (typeof details.actual === "number" && typeof details.expected === "number") {
+                                       if (!isNaN(details.actual) && !isNaN(details.expected)) {
+                                               showDiff = true;
+                                               diff = details.actual - details.expected;
+                                               diff = (diff > 0 ? "+" : "") + diff;
+                                       }
+                               } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
+                                       diff = QUnit.diff(expected, actual);
+
+                                       // don't show diff if there is zero overlap
+                                       showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
+                               }
+
+                               if (showDiff) {
+                                       message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
+                               }
+                       } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
+                               message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
+                       } else {
+                               message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
+                       }
+
+                       if (details.source) {
+                               message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
+                       }
+
+                       message += "</table>";
+
+                       // This occurs when pushFailure is set and we have an extracted stack trace
+               } else if (!details.result && details.source) {
+                       message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
+               }
+
+               assertList = testItem.getElementsByTagName("ol")[0];
+
+               assertLi = document$$1.createElement("li");
+               assertLi.className = details.result ? "pass" : "fail";
+               assertLi.innerHTML = message;
+               assertList.appendChild(assertLi);
+       });
+
+       QUnit.testDone(function (details) {
+               var testTitle,
+                   time,
+                   testItem,
+                   assertList,
+                   good,
+                   bad,
+                   testCounts,
+                   skipped,
+                   sourceName,
+                   tests = id("qunit-tests");
+
+               if (!tests) {
+                       return;
+               }
+
+               testItem = id("qunit-test-output-" + details.testId);
+
+               assertList = testItem.getElementsByTagName("ol")[0];
+
+               good = details.passed;
+               bad = details.failed;
+
+               // This test passed if it has no unexpected failed assertions
+               var testPassed = details.failed > 0 ? details.todo : !details.todo;
+
+               if (testPassed) {
+
+                       // Collapse the passing tests
+                       addClass(assertList, "qunit-collapsed");
+               } else if (config.collapse) {
+                       if (!collapseNext) {
+
+                               // Skip collapsing the first failing test
+                               collapseNext = true;
+                       } else {
+
+                               // Collapse remaining tests
+                               addClass(assertList, "qunit-collapsed");
+                       }
+               }
+
+               // The testItem.firstChild is the test name
+               testTitle = testItem.firstChild;
+
+               testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
+
+               testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
+
+               if (details.skipped) {
+                       stats.skippedTests++;
+
+                       testItem.className = "skipped";
+                       skipped = document$$1.createElement("em");
+                       skipped.className = "qunit-skipped-label";
+                       skipped.innerHTML = "skipped";
+                       testItem.insertBefore(skipped, testTitle);
+               } else {
+                       addEvent(testTitle, "click", function () {
+                               toggleClass(assertList, "qunit-collapsed");
+                       });
+
+                       testItem.className = testPassed ? "pass" : "fail";
+
+                       if (details.todo) {
+                               var todoLabel = document$$1.createElement("em");
+                               todoLabel.className = "qunit-todo-label";
+                               todoLabel.innerHTML = "todo";
+                               testItem.className += " todo";
+                               testItem.insertBefore(todoLabel, testTitle);
+                       }
+
+                       time = document$$1.createElement("span");
+                       time.className = "runtime";
+                       time.innerHTML = details.runtime + " ms";
+                       testItem.insertBefore(time, assertList);
+
+                       if (!testPassed) {
+                               stats.failedTests++;
+                       } else if (details.todo) {
+                               stats.todoTests++;
+                       } else {
+                               stats.passedTests++;
+                       }
+               }
+
+               // Show the source of the test when showing assertions
+               if (details.source) {
+                       sourceName = document$$1.createElement("p");
+                       sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
+                       addClass(sourceName, "qunit-source");
+                       if (testPassed) {
+                               addClass(sourceName, "qunit-collapsed");
+                       }
+                       addEvent(testTitle, "click", function () {
+                               toggleClass(sourceName, "qunit-collapsed");
+                       });
+                       testItem.appendChild(sourceName);
+               }
+       });
+
+       // Avoid readyState issue with phantomjs
+       // Ref: #818
+       var notPhantom = function (p) {
+               return !(p && p.version && p.version.major > 0);
+       }(window.phantom);
+
+       if (notPhantom && document$$1.readyState === "complete") {
+               QUnit.load();
+       } else {
+               addEvent(window, "load", QUnit.load);
+       }
+
+       // Wrap window.onerror. We will call the original window.onerror to see if
+       // the existing handler fully handles the error; if not, we will call the
+       // QUnit.onError function.
+       var originalWindowOnError = window.onerror;
+
+       // Cover uncaught exceptions
+       // Returning true will suppress the default browser handler,
+       // returning false will let it run.
+       window.onerror = function (message, fileName, lineNumber) {
+               var ret = false;
+               if (originalWindowOnError) {
+                       for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
+                               args[_key - 3] = arguments[_key];
+                       }
+
+                       ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
+               }
+
+               // Treat return value as window.onerror itself does,
+               // Only do our handling if not suppressed.
+               if (ret !== true) {
+                       var error = {
+                               message: message,
+                               fileName: fileName,
+                               lineNumber: lineNumber
+                       };
+
+                       ret = QUnit.onError(error);
+               }
+
+               return ret;
+       };
+  })();
+
+  /*
+   * This file is a modified version of google-diff-match-patch's JavaScript implementation
+   * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
+   * modifications are licensed as more fully set forth in LICENSE.txt.
+   *
+   * The original source of google-diff-match-patch is attributable and licensed as follows:
+   *
+   * Copyright 2006 Google Inc.
+   * https://code.google.com/p/google-diff-match-patch/
+   *
+   * Licensed under the Apache License, Version 2.0 (the "License");
+   * you may not use this file except in compliance with the License.
+   * You may obtain a copy of the License at
+   *
+   * https://www.apache.org/licenses/LICENSE-2.0
+   *
+   * Unless required by applicable law or agreed to in writing, software
+   * distributed under the License is distributed on an "AS IS" BASIS,
+   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   * See the License for the specific language governing permissions and
+   * limitations under the License.
+   *
+   * More Info:
+   *  https://code.google.com/p/google-diff-match-patch/
+   *
+   * Usage: QUnit.diff(expected, actual)
+   *
+   */
+  QUnit.diff = function () {
+       function DiffMatchPatch() {}
+
+       //  DIFF FUNCTIONS
+
+       /**
+    * The data structure representing a diff is an array of tuples:
+    * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
+    * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
+    */
+       var DIFF_DELETE = -1,
+           DIFF_INSERT = 1,
+           DIFF_EQUAL = 0;
+
+       /**
+    * Find the differences between two texts.  Simplifies the problem by stripping
+    * any common prefix or suffix off the texts before diffing.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean=} optChecklines Optional speedup flag. If present and false,
+    *     then don't run a line-level diff first to identify the changed areas.
+    *     Defaults to true, which does a faster, slightly less optimal diff.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
+               var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
+
+               // The diff must be complete in up to 1 second.
+               deadline = new Date().getTime() + 1000;
+
+               // Check for null inputs.
+               if (text1 === null || text2 === null) {
+                       throw new Error("Null input. (DiffMain)");
+               }
+
+               // Check for equality (speedup).
+               if (text1 === text2) {
+                       if (text1) {
+                               return [[DIFF_EQUAL, text1]];
+                       }
+                       return [];
+               }
+
+               if (typeof optChecklines === "undefined") {
+                       optChecklines = true;
+               }
+
+               checklines = optChecklines;
+
+               // Trim off common prefix (speedup).
+               commonlength = this.diffCommonPrefix(text1, text2);
+               commonprefix = text1.substring(0, commonlength);
+               text1 = text1.substring(commonlength);
+               text2 = text2.substring(commonlength);
+
+               // Trim off common suffix (speedup).
+               commonlength = this.diffCommonSuffix(text1, text2);
+               commonsuffix = text1.substring(text1.length - commonlength);
+               text1 = text1.substring(0, text1.length - commonlength);
+               text2 = text2.substring(0, text2.length - commonlength);
+
+               // Compute the diff on the middle block.
+               diffs = this.diffCompute(text1, text2, checklines, deadline);
+
+               // Restore the prefix and suffix.
+               if (commonprefix) {
+                       diffs.unshift([DIFF_EQUAL, commonprefix]);
+               }
+               if (commonsuffix) {
+                       diffs.push([DIFF_EQUAL, commonsuffix]);
+               }
+               this.diffCleanupMerge(diffs);
+               return diffs;
+       };
+
+       /**
+    * Reduce the number of edits by eliminating operationally trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
+               var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
+               changes = false;
+               equalities = []; // Stack of indices where equalities are found.
+               equalitiesLength = 0; // Keeping our own length var is faster in JS.
+               /** @type {?string} */
+               lastequality = null;
+
+               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+               pointer = 0; // Index of current position.
+
+               // Is there an insertion operation before the last equality.
+               preIns = false;
+
+               // Is there a deletion operation before the last equality.
+               preDel = false;
+
+               // Is there an insertion operation after the last equality.
+               postIns = false;
+
+               // Is there a deletion operation after the last equality.
+               postDel = false;
+               while (pointer < diffs.length) {
+
+                       // Equality found.
+                       if (diffs[pointer][0] === DIFF_EQUAL) {
+                               if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
+
+                                       // Candidate found.
+                                       equalities[equalitiesLength++] = pointer;
+                                       preIns = postIns;
+                                       preDel = postDel;
+                                       lastequality = diffs[pointer][1];
+                               } else {
+
+                                       // Not a candidate, and can never become one.
+                                       equalitiesLength = 0;
+                                       lastequality = null;
+                               }
+                               postIns = postDel = false;
+
+                               // An insertion or deletion.
+                       } else {
+
+                               if (diffs[pointer][0] === DIFF_DELETE) {
+                                       postDel = true;
+                               } else {
+                                       postIns = true;
+                               }
+
+                               /*
+       * Five types to be split:
+       * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+       * <ins>A</ins>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<ins>C</ins>
+       * <ins>A</del>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<del>C</del>
+       */
+                               if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
+
+                                       // Duplicate record.
+                                       diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+                                       // Change second copy to insert.
+                                       diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+                                       equalitiesLength--; // Throw away the equality we just deleted;
+                                       lastequality = null;
+                                       if (preIns && preDel) {
+
+                                               // No changes made which could affect previous entry, keep going.
+                                               postIns = postDel = true;
+                                               equalitiesLength = 0;
+                                       } else {
+                                               equalitiesLength--; // Throw away the previous equality.
+                                               pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+                                               postIns = postDel = false;
+                                       }
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+       };
+
+       /**
+    * Convert a diff array into a pretty HTML report.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {integer} string to be beautified.
+    * @return {string} HTML representation.
+    */
+       DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
+               var op,
+                   data,
+                   x,
+                   html = [];
+               for (x = 0; x < diffs.length; x++) {
+                       op = diffs[x][0]; // Operation (insert, delete, equal)
+                       data = diffs[x][1]; // Text of change.
+                       switch (op) {
+                               case DIFF_INSERT:
+                                       html[x] = "<ins>" + escapeText(data) + "</ins>";
+                                       break;
+                               case DIFF_DELETE:
+                                       html[x] = "<del>" + escapeText(data) + "</del>";
+                                       break;
+                               case DIFF_EQUAL:
+                                       html[x] = "<span>" + escapeText(data) + "</span>";
+                                       break;
+                       }
+               }
+               return html.join("");
+       };
+
+       /**
+    * Determine the common prefix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the start of each
+    *     string.
+    */
+       DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
+               var pointermid, pointermax, pointermin, pointerstart;
+
+               // Quick check for common null cases.
+               if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
+                       return 0;
+               }
+
+               // Binary search.
+               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+               pointermin = 0;
+               pointermax = Math.min(text1.length, text2.length);
+               pointermid = pointermax;
+               pointerstart = 0;
+               while (pointermin < pointermid) {
+                       if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
+                               pointermin = pointermid;
+                               pointerstart = pointermin;
+                       } else {
+                               pointermax = pointermid;
+                       }
+                       pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+               }
+               return pointermid;
+       };
+
+       /**
+    * Determine the common suffix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of each string.
+    */
+       DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
+               var pointermid, pointermax, pointermin, pointerend;
+
+               // Quick check for common null cases.
+               if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
+                       return 0;
+               }
+
+               // Binary search.
+               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+               pointermin = 0;
+               pointermax = Math.min(text1.length, text2.length);
+               pointermid = pointermax;
+               pointerend = 0;
+               while (pointermin < pointermid) {
+                       if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
+                               pointermin = pointermid;
+                               pointerend = pointermin;
+                       } else {
+                               pointermax = pointermid;
+                       }
+                       pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+               }
+               return pointermid;
+       };
+
+       /**
+    * Find the differences between two texts.  Assumes that the texts do not
+    * have any common prefix or suffix.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean} checklines Speedup flag.  If false, then don't run a
+    *     line-level diff first to identify the changed areas.
+    *     If true, then run a faster, slightly less optimal diff.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
+               var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
+
+               if (!text1) {
+
+                       // Just add some text (speedup).
+                       return [[DIFF_INSERT, text2]];
+               }
+
+               if (!text2) {
+
+                       // Just delete some text (speedup).
+                       return [[DIFF_DELETE, text1]];
+               }
+
+               longtext = text1.length > text2.length ? text1 : text2;
+               shorttext = text1.length > text2.length ? text2 : text1;
+               i = longtext.indexOf(shorttext);
+               if (i !== -1) {
+
+                       // Shorter text is inside the longer text (speedup).
+                       diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
+
+                       // Swap insertions for deletions if diff is reversed.
+                       if (text1.length > text2.length) {
+                               diffs[0][0] = diffs[2][0] = DIFF_DELETE;
+                       }
+                       return diffs;
+               }
+
+               if (shorttext.length === 1) {
+
+                       // Single character string.
+                       // After the previous speedup, the character can't be an equality.
+                       return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+               }
+
+               // Check to see if the problem can be split in two.
+               hm = this.diffHalfMatch(text1, text2);
+               if (hm) {
+
+                       // A half-match was found, sort out the return data.
+                       text1A = hm[0];
+                       text1B = hm[1];
+                       text2A = hm[2];
+                       text2B = hm[3];
+                       midCommon = hm[4];
+
+                       // Send both pairs off for separate processing.
+                       diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
+                       diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
+
+                       // Merge the results.
+                       return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
+               }
+
+               if (checklines && text1.length > 100 && text2.length > 100) {
+                       return this.diffLineMode(text1, text2, deadline);
+               }
+
+               return this.diffBisect(text1, text2, deadline);
+       };
+
+       /**
+    * Do the two texts share a substring which is at least half the length of the
+    * longer text?
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {Array.<string>} Five element Array, containing the prefix of
+    *     text1, the suffix of text1, the prefix of text2, the suffix of
+    *     text2 and the common middle.  Or null if there was no match.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
+               var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
+
+               longtext = text1.length > text2.length ? text1 : text2;
+               shorttext = text1.length > text2.length ? text2 : text1;
+               if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
+                       return null; // Pointless.
+               }
+               dmp = this; // 'this' becomes 'window' in a closure.
+
+               /**
+     * Does a substring of shorttext exist within longtext such that the substring
+     * is at least half the length of longtext?
+     * Closure, but does not reference any external variables.
+     * @param {string} longtext Longer string.
+     * @param {string} shorttext Shorter string.
+     * @param {number} i Start index of quarter length substring within longtext.
+     * @return {Array.<string>} Five element Array, containing the prefix of
+     *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
+     *     of shorttext and the common middle.  Or null if there was no match.
+     * @private
+     */
+               function diffHalfMatchI(longtext, shorttext, i) {
+                       var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+
+                       // Start with a 1/4 length substring at position i as a seed.
+                       seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
+                       j = -1;
+                       bestCommon = "";
+                       while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
+                               prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
+                               suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
+                               if (bestCommon.length < suffixLength + prefixLength) {
+                                       bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
+                                       bestLongtextA = longtext.substring(0, i - suffixLength);
+                                       bestLongtextB = longtext.substring(i + prefixLength);
+                                       bestShorttextA = shorttext.substring(0, j - suffixLength);
+                                       bestShorttextB = shorttext.substring(j + prefixLength);
+                               }
+                       }
+                       if (bestCommon.length * 2 >= longtext.length) {
+                               return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
+                       } else {
+                               return null;
+                       }
+               }
+
+               // First check if the second quarter is the seed for a half-match.
+               hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
+
+               // Check again based on the third quarter.
+               hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
+               if (!hm1 && !hm2) {
+                       return null;
+               } else if (!hm2) {
+                       hm = hm1;
+               } else if (!hm1) {
+                       hm = hm2;
+               } else {
+
+                       // Both matched.  Select the longest.
+                       hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
+               }
+
+               // A half-match was found, sort out the return data.
+               if (text1.length > text2.length) {
+                       text1A = hm[0];
+                       text1B = hm[1];
+                       text2A = hm[2];
+                       text2B = hm[3];
+               } else {
+                       text2A = hm[0];
+                       text2B = hm[1];
+                       text1A = hm[2];
+                       text1B = hm[3];
+               }
+               midCommon = hm[4];
+               return [text1A, text1B, text2A, text2B, midCommon];
+       };
+
+       /**
+    * Do a quick line-level diff on both strings, then rediff the parts for
+    * greater accuracy.
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
+               var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
+
+               // Scan the text on a line-by-line basis first.
+               a = this.diffLinesToChars(text1, text2);
+               text1 = a.chars1;
+               text2 = a.chars2;
+               linearray = a.lineArray;
+
+               diffs = this.DiffMain(text1, text2, false, deadline);
+
+               // Convert the diff back to original text.
+               this.diffCharsToLines(diffs, linearray);
+
+               // Eliminate freak matches (e.g. blank lines)
+               this.diffCleanupSemantic(diffs);
+
+               // Rediff any replacement blocks, this time character-by-character.
+               // Add a dummy entry at the end.
+               diffs.push([DIFF_EQUAL, ""]);
+               pointer = 0;
+               countDelete = 0;
+               countInsert = 0;
+               textDelete = "";
+               textInsert = "";
+               while (pointer < diffs.length) {
+                       switch (diffs[pointer][0]) {
+                               case DIFF_INSERT:
+                                       countInsert++;
+                                       textInsert += diffs[pointer][1];
+                                       break;
+                               case DIFF_DELETE:
+                                       countDelete++;
+                                       textDelete += diffs[pointer][1];
+                                       break;
+                               case DIFF_EQUAL:
+
+                                       // Upon reaching an equality, check for prior redundancies.
+                                       if (countDelete >= 1 && countInsert >= 1) {
+
+                                               // Delete the offending records and add the merged ones.
+                                               diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
+                                               pointer = pointer - countDelete - countInsert;
+                                               a = this.DiffMain(textDelete, textInsert, false, deadline);
+                                               for (j = a.length - 1; j >= 0; j--) {
+                                                       diffs.splice(pointer, 0, a[j]);
+                                               }
+                                               pointer = pointer + a.length;
+                                       }
+                                       countInsert = 0;
+                                       countDelete = 0;
+                                       textDelete = "";
+                                       textInsert = "";
+                                       break;
+                       }
+                       pointer++;
+               }
+               diffs.pop(); // Remove the dummy entry at the end.
+
+               return diffs;
+       };
+
+       /**
+    * Find the 'middle snake' of a diff, split the problem in two
+    * and return the recursively constructed diff.
+    * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
+               var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+
+               // Cache the text lengths to prevent multiple calls.
+               text1Length = text1.length;
+               text2Length = text2.length;
+               maxD = Math.ceil((text1Length + text2Length) / 2);
+               vOffset = maxD;
+               vLength = 2 * maxD;
+               v1 = new Array(vLength);
+               v2 = new Array(vLength);
+
+               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
+               // integers and undefined.
+               for (x = 0; x < vLength; x++) {
+                       v1[x] = -1;
+                       v2[x] = -1;
+               }
+               v1[vOffset + 1] = 0;
+               v2[vOffset + 1] = 0;
+               delta = text1Length - text2Length;
+
+               // If the total number of characters is odd, then the front path will collide
+               // with the reverse path.
+               front = delta % 2 !== 0;
+
+               // Offsets for start and end of k loop.
+               // Prevents mapping of space beyond the grid.
+               k1start = 0;
+               k1end = 0;
+               k2start = 0;
+               k2end = 0;
+               for (d = 0; d < maxD; d++) {
+
+                       // Bail out if deadline is reached.
+                       if (new Date().getTime() > deadline) {
+                               break;
+                       }
+
+                       // Walk the front path one step.
+                       for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+                               k1Offset = vOffset + k1;
+                               if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
+                                       x1 = v1[k1Offset + 1];
+                               } else {
+                                       x1 = v1[k1Offset - 1] + 1;
+                               }
+                               y1 = x1 - k1;
+                               while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
+                                       x1++;
+                                       y1++;
+                               }
+                               v1[k1Offset] = x1;
+                               if (x1 > text1Length) {
+
+                                       // Ran off the right of the graph.
+                                       k1end += 2;
+                               } else if (y1 > text2Length) {
+
+                                       // Ran off the bottom of the graph.
+                                       k1start += 2;
+                               } else if (front) {
+                                       k2Offset = vOffset + delta - k1;
+                                       if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
+
+                                               // Mirror x2 onto top-left coordinate system.
+                                               x2 = text1Length - v2[k2Offset];
+                                               if (x1 >= x2) {
+
+                                                       // Overlap detected.
+                                                       return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Walk the reverse path one step.
+                       for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+                               k2Offset = vOffset + k2;
+                               if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
+                                       x2 = v2[k2Offset + 1];
+                               } else {
+                                       x2 = v2[k2Offset - 1] + 1;
+                               }
+                               y2 = x2 - k2;
+                               while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
+                                       x2++;
+                                       y2++;
+                               }
+                               v2[k2Offset] = x2;
+                               if (x2 > text1Length) {
+
+                                       // Ran off the left of the graph.
+                                       k2end += 2;
+                               } else if (y2 > text2Length) {
+
+                                       // Ran off the top of the graph.
+                                       k2start += 2;
+                               } else if (!front) {
+                                       k1Offset = vOffset + delta - k2;
+                                       if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
+                                               x1 = v1[k1Offset];
+                                               y1 = vOffset + x1 - k1Offset;
+
+                                               // Mirror x2 onto top-left coordinate system.
+                                               x2 = text1Length - x2;
+                                               if (x1 >= x2) {
+
+                                                       // Overlap detected.
+                                                       return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // Diff took too long and hit the deadline or
+               // number of diffs equals number of characters, no commonality at all.
+               return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+       };
+
+       /**
+    * Given the location of the 'middle snake', split the diff in two parts
+    * and recurse.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} x Index of split point in text1.
+    * @param {number} y Index of split point in text2.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
+               var text1a, text1b, text2a, text2b, diffs, diffsb;
+               text1a = text1.substring(0, x);
+               text2a = text2.substring(0, y);
+               text1b = text1.substring(x);
+               text2b = text2.substring(y);
+
+               // Compute both diffs serially.
+               diffs = this.DiffMain(text1a, text2a, false, deadline);
+               diffsb = this.DiffMain(text1b, text2b, false, deadline);
+
+               return diffs.concat(diffsb);
+       };
+
+       /**
+    * Reduce the number of edits by eliminating semantically trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
+               var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
+               changes = false;
+               equalities = []; // Stack of indices where equalities are found.
+               equalitiesLength = 0; // Keeping our own length var is faster in JS.
+               /** @type {?string} */
+               lastequality = null;
+
+               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+               pointer = 0; // Index of current position.
+
+               // Number of characters that changed prior to the equality.
+               lengthInsertions1 = 0;
+               lengthDeletions1 = 0;
+
+               // Number of characters that changed after the equality.
+               lengthInsertions2 = 0;
+               lengthDeletions2 = 0;
+               while (pointer < diffs.length) {
+                       if (diffs[pointer][0] === DIFF_EQUAL) {
+                               // Equality found.
+                               equalities[equalitiesLength++] = pointer;
+                               lengthInsertions1 = lengthInsertions2;
+                               lengthDeletions1 = lengthDeletions2;
+                               lengthInsertions2 = 0;
+                               lengthDeletions2 = 0;
+                               lastequality = diffs[pointer][1];
+                       } else {
+                               // An insertion or deletion.
+                               if (diffs[pointer][0] === DIFF_INSERT) {
+                                       lengthInsertions2 += diffs[pointer][1].length;
+                               } else {
+                                       lengthDeletions2 += diffs[pointer][1].length;
+                               }
+
+                               // Eliminate an equality that is smaller or equal to the edits on both
+                               // sides of it.
+                               if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
+
+                                       // Duplicate record.
+                                       diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+                                       // Change second copy to insert.
+                                       diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+
+                                       // Throw away the equality we just deleted.
+                                       equalitiesLength--;
+
+                                       // Throw away the previous equality (it needs to be reevaluated).
+                                       equalitiesLength--;
+                                       pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+
+                                       // Reset the counters.
+                                       lengthInsertions1 = 0;
+                                       lengthDeletions1 = 0;
+                                       lengthInsertions2 = 0;
+                                       lengthDeletions2 = 0;
+                                       lastequality = null;
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               // Normalize the diff.
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+
+               // Find any overlaps between deletions and insertions.
+               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
+               //   -> <del>abc</del>xxx<ins>def</ins>
+               // e.g: <del>xxxabc</del><ins>defxxx</ins>
+               //   -> <ins>def</ins>xxx<del>abc</del>
+               // Only extract an overlap if it is as big as the edit ahead or behind it.
+               pointer = 1;
+               while (pointer < diffs.length) {
+                       if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
+                               deletion = diffs[pointer - 1][1];
+                               insertion = diffs[pointer][1];
+                               overlapLength1 = this.diffCommonOverlap(deletion, insertion);
+                               overlapLength2 = this.diffCommonOverlap(insertion, deletion);
+                               if (overlapLength1 >= overlapLength2) {
+                                       if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
+
+                                               // Overlap found.  Insert an equality and trim the surrounding edits.
+                                               diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
+                                               diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
+                                               diffs[pointer + 1][1] = insertion.substring(overlapLength1);
+                                               pointer++;
+                                       }
+                               } else {
+                                       if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
+
+                                               // Reverse overlap found.
+                                               // Insert an equality and swap and trim the surrounding edits.
+                                               diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
+
+                                               diffs[pointer - 1][0] = DIFF_INSERT;
+                                               diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
+                                               diffs[pointer + 1][0] = DIFF_DELETE;
+                                               diffs[pointer + 1][1] = deletion.substring(overlapLength2);
+                                               pointer++;
+                                       }
+                               }
+                               pointer++;
+                       }
+                       pointer++;
+               }
+       };
+
+       /**
+    * Determine if the suffix of one string is the prefix of another.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of the first
+    *     string and the start of the second string.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
+               var text1Length, text2Length, textLength, best, length, pattern, found;
+
+               // Cache the text lengths to prevent multiple calls.
+               text1Length = text1.length;
+               text2Length = text2.length;
+
+               // Eliminate the null case.
+               if (text1Length === 0 || text2Length === 0) {
+                       return 0;
+               }
+
+               // Truncate the longer string.
+               if (text1Length > text2Length) {
+                       text1 = text1.substring(text1Length - text2Length);
+               } else if (text1Length < text2Length) {
+                       text2 = text2.substring(0, text1Length);
+               }
+               textLength = Math.min(text1Length, text2Length);
+
+               // Quick check for the worst case.
+               if (text1 === text2) {
+                       return textLength;
+               }
+
+               // Start by looking for a single character match
+               // and increase length until no match is found.
+               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
+               best = 0;
+               length = 1;
+               while (true) {
+                       pattern = text1.substring(textLength - length);
+                       found = text2.indexOf(pattern);
+                       if (found === -1) {
+                               return best;
+                       }
+                       length += found;
+                       if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
+                               best = length;
+                               length++;
+                       }
+               }
+       };
+
+       /**
+    * Split two texts into an array of strings.  Reduce the texts to a string of
+    * hashes where each Unicode character represents one line.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
+    *     An object containing the encoded text1, the encoded text2 and
+    *     the array of unique strings.
+    *     The zeroth element of the array of unique strings is intentionally blank.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
+               var lineArray, lineHash, chars1, chars2;
+               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
+               lineHash = {}; // E.g. lineHash['Hello\n'] === 4
+
+               // '\x00' is a valid character, but various debuggers don't like it.
+               // So we'll insert a junk entry to avoid generating a null character.
+               lineArray[0] = "";
+
+               /**
+     * Split a text into an array of strings.  Reduce the texts to a string of
+     * hashes where each Unicode character represents one line.
+     * Modifies linearray and linehash through being a closure.
+     * @param {string} text String to encode.
+     * @return {string} Encoded string.
+     * @private
+     */
+               function diffLinesToCharsMunge(text) {
+                       var chars, lineStart, lineEnd, lineArrayLength, line;
+                       chars = "";
+
+                       // Walk the text, pulling out a substring for each line.
+                       // text.split('\n') would would temporarily double our memory footprint.
+                       // Modifying text would create many large strings to garbage collect.
+                       lineStart = 0;
+                       lineEnd = -1;
+
+                       // Keeping our own length variable is faster than looking it up.
+                       lineArrayLength = lineArray.length;
+                       while (lineEnd < text.length - 1) {
+                               lineEnd = text.indexOf("\n", lineStart);
+                               if (lineEnd === -1) {
+                                       lineEnd = text.length - 1;
+                               }
+                               line = text.substring(lineStart, lineEnd + 1);
+                               lineStart = lineEnd + 1;
+
+                               if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
+                                       chars += String.fromCharCode(lineHash[line]);
+                               } else {
+                                       chars += String.fromCharCode(lineArrayLength);
+                                       lineHash[line] = lineArrayLength;
+                                       lineArray[lineArrayLength++] = line;
+                               }
+                       }
+                       return chars;
+               }
+
+               chars1 = diffLinesToCharsMunge(text1);
+               chars2 = diffLinesToCharsMunge(text2);
+               return {
+                       chars1: chars1,
+                       chars2: chars2,
+                       lineArray: lineArray
+               };
+       };
+
+       /**
+    * Rehydrate the text in a diff from a string of line hashes to real lines of
+    * text.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {!Array.<string>} lineArray Array of unique strings.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
+               var x, chars, text, y;
+               for (x = 0; x < diffs.length; x++) {
+                       chars = diffs[x][1];
+                       text = [];
+                       for (y = 0; y < chars.length; y++) {
+                               text[y] = lineArray[chars.charCodeAt(y)];
+                       }
+                       diffs[x][1] = text.join("");
+               }
+       };
+
+       /**
+    * Reorder and merge like edit sections.  Merge equalities.
+    * Any edit section can move as long as it doesn't cross an equality.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
+               var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
+               diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
+               pointer = 0;
+               countDelete = 0;
+               countInsert = 0;
+               textDelete = "";
+               textInsert = "";
+
+               while (pointer < diffs.length) {
+                       switch (diffs[pointer][0]) {
+                               case DIFF_INSERT:
+                                       countInsert++;
+                                       textInsert += diffs[pointer][1];
+                                       pointer++;
+                                       break;
+                               case DIFF_DELETE:
+                                       countDelete++;
+                                       textDelete += diffs[pointer][1];
+                                       pointer++;
+                                       break;
+                               case DIFF_EQUAL:
+
+                                       // Upon reaching an equality, check for prior redundancies.
+                                       if (countDelete + countInsert > 1) {
+                                               if (countDelete !== 0 && countInsert !== 0) {
+
+                                                       // Factor out any common prefixes.
+                                                       commonlength = this.diffCommonPrefix(textInsert, textDelete);
+                                                       if (commonlength !== 0) {
+                                                               if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
+                                                                       diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
+                                                               } else {
+                                                                       diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
+                                                                       pointer++;
+                                                               }
+                                                               textInsert = textInsert.substring(commonlength);
+                                                               textDelete = textDelete.substring(commonlength);
+                                                       }
+
+                                                       // Factor out any common suffixies.
+                                                       commonlength = this.diffCommonSuffix(textInsert, textDelete);
+                                                       if (commonlength !== 0) {
+                                                               diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
+                                                               textInsert = textInsert.substring(0, textInsert.length - commonlength);
+                                                               textDelete = textDelete.substring(0, textDelete.length - commonlength);
+                                                       }
+                                               }
+
+                                               // Delete the offending records and add the merged ones.
+                                               if (countDelete === 0) {
+                                                       diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
+                                               } else if (countInsert === 0) {
+                                                       diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
+                                               } else {
+                                                       diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
+                                               }
+                                               pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
+                                       } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
+
+                                               // Merge this equality with the previous one.
+                                               diffs[pointer - 1][1] += diffs[pointer][1];
+                                               diffs.splice(pointer, 1);
+                                       } else {
+                                               pointer++;
+                                       }
+                                       countInsert = 0;
+                                       countDelete = 0;
+                                       textDelete = "";
+                                       textInsert = "";
+                                       break;
+                       }
+               }
+               if (diffs[diffs.length - 1][1] === "") {
+                       diffs.pop(); // Remove the dummy entry at the end.
+               }
+
+               // Second pass: look for single edits surrounded on both sides by equalities
+               // which can be shifted sideways to eliminate an equality.
+               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+               changes = false;
+               pointer = 1;
+
+               // Intentionally ignore the first and last element (don't need checking).
+               while (pointer < diffs.length - 1) {
+                       if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
+
+                               diffPointer = diffs[pointer][1];
+                               position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
+
+                               // This is a single edit surrounded by equalities.
+                               if (position === diffs[pointer - 1][1]) {
+
+                                       // Shift the edit over the previous equality.
+                                       diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
+                                       diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
+                                       diffs.splice(pointer - 1, 1);
+                                       changes = true;
+                               } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
+
+                                       // Shift the edit over the next equality.
+                                       diffs[pointer - 1][1] += diffs[pointer + 1][1];
+                                       diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
+                                       diffs.splice(pointer + 1, 1);
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               // If shifts were made, the diff needs reordering and another shift sweep.
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+       };
+
+       return function (o, n) {
+               var diff, output, text;
+               diff = new DiffMatchPatch();
+               output = diff.DiffMain(o, n);
+               diff.diffCleanupEfficiency(output);
+               text = diff.diffPrettyHtml(output);
+
+               return text;
+       };
+  }();
+
+}((function() { return this; }())));
index 7773866..40b3baf 100644 (file)
@@ -64,8 +64,7 @@
                        if ( $badge.length ) {
                                $badge
                                        .toggleClass( 'mw-badge-important', isImportant )
-                                       .find( '.mw-badge-content' )
-                                               .text( text );
+                                       .find( '.mw-badge-content' ).text( text );
                        } else {
                                // Otherwise, create a new badge with the specified text and style
                                $badge = $( '<div class="mw-badge"></div>' )
index 2be1dba..a5b136d 100644 (file)
@@ -42,7 +42,7 @@
 
                        // Look for rgb(num%,num%,num%)
                        // eslint-disable-next-line no-cond-assign
-                       if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec( color ) ) {
+                       if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec( color ) ) {
                                return [
                                        parseFloat( result[ 1 ] ) * 2.55,
                                        parseFloat( result[ 2 ] ) * 2.55,
index 5ce9b1f..aa76d6d 100644 (file)
                        buildDefaultToggleLink = function () {
                                return $( '<a class="mw-collapsible-text"></a>' )
                                        .text( collapseText )
-                                       .wrap( '<span class="mw-collapsible-toggle"></span>' ).parent()
-                                               .attr( {
-                                                       role: 'button',
-                                                       tabindex: 0
-                                               } )
-                                               .prepend( '<span>[</span>' )
-                                               .append( '<span>]</span>' )
-                                               .on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
+                                       .wrap( '<span class="mw-collapsible-toggle"></span>' )
+                                       .parent()
+                                       .attr( {
+                                               role: 'button',
+                                               tabindex: 0
+                                       } )
+                                       .prepend( '<span>[</span>' )
+                                       .append( '<span>]</span>' )
+                                       .on( 'click.mw-collapsible keypress.mw-collapsible', actionHandler );
                        };
 
                        // Check if this element has a custom position for the toggle link
index 6d478bd..4bcccdd 100644 (file)
@@ -11,7 +11,7 @@
                },
                trimRight: function ( str ) {
                        return str === null ?
-                                       '' : str.toString().replace( /\s+$/, '' );
+                               '' : str.toString().replace( /\s+$/, '' );
                },
                ucFirst: function ( str ) {
                        return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
        } );
 
        mw.log.deprecate( $, 'escapeRE', function ( str ) {
-               return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
+               return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ); // eslint-disable-line no-useless-escape
        }, 'Use mediawiki.RegExp instead.' );
 
 }( jQuery, mediaWiki ) );
index 75f1ba6..4f4edc9 100644 (file)
@@ -19,7 +19,6 @@
  * @class jQuery.plugin.suggestions
  */
 
- // jscs:disable checkParamNames
 /**
  * @method suggestions
  * @chainable
@@ -94,7 +93,6 @@
  * @param {boolean} [options.highlightInput=false] Whether to highlight matched portions of the
  *  input or not.
  */
- // jscs:enable checkParamNames
 
 ( function ( $, mw ) {
 
                                                        27, // escape
                                                        13, // enter
                                                        46, // delete
-                                                       8   // backspace
+                                                       8 //   backspace
                                                ];
                                                if ( context.data.keypressedCount === 0 &&
                                                        e.which === context.data.keypressed &&
index 8d019e5..ec91773 100644 (file)
 
                // Build RegEx
                // Any date formated with . , ' - or /
-               ts.dateRegex[ 0 ] = new RegExp( /^\s*(\d{1,2})[\,\.\-\/'\s]{1,2}(\d{1,2})[\,\.\-\/'\s]{1,2}(\d{2,4})\s*?/i );
+               ts.dateRegex[ 0 ] = new RegExp( /^\s*(\d{1,2})[,.\-/'\s]{1,2}(\d{1,2})[,.\-/'\s]{1,2}(\d{2,4})\s*?/i );
 
                // Written Month name, dmy
                ts.dateRegex[ 1 ] = new RegExp(
                        }
 
                        columnToCell = [];
-                       cellsInRow = ( $row[ 0 ].cells.length ) || 0;  // all cells in this row
+                       cellsInRow = ( $row[ 0 ].cells.length ) || 0; // all cells in this row
                        index = 0; // real cell index in this row
                        for ( j = 0; j < columns; index++ ) {
                                if ( index === cellsInRow ) {
                }
                ts.rgx = {
                        IPAddress: [
-                               new RegExp( /^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/ )
+                               new RegExp( /^\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}$/ )
                        ],
                        currency: [
                                new RegExp( /(^[£$€¥]|[£$€¥]$)/ ),
                                new RegExp( /(https?|ftp|file):\/\// )
                        ],
                        isoDate: [
-                               new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)([T\s]((([01]\d|2[0-3])(:?[0-5]\d)?|24:?00)?(:?([0-5]\d|60))?([.,]\d+)?)([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
+                               new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)([T\s]((([01]\d|2[0-3])(:?[0-5]\d)?|24:?00)?(:?([0-5]\d|60))?([.,]\d+)?)([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
                                new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)/ )
                        ],
                        usLongDate: [
                        return getParserById( id );
                },
 
-               getParsers: function () {  // for table diagnosis
+               getParsers: function () { // for table diagnosis
                        return parsers;
                }
        };
index 53c1fbb..2b6fc9d 100644 (file)
                                        role: 'navigation',
                                        'aria-labelledby': 'p-lang-label'
                                } )
-                               .append( $( '<h3>' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) )
-                               .append( $( '<div>' ).addClass( 'body' ).append( '<ul>' ) )
+                                       .append( $( '<h3>' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) )
+                                       .append( $( '<div>' ).addClass( 'body' ).append( '<ul>' ) )
                        );
                }
 
index b3b0af2..6f49fa6 100644 (file)
@@ -23,8 +23,7 @@ jQuery( function ( $ ) {
                        return true;
                }
 
-               $lis
-               .each( function () {
+               $lis.each( function () {
                        $li = $( this );
                        $inputs = $li.find( 'input[type="radio"]' );
                        $oldidRadio = $inputs.filter( '[name="oldid"]' ).eq( 0 );
@@ -97,8 +96,7 @@ jQuery( function ( $ ) {
                        // Also remove potentially conflicting id attributes that we don't need anyway
                        $copyForm
                                .css( 'display', 'none' )
-                               .find( '[id]' )
-                                       .removeAttr( 'id' )
+                               .find( '[id]' ).removeAttr( 'id' )
                                .end()
                                .insertAfter( $historyCompareForm )
                                .submit();
index a3a82d5..0d000c9 100644 (file)
                $col = $( '<td colspan="2"></td>' );
 
                $link = $( '<a>' )
-               .text( showText )
-               .attr( {
-                       role: 'button',
-                       tabindex: 0
-               } )
-               .on( 'click keypress', function ( e ) {
-                       if (
-                               e.type === 'click' ||
-                               e.type === 'keypress' && e.which === 13
-                       ) {
-                               if ( $table.hasClass( 'collapsed' ) ) {
-                                       $( this ).text( hideText );
-                               } else {
-                                       $( this ).text( showText );
+                       .text( showText )
+                       .attr( {
+                               role: 'button',
+                               tabindex: 0
+                       } )
+                       .on( 'click keypress', function ( e ) {
+                               if (
+                                       e.type === 'click' ||
+                                       e.type === 'keypress' && e.which === 13
+                               ) {
+                                       if ( $table.hasClass( 'collapsed' ) ) {
+                                               $( this ).text( hideText );
+                                       } else {
+                                               $( this ).text( showText );
+                                       }
+                                       $table.toggleClass( 'expanded collapsed' );
                                }
-                               $table.toggleClass( 'expanded collapsed' );
-                       }
-               } );
+                       } );
 
                $col.append( $link );
                $row.append( $col );
index b4418e5..dec0fe8 100644 (file)
@@ -8,19 +8,26 @@
 
 /**
  * Hide all the elements irrelevant for printing
+ * Skins however can and should override.
  */
+/* General hide-in-print class, please only use sparely */
 .noprint,
-#jump-to-nav,
+/* Various content classes, in alphabetical order */
+.catlinks,
+.magnify,
+.mw-cite-backlink,
 .mw-jump,
-#column-one,
 .mw-editsection,
 .mw-editsection-like,
-#footer-places,
 .mw-hidden-catlinks,
-.usermessage,
+.mw-indicators,
+.mw-redirectedfrom,
 .patrollink,
-.ns-0 .mw-redirectedfrom,
-.magnify,
+.usermessage,
+/* Various content ids, in alphabetical order */
+#column-one,
+#footer-places,
+#jump-to-nav,
 #mw-navigation,
 #siteNotice,
 /* Deprecated, changed in core */
@@ -43,6 +50,42 @@ body {
        padding: 0;
 }
 
+/* Links */
+a {
+       background: none !important; /* stylelint-disable-line declaration-no-important */
+       padding: 0 !important; /* stylelint-disable-line declaration-no-important */
+}
+
+a,
+a.external,
+a.new,
+a.stub {
+       /* IE 6 & 7 don't understand `inherit` */
+       color: #000 !important; /* stylelint-disable-line declaration-no-important */
+       text-decoration: none !important; /* stylelint-disable-line declaration-no-important */
+       /* Modern browser will apply this, IE 6 & 7 ignore the unknown */
+       color: inherit !important; /* stylelint-disable-line declaration-block-no-duplicate-properties, declaration-no-important */
+       text-decoration: inherit !important; /* stylelint-disable-line declaration-block-no-duplicate-properties, declaration-no-important */
+}
+
+/* Expand URLs for printing */
+.mw-body-content a.external.text:after,
+.mw-body-content a.external.autonumber:after {
+       content: ' (' attr( href ) ')';
+       word-break: break-all;
+       word-wrap: break-word;
+}
+
+/* Expand protocol-relative URLs for printing */
+.mw-body-content a.external.text[href^='//']:after,
+.mw-body-content a.external.autonumber[href^='//']:after {
+       content: ' (https:' attr( href ) ')';
+}
+
+dt {
+       font-weight: bold;
+}
+
 h1,
 h2,
 h3,
@@ -52,24 +95,30 @@ h6 {
        font-weight: bold;
        /* Pagination */
        page-break-after: avoid;
-}
-
-dt {
-       font-weight: bold;
-}
-
-ul {
-       list-style-type: square;
+       page-break-before: avoid;
 }
 
 p {
        margin: 1em 0;
-       line-height: 1.2em;
+       line-height: 1.2;
        /* Pagination */
        orphans: 3;
        widows: 3;
 }
 
+img,
+figure,
+.wikitable,
+.thumb {
+       /* Pagination */
+       page-break-inside: avoid;
+}
+
+img {
+       border: 0;
+       vertical-align: middle;
+}
+
 pre,
 .mw-code {
        background: #fff;
@@ -82,17 +131,8 @@ pre,
        overflow: auto;
 }
 
-img,
-figure,
-.wikitable,
-.thumb {
-       /* Pagination */
-       page-break-inside: avoid;
-}
-
-img {
-       border: 0;
-       vertical-align: middle;
+ul {
+       list-style-type: square;
 }
 
 /**
@@ -129,13 +169,13 @@ img {
        display: table;
 }
 
-/* Separate columns for tocnumber and toctext */
+/* Separate columns for `.tocnumber` and `.toctext` */
 .tocnumber,
 .toctext {
        display: table-cell;
 }
 
-/* Space between the columns for tocnumber and toctext */
+/* Space between those columns */
 .tocnumber {
        padding-left: 0;
        padding-right: 0.5em;
@@ -153,53 +193,12 @@ img {
        padding-right: 0;
 }
 
-#footer {
-       background: #fff;
-       color: #000;
-       margin-top: 1em;
-       border-top: 1pt solid #aaa;
-       direction: ltr;
-}
-
 /**
- * Links
- */
-a {
-       background: none !important; /* stylelint-disable-line declaration-no-important */
-       padding: 0 !important; /* stylelint-disable-line declaration-no-important */
-}
-
-/* Expand URLs for printing */
-.mw-body-content a.external.text:after,
-.mw-body-content a.external.autonumber:after {
-       content: ' (' attr( href ) ')';
-       word-break: break-all;
-       word-wrap: break-word;
-}
-
-/* Expand protocol-relative URLs for printing */
-.mw-body-content a.external.text[href^='//']:after,
-.mw-body-content a.external.autonumber[href^='//']:after {
-       content: ' (https:' attr( href ) ')';
-}
-
-a,
-a.external,
-a.new,
-a.stub {
-       /* IE 6 & 7 don't understand `inherit` */
-       color: #000 !important; /* stylelint-disable-line declaration-no-important */
-       text-decoration: none !important; /* stylelint-disable-line declaration-no-important */
-       /* Modern browser will apply this, IE 6 & 7 ignore the unknown */
-       color: inherit !important; /* stylelint-disable-line declaration-block-no-duplicate-properties, declaration-no-important */
-       text-decoration: inherit !important; /* stylelint-disable-line declaration-block-no-duplicate-properties, declaration-no-important */
-}
-
-/**
- * Floating divs
+ * Floating divs (and thumbnails)
  */
 /* @noflip */
-div.floatright {
+div.floatright,
+div.tright {
        float: right;
        clear: right;
        position: relative;
@@ -211,7 +210,8 @@ div.floatright p {
 }
 
 /* @noflip */
-div.floatleft {
+div.floatleft,
+div.tleft {
        float: left;
        clear: left;
        position: relative;
@@ -255,25 +255,11 @@ html .thumbimage {
 html .thumbcaption {
        border: 0;
        text-align: left;
-       line-height: 1.4em;
+       line-height: 1.4;
        padding: 3px;
        font-size: 94%;
 }
 
-/* @noflip */
-div.tright {
-       float: right;
-       clear: right;
-       margin: 0.5em 0 0.8em 1.4em;
-}
-
-/* @noflip */
-div.tleft {
-       float: left;
-       clear: left;
-       margin: 0.5em 1.4em 0.8em 0;
-}
-
 img.thumbborder {
        border: 1pt solid #ddd;
 }
@@ -290,6 +276,11 @@ table.mw_metadata {
        border-collapse: collapse;
 }
 
+table.wikitable > caption,
+.mw_metadata caption {
+       font-weight: bold;
+}
+
 table.wikitable > tr > th,
 table.wikitable > tr > td,
 table.wikitable > * > tr > th,
@@ -308,11 +299,6 @@ table.wikitable > * > tr > th,
        text-align: center;
 }
 
-table.wikitable > caption,
-.mw_metadata caption {
-       font-weight: bold;
-}
-
 table.listing,
 table.listing td {
        border: 1pt solid #000;
@@ -330,17 +316,29 @@ table.listing td {
 
 .catlinks li {
        display: inline-block;
-       line-height: 1.15em;
-       padding: 0 0.4em;
-       border-left: 1pt solid #aaa;
+       line-height: 1.15;
        margin: 0.1em 0;
+       border-left: 1pt solid #aaa;
+       padding: 0 0.4em;
 }
 
 .catlinks li:first-child {
-       padding-left: 0.2em;
        border-left: 0;
+       padding-left: 0.2em;
 }
 
+/**
+ * Footer
+ */
 .printfooter {
        padding: 1em 0;
 }
+
+#footer {
+       background: #fff;
+       color: #000;
+       margin-top: 1em;
+       border-top: 1pt solid #aaa;
+       padding-top: 5px;
+       direction: ltr;
+}
index c839a13..49c0b82 100644 (file)
         *
         * @param {jQuery|string} changesListContent
         * @param {jQuery} $fieldset
+        * @param {boolean} [isInitialDOM] Using the initial (already attached) DOM elements
         */
-       mw.rcfilters.dm.ChangesListViewModel.prototype.update = function ( changesListContent, $fieldset ) {
+       mw.rcfilters.dm.ChangesListViewModel.prototype.update = function ( changesListContent, $fieldset, isInitialDOM ) {
                this.valid = true;
-               this.emit( 'update', changesListContent, $fieldset );
+               this.emit( 'update', changesListContent, $fieldset, isInitialDOM );
        };
 
 }( mediaWiki ) );
index 2307f30..b6eda0f 100644 (file)
@@ -13,6 +13,8 @@
         *  is a part of.
         * @cfg {string} [title] Group title
         * @cfg {boolean} [hidden] This group is hidden from the regular menu views
+        * @cfg {boolean} [allowArbitrary] Allows for an arbitrary value to be added to the
+        *  group from the URL, even if it wasn't initially set up.
         * @cfg {string} [separator='|'] Value separator for 'string_options' groups
         * @cfg {boolean} [active] Group is active
         * @cfg {boolean} [fullCoverage] This filters in this group collectively cover all results
                this.view = config.view || 'default';
                this.title = config.title || name;
                this.hidden = !!config.hidden;
+               this.allowArbitrary = !!config.allowArbitrary;
                this.separator = config.separator || '|';
                this.labelPrefixKey = config.labelPrefixKey;
 
+               this.currSelected = null;
                this.active = !!config.active;
                this.fullCoverage = !!config.fullCoverage;
 
@@ -48,6 +52,7 @@
 
                this.conflicts = config.conflicts || {};
                this.defaultParams = {};
+               this.defaultFilters = {};
 
                this.aggregate( { update: 'filterItemUpdate' } );
                this.connect( this, { filterItemUpdate: 'onFilterItemUpdate' } );
@@ -75,7 +80,8 @@
         * @param {string|Object} [groupDefault] Definition of the group default
         */
        mw.rcfilters.dm.FilterGroup.prototype.initializeFilters = function ( filterDefinition, groupDefault ) {
-               var supersetMap = {},
+               var defaultParam,
+                       supersetMap = {},
                        model = this,
                        items = [];
 
@@ -84,6 +90,7 @@
                        var subsetNames = [],
                                filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, {
                                        group: model.getName(),
+                                       useDefaultAsBaseValue: !!filter.useDefaultAsBaseValue,
                                        label: filter.label || filter.name,
                                        description: filter.description || '',
                                        labelPrefixKey: model.labelPrefixKey,
                        items.push( filterItem );
 
                        // Store default parameter state; in this case, default is defined per filter
-                       if ( model.getType() === 'send_unselected_if_any' ) {
+                       if (
+                               model.getType() === 'send_unselected_if_any' ||
+                               model.getType() === 'boolean'
+                       ) {
                                // Store the default parameter state
                                // For this group type, parameter values are direct
                                // We need to convert from a boolean to a string ('1' and '0')
                                } )
                        ).join( this.getSeparator() );
                } else if ( this.getType() === 'single_option' ) {
+                       defaultParam = groupDefault !== undefined ?
+                               groupDefault : this.getItems()[ 0 ].getParamName();
+
                        // For this group, the parameter is the group name,
-                       // and a single item can be selected, or none at all
-                       // The item also must be recognized or none is set as
-                       // default
-                       model.defaultParams[ this.getName() ] = this.getItemByParamName( groupDefault ) ? groupDefault : '';
+                       // and a single item can be selected: default or first item
+                       this.defaultParams[ this.getName() ] = defaultParam;
+
+                       // Single option means there must be a single option
+                       // selected, so we have to either select the default
+                       // or select the first option
+                       this.selectItemByParamName( defaultParam );
                }
+
+               // Store default filter state based on default params
+               this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() );
+
+               // Check for filters that should be initially selected by their default value
+               this.getItems().forEach( function ( item ) {
+                       if (
+                               item.isUsingDefaultAsBaseValue() &&
+                               (
+                                       // This setting can only be applied to these groups
+                                       // the other groups are way too complex for that
+                                       model.getType() === 'single_option' ||
+                                       model.getType() === 'boolean'
+                               )
+                       ) {
+                               // Apply selection
+                               item.toggleSelected( !!model.defaultFilters[ item.getName() ] );
+                       }
+               } );
        };
 
        /**
         */
        mw.rcfilters.dm.FilterGroup.prototype.onFilterItemUpdate = function ( item ) {
                // Update state
-               var active = this.areAnySelected(),
-                       itemName = item && item.getName();
+               var changed = false,
+                       active = this.areAnySelected();
+
+               if (
+                       item.isSelected() &&
+                       this.getType() === 'single_option' &&
+                       this.currSelected &&
+                       this.currSelected !== item
+               ) {
+                       this.currSelected.toggleSelected( false );
+               }
 
-               if ( item.isSelected() && this.getType() === 'single_option' ) {
-                       // Change the selection to only be the newly selected item
-                       this.getItems().forEach( function ( filterItem ) {
-                               if ( filterItem.getName() !== itemName ) {
-                                       filterItem.toggleSelected( false );
-                               }
-                       } );
+               // For 'single_option' groups, check if we just unselected all
+               // items. This should never be the result. If we did unselect
+               // all (like resetting all filters to false) then this group
+               // must choose its default item or the first item in the group
+               if (
+                       this.getType() === 'single_option' &&
+                       !this.getItems().some( function ( filterItem ) {
+                               return filterItem.isSelected();
+                       } )
+               ) {
+                       // Single option means there must be a single option
+                       // selected, so we have to either select the default
+                       // or select the first option
+                       this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] );
+                       this.currSelected.toggleSelected( true );
+                       changed = true;
                }
 
-               if ( this.active !== active ) {
+               if (
+                       changed ||
+                       this.active !== active ||
+                       this.currSelected !== item
+               ) {
                        this.active = active;
+                       this.currSelected = item;
+
                        this.emit( 'update' );
                }
        };
                return this.hidden;
        };
 
+       /**
+        * Get group allow arbitrary state
+        *
+        * @return {boolean} Group allows an arbitrary value from the URL
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.isAllowArbitrary = function () {
+               return this.allowArbitrary;
+       };
+
        /**
         * Get group name
         *
                return this.defaultParams;
        };
 
+       /**
+        * Get the default filter state of this group
+        *
+        * @return {Object} Default filter state
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getDefaultFilters = function () {
+               return this.defaultFilters;
+       };
+
+       /**
+        * This is for a single_option and string_options group types
+        * it returns the value of the default
+        *
+        * @return {string} Value of the default
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getDefaulParamValue = function () {
+               return this.defaultParams[ this.getName() ];
+       };
        /**
         * Get the messags defining the 'whats this' popup for this group
         *
                var values,
                        areAnySelected = false,
                        buildFromCurrentState = !filterRepresentation,
+                       defaultFilters = this.getDefaultFilters(),
                        result = {},
                        model = this,
                        filterParamNames = {},
                        } else if ( !filterRepresentation[ item.getName() ] ) {
                                // We are given a filter representation, but we have to make
                                // sure that we fill in the missing filters if there are any
-                               // we will assume they are all falsey
-                               filterRepresentation[ item.getName() ] = false;
+                               // we will assume they are all falsey, unless they have
+                               // isUsingDefaultAsBaseValue, in which case they get their
+                               // default state
+                               if (
+                                       item.isUsingDefaultAsBaseValue() &&
+                                       (
+                                               // This setting can only be applied to these groups
+                                               // the other groups are way too complex for that
+                                               model.getType() === 'single_option' ||
+                                               model.getType() === 'boolean'
+                                       )
+                               ) {
+                                       filterRepresentation[ item.getName() ] = !!defaultFilters[ item.getName() ];
+                               } else {
+                                       filterRepresentation[ item.getName() ] = false;
+                               }
                        }
 
                        if ( filterRepresentation[ item.getName() ] ) {
                } );
 
                // Build result
-               if ( this.getType() === 'send_unselected_if_any' ) {
+               if (
+                       this.getType() === 'send_unselected_if_any' ||
+                       this.getType() === 'boolean'
+               ) {
                        // First, check if any of the items are selected at all.
                        // If none is selected, we're treating it as if they are
                        // all false
 
                        // Go over the items and define the correct values
                        $.each( filterRepresentation, function ( name, value ) {
-                               result[ filterParamNames[ name ] ] = areAnySelected ?
-                                       // We must store all parameter values as strings '0' or '1'
-                                       String( Number( !value ) ) :
-                                       '0';
+                               // We must store all parameter values as strings '0' or '1'
+                               if ( model.getType() === 'send_unselected_if_any' ) {
+                                       result[ filterParamNames[ name ] ] = areAnySelected ?
+                                               String( Number( !value ) ) :
+                                               '0';
+                               } else if ( model.getType() === 'boolean' ) {
+                                       // Representation is straight-forward and direct from
+                                       // the parameter value to the filter state
+                                       result[ filterParamNames[ name ] ] = String( Number( !!value ) );
+                               }
                        } );
                } else if ( this.getType() === 'string_options' ) {
                        values = [];
         * Get the filter representation this group would provide
         * based on given parameter states.
         *
-        * @param {Object|string} [paramRepresentation] An object defining a parameter
+        * @param {Object} [paramRepresentation] An object defining a parameter
         *  state to translate the filter state from. If not given, an object
         *  representing all filters as falsey is returned; same as if the parameter
         *  given were an empty object, or had some of the filters missing.
         * @return {Object} Filter representation
         */
        mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) {
-               var areAnySelected, paramValues,
+               var areAnySelected, paramValues, defaultValue, item, currentValue,
+                       oneWasSelected = false,
+                       defaultParams = this.getDefaultParams(),
+                       defaultFilters = this.getDefaultFilters(),
+                       expandedParams = $.extend( true, {}, paramRepresentation ),
                        model = this,
                        paramToFilterMap = {},
                        result = {};
 
-               if ( this.getType() === 'send_unselected_if_any' ) {
-                       paramRepresentation = paramRepresentation || {};
-                       // Expand param representation to include all filters in the group
+               paramRepresentation = paramRepresentation || {};
+               if (
+                       this.getType() === 'send_unselected_if_any' ||
+                       this.getType() === 'boolean'
+               ) {
+                       // Go over param representation; map and check for selections
                        this.getItems().forEach( function ( filterItem ) {
                                var paramName = filterItem.getParamName();
 
-                               paramRepresentation[ paramName ] = paramRepresentation[ paramName ] || '0';
+                               expandedParams[ paramName ] = paramRepresentation[ paramName ] || '0';
                                paramToFilterMap[ paramName ] = filterItem;
 
                                if ( Number( paramRepresentation[ filterItem.getParamName() ] ) ) {
                                }
                        } );
 
-                       $.each( paramRepresentation, function ( paramName, paramValue ) {
-                               var filterItem = paramToFilterMap[ paramName ];
+                       $.each( expandedParams, function ( paramName, paramValue ) {
+                               var value = paramValue,
+                                       filterItem = paramToFilterMap[ paramName ];
 
-                               result[ filterItem.getName() ] = areAnySelected ?
+                               if ( model.getType() === 'send_unselected_if_any' ) {
                                        // Flip the definition between the parameter
                                        // state and the filter state
                                        // This is what the 'toggleSelected' value of the filter is
-                                       !Number( paramValue ) :
-                                       // Otherwise, there are no selected items in the
-                                       // group, which means the state is false
-                                       false;
+                                       result[ filterItem.getName() ] = areAnySelected ?
+                                               !Number( paramValue ) :
+                                               // Otherwise, there are no selected items in the
+                                               // group, which means the state is false
+                                               false;
+                               } else if ( model.getType() === 'boolean' ) {
+                                       // Straight-forward definition of state
+                                       if (
+                                               filterItem.isUsingDefaultAsBaseValue() &&
+                                               paramRepresentation[ filterItem.getParamName() ] === undefined
+                                       ) {
+                                               value = defaultParams[ filterItem.getParamName() ];
+                                       }
+                                       result[ filterItem.getName() ] = !!Number( value );
+                               }
                        } );
                } else if ( this.getType() === 'string_options' ) {
-                       paramRepresentation = paramRepresentation || '';
+                       currentValue = paramRepresentation[ this.getName() ] || '';
 
                        // Normalize the given parameter values
                        paramValues = mw.rcfilters.utils.normalizeParamOptions(
                                // Given
-                               paramRepresentation.split(
+                               currentValue.split(
                                        this.getSeparator()
                                ),
                                // Allowed values
                        );
                        // Translate the parameter values into a filter selection state
                        this.getItems().forEach( function ( filterItem ) {
+                               // All true (either because all values are written or the term 'all' is written)
+                               // is the same as all filters set to true
                                result[ filterItem.getName() ] = (
-                                               // If it is the word 'all'
-                                               paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
-                                               // All values are written
-                                               paramValues.length === model.getItemCount()
-                                       ) ?
-                                       // All true (either because all values are written or the term 'all' is written)
-                                       // is the same as all filters set to true
+                                       // If it is the word 'all'
+                                       paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
+                                       // All values are written
+                                       paramValues.length === model.getItemCount()
+                               ) ?
                                        true :
                                        // Otherwise, the filter is selected only if it appears in the parameter values
                                        paramValues.indexOf( filterItem.getParamName() ) > -1;
                        } );
                } else if ( this.getType() === 'single_option' ) {
-                       // There is parameter that fits a single filter, or none at all
+                       // There is parameter that fits a single filter and if not, get the default
                        this.getItems().forEach( function ( filterItem ) {
-                               result[ filterItem.getName() ] = filterItem.getParamName() === paramRepresentation;
+                               var selected = false;
+
+                               if (
+                                       filterItem.isUsingDefaultAsBaseValue() &&
+                                       paramRepresentation[ model.getName() ] === undefined
+                               ) {
+                                       selected = !!Number( paramRepresentation[ model.getName() ] );
+                               } else {
+                                       selected = filterItem.getParamName() === paramRepresentation[ model.getName() ];
+                               }
+                               result[ filterItem.getName() ] = selected;
+                               oneWasSelected = oneWasSelected || selected;
                        } );
                }
 
                // Go over result and make sure all filters are represented.
                // If any filters are missing, they will get a falsey value
                this.getItems().forEach( function ( filterItem ) {
-                       result[ filterItem.getName() ] = !!result[ filterItem.getName() ];
+                       if (
+                               (
+                                       // This setting can only be applied to these groups
+                                       // the other groups are way too complex for that
+                                       model.getType() === 'single_option' ||
+                                       model.getType() === 'boolean'
+                               ) &&
+                               result[ filterItem.getName() ] === undefined &&
+                               filterItem.isUsingDefaultAsBaseValue()
+                       ) {
+                               result[ filterItem.getName() ] = !!defaultFilters[ filterItem.getName() ];
+                       }
+                       oneWasSelected = oneWasSelected || !!result[ filterItem.getName() ];
                } );
 
+               // Make sure that at least one option is selected in
+               // single_option groups, no matter what path was taken
+               // If none was selected by the given definition, then
+               // we need to select the one in the base state -- either
+               // the default given, or the first item
+               if (
+                       this.getType() === 'single_option' &&
+                       !oneWasSelected
+               ) {
+                       defaultValue = this.getDefaultParams();
+                       item = this.getItemByParamName( defaultValue[ this.getName() ] );
+                       result[ item.getName() ] = true;
+               }
+
                return result;
        };
 
                } )[ 0 ];
        };
 
+       /**
+        * Select an item by its parameter name
+        *
+        * @param {string} paramName Filter parameter name
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.selectItemByParamName = function ( paramName ) {
+               this.getItems().forEach( function ( item ) {
+                       item.toggleSelected( item.getParamName() === paramName );
+               } );
+       };
+
        /**
         * Get item by its parameter name
         *
index 75431d9..3281735 100644 (file)
                } );
 
                // Collect views
-               allViews = {
+               allViews = $.extend( true, {
                        'default': {
                                title: mw.msg( 'rcfilters-filterlist-title' ),
                                groups: filterGroups
                        }
-               };
-
-               if ( views && mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ) ) {
-                       // If we have extended views, add them in
-                       $.extend( true, allViews, views );
-               }
+               }, views );
 
                // Go over all views
                $.each( allViews, function ( viewName, viewData ) {
 
                // Create a map between known parameters and their models
                $.each( this.groups, function ( group, groupModel ) {
-                       if ( groupModel.getType() === 'send_unselected_if_any' ) {
+                       if (
+                               groupModel.getType() === 'send_unselected_if_any' ||
+                               groupModel.getType() === 'boolean'
+                       ) {
                                // Individual filters
                                groupModel.getItems().forEach( function ( filterItem ) {
                                        model.parameterMap[ filterItem.getParamName() ] = filterItem;
                //    group2: "param4|param5"
                // }
                $.each( params, function ( paramName, paramValue ) {
-                       var itemOrGroup = model.parameterMap[ paramName ];
-
-                       if ( itemOrGroup instanceof mw.rcfilters.dm.FilterItem ) {
-                               groupMap[ itemOrGroup.getGroupName() ] = groupMap[ itemOrGroup.getGroupName() ] || {};
-                               groupMap[ itemOrGroup.getGroupName() ][ itemOrGroup.getParamName() ] = paramValue;
-                       } else if ( itemOrGroup instanceof mw.rcfilters.dm.FilterGroup ) {
-                               // This parameter represents a group (values are the filters)
-                               // this is equivalent to checking if the group is 'string_options'
-                               groupMap[ itemOrGroup.getName() ] = groupMap[ itemOrGroup.getName() ] || {};
-                               groupMap[ itemOrGroup.getName() ] = paramValue;
+                       var groupName,
+                               itemOrGroup = model.parameterMap[ paramName ];
+
+                       if ( itemOrGroup ) {
+                               groupName = itemOrGroup instanceof mw.rcfilters.dm.FilterItem ?
+                                       itemOrGroup.getGroupName() : itemOrGroup.getName();
+
+                               groupMap[ groupName ] = groupMap[ groupName ] || {};
+                               groupMap[ groupName ][ paramName ] = paramValue;
                        }
                } );
 
                // Check if there are either any selected items or any items
                // that have highlight enabled
                return !this.getItems().some( function ( filterItem ) {
-                       return filterItem.isSelected() || filterItem.isHighlighted();
+                       return !filterItem.getGroupModel().isHidden() && ( filterItem.isSelected() || filterItem.isHighlighted() );
                } );
        };
 
                } );
        };
 
+       /**
+        * Get all selected items
+        *
+        * @return {mw.rcfilters.dm.FilterItem[]} Selected items
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedItems = function () {
+               var allSelected = [];
+
+               $.each( this.getFilterGroups(), function ( groupName, groupModel ) {
+                       allSelected = allSelected.concat( groupModel.getSelectedItems() );
+               } );
+
+               return allSelected;
+       };
        /**
         * Switch the current view
         *
index aa82e21..54a4dbe 100644 (file)
@@ -32,6 +32,7 @@
                this.namePrefix = config.namePrefix || 'item_';
                this.name = this.namePrefix + param;
 
+               this.useDefaultAsBaseValue = !!config.useDefaultAsBaseValue;
                this.label = config.label || this.name;
                this.labelPrefixKey = config.labelPrefixKey;
                this.description = config.description || '';
                return this.identifiers;
        };
 
+       /**
+        * Check whether the item uses its default state as a base value
+        *
+        * @return {boolean} Use default as base value
+        */
+       mw.rcfilters.dm.ItemModel.prototype.isUsingDefaultAsBaseValue = function () {
+               return this.useDefaultAsBaseValue;
+       };
+
        /**
         * Toggle the highlight feature on and off for this filter.
         * It only works if highlight is supported for this filter.
index 5858566..3b8ebbd 100644 (file)
@@ -15,6 +15,8 @@
                this.baseFilterState = {};
                this.uriProcessor = null;
                this.initializing = false;
+
+               this.prevLoggedItems = [];
        };
 
        /* Initialization */
                        views = {},
                        items = [],
                        uri = new mw.Uri(),
-                       $changesList = $( '.mw-changeslist' ).first().contents();
+                       $changesList = $( '.mw-changeslist' ).first().contents(),
+                       experimentalViews = mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ),
+                       createFilterDataFromNumber = function ( num, convertedNumForLabel ) {
+                               return {
+                                       name: String( num ),
+                                       label: mw.language.convertNumber( convertedNumForLabel )
+                               };
+                       };
 
                // Prepare views
-               if ( namespaceStructure ) {
+               if ( namespaceStructure && experimentalViews ) {
                        items = [];
                        $.each( namespaceStructure, function ( namespaceID, label ) {
                                // Build and clean up the individual namespace items definition
@@ -66,7 +75,7 @@
                                } ]
                        };
                }
-               if ( tagList ) {
+               if ( tagList && experimentalViews ) {
                        views.tags = {
                                title: mw.msg( 'rcfilters-view-tags' ),
                                trigger: '#',
                        };
                }
 
+               // Add parameter range operations
+               views.range = {
+                       groups: [
+                               {
+                                       name: 'limit',
+                                       type: 'single_option',
+                                       title: '', // Because it's a hidden group, this title actually appears nowhere
+                                       hidden: true,
+                                       allowArbitrary: true,
+                                       validate: $.isNumeric,
+                                       sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
+                                       'default': '50',
+                                       filters: [ 50, 100, 250, 500 ].map( function ( num ) {
+                                               return createFilterDataFromNumber( num, num );
+                                       } )
+                               },
+                               {
+                                       name: 'days',
+                                       type: 'single_option',
+                                       title: '', // Because it's a hidden group, this title actually appears nowhere
+                                       hidden: true,
+                                       allowArbitrary: true,
+                                       validate: $.isNumeric,
+                                       sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
+                                       'default': '7',
+                                       filters: [
+                                               // Hours (1, 2, 6, 12)
+                                               0.04166, 0.0833, 0.25, 0.5,
+                                               // Days
+                                               1, 3, 7, 14, 30
+                                       ].map( function ( num ) {
+                                               return createFilterDataFromNumber(
+                                                       num,
+                                                       // Convert fractions of days to number of hours for the labels
+                                                       num < 1 ? Math.round( num * 24 ) : num
+                                               );
+                                       } )
+                               }
+                       ]
+               };
+
+               // Before we do anything, we need to see if we require another item in the
+               // groups that have 'AllowArbitrary'. For the moment, those are only single_option
+               // groups; if we ever expand it, this might need further generalization:
+               $.each( views, function ( viewName, viewData ) {
+                       viewData.groups.forEach( function ( groupData ) {
+                               // This is only true for single_option and string_options
+                               // We assume these are the only groups that will allow for
+                               // arbitrary, since it doesn't make any sense for the other
+                               // groups.
+                               var uriValue = uri.query[ groupData.name ];
+
+                               if (
+                                       // If the group allows for arbitrary data
+                                       groupData.allowArbitrary &&
+                                       // and it is single_option (or string_options, but we
+                                       // don't have cases of those yet, nor do we plan to)
+                                       groupData.type === 'single_option' &&
+                                       // and if there is a valid value in the URI already
+                                       uri.query[ groupData.name ] !== undefined &&
+                                       // and, if there is a validate method and it passes on
+                                       // the data
+                                       ( !groupData.validate || groupData.validate( uri.query[ groupData.name ] ) ) &&
+                                       // but if that value isn't already in the definition
+                                       groupData.filters
+                                               .map( function ( filterData ) {
+                                                       return filterData.name;
+                                               } )
+                                               .indexOf( uri.query[ groupData.name ] ) === -1
+                               ) {
+                                       // Add the filter information
+                                       if ( groupData.name === 'days' ) {
+                                               // Specific fix for hours/days which go by the same param
+                                               groupData.filters.push( createFilterDataFromNumber(
+                                                       uriValue,
+                                                       // In this case we don't want to round because it can be arbitrary
+                                                       // weird numbers but we want to round to 2 decimal digits
+                                                       Number( uriValue ) < 1 ?
+                                                               ( Number( uriValue ) * 24 ).toFixed( 2 ) :
+                                                               Number( uriValue )
+                                               ) );
+                                       } else {
+                                               groupData.filters.push( createFilterDataFromNumber( uriValue, uriValue ) );
+                                       }
+
+                                       // If there's a sort function set up, re-sort the values
+                                       if ( groupData.sortFunc ) {
+                                               groupData.filters.sort( groupData.sortFunc );
+                                       }
+                               }
+                       } );
+               } );
+
                // Initialize the model
                this.filtersModel.initializeFilters( filterStructure, views );
 
                        // so it gets processed
                        this.changesListModel.update(
                                $changesList.length ? $changesList : 'NO_RESULTS',
-                               $( 'fieldset.rcoptions' ).first()
+                               $( 'fieldset.rcoptions' ).first(),
+                               true // We're using existing DOM elements
                        );
                }
 
                        this.filtersModel.toggleFilterSelected( filterName, false );
                        this.updateChangesList();
                        this.filtersModel.reassessFilterInteractions( filterItem );
+
+                       // Log filter grouping
+                       this.trackFilterGroupings( 'removefilter' );
                }
 
                if ( isHighlighted ) {
                        this.filtersModel.reassessFilterInteractions();
 
                        this.updateChangesList();
+
+                       // Log filter grouping
+                       this.trackFilterGroupings( 'savedfilters' );
                }
        };
 
                );
        };
 
+       /**
+        * Track filter grouping usage
+        *
+        * @param {string} action Action taken
+        */
+       mw.rcfilters.Controller.prototype.trackFilterGroupings = function ( action ) {
+               var controller = this,
+                       rightNow = new Date().getTime(),
+                       randomIdentifier = String( mw.user.sessionId() ) + String( rightNow ) + String( Math.random() ),
+                       // Get all current filters
+                       filters = this.filtersModel.getSelectedItems().map( function ( item ) {
+                               return item.getName();
+                       } );
+
+               action = action || 'filtermenu';
+
+               // Check if these filters were the ones we just logged previously
+               // (Don't log the same grouping twice, in case the user opens/closes)
+               // the menu without action, or with the same result
+               if (
+                       // Only log if the two arrays are different in size
+                       filters.length !== this.prevLoggedItems.length ||
+                       // Or if any filters are not the same as the cached filters
+                       filters.some( function ( filterName ) {
+                               return controller.prevLoggedItems.indexOf( filterName ) === -1;
+                       } ) ||
+                       // Or if any cached filters are not the same as given filters
+                       this.prevLoggedItems.some( function ( filterName ) {
+                               return filters.indexOf( filterName ) === -1;
+                       } )
+               ) {
+                       filters.forEach( function ( filterName ) {
+                               mw.track(
+                                       'event.ChangesListFilterGrouping',
+                                       {
+                                               action: action,
+                                               groupIdentifier: randomIdentifier,
+                                               filter: filterName,
+                                               userId: mw.user.getId()
+                                       }
+                               );
+                       } );
+
+                       // Cache the filter names
+                       this.prevLoggedItems = filters;
+               }
+       };
 }( mediaWiki, jQuery ) );
index b7852d0..b4ea8af 100644 (file)
                // wiki default.
                // Any subsequent change of the URL through the RCFilters
                // system will receive 'urlversion=2'
-               var base = this.getVersion( uriQuery ) === 2 ?
-                       {} :
-                       this.filtersModel.getDefaultParams();
+               var hiddenParamDefaults = {},
+                       base = this.getVersion( uriQuery ) === 2 ?
+                               {} :
+                               this.filtersModel.getDefaultParams();
+
+               // Go over the model and get all hidden parameters' defaults
+               // These defaults should be applied regardless of the urlversion
+               // but be overridden by the URL params if they exist
+               $.each( this.filtersModel.getFilterGroups(), function ( groupName, groupModel ) {
+                       if ( groupModel.isHidden() ) {
+                               $.extend( true, hiddenParamDefaults, groupModel.getDefaultParams() );
+                       }
+               } );
 
                return this.minimizeQuery(
-                       $.extend( true, {}, base, uriQuery, { urlversion: '2' } )
+                       $.extend( true, {}, hiddenParamDefaults, base, uriQuery, { urlversion: '2' } )
                );
        };
 
index 6cd2d0b..e758f26 100644 (file)
@@ -36,8 +36,6 @@
 
                        $( '.rcfilters-container' ).append( filtersWidget.$element );
                        $( 'body' ).append( $overlay );
-
-                       // Set as ready
                        $( '.rcfilters-head' ).addClass( 'mw-rcfilters-ui-ready' );
 
                        $( 'a.mw-helplink' ).attr(
index 844e41c..9f3b809 100644 (file)
@@ -1,5 +1,5 @@
 // Corrections for the standard special page
-.client-js{
+.client-js {
        .rcoptions {
                border: 0;
                border-bottom: 1px solid #a2a9b1;
        }
 
        .rcfilters-head {
-               min-height: 270px;
+               min-height: 310px;
+
                &:not( .mw-rcfilters-ui-ready ) {
-                       /* @embed */
-                       background-image: url( ../images/pending.gif );
-                       margin: 0;
+                       opacity: 0.5;
+                       pointer-events: none;
 
-                       * {
-                               visibility: hidden;
+                       .rcoptions {
+                               display: none;
                        }
                }
        }
                margin: 0;
        }
 
-       .mw-changeslist-empty {
-               // Hide the 'empty' message when we load rcfilters
-               // since we replace it anyways with a specific
-               // message of our own
+       .mw-changeslist {
+               &-empty {
+                       // Hide the 'empty' message when we load rcfilters
+                       // since we replace it anyways with a specific
+                       // message of our own
+                       display: none;
+               }
+
+               &:not( .mw-rcfilters-ui-ready ) {
+                       opacity: 0.5;
+               }
+       }
+
+       .rcfilters-spinner {
+               margin: -2em auto 0;
+               width: 70px;
+               opacity: 0.8;
                display: none;
+               white-space: nowrap;
+
+               &:not( .mw-rcfilters-ui-ready ) {
+                       display: block;
+               }
+
+               & .rcfilters-spinner-bounce,
+               &:before,
+               &:after {
+                       content: '';
+                       display: inline-block;
+                       width: 12px;
+                       height: 12px;
+                       background-color: #c8ccd1;
+                       border-radius: 100%;
+                       animation: rcfiltersBouncedelay 1.5s infinite ease-in-out;
+                       animation-fill-mode: both;
+                       animation-delay: -0.16s;
+               }
+
+               &:before {
+                       animation-delay: -0.33s;
+               }
+
+               &:after {
+                       animation-delay: 0s;
+               }
+
        }
 }
 
 .mw-rcfilters-staticfilters-selected {
        font-weight: bold;
 }
+
+@keyframes rcfiltersBouncedelay {
+       0%,
+       100%,
+       80% {
+               transform: scale( 0.7 );
+       }
+       40% {
+               transform: scale( 1 );
+               background-color: #a2a9b1;
+       }
+}
diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.DatePopupWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.DatePopupWidget.less
new file mode 100644 (file)
index 0000000..4155779
--- /dev/null
@@ -0,0 +1,5 @@
+.mw-rcfilters-ui-datePopupWidget {
+       &-days {
+               margin-top: 1em;
+       }
+}
index 420bb44..7ee601f 100644 (file)
@@ -1,3 +1,5 @@
+@import 'mediawiki.mixins';
+
 .mw-rcfilters-ui-filterTagMultiselectWidget {
        max-width: none;
 
                margin-top: 1.6em;
        }
 
+       .oo-ui-tagMultiselectWidget.oo-ui-widget-enabled &-animate.oo-ui-tagMultiselectWidget-handle {
+               .transition( background-color 500ms ease-out );
+       }
+
+       .oo-ui-tagMultiselectWidget.oo-ui-widget-enabled &-emphasize.oo-ui-tagMultiselectWidget-handle {
+               background-color: #eaf3ff; // Accent90
+       }
+
        &-wrapper {
                .mw-rcfilters-ui-table {
                        margin-top: 0.3em;
@@ -29,7 +39,8 @@
                        }
 
                        &-savedQueryTitle {
-                               color: #72777d;
+                               color: #222; // Base10
+                               font-weight: bold;
                                margin-left: 1em;
                        }
                }
index 5aa866d..df4592c 100644 (file)
 
        &-bottom {
                margin-top: 1em;
+
+               .mw-rcfilters-ui-changesLimitButtonWidget,
+               .mw-rcfilters-ui-dateButtonWidget {
+                       display: inline-block;
+
+                       &:not( :first-child ) {
+                               margin-left: 0.5em;
+                       }
+               }
        }
 }
index 63ea264..04f4174 100644 (file)
@@ -1,10 +1,13 @@
 .mw-rcfilters-ui-liveUpdateButtonWidget {
+       margin-left: 1em;
+
        &.oo-ui-toggleWidget-on {
                position: relative;
                overflow: hidden;
                &:after {
                        content: '';
                        mix-blend-mode: screen;
+                       pointer-events: none;
                        position: absolute;
                        width: 1.875em;
                        height: 1.875em;
index fb0b93b..2daa76e 100644 (file)
@@ -1,5 +1,6 @@
 .mw-rcfilters-ui-savedLinksListItemWidget {
-       padding: 0.2em 0.7em;
+       padding: 0 0.5em;
+       line-height: normal;
 
        &:hover {
                // Mimicking optionWidget styles
@@ -48,6 +49,7 @@
 
        &-content {
                width: 100%;
+               line-height: normal;
        }
 
 }
diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ValuePickerWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ValuePickerWidget.less
new file mode 100644 (file)
index 0000000..38ad1ee
--- /dev/null
@@ -0,0 +1,7 @@
+.mw-rcfilters-ui-valuePickerWidget {
+       &-title {
+               display: block;
+               font-weight: bold;
+               margin-bottom: 0.5em;
+       }
+}
diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js
new file mode 100644 (file)
index 0000000..61ee4a5
--- /dev/null
@@ -0,0 +1,107 @@
+( function ( mw ) {
+       /**
+        * Widget defining the button controlling the popup for the number of results
+        *
+        * @extends OO.ui.Widget
+        *
+        * @constructor
+        * @param {mw.rcfilters.Controller} controller Controller
+        * @param {mw.rcfilters.dm.FiltersViewModel} model View model
+        * @param {Object} [config] Configuration object
+        * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+        */
+       mw.rcfilters.ui.ChangesLimitButtonWidget = function MwRcfiltersUiChangesLimitWidget( controller, model, config ) {
+               config = config || {};
+
+               // Parent
+               mw.rcfilters.ui.ChangesLimitButtonWidget.parent.call( this, config );
+
+               this.controller = controller;
+               this.model = model;
+
+               this.$overlay = config.$overlay || this.$element;
+
+               this.button = null;
+               this.limitGroupModel = null;
+
+               this.model.connect( this, {
+                       initialize: 'onModelInitialize'
+               } );
+
+               this.$element
+                       .addClass( 'mw-rcfilters-ui-changesLimitButtonWidget' );
+       };
+
+       /* Initialization */
+
+       OO.inheritClass( mw.rcfilters.ui.ChangesLimitButtonWidget, OO.ui.Widget );
+
+       /**
+        * Respond to model initialize event
+        */
+       mw.rcfilters.ui.ChangesLimitButtonWidget.prototype.onModelInitialize = function () {
+               var changesLimitPopupWidget, selectedItem, currentValue;
+
+               this.limitGroupModel = this.model.getGroup( 'limit' );
+
+               // HACK: We need the model to be ready before we populate the button
+               // and the widget, because we require the filter items for the
+               // limit and their events. This addition is only done after the
+               // model is initialized.
+               // Note: This will be fixed soon!
+               if ( this.limitGroupModel ) {
+                       changesLimitPopupWidget = new mw.rcfilters.ui.ChangesLimitPopupWidget(
+                               this.limitGroupModel
+                       );
+
+                       selectedItem = this.limitGroupModel.getSelectedItems()[ 0 ];
+                       currentValue = ( selectedItem && selectedItem.getLabel() ) ||
+                               mw.language.convertNumber( this.limitGroupModel.getDefaultParamValue() );
+
+                       this.button = new OO.ui.PopupButtonWidget( {
+                               indicator: 'down',
+                               label: mw.msg( 'rcfilters-limit-shownum', currentValue ),
+                               $overlay: this.$overlay,
+                               popup: {
+                                       width: 300,
+                                       padded: true,
+                                       anchor: false,
+                                       align: 'backwards',
+                                       $autoCloseIgnore: this.$overlay,
+                                       $content: changesLimitPopupWidget.$element
+                               }
+                       } );
+
+                       // Events
+                       this.limitGroupModel.connect( this, { update: 'onLimitGroupModelUpdate' } );
+                       changesLimitPopupWidget.connect( this, { limit: 'onPopupLimit' } );
+
+                       this.$element.append( this.button.$element );
+               }
+       };
+
+       /**
+        * Respond to popup limit change event
+        *
+        * @param {string} filterName Chosen filter name
+        */
+       mw.rcfilters.ui.ChangesLimitButtonWidget.prototype.onPopupLimit = function ( filterName ) {
+               this.controller.toggleFilterSelect( filterName, true );
+       };
+
+       /**
+        * Respond to limit choose event
+        *
+        * @param {string} filterName Filter name
+        */
+       mw.rcfilters.ui.ChangesLimitButtonWidget.prototype.onLimitGroupModelUpdate = function () {
+               var item = this.limitGroupModel.getSelectedItems()[ 0 ],
+                       label = item && item.getLabel();
+
+               // Update the label
+               if ( label ) {
+                       this.button.setLabel( mw.msg( 'rcfilters-limit-shownum', label ) );
+               }
+       };
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitPopupWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitPopupWidget.js
new file mode 100644 (file)
index 0000000..02101ab
--- /dev/null
@@ -0,0 +1,47 @@
+( function ( mw ) {
+       /**
+        * Widget defining the popup to choose number of results
+        *
+        * @extends OO.ui.Widget
+        *
+        * @constructor
+        * @param {mw.rcfilters.dm.FilterGroup} model Group model for 'limit'
+        * @param {Object} [config] Configuration object
+        */
+       mw.rcfilters.ui.ChangesLimitPopupWidget = function MwRcfiltersUiChangesLimitPopupWidget( model, config ) {
+               config = config || {};
+
+               // Parent
+               mw.rcfilters.ui.ChangesLimitPopupWidget.parent.call( this, config );
+
+               this.model = model;
+
+               this.valuePicker = new mw.rcfilters.ui.ValuePickerWidget(
+                       this.model,
+                       {
+                               label: mw.msg( 'rcfilters-limit-title' )
+                       }
+               );
+
+               // Events
+               this.valuePicker.connect( this, { choose: [ 'emit', 'limit' ] } );
+
+               // Initialize
+               this.$element
+                       .addClass( 'mw-rcfilters-ui-changesLimitPopupWidget' )
+                       .append( this.valuePicker.$element );
+       };
+
+       /* Initialization */
+
+       OO.inheritClass( mw.rcfilters.ui.ChangesLimitPopupWidget, OO.ui.Widget );
+
+       /* Events */
+
+       /**
+        * @event limit
+        * @param {string} name Item name
+        *
+        * A limit item was chosen
+        */
+}( mediaWiki ) );
index 843acce..c2533df 100644 (file)
@@ -3,7 +3,6 @@
         * List of changes
         *
         * @extends OO.ui.Widget
-        * @mixins OO.ui.mixin.PendingElement
         *
         * @constructor
         * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel View model
@@ -23,8 +22,6 @@
 
                // Parent
                mw.rcfilters.ui.ChangesListWrapperWidget.parent.call( this, config );
-               // Mixin constructors
-               OO.ui.mixin.PendingElement.call( this, config );
 
                this.filtersViewModel = filtersViewModel;
                this.changesListViewModel = changesListViewModel;
@@ -51,7 +48,6 @@
        /* Initialization */
 
        OO.inheritClass( mw.rcfilters.ui.ChangesListWrapperWidget, OO.ui.Widget );
-       OO.mixinClass( mw.rcfilters.ui.ChangesListWrapperWidget, OO.ui.mixin.PendingElement );
 
        /**
         * Respond to the highlight feature being toggled on and off
         * Respond to changes list model invalidate
         */
        mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelInvalidate = function () {
-               this.pushPending();
+               $( '.rcfilters-spinner' ).removeClass( 'mw-rcfilters-ui-ready' );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
         * Respond to changes list model update
         *
         * @param {jQuery|string} $changesListContent The content of the updated changes list
+        * @param {jQuery} $fieldset The content of the updated fieldset
+        * @param {boolean} isInitialDOM Whether $changesListContent is the existing (already attached) DOM
         */
-       mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelUpdate = function ( $changesListContent ) {
+       mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelUpdate = function ( $changesListContent, $fieldset, isInitialDOM ) {
                var conflictItem,
                        $message = $( '<div>' )
                                .addClass( 'mw-rcfilters-ui-changesListWrapperWidget-results' ),
                        this.$element.append( $message );
                } else {
                        this.$changesListContent = $changesListContent;
-                       this.$element.empty().append( this.$changesListContent );
+                       if ( !isInitialDOM ) {
+                               this.$element.empty().append( this.$changesListContent );
+                       }
                        // Set up highlight containers
                        this.setupHighlightContainers( this.$element );
 
                        // Apply highlight
                        this.applyHighlight();
 
-                       // Make sure enhanced RC re-initializes correctly
-                       mw.hook( 'wikipage.content' ).fire( this.$element );
+                       if ( !isInitialDOM ) {
+                               // Make sure enhanced RC re-initializes correctly
+                               mw.hook( 'wikipage.content' ).fire( this.$element );
+                       }
                }
-               this.popPending();
+
+               $( '.rcfilters-spinner' ).addClass( 'mw-rcfilters-ui-ready' );
+               this.$element.addClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js
new file mode 100644 (file)
index 0000000..1569f38
--- /dev/null
@@ -0,0 +1,115 @@
+( function ( mw ) {
+       /**
+        * Widget defining the button controlling the popup for the date range for the results
+        *
+        * @extends OO.ui.Widget
+        *
+        * @constructor
+        * @param {mw.rcfilters.Controller} controller Controller
+        * @param {mw.rcfilters.dm.FiltersViewModel} model View model
+        * @param {Object} [config] Configuration object
+        * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+        */
+       mw.rcfilters.ui.DateButtonWidget = function MwRcfiltersUiDateButtonWidget( controller, model, config ) {
+               config = config || {};
+
+               // Parent
+               mw.rcfilters.ui.ChangesLimitButtonWidget.parent.call( this, config );
+
+               this.controller = controller;
+               this.model = model;
+
+               this.$overlay = config.$overlay || this.$element;
+
+               this.button = null;
+               this.daysGroupModel = null;
+
+               this.model.connect( this, {
+                       initialize: 'onModelInitialize'
+               } );
+
+               this.$element
+                       .addClass( 'mw-rcfilters-ui-dateButtonWidget' );
+       };
+
+       /* Initialization */
+
+       OO.inheritClass( mw.rcfilters.ui.DateButtonWidget, OO.ui.Widget );
+
+       /**
+        * Respond to model initialize event
+        */
+       mw.rcfilters.ui.DateButtonWidget.prototype.onModelInitialize = function () {
+               var datePopupWidget;
+
+               this.daysGroupModel = this.model.getGroup( 'days' );
+
+               // HACK: We need the model to be ready before we populate the button
+               // and the widget, because we require the filter items for the
+               // limit and their events. This addition is only done after the
+               // model is initialized.
+               // Note: This will be fixed soon!
+               if ( this.daysGroupModel ) {
+                       datePopupWidget = new mw.rcfilters.ui.DatePopupWidget(
+                               this.daysGroupModel
+                       );
+
+                       this.button = new OO.ui.PopupButtonWidget( {
+                               indicator: 'down',
+                               icon: 'calendar',
+                               $overlay: this.$overlay,
+                               popup: {
+                                       width: 300,
+                                       padded: true,
+                                       anchor: false,
+                                       align: 'backwards',
+                                       $autoCloseIgnore: this.$overlay,
+                                       $content: datePopupWidget.$element
+                               }
+                       } );
+                       this.updateButtonLabel();
+
+                       // Events
+                       this.daysGroupModel.connect( this, { update: 'onDaysGroupModelUpdate' } );
+                       datePopupWidget.connect( this, { days: 'onPopupDays' } );
+
+                       this.$element.append( this.button.$element );
+               }
+       };
+
+       /**
+        * Respond to popup limit change event
+        *
+        * @param {string} filterName Chosen filter name
+        */
+       mw.rcfilters.ui.DateButtonWidget.prototype.onPopupDays = function ( filterName ) {
+               this.controller.toggleFilterSelect( filterName, true );
+       };
+
+       /**
+        * Respond to limit choose event
+        *
+        * @param {string} filterName Filter name
+        */
+       mw.rcfilters.ui.DateButtonWidget.prototype.onDaysGroupModelUpdate = function () {
+               this.updateButtonLabel();
+       };
+
+       /**
+        * Update the button label
+        */
+       mw.rcfilters.ui.DateButtonWidget.prototype.updateButtonLabel = function () {
+               var item = this.daysGroupModel.getSelectedItems()[ 0 ];
+
+               // Update the label
+               if ( item ) {
+                       this.button.setLabel(
+                               mw.msg(
+                                       Number( item.getParamName() ) < 1 ?
+                                               'rcfilters-days-show-hours' : 'rcfilters-days-show-days',
+                                       item.getLabel()
+                               )
+                       );
+               }
+       };
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DatePopupWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DatePopupWidget.js
new file mode 100644 (file)
index 0000000..6971df5
--- /dev/null
@@ -0,0 +1,61 @@
+( function ( mw ) {
+       /**
+        * Widget defining the popup to choose date for the results
+        *
+        * @extends OO.ui.Widget
+        *
+        * @constructor
+        * @param {mw.rcfilters.dm.FilterGroup} model Group model for 'days'
+        * @param {Object} [config] Configuration object
+        */
+       mw.rcfilters.ui.DatePopupWidget = function MwRcfiltersUiDatePopupWidget( model, config ) {
+               config = config || {};
+
+               // Parent
+               mw.rcfilters.ui.ChangesLimitPopupWidget.parent.call( this, config );
+
+               this.model = model;
+
+               this.hoursValuePicker = new mw.rcfilters.ui.ValuePickerWidget(
+                       this.model,
+                       {
+                               classes: [ 'mw-rcfilters-ui-datePopupWidget-hours' ],
+                               label: mw.msg( 'rcfilters-hours-title' ),
+                               itemFilter: function ( itemModel ) { return Number( itemModel.getParamName() ) < 1; }
+                       }
+               );
+               this.daysValuePicker = new mw.rcfilters.ui.ValuePickerWidget(
+                       this.model,
+                       {
+                               classes: [ 'mw-rcfilters-ui-datePopupWidget-days' ],
+                               label: mw.msg( 'rcfilters-days-title' ),
+                               itemFilter: function ( itemModel ) { return Number( itemModel.getParamName() ) >= 1; }
+                       }
+               );
+
+               // Events
+               this.hoursValuePicker.connect( this, { choose: [ 'emit', 'days' ] } );
+               this.daysValuePicker.connect( this, { choose: [ 'emit', 'days' ] } );
+
+               // Initialize
+               this.$element
+                       .addClass( 'mw-rcfilters-ui-datePopupWidget' )
+                       .append(
+                               this.hoursValuePicker.$element,
+                               this.daysValuePicker.$element
+                       );
+       };
+
+       /* Initialization */
+
+       OO.inheritClass( mw.rcfilters.ui.DatePopupWidget, OO.ui.Widget );
+
+       /* Events */
+
+       /**
+        * @event days
+        * @param {string} name Item name
+        *
+        * A days item was chosen
+        */
+}( mediaWiki ) );
index d0ad8d5..da71d70 100644 (file)
                // Invert namespaces button
                this.invertNamespacesButton = new OO.ui.ToggleButtonWidget( {
                        icon: '',
-                       label: mw.msg( 'invert' ),
                        classes: [ 'mw-rcfilters-ui-filterMenuHeaderWidget-invertNamespacesButton' ]
                } );
                this.invertNamespacesButton.toggle( this.model.getCurrentView() === 'namespaces' );
+               this.updateInvertButton( this.model.areNamespacesInverted() );
 
                // Events
                this.backButton.connect( this, { click: 'onBackButtonClick' } );
         * @param {boolean} isInverted Namespaces selection is inverted
         */
        mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onModelInvertChange = function ( isInverted ) {
+               this.updateInvertButton( isInverted );
+       };
+
+       /**
+        * Update the state of the invert button
+        *
+        * @param {boolean} isInverted Namespaces selection is inverted
+        */
+       mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.updateInvertButton = function ( isInverted ) {
                this.invertNamespacesButton.setActive( isInverted );
+               this.invertNamespacesButton.setLabel(
+                       isInverted ?
+                               mw.msg( 'rcfilters-exclude-button-on' ) :
+                               mw.msg( 'rcfilters-exclude-button-off' )
+               );
        };
 
        mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onBackButtonClick = function () {
index 9f41712..88479c3 100644 (file)
@@ -72,7 +72,6 @@
                                classes: [ 'mw-rcfilters-ui-filterMenuSectionOptionWidget-whatsThisButton' ],
                                flags: [ 'progressive' ],
                                popup: {
-                                       $autoCloseIgnore: this.$element.add( this.$overlay ),
                                        padded: false,
                                        align: 'center',
                                        position: 'above',
index dcada85..6a6790f 100644 (file)
                                // Clear the input
                                this.input.setValue( '' );
                        }
+
+                       // Log filter grouping
+                       this.controller.trackFilterGroupings( 'filtermenu' );
                }
 
                this.input.setIcon( isVisible ? 'search' : 'menu' );
                                !this.isEmpty() &&
                                !this.matchingQuery
                        );
+
+                       if ( this.matchingQuery ) {
+                               this.emphasize();
+                       }
                }
        };
 
         * @param {mw.rcfilters.dm.FilterItem} item Filter item model
         */
        mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onModelItemUpdate = function ( item ) {
+               if ( item.getGroupModel().isHidden() ) {
+                       return;
+               }
+
                if (
                        item.isSelected() ||
                        (
                }
        };
 
+       mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.emphasize = function () {
+               if (
+                       !this.$handle.hasClass( 'mw-rcfilters-ui-filterTagMultiselectWidget-animate' )
+               ) {
+                       this.$handle
+                               .addClass( 'mw-rcfilters-ui-filterTagMultiselectWidget-emphasize' )
+                               .addClass( 'mw-rcfilters-ui-filterTagMultiselectWidget-animate' );
+
+                       setTimeout( function () {
+                               this.$handle
+                                       .removeClass( 'mw-rcfilters-ui-filterTagMultiselectWidget-emphasize' );
+
+                               setTimeout( function () {
+                                       this.$handle
+                                               .removeClass( 'mw-rcfilters-ui-filterTagMultiselectWidget-animate' );
+                               }.bind( this ), 1000 );
+                       }.bind( this ), 500 );
+
+               }
+       };
        /**
         * Scroll the element to top within its container
         *
index a748063..9b4ce7f 100644 (file)
                        this.controller
                );
 
+               this.numChangesWidget = new mw.rcfilters.ui.ChangesLimitButtonWidget(
+                       this.controller,
+                       this.model,
+                       {
+                               $overlay: this.$overlay
+                       }
+               );
+
+               this.dateWidget = new mw.rcfilters.ui.DateButtonWidget(
+                       this.controller,
+                       this.model,
+                       {
+                               $overlay: this.$overlay
+                       }
+               );
+
                // Initialize
                this.$element
                        .addClass( 'mw-rcfilters-ui-filterWrapperWidget' );
                        this.$element.append(
                                this.savedLinksListWidget.$element
                        );
-
                }
 
                $bottom = $( '<div>' )
-                       .addClass( 'mw-rcfilters-ui-filterWrapperWidget-bottom' );
+                       .addClass( 'mw-rcfilters-ui-filterWrapperWidget-bottom' )
+                       .append(
+                               this.numChangesWidget.$element,
+                               this.dateWidget.$element
+                       );
 
-               if ( mw.config.get( 'wgStructuredChangeFiltersEnableLiveUpdate' ) ) {
+               if (
+                       mw.config.get( 'wgStructuredChangeFiltersEnableLiveUpdate' ) ||
+                       // Allow users to enable live update with ?liveupdate=1
+                       new mw.Uri().query.liveupdate
+               ) {
                        $bottom.append( this.liveUpdateButton.$element );
                }
 
index dbee65c..ee8e0bc 100644 (file)
@@ -19,8 +19,6 @@
                mw.rcfilters.ui.FormWrapperWidget.parent.call( this, $.extend( {}, config, {
                        $element: $formRoot
                } ) );
-               // Mixin constructors
-               OO.ui.mixin.PendingElement.call( this, config );
 
                this.changeListModel = changeListModel;
                this.filtersModel = filtersModel;
@@ -48,7 +46,6 @@
        /* Initialization */
 
        OO.inheritClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.Widget );
-       OO.mixinClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.mixin.PendingElement );
 
        /**
         * Respond to link click
@@ -89,8 +86,8 @@
         * Respond to model invalidate
         */
        mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelInvalidate = function () {
-               this.pushPending();
                this.$submitButton.prop( 'disabled', true );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
         *
         * @param {jQuery|string} $changesList Updated changes list
         * @param {jQuery} $fieldset Updated fieldset
+        * @param {boolean} isInitialDOM Whether $changesListContent is the existing (already attached) DOM
         */
-       mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelUpdate = function ( $changesList, $fieldset ) {
+       mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelUpdate = function ( $changesList, $fieldset, isInitialDOM ) {
                this.$submitButton.prop( 'disabled', false );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
 
                // Replace the entire fieldset
                this.$element.empty().append( $fieldset.contents() );
-               // Make sure enhanced RC re-initializes correctly
-               mw.hook( 'wikipage.content' ).fire( this.$element );
 
-               this.cleanUpFieldset();
+               if ( !isInitialDOM ) {
+                       // Make sure enhanced RC re-initializes correctly
+                       mw.hook( 'wikipage.content' ).fire( this.$element );
+               }
 
-               this.popPending();
+               this.cleanUpFieldset();
        };
 
        /**
                        this.$element.find( '.mw-tagfilter-label' ).closest( 'tr' ).detach();
                }
 
+               // Hide limit and days
+               this.$element.find( '.rclinks' ).detach();
+
                if ( !this.$element.find( '.mw-recentchanges-table tr' ).length ) {
+                       this.$element.find( '.mw-recentchanges-table' ).detach();
                        this.$element.find( 'hr' ).detach();
                }
+               if ( !this.$element.find( '.rcshowhide' ).contents().length ) {
+                       this.$element.find( '.rcshowhide' ).detach();
+                       // If we're hiding rcshowhide, the '<br>'s are around it,
+                       // there's no need for them either.
+                       this.$element.find( 'br' ).detach();
+               }
        };
 }( mediaWiki ) );
index 8bab981..90ee4d7 100644 (file)
@@ -15,7 +15,7 @@
                mw.rcfilters.ui.LiveUpdateButtonWidget.parent.call( this, $.extend( {
                        icon: 'play',
                        label: mw.message( 'rcfilters-liveupdates-button' ).text()
-               } ), config );
+               }, config ) );
 
                this.controller = controller;
 
index 64b9ac9..07d4506 100644 (file)
@@ -34,8 +34,7 @@
 
                this.inputValue = '';
                this.$overlay = config.$overlay || this.$element;
-               this.$body = $( '<div>' )
-                               .addClass( 'mw-rcfilters-ui-menuSelectWidget-body' );
+               this.$body = $( '<div>' ).addClass( 'mw-rcfilters-ui-menuSelectWidget-body' );
                this.footers = [];
 
                // Parent
index 3b5e7d9..27a21d6 100644 (file)
@@ -86,6 +86,8 @@
        /* Initialization */
        OO.inheritClass( mw.rcfilters.ui.SavedLinksListWidget, OO.ui.Widget );
 
+       /* Methods */
+
        /**
         * Respond to menu item click event
         *
diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.js
new file mode 100644 (file)
index 0000000..7045ab6
--- /dev/null
@@ -0,0 +1,109 @@
+( function ( mw ) {
+       /**
+        * Widget defining the behavior used to choose from a set of values
+        * in a single_value group
+        *
+        * @extends OO.ui.Widget
+        * @mixins OO.ui.mixin.LabelElement
+        *
+        * @constructor
+        * @param {mw.rcfilters.dm.FilterGroup} model Group model
+        * @param {Object} [config] Configuration object
+        * @cfg {Function} [itemFilter] A filter function for the items from the
+        *  model. If not given, all items will be included. The function must
+        *  handle item models and return a boolean whether the item is included
+        *  or not. Example: function ( itemModel ) { return itemModel.isSelected(); }
+        */
+       mw.rcfilters.ui.ValuePickerWidget = function MwRcfiltersUiValuePickerWidget( model, config ) {
+               config = config || {};
+
+               // Parent
+               mw.rcfilters.ui.ValuePickerWidget.parent.call( this, config );
+               // Mixin constructors
+               OO.ui.mixin.LabelElement.call( this, config );
+
+               this.model = model;
+               this.itemFilter = config.itemFilter || function () { return true; };
+
+               // Build the selection from the item models
+               this.selectWidget = new OO.ui.ButtonSelectWidget();
+               this.initializeSelectWidget();
+
+               // Events
+               this.model.connect( this, { update: 'onModelUpdate' } );
+               this.selectWidget.connect( this, { choose: 'onSelectWidgetChoose' } );
+
+               // Initialize
+               this.$element
+                       .addClass( 'mw-rcfilters-ui-valuePickerWidget' )
+                       .append(
+                               this.$label
+                                       .addClass( 'mw-rcfilters-ui-valuePickerWidget-title' ),
+                               this.selectWidget.$element
+                       );
+       };
+
+       /* Initialization */
+
+       OO.inheritClass( mw.rcfilters.ui.ValuePickerWidget, OO.ui.Widget );
+       OO.mixinClass( mw.rcfilters.ui.ValuePickerWidget, OO.ui.mixin.LabelElement );
+
+       /* Events */
+
+       /**
+        * @event choose
+        * @param {string} name Item name
+        *
+        * An item has been chosen
+        */
+
+       /* Methods */
+
+       /**
+        * Respond to model update event
+        */
+       mw.rcfilters.ui.ValuePickerWidget.prototype.onModelUpdate = function () {
+               this.selectCurrentModelItem();
+       };
+
+       /**
+        * Respond to select widget choose event
+        *
+        * @param {OO.ui.ButtonOptionWidget} chosenItem Chosen item
+        * @fires choose
+        */
+       mw.rcfilters.ui.ValuePickerWidget.prototype.onSelectWidgetChoose = function ( chosenItem ) {
+               this.emit( 'choose', chosenItem.getData() );
+       };
+
+       /**
+        * Initialize the select widget
+        */
+       mw.rcfilters.ui.ValuePickerWidget.prototype.initializeSelectWidget = function () {
+               var items = this.model.getItems()
+                       .filter( this.itemFilter )
+                       .map( function ( filterItem ) {
+                               return new OO.ui.ButtonOptionWidget( {
+                                       data: filterItem.getName(),
+                                       label: filterItem.getLabel()
+                               } );
+                       } );
+
+               this.selectWidget.clearItems();
+               this.selectWidget.addItems( items );
+
+               this.selectCurrentModelItem();
+       };
+
+       /**
+        * Select the current item that corresponds with the model item
+        * that is currently selected
+        */
+       mw.rcfilters.ui.ValuePickerWidget.prototype.selectCurrentModelItem = function () {
+               var selectedItem = this.model.getSelectedItems()[ 0 ];
+
+               if ( selectedItem ) {
+                       this.selectWidget.selectItemByData( selectedItem.getName() );
+               }
+       };
+}( mediaWiki ) );
index 3f34951..f65a257 100644 (file)
@@ -1,7 +1,12 @@
-/* jshint -W024*/
 ( function ( mw, $ ) {
        $( function () {
-               mw.widgets.DateInputWidget.static.infuse( 'mw-date-start' );
-               mw.widgets.DateInputWidget.static.infuse( 'mw-date-end' );
+               var startInput = mw.widgets.DateInputWidget.static.infuse( 'mw-date-start' ),
+                       endInput = mw.widgets.DateInputWidget.static.infuse( 'mw-date-end' );
+
+               startInput.on( 'deactivate', function ( userSelected ) {
+                       if ( userSelected ) {
+                               endInput.focus();
+                       }
+               } );
        } );
 }( mediaWiki, jQuery ) );
index 84a9a96..63f2d98 100644 (file)
@@ -21,8 +21,8 @@
                                'aria-labelledby': labelFunc
                        } );
                $fieldsets.not( '#mw-prefsection-personal' )
-                               .hide()
-                               .attr( 'aria-hidden', 'true' );
+                       .hide()
+                       .attr( 'aria-hidden', 'true' );
 
                // T115692: The following is kept for backwards compatibility with older skins
                $preferences.addClass( 'jsprefs' );
@@ -72,8 +72,7 @@
                                $tab.attr( {
                                        tabIndex: 0,
                                        'aria-selected': 'true'
-                               } )
-                               .focus()
+                               } ).focus()
                                        .parent().addClass( 'selected' );
 
                                $preferences.children( 'fieldset' ).hide().attr( 'aria-hidden', 'true' );
                function detectHash() {
                        var hash = location.hash,
                                matchedElement, parentSection;
-                       if ( hash.match( /^#mw-prefsection-[\w\-]+/ ) ) {
+                       if ( hash.match( /^#mw-prefsection-[\w-]+/ ) ) {
                                mw.storage.session.remove( 'mwpreferences-prevTab' );
                                switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
-                       } else if ( hash.match( /^#mw-[\w\-]+/ ) ) {
+                       } else if ( hash.match( /^#mw-[\w-]+/ ) ) {
                                matchedElement = document.getElementById( hash.slice( 1 ) );
                                parentSection = $( matchedElement ).closest( '.prefsection' );
                                if ( parentSection.length ) {
                ) {
                        $( window ).on( 'hashchange', function () {
                                var hash = location.hash;
-                               if ( hash.match( /^#mw-[\w\-]+/ ) ) {
+                               if ( hash.match( /^#mw-[\w-]+/ ) ) {
                                        detectHash();
                                } else if ( hash === '' ) {
                                        switchPrefTab( 'personal', 'noHash' );
                                }
                        } )
-                       // Run the function immediately to select the proper tab on startup.
-                       .trigger( 'hashchange' );
+                               // Run the function immediately to select the proper tab on startup.
+                               .trigger( 'hashchange' );
                // In older browsers we'll bind a click handler as fallback.
                // We must not have onhashchange *and* the click handlers, otherwise
                // the click handler calls switchPrefTab() which sets the hash value,
index 9518283..648bf67 100644 (file)
@@ -56,8 +56,7 @@
                piprop: 'thumbnail',
                pithumbsize: 300,
                formatversion: 2
-       } )
-       .done( function ( resp ) {
+       } ).done( function ( resp ) {
                var results = ( resp.query && resp.query.pages ) ? resp.query.pages : false,
                        multimediaWidgetTemplate;
 
index ad49a42..a7a3bd3 100644 (file)
                if ( v.normalize ) {
                        v = v.normalize();
                }
-               re = new RegExp( '^\\s*' + v.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ), 'i' );
+               re = new RegExp( '^\\s*' + v.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ), 'i' ); // eslint-disable-line no-useless-escape
                for ( k in this.values ) {
                        k = +k;
                        if ( !isNaN( k ) && re.test( this.values[ k ] ) ) {
index e42ab25..9233eef 100644 (file)
 
        /* Methods */
 
+       /**
+        * Get the currently focused field, if any
+        *
+        * @private
+        * @return {jQuery}
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.getFocusedField = function () {
+               return this.$fields.find( this.getElementDocument().activeElement );
+       };
+
        /**
         * Convert a date string to a Date
         *
                var delta = 0,
                        spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
 
-               if ( this.isDisabled() ) {
+               if ( this.isDisabled() || !this.getFocusedField().length ) {
                        return;
                }
 
         * @inheritdoc
         */
        mw.widgets.datetime.DateTimeInputWidget.prototype.focus = function () {
-               if ( !this.$fields.find( document.activeElement ).length ) {
+               if ( !this.getFocusedField().length ) {
                        this.$fields.find( '.mw-widgets-datetime-dateTimeInputWidget-editField' ).first().focus();
                }
                return this;
         * @inheritdoc
         */
        mw.widgets.datetime.DateTimeInputWidget.prototype.blur = function () {
-               this.$fields.find( document.activeElement ).blur();
+               this.getFocusedField().blur();
                return this;
        };
 
index 5678a80..51b9815 100644 (file)
@@ -9,12 +9,15 @@
 mediaWiki.widgets.visibleByteLimit = function ( textInputWidget, limit ) {
        limit = limit || +textInputWidget.$input.attr( 'maxlength' );
 
+       // Temporarily disabled whilst upstream bug is fixed; T169982
+       /*
        function updateCount() {
                textInputWidget.setLabel( ( limit - $.byteLength( textInputWidget.getValue() ) ).toString() );
        }
        textInputWidget.on( 'change', updateCount );
        // Initialise value
        updateCount();
+       */
 
        // Actually enforce limit
        textInputWidget.$input.byteLimit( limit );
index 1cc168a..08266f0 100644 (file)
                var queryValue = this.query.getValue().trim();
 
                if ( queryValue.match( this.externalLinkUrlProtocolsRegExp ) ) {
-                       queryValue = queryValue.match( /.+\/([^\/]+)/ )[ 1 ];
+                       queryValue = queryValue.match( /.+\/([^/]+)/ )[ 1 ];
                }
                return queryValue;
        };
index c5a2dd4..b91617e 100644 (file)
                                        );
                                        currentMonth.add( 1, 'month' );
                                }
-                               // Shuffle the array to display months in columns rather than rows.
+                               // Shuffle the array to display months in columns rather than rows:
+                               // | Jan | Jul |
+                               // | Feb | Aug |
+                               // | Mar | Sep |
+                               // | Apr | Oct |
+                               // | May | Nov |
+                               // | Jun | Dec |
                                items = [
-                                       items[ 0 ], items[ 6 ],      //  | January  | July      |
-                                       items[ 1 ], items[ 7 ],      //  | February | August    |
-                                       items[ 2 ], items[ 8 ],      //  | March    | September |
-                                       items[ 3 ], items[ 9 ],      //  | April    | October   |
-                                       items[ 4 ], items[ 10 ],     //  | May      | November  |
-                                       items[ 5 ], items[ 11 ]      //  | June     | December  |
+                                       items[ 0 ], items[ 6 ],
+                                       items[ 1 ], items[ 7 ],
+                                       items[ 2 ], items[ 8 ],
+                                       items[ 3 ], items[ 9 ],
+                                       items[ 4 ], items[ 10 ],
+                                       items[ 5 ], items[ 11 ]
                                ];
                                break;
 
index 50a84f8..ce9cf36 100644 (file)
                } );
                this.inCalendar = 0;
                this.inTextInput = 0;
+               this.closing = false;
                this.inputFormat = config.inputFormat;
                this.displayFormat = config.displayFormat;
                this.longDisplayFormat = config.longDisplayFormat;
                this.$handle.on( {
                        click: this.onClick.bind( this ),
                        keypress: this.onKeyPress.bind( this ),
-                       focus: this.activate.bind( this )
+                       focus: this.onFocus.bind( this )
                } );
 
                // Initialization
        OO.inheritClass( mw.widgets.DateInputWidget, OO.ui.TextInputWidget );
        OO.mixinClass( mw.widgets.DateInputWidget, OO.ui.mixin.IndicatorElement );
 
+       /* Events */
+
+       /**
+        * Fired when the widget is deactivated (i.e. the calendar is closed). This can happen because
+        * the user selected a value, or because the user blurred the widget.
+        *
+        * @event deactivate
+        * @param {boolean} userSelected Whether the deactivation happened because the user selected a value
+        */
+
        /* Methods */
 
        /**
         * Deactivate this input field for data entry. Closes the calendar and hides the text field.
         *
         * @private
+        * @param {boolean} [userSelected] Whether we are deactivating because the user selected a value
         */
-       mw.widgets.DateInputWidget.prototype.deactivate = function () {
+       mw.widgets.DateInputWidget.prototype.deactivate = function ( userSelected ) {
                this.$element.removeClass( 'mw-widget-dateInputWidget-active' );
                this.$handle.show();
                this.textInput.toggle( false );
                this.calendar.toggle( false );
                this.setValidityFlag();
+
+               if ( userSelected ) {
+                       // Prevent focusing the handle from reopening the calendar
+                       this.closing = true;
+                       this.$handle.focus();
+                       this.closing = false;
+               }
+
+               this.emit( 'deactivate', !!userSelected );
        };
 
        /**
                }
        };
 
+       /**
+        * Handle focus events.
+        *
+        * @private
+        */
+       mw.widgets.DateInputWidget.prototype.onFocus = function () {
+               if ( !this.closing ) {
+                       this.activate();
+               }
+       };
+
        /**
         * Handle calendar key press events.
         *
         */
        mw.widgets.DateInputWidget.prototype.onCalendarKeyPress = function ( e ) {
                if ( !this.isDisabled() && e.which === OO.ui.Keys.ENTER ) {
-                       this.deactivate();
-                       this.$handle.focus();
+                       this.deactivate( true );
                        return false;
                }
        };
                if (
                        !this.isDisabled() &&
                        e.which === 1 &&
-                       $( e.target ).hasClass( 'mw-widget-calendarWidget-day' )
+                       (
+                               $( e.target ).hasClass( 'mw-widget-calendarWidget-day' ) ||
+                               $( e.target ).hasClass( 'mw-widget-calendarWidget-month' )
+                       )
                ) {
-                       this.deactivate();
-                       this.$handle.focus();
+                       this.deactivate( true );
                        return false;
                }
        };
         * @private
         */
        mw.widgets.DateInputWidget.prototype.onEnter = function () {
-               this.deactivate();
-               this.$handle.focus();
+               this.deactivate( true );
        };
 
        /**
index 22e5874..08997aa 100644 (file)
@@ -10,9 +10,7 @@
         * Creates a mw.widgets.SearchInputWidget object.
         *
         * @class
-        * @extends OO.ui.SearchInputWidget
-        * @mixins mw.widgets.TitleWidget
-        * @mixins OO.ui.mixin.LookupElement
+        * @extends mw.widgets.TitleInputWidget
         *
         * @constructor
         * @param {Object} [config] Configuration options
                var $form = config.$input ? config.$input.closest( 'form' ) : $();
 
                config = $.extend( {
+                       icon: 'search',
+                       maxLength: undefined,
                        performSearchOnClick: true,
-                       dataLocation: 'header'
+                       dataLocation: 'header',
+                       namespace: 0
                }, config );
 
                // Parent constructor
-               mw.widgets.SearchInputWidget.parent.call( this, $.extend( {}, config, {
-                       autocomplete: false
-               } ) );
-
-               // Mixin constructors
-               mw.widgets.TitleWidget.call( this, config );
-               OO.ui.mixin.LookupElement.call( this, config );
+               mw.widgets.SearchInputWidget.parent.call( this, config );
 
                // Initialization
                this.$element.addClass( 'mw-widget-searchInputWidget' );
                this.lookupMenu.$element.addClass( 'mw-widget-searchWidget-menu' );
                this.lastLookupItems = [];
                if ( !config.pushPending ) {
-                       // TODO This actually overrides a method, that's pretty crazy. Surely there's a better way?
                        this.pushPending = false;
                }
                if ( config.dataLocation ) {
                                )
                        } );
                }.bind( this ) );
+
+               this.$element.addClass( 'oo-ui-textInputWidget-type-search' );
+               this.updateSearchIndicator();
+               this.connect( this, {
+                       disable: 'onDisable'
+               } );
        };
 
        /* Setup */
 
-       OO.inheritClass( mw.widgets.SearchInputWidget, OO.ui.SearchInputWidget );
-       OO.mixinClass( mw.widgets.SearchInputWidget, mw.widgets.TitleWidget );
-       OO.mixinClass( mw.widgets.SearchInputWidget, OO.ui.mixin.LookupElement );
+       OO.inheritClass( mw.widgets.SearchInputWidget, mw.widgets.TitleInputWidget );
 
        /* Methods */
 
        /**
-        * @inheritdoc mw.widgets.TitleWidget
+        * @inheritdoc
+        * @protected
+        */
+       mw.widgets.SearchInputWidget.prototype.getInputElement = function () {
+               return $( '<input>' ).attr( 'type', 'search' );
+       };
+
+       /**
+        * @inheritdoc
         */
-       mw.widgets.SearchInputWidget.prototype.getQueryValue = function () {
-               return this.getValue();
+       mw.widgets.SearchInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
+               if ( e.which === OO.ui.MouseButtons.LEFT ) {
+                       // Clear the text field
+                       this.setValue( '' );
+                       this.$input[ 0 ].focus();
+                       return false;
+               }
+       };
+
+       /**
+        * Update the 'clear' indicator displayed on type: 'search' text
+        * fields, hiding it when the field is already empty or when it's not
+        * editable.
+        */
+       mw.widgets.SearchInputWidget.prototype.updateSearchIndicator = function () {
+               if ( this.getValue() === '' || this.isDisabled() || this.isReadOnly() ) {
+                       this.setIndicator( null );
+               } else {
+                       this.setIndicator( 'clear' );
+               }
        };
 
        /**
-        * @inheritdoc OO.ui.mixin.LookupElement
+        * @see OO.ui.SearchInputWidget#onChange
         */
-       mw.widgets.SearchInputWidget.prototype.getLookupRequest = function () {
-               return this.getSuggestionsPromise();
+       mw.widgets.SearchInputWidget.prototype.onChange = function () {
+               mw.widgets.SearchInputWidget.parent.prototype.onChange.call( this );
+               this.updateSearchIndicator();
+       };
+
+       /**
+        * Handle disable events.
+        *
+        * @param {boolean} disabled Element is disabled
+        * @private
+        */
+       mw.widgets.SearchInputWidget.prototype.onDisable = function () {
+               this.updateSearchIndicator();
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SearchInputWidget.prototype.setReadOnly = function ( state ) {
+               mw.widgets.SearchInputWidget.parent.prototype.setReadOnly.call( this, state );
+               this.updateSearchIndicator();
+               return this;
        };
 
        /**
        };
 
        /**
-        * @inheritdoc OO.ui.mixin.LookupElement
+        * @inheritdoc mw.widgets.TitleInputWidget
         */
        mw.widgets.SearchInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
                var resp;
 
+               // mw.widgets.TitleInputWidget uses response.query, which doesn't exist for opensearch,
+               // so return the whole response (titles only, and links)
                resp = {
                        data: response || {},
                        metadata: {
        };
 
        /**
-        * @inheritdoc OO.ui.mixin.LookupElement
+        * @inheritdoc
         */
-       mw.widgets.SearchInputWidget.prototype.onLookupMenuItemChoose = function ( item ) {
-               this.closeLookupMenu();
-               this.setLookupsDisabled( true );
-               this.setValue( item.getData() );
-               this.setLookupsDisabled( !this.suggestions );
+       mw.widgets.SearchInputWidget.prototype.onLookupMenuItemChoose = function () {
+               mw.widgets.SearchInputWidget.parent.prototype.onLookupMenuItemChoose.apply( this, arguments );
 
                if ( this.performSearchOnClick ) {
                        this.$element.closest( 'form' ).submit();
        };
 
        /**
-        * @inheritdoc OO.ui.mixin.LookupElement
+        * @inheritdoc
         */
-       mw.widgets.SearchInputWidget.prototype.getLookupMenuOptionsFromData = function ( response ) {
-               var items = this.getOptionsFromData( response );
+       mw.widgets.SearchInputWidget.prototype.getLookupMenuOptionsFromData = function () {
+               var items = mw.widgets.SearchInputWidget.parent.prototype.getLookupMenuOptionsFromData.apply(
+                       this, arguments
+               );
 
                this.lastLookupItems = items.map( function ( item ) {
                        return item.data;
                return items;
        };
 
-       /**
-        * @inheritdoc
-        */
-       mw.widgets.SearchInputWidget.prototype.focus = function () {
-               var retval;
-
-               // Prevent programmatic focus from opening the menu
-               this.setLookupsDisabled( true );
-
-               // Parent method
-               retval = mw.widgets.SearchInputWidget.parent.prototype.focus.apply( this, arguments );
-
-               this.setLookupsDisabled( !this.suggestions );
-
-               return retval;
-       };
-
 }( jQuery, mediaWiki ) );
index cdc4dbf..322143d 100644 (file)
@@ -19,8 +19,7 @@
                                title: String( page ),
                                user: user,
                                uselang: mw.config.get( 'wgUserLanguage' )
-                       }, params ) )
-                       .then( function ( data ) {
+                       }, params ) ).then( function ( data ) {
                                return data.rollback;
                        } );
                }
index 219dfb8..4a2895d 100644 (file)
 
                        upload = this.uploadWithFormData( file, data );
                        return upload.then(
-                                       null,
-                                       // If the call fails, we may want to try again...
-                                       retries === 0 ? null : retry,
-                                       function ( fraction ) {
-                                               // Since we're only uploading small parts of a file, we
-                                               // need to adjust the reported progress to reflect where
-                                               // we actually are in the combined upload
-                                               return ( start + fraction * ( end - start ) ) / file.size;
-                                       }
-                               ).promise( { abort: upload.abort } );
+                               null,
+                               // If the call fails, we may want to try again...
+                               retries === 0 ? null : retry,
+                               function ( fraction ) {
+                                       // Since we're only uploading small parts of a file, we
+                                       // need to adjust the reported progress to reflect where
+                                       // we actually are in the combined upload
+                                       return ( start + fraction * ( end - start ) ) / file.size;
+                               }
+                       ).promise( { abort: upload.abort } );
                },
 
                /**
index 3bf75ae..6d3c9fd 100644 (file)
@@ -20,7 +20,7 @@
         */
        function hideIfGetField( $el, name ) {
                var $found, $p, $widget,
-                       suffix = name.replace( /^([^\[]+)/, '[$1]' );
+                       suffix = name.replace( /^([^[]+)/, '[$1]' );
 
                function nameFilter() {
                        return this.name === name ||
index 7a1fa7f..ac2bb02 100644 (file)
         */
        mw.ForeignStructuredUpload.BookletLayout.prototype.saveFile = function () {
                var title = mw.Title.newFromText(
-                               this.getFilename(),
-                               mw.config.get( 'wgNamespaceIds' ).file
-                       );
+                       this.getFilename(),
+                       mw.config.get( 'wgNamespaceIds' ).file
+               );
 
                return this.uploadPromise
                        .then( this.validateFilename.bind( this, title ) )
index 1da4ab4..91cdc2d 100644 (file)
@@ -16,7 +16,7 @@
                 * @return {string} Escaped string
                 */
                escape: function ( str ) {
-                       return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' );
+                       return str.replace( /([\\{}()|.?*+\-^$\[\]])/g, '\\$1' ); // eslint-disable-line no-useless-escape
                }
        };
 }( mediaWiki ) );
index 253e0ef..398adbb 100644 (file)
                        },
                        // brackets, greater than
                        {
-                               pattern: /[\]\}>]/g,
+                               pattern: /[}\]>]/g,
                                replace: ')',
                                generalRule: true
                        },
                        // brackets, lower than
                        {
-                               pattern: /[\[\{<]/g,
+                               pattern: /[{[<]/g,
                                replace: '(',
                                generalRule: true
                        },
                }
 
                // Any remaining initial :s are illegal.
-               title = title.replace( /^\:+/, '' );
+               title = title.replace( /^:+/, '' );
 
                return Title.newFromText( title, namespace );
        };
                        thumbPhpRegex = /thumb\.php/,
                        regexes = [
                                // Thumbnails
-                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)\/[^\s\/]+-[^\s\/]*$/,
+                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)\/[^\s/]+-[^\s/]*$/,
 
                                // Full size images
-                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)$/,
+                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)$/,
 
                                // Thumbnails in non-hashed upload directories
-                               /\/([^\s\/]+)\/[^\s\/]+-(?:\1|thumbnail)[^\s\/]*$/,
+                               /\/([^\s/]+)\/[^\s/]+-(?:\1|thumbnail)[^\s/]*$/,
 
                                // Full-size images in non-hashed upload directories
-                               /\/([^\s\/]+)$/
+                               /\/([^\s/]+)$/
                        ],
 
                        recount = regexes.length;
index 3c1a668..939b841 100644 (file)
                                return $( '<div>' ).prop( {
                                        id: 'mw-debug-' + id,
                                        className: 'mw-debug-bit'
-                               } )
-                               .appendTo( $bits );
+                               } ).appendTo( $bits );
                        }
 
                        /**
                                        id: 'mw-debug-' + id,
                                        className: 'mw-debug-bit mw-debug-panelink'
                                } )
-                               .append( paneLabel( id, text ) )
-                               .appendTo( $bits );
+                                       .append( paneLabel( id, text ) )
+                                       .appendTo( $bits );
                        }
 
                        paneTriggerBitDiv( 'console', 'Console', this.data.log.length );
                                .append( $( '<th>SQL</th>' ) )
                                .append( $( '<th>Time</th>' ).css( 'width', '8em' ) )
                                .append( $( '<th>Call</th>' ).css( 'width', '18em' ) )
-                       .appendTo( $table );
+                               .appendTo( $table );
 
                        for ( i = 0, length = this.data.queries.length; i < length; i += 1 ) {
                                query = this.data.queries[ i ];
                                        .append( $( '<td>' ).text( query.sql ) )
                                        .append( $( '<td class="stats">' ).text( ( query.time * 1000 ).toFixed( 4 ) + 'ms' ) )
                                        .append( $( '<td>' ).text( query[ 'function' ] ) )
-                               .appendTo( $table );
+                                       .appendTo( $table );
                        }
 
                        return $table;
index f0e13b4..2d55094 100644 (file)
         */
        mw.Feedback.Dialog.prototype.validateFeedbackForm = function () {
                var isValid = (
-                               (
-                                       !this.useragentMandatory ||
-                                       this.useragentCheckbox.isSelected()
-                               ) &&
-                               this.feedbackSubjectInput.getValue()
-                       );
+                       (
+                               !this.useragentMandatory ||
+                               this.useragentCheckbox.isSelected()
+                       ) &&
+                       this.feedbackSubjectInput.getValue()
+               );
 
                this.actions.setAbilities( { submit: isValid } );
        };
diff --git a/resources/src/mediawiki/mediawiki.hlist-allskins.less b/resources/src/mediawiki/mediawiki.hlist-allskins.less
new file mode 100644 (file)
index 0000000..d7071e4
--- /dev/null
@@ -0,0 +1,21 @@
+.hlist {
+       dl,
+       ol,
+       ul {
+               margin: 0;
+               padding: 0;
+
+               dl,
+               ol,
+               ul {
+                       display: inline;
+               }
+       }
+
+       dd,
+       dt,
+       li {
+               margin: 0;
+               display: inline;
+       }
+}
index c0788a4..2663d87 100644 (file)
@@ -2,31 +2,6 @@
  * Stylesheet for mediawiki.hlist module
  * @author [[User:Edokter]]
  */
-.hlist dl,
-.hlist ol,
-.hlist ul {
-       margin: 0;
-       padding: 0;
-}
-/* Display list items inline */
-.hlist dd,
-.hlist dt,
-.hlist li {
-       margin: 0;
-       display: inline;
-}
-/* Display nested lists inline */
-.hlist dl dl,
-.hlist dl ol,
-.hlist dl ul,
-.hlist ol dl,
-.hlist ol ol,
-.hlist ol ul,
-.hlist ul dl,
-.hlist ul ol,
-.hlist ul ul {
-       display: inline;
-}
 /* Generate interpuncts */
 .hlist dt:after {
        content: ':';
index 6d3b4f0..e1681fa 100644 (file)
                        // recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
                        // This may be because, to save code, memoization was removed
 
+                       /* eslint-disable no-useless-escape */
                        regularLiteral = makeRegexParser( /^[^{}\[\]$<\\]/ );
                        regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ );
                        regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ );
                        regularLiteralWithSquareBrackets = makeRegexParser( /^[^{}$\\]/ );
+                       /* eslint-enable no-useless-escape */
 
                        backslash = makeStringParser( '\\' );
                        doubleQuote = makeStringParser( '"' );
                        templateName = transform(
                                // see $wgLegalTitleChars
                                // not allowing : due to the need to catch "PLURAL:$1"
-                               makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
+                               makeRegexParser( /^[ !"$&'()*,./0-9;=?@A-Z^_`a-z~\x80-\xFF+-]+/ ),
                                function ( result ) { return result.toString(); }
                        );
                        function templateParam() {
                                        $el.attr( {
                                                role: 'button',
                                                tabindex: 0
-                                       } )
-                                       .on( 'click keypress', function ( e ) {
+                                       } ).on( 'click keypress', function ( e ) {
                                                if (
                                                        e.type === 'click' ||
                                                        e.type === 'keypress' && e.which === 13
        // Replace the default message parser with jqueryMsg
        oldParser = mw.Message.prototype.parser;
        mw.Message.prototype.parser = function () {
-               if ( this.format === 'plain' || !/\{\{|[\[<>&]/.test( this.map.get( this.key ) ) ) {
+               if ( this.format === 'plain' || !/\{\{|[<>[&]/.test( this.map.get( this.key ) ) ) {
                        // Fall back to mw.msg's simple parser
                        return oldParser.apply( this );
                }
index 18f7f0a..c5989c0 100644 (file)
        function setGlobalMapValue( map, key, value ) {
                map.values[ key ] = value;
                log.deprecate(
-                               window,
-                               key,
-                               value,
-                               // Deprecation notice for mw.config globals (T58550, T72470)
-                               map === mw.config && 'Use mw.config instead.'
+                       window,
+                       key,
+                       value,
+                       // Deprecation notice for mw.config globals (T58550, T72470)
+                       map === mw.config && 'Use mw.config instead.'
                );
        }
 
index d36c4a0..dfacfc6 100644 (file)
@@ -35,7 +35,7 @@
 
                if ( options.tag ) {
                        // Sanitize options.tag before it is used by any code. (Including Notification class methods)
-                       options.tag = options.tag.replace( /[ _\-]+/g, '-' ).replace( /[^\-a-z0-9]+/ig, '' );
+                       options.tag = options.tag.replace( /[ _-]+/g, '-' ).replace( /[^-a-z0-9]+/ig, '' );
                        if ( options.tag ) {
                                $notification.addClass( 'mw-notification-tag-' + options.tag );
                        } else {
@@ -45,7 +45,7 @@
 
                if ( options.type ) {
                        // Sanitize options.type
-                       options.type = options.type.replace( /[ _\-]+/g, '-' ).replace( /[^\-a-z0-9]+/ig, '' );
+                       options.type = options.type.replace( /[ _-]+/g, '-' ).replace( /[^-a-z0-9]+/ig, '' );
                        $notification.addClass( 'mw-notification-type-' + options.type );
                }
 
index 6a6aa15..650092b 100644 (file)
@@ -44,8 +44,7 @@
         *  by that time.
         */
        mw.requestIdleCallback = window.requestIdleCallback ?
-               // Bind because it throws TypeError if context is not window
-               window.requestIdleCallback.bind( window ) :
+               window.requestIdleCallback.bind( window ) : // Bind because it throws TypeError if context is not window
                mw.requestIdleCallbackInternal;
        // Note: Polyfill was previously disabled due to
        // https://bugs.chromium.org/p/chromium/issues/detail?id=647870
index bcb6c33..1c1150e 100644 (file)
@@ -87,9 +87,9 @@
                 */
                function getInputLocation( context ) {
                        return context.config.$region
-                                       .closest( 'form' )
-                                       .find( '[data-search-loc]' )
-                                       .data( 'search-loc' ) || 'header';
+                               .closest( 'form' )
+                               .find( '[data-search-loc]' )
+                               .data( 'search-loc' ) || 'header';
                }
 
                /**
                                var $this = $( this );
                                $this
                                        .data( 'suggestions-context' )
-                                       .data.$container
-                                               .css( 'fontSize', $this.css( 'fontSize' ) );
+                                       .data.$container.css( 'fontSize', $this.css( 'fontSize' ) );
                        } );
 
                // Ensure that the thing is actually present!
index 53e8d93..5e10a5b 100644 (file)
@@ -45,8 +45,8 @@
                                        $tocToggleLink
                                                .wrap( '<span class="toctoggle"></span>' )
                                                .parent()
-                                                       .prepend( '&nbsp;[' )
-                                                       .append( ']&nbsp;' )
+                                               .prepend( '&nbsp;[' )
+                                               .append( ']&nbsp;' )
                                );
 
                                if ( hideToc ) {
index 6d6d46d..d8fb249 100644 (file)
@@ -33,8 +33,7 @@
                                formatversion: 2,
                                action: 'patrol',
                                rcid: rcid
-                       } )
-                       .done( function ( data ) {
+                       } ).done( function ( data ) {
                                var title;
                                // Remove all patrollinks from the page (including any spinners inside).
                                $patrolLinks.closest( '.patrollink' ).remove();
@@ -46,8 +45,7 @@
                                        // This should never happen as errors should trigger fail
                                        mw.notify( mw.msg( 'markedaspatrollederrornotify' ), { type: 'error' } );
                                }
-                       } )
-                       .fail( function ( error ) {
+                       } ).fail( function ( error ) {
                                $spinner.remove();
                                // Restore the patrol link. This allows the user to try again
                                // (or open it in a new window, bypassing this ajax module).
index d94b158..6db518d 100644 (file)
@@ -45,8 +45,8 @@
                                        $( e.delegateTarget ).remove();
                                }, function ( errorCode, data ) {
                                        var message = data && data.error && data.error.messageHtml ?
-                                               $.parseHTML( data.error.messageHtml ) :
-                                               mw.msg( 'rollbackfailed' ),
+                                                       $.parseHTML( data.error.messageHtml ) :
+                                                       mw.msg( 'rollbackfailed' ),
                                                type = errorCode === 'alreadyrolled' ? 'warn' : 'error';
 
                                        mw.notify( message, {
index 017d9fb..dffa863 100644 (file)
@@ -10,7 +10,7 @@
        };
        OO.ui.isMobile = function () {
                if ( isMobile === undefined ) {
-                       isMobile = mw.config.get( 'skin' ) === 'minerva';
+                       isMobile = !!mw.config.get( 'wgMFMode' );
                }
                return isMobile;
        };
index 9255733..feed77f 100644 (file)
@@ -168,7 +168,7 @@ class ParserTestRunner {
                global $wgParserTestFiles;
 
                // Add core test files
-               $files = array_map( function( $item ) {
+               $files = array_map( function ( $item ) {
                        return __DIR__ . "/$item";
                }, self::$coreTestFiles );
 
@@ -1036,6 +1036,9 @@ class ParserTestRunner {
                $linkHolderBatchSize =
                        self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
 
+               // Default to fallback skin, but allow it to be overridden
+               $skin = self::getOptionValue( 'skin', $opts, 'fallback' );
+
                $setup = [
                        'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
                        'wgLanguageCode' => $langCode,
@@ -1105,7 +1108,13 @@ class ParserTestRunner {
                $context = RequestContext::getMain();
                $context->setUser( $user );
                $context->setLanguage( $lang );
-               $teardown[] = function () use ( $context ) {
+               // And the skin!
+               $oldSkin = $context->getSkin();
+               $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
+               $context->setSkin( $skinFactory->makeSkin( $skin ) );
+               $context->setOutput( new OutputPage( $context ) );
+               $setup['wgOut'] = $context->getOutput();
+               $teardown[] = function () use ( $context, $oldSkin ) {
                        // Clear language conversion tables
                        $wrapper = TestingAccessWrapper::newFromObject(
                                $context->getLanguage()->getConverter()
@@ -1114,6 +1123,8 @@ class ParserTestRunner {
                        // Reset context to the restored globals
                        $context->setUser( $GLOBALS['wgUser'] );
                        $context->setLanguage( $GLOBALS['wgContLang'] );
+                       $context->setSkin( $oldSkin );
+                       $context->setOutput( $GLOBALS['wgOut'] );
                };
 
                $teardown[] = $this->executeSetupSnippets( $setup );
index e8ccd9d..f8ba742 100644 (file)
@@ -28364,3 +28364,25 @@ wgRawHtml=1
 <style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style>
 </div>
 !! end
+
+!! test
+Decoding of HTML entities in headings and links for IDs and link fragments (T103714)
+!! wikitext
+== A&B&amp;C&amp;amp;D&amp;amp;amp;E ==
+[[#A&B&amp;C&amp;amp;D&amp;amp;amp;E]]
+!! html/php
+<h2><span class="mw-headline" id="A.26B.26C.26amp.3BD.26amp.3Bamp.3BE">A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p><a href="#A.26B.26C.26D.26amp.3BE">#A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</a>
+</p>
+!! end
+
+!! test
+Decoding of HTML entities in indicator names for IDs (T104196)
+!! options
+showindicators
+!! wikitext
+<indicator name="1&2&amp;3&amp;amp;4&amp;amp;amp;5">Indicator</indicator>
+!! html/php
+1&2&3&amp;4&amp;amp;5=Indicator
+
+!! end
index 9bd5d8d..1015b0b 100644 (file)
@@ -22,7 +22,8 @@
  * @param string $text1
  * @param string $text2
  * @param int $numContextLines
+ * @param int $movedParagraphDetectionCutoff
  * @return string
  */
-function wikidiff2_do_diff( $text1, $text2, $numContextLines ) {
+function wikidiff2_do_diff( $text1, $text2, $numContextLines, $movedParagraphDetectionCutoff = 0 ) {
 }
index 15833dc..215d292 100644 (file)
@@ -640,8 +640,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         * The key is added to the array of globals that will be reset afterwards
         * in the tearDown().
         *
-        * @example
-        * <code>
+        * @par Example
+        * @code
         *     protected function setUp() {
         *         $this->setMwGlobals( 'wgRestrictStuff', true );
         *     }
@@ -656,7 +656,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         *     }
         *
         *     function testQuux() {}
-        * </code>
+        * @endcode
         *
         * @param array|string $pairs Key to the global variable, or an array
         *  of key/value pairs.
@@ -1087,10 +1087,15 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        $page->doEditContent(
                                new WikitextContent( 'UTContent' ),
                                'UTPageSummary',
-                               EDIT_NEW,
+                               EDIT_NEW | EDIT_SUPPRESS_RC,
                                false,
                                $user
                        );
+                       // an edit always attempt to purge backlink links such as history
+                       // pages. That is unneccessary.
+                       JobQueueGroup::singleton()->get( 'htmlCacheUpdate' )->delete();
+                       // WikiPages::doEditUpdates randomly adds RC purges
+                       JobQueueGroup::singleton()->get( 'recentChangesUpdate' )->delete();
 
                        // doEditContent() probably started the session via
                        // User::loadFromSession(). Close it now.
index a8a8f4d..d8f89fb 100644 (file)
@@ -94,6 +94,7 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
        protected $isKnownEmpty = false;
        protected $type = ResourceLoaderModule::LOAD_GENERAL;
        protected $targets = [ 'phpunit' ];
+       protected $shouldEmbed = null;
 
        public function __construct( $options = [] ) {
                foreach ( $options as $key => $value ) {
@@ -143,6 +144,10 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
                return $this->isKnownEmpty;
        }
 
+       public function shouldEmbedModule( ResourceLoaderContext $context ) {
+               return $this->shouldEmbed !== null ? $this->shouldEmbed : parent::shouldEmbedModule( $context );
+       }
+
        public function enableModuleContentVersion() {
                return true;
        }
index 76a4f51..802f9b1 100644 (file)
@@ -39,7 +39,7 @@ class DeprecatedGlobalTest extends MediaWikiTestCase {
                global $wgDummyLazy;
 
                $called = false;
-               $factory = function() use ( &$called ) {
+               $factory = function () use ( &$called ) {
                        $called = true;
                        return new HashBagOStuff();
                };
index 89416f2..ae858f5 100644 (file)
@@ -23,7 +23,7 @@ class GitInfoTest extends MediaWikiTestCase {
        public function testValidJsonData() {
                global $IP;
 
-               $this->assertValidGitInfo( new GitInfo( "$IP/testValidJsonData") );
+               $this->assertValidGitInfo( new GitInfo( "$IP/testValidJsonData" ) );
                $this->assertValidGitInfo( new GitInfo( __DIR__ . "/../data/gitinfo/extension" ) );
        }
 
index ada516d..d78c1e7 100644 (file)
@@ -131,10 +131,9 @@ class PreferencesTest extends MediaWikiTestCase {
                        ->method( 'getConfig' )
                        ->willReturn( $configMock );
 
-               $this->setTemporaryHook( 'PreferencesFormPreSave', function(
+               $this->setTemporaryHook( 'PreferencesFormPreSave', function (
                        $formData, $form, $user, &$result, $oldUserOptions )
                        use ( $newOptions, $oldOptions, $userMock ) {
-
                        $this->assertSame( $userMock, $user );
                        foreach ( $newOptions as $option => $value ) {
                                $this->assertSame( $value, $formData[ $option ] );
index abcf1d4..6d093b0 100644 (file)
@@ -343,6 +343,41 @@ class SanitizerTest extends MediaWikiTestCase {
                ];
        }
 
+       /**
+        * Test Sanitizer::escapeId
+        *
+        * @dataProvider provideEscapeId
+        * @covers Sanitizer::escapeId
+        */
+       public function testEscapeId( $input, $output ) {
+               $this->assertEquals(
+                       $output,
+                       Sanitizer::escapeId( $input, [ 'noninitial', 'legacy' ] )
+               );
+       }
+
+       public static function provideEscapeId() {
+               return [
+                       [ '+', '.2B' ],
+                       [ '&', '.26' ],
+                       [ '=', '.3D' ],
+                       [ ':', ':' ],
+                       [ ';', '.3B' ],
+                       [ '@', '.40' ],
+                       [ '$', '.24' ],
+                       [ '-_.', '-_.' ],
+                       [ '!', '.21' ],
+                       [ '*', '.2A' ],
+                       [ '/', '.2F' ],
+                       [ '[]', '.5B.5D' ],
+                       [ '<>', '.3C.3E' ],
+                       [ '\'', '.27' ],
+                       [ '§', '.C2.A7' ],
+                       [ 'Test:A & B/Here', 'Test:A_.26_B.2FHere' ],
+                       [ 'A&B&amp;C&amp;amp;D&amp;amp;amp;E', 'A.26B.26C.26amp.3BD.26amp.3Bamp.3BE' ],
+               ];
+       }
+
        /**
         * Test escapeIdReferenceList for consistency with escapeId
         *
diff --git a/tests/phpunit/includes/SiteStatsTest.php b/tests/phpunit/includes/SiteStatsTest.php
new file mode 100644 (file)
index 0000000..ea476a7
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+class SiteStatsTest extends MediaWikiTestCase {
+
+       /**
+        * @covers SiteStats::jobs
+        */
+       function testJobsCountGetCached() {
+               $this->setService( 'MainWANObjectCache',
+                       new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ) );
+               $cache = \MediaWiki\MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $jobq = JobQueueGroup::singleton();
+
+               $jobq->push( new NullJob( Title::newMainPage(), [] ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                        'A single job enqueued bumps jobscount stat to 1' );
+
+               $jobq->push( new NullJob( Title::newMainPage(), [] ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                       'SiteStats::jobs() count does not reflect addition ' .
+                       'of a second job (cached)'
+               );
+
+               $jobq->get( 'null' )->delete();  // clear jobqueue
+               $this->assertEquals( 0, $jobq->get( 'null' )->getSize(),
+                       'Job queue for NullJob has been cleaned' );
+
+               $cache->delete( $cache->makeKey( 'SiteStats', 'jobscount' ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                       'jobs count is kept in process cache' );
+
+               $cache->clearProcessCache();
+               $this->assertEquals( 0, SiteStats::jobs() );
+       }
+
+}
index a2c0d39..d47481c 100644 (file)
@@ -526,6 +526,10 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
         * @param array $expect
         */
        public function testGetMessageFromException( $exception, $options, $expect ) {
+               if ( $exception instanceof UsageException ) {
+                       $this->hideDeprecated( 'UsageException::getMessageArray' );
+               }
+
                $result = new ApiResult( 8388608 );
                $formatter = new ApiErrorFormatter( $result, Language::factory( 'en' ), 'html', false );
 
@@ -571,6 +575,12 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
        }
 
        public static function provideGetMessageFromException() {
+               MediaWiki\suppressWarnings();
+               $usageException = new UsageException(
+                       '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ]
+               );
+               MediaWiki\restoreWarnings();
+
                return [
                        'Normal exception' => [
                                new RuntimeException( '<b>Something broke!</b>' ),
@@ -591,7 +601,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                ]
                        ],
                        'UsageException' => [
-                               new UsageException( '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ] ),
+                               $usageException,
                                [],
                                [
                                        'text' => '&#60;b&#62;Something broke!&#60;/b&#62;',
@@ -600,7 +610,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                ]
                        ],
                        'UsageException, wrapped' => [
-                               new UsageException( '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ] ),
+                               $usageException,
                                [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
                                [
                                        'text' => '(&#60;b&#62;Something broke!&#60;/b&#62;)',
index ea33a9e..ad334e9 100644 (file)
@@ -500,6 +500,10 @@ class ApiMainTest extends ApiTestCase {
                        MWExceptionHandler::getRedactedTraceAsString( $dbex )
                )->inLanguage( 'en' )->useDatabase( false )->text();
 
+               MediaWiki\suppressWarnings();
+               $usageEx = new UsageException( 'Usage exception!', 'ue', 0, [ 'foo' => 'bar' ] );
+               MediaWiki\restoreWarnings();
+
                $apiEx1 = new ApiUsageException( null,
                        StatusValue::newFatal( new ApiRawMessage( 'An error', 'sv-error1' ) ) );
                TestingAccessWrapper::newFromObject( $apiEx1 )->modulePath = 'foo+bar';
@@ -545,7 +549,7 @@ class ApiMainTest extends ApiTestCase {
                                ]
                        ],
                        [
-                               new UsageException( 'Usage exception!', 'ue', 0, [ 'foo' => 'bar' ] ),
+                               $usageEx,
                                [ 'existing-error', 'ue' ],
                                [
                                        'warnings' => [
diff --git a/tests/phpunit/includes/changetags/ChangeTagsTest.php b/tests/phpunit/includes/changetags/ChangeTagsTest.php
new file mode 100644 (file)
index 0000000..723d685
--- /dev/null
@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * @covers ChangeTags
+ */
+class ChangeTagsTest extends MediaWikiTestCase {
+
+       // TODO only modifyDisplayQuery is tested, nothing else is
+
+       /** @dataProvider provideModifyDisplayQuery */
+       public function testModifyDisplayQuery( $origQuery, $filter_tag, $useTags, $modifiedQuery ) {
+               $this->setMwGlobals( 'wgUseTagFilter', $useTags );
+               // HACK resolve deferred group concats (see comment in provideModifyDisplayQuery)
+               if ( isset( $modifiedQuery['fields']['ts_tags'] ) ) {
+                       $modifiedQuery['fields']['ts_tags'] = call_user_func_array(
+                               [ wfGetDB( DB_REPLICA ), 'buildGroupConcatField' ],
+                               $modifiedQuery['fields']['ts_tags']
+                       );
+               }
+               if ( isset( $modifiedQuery['exception'] ) ) {
+                       $this->setExpectedException( $modifiedQuery['exception'] );
+               }
+               ChangeTags::modifyDisplayQuery(
+                       $origQuery['tables'],
+                       $origQuery['fields'],
+                       $origQuery['conds'],
+                       $origQuery['join_conds'],
+                       $origQuery['options'],
+                       $filter_tag
+               );
+               if ( !isset( $modifiedQuery['exception'] ) ) {
+                       $this->assertArrayEquals(
+                               $modifiedQuery,
+                               $origQuery,
+                               /* ordered = */ false,
+                               /* named = */ true
+                       );
+               }
+       }
+
+       public function provideModifyDisplayQuery() {
+               // HACK if we call $dbr->buildGroupConcatField() now, it will return the wrong table names
+               // We have to have the test runner call it instead
+               $groupConcats = [
+                       'recentchanges' => [ ',', 'change_tag', 'ct_tag', 'ct_rc_id=rc_id' ],
+                       'logging' => [ ',', 'change_tag', 'ct_tag', 'ct_log_id=log_id' ],
+                       'revision' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=rev_id' ],
+                       'archive' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=ar_rev_id' ],
+               ];
+
+               return [
+                       'simple recentchanges query' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               '', // no tag filter
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'simple query with strings' => [
+                               [
+                                       'tables' => 'recentchanges',
+                                       'fields' => 'rc_id',
+                                       'conds' => "rc_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY rc_timestamp DESC',
+                               ],
+                               '', // no tag filter
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with single tag filter' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'logging query with single tag filter and strings' => [
+                               [
+                                       'tables' => 'logging',
+                                       'fields' => 'log_id',
+                                       'conds' => "log_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY log_timestamp DESC',
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'logging', 'change_tag' ],
+                                       'fields' => [ 'log_id', 'ts_tags' => $groupConcats['logging'] ],
+                                       'conds' => [ "log_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_log_id=log_id' ] ],
+                                       'options' => [ 'ORDER BY log_timestamp DESC' ],
+                               ]
+                       ],
+                       'revision query with single tag filter' => [
+                               [
+                                       'tables' => [ 'revision' ],
+                                       'fields' => [ 'rev_id', 'rev_timestamp' ],
+                                       'conds' => [ "rev_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'revision', 'change_tag' ],
+                                       'fields' => [ 'rev_id', 'rev_timestamp', 'ts_tags' => $groupConcats['revision'] ],
+                                       'conds' => [ "rev_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=rev_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
+                               ]
+                       ],
+                       'archive query with single tag filter' => [
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp' ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'archive', 'change_tag' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=ar_rev_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ]
+                       ],
+                       'unsupported table name throws exception (even without tag filter)' => [
+                               [
+                                       'tables' => [ 'foobar' ],
+                                       'fields' => [ 'fb_id', 'fb_timestamp' ],
+                                       'conds' => [ "fb_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'fb_timestamp DESC' ],
+                               ],
+                               '',
+                               true, // tag filtering enabled
+                               [ 'exception' => MWException::class ]
+                       ],
+                       'tag filter ignored when tag filtering is disabled' => [
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp' ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ],
+                               'foo',
+                               false, // tag filtering disabled
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC', 'DISTINCT' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter that already has DISTINCT' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter with strings' => [
+                               [
+                                       'tables' => 'recentchanges',
+                                       'fields' => 'rc_id',
+                                       'conds' => "rc_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY rc_timestamp DESC',
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY rc_timestamp DESC', 'DISTINCT' ],
+                               ]
+                       ],
+               ];
+       }
+
+}
index ba38128..608d8d9 100644 (file)
@@ -47,7 +47,7 @@ class ConfigFactoryTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers ConfigFactory::register
+        * @covers ConfigFactory::salvage
         */
        public function testSalvage() {
                $oldFactory = new ConfigFactory();
@@ -83,7 +83,7 @@ class ConfigFactoryTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers ConfigFactory::register
+        * @covers ConfigFactory::getConfigNames
         */
        public function testGetConfigNames() {
                $factory = new ConfigFactory();
@@ -96,7 +96,7 @@ class ConfigFactoryTest extends MediaWikiTestCase {
        /**
         * @covers ConfigFactory::makeConfig
         */
-       public function testMakeConfig() {
+       public function testMakeConfigWithCallback() {
                $factory = new ConfigFactory();
                $factory->register( 'unittest', 'GlobalVarConfig::newInstance' );
 
@@ -105,6 +105,16 @@ class ConfigFactoryTest extends MediaWikiTestCase {
                $this->assertSame( $conf, $factory->makeConfig( 'unittest' ) );
        }
 
+       /**
+        * @covers ConfigFactory::makeConfig
+        */
+       public function testMakeConfigWithObject() {
+               $factory = new ConfigFactory();
+               $conf = new HashConfig();
+               $factory->register( 'test', $conf );
+               $this->assertSame( $conf, $factory->makeConfig( 'test' ) );
+       }
+
        /**
         * @covers ConfigFactory::makeConfig
         */
index 763bfa8..19cffa2 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Wikimedia\TestingAccessWrapper;
+
 class EtcConfigTest extends PHPUnit_Framework_TestCase {
 
        private function createConfigMock( array $options = [] ) {
@@ -359,4 +361,155 @@ class EtcConfigTest extends PHPUnit_Framework_TestCase {
 
                $this->assertSame( 'from-cache-expired', $mock->get( 'known' ) );
        }
+
+       public static function provideFetchFromServer() {
+               return [
+                       '200 OK - Success' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => json_encode( [ 'val' => true ] )
+                                               ],
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [ 'foo' => true ], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Skip dir' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => json_encode( [ 'val' => true ] )
+                                               ],
+                                               [
+                                                       'key' => '/example/sub',
+                                                       'dir' => true
+                                               ],
+                                               [
+                                                       'key' => '/example/bar',
+                                                       'value' => json_encode( [ 'val' => false ] )
+                                               ],
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [ 'foo' => true, 'bar' => false ], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Bad value' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => ';"broken{value'
+                                               ]
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       "Failed to parse value for 'foo'.",
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Empty node list' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => '{"node":{"nodes":[]}}',
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Invalid JSON' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => '(curl error: no status set)',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       "Unexpected JSON response; missing 'nodes' list.",
+                                       false // retry
+                               ],
+                       ],
+                       '404 Not Found' => [
+                               'http' => [
+                                       'code' => 404,
+                                       'reason' => 'Not Found',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       'HTTP 404 (Not Found)',
+                                       false // retry
+                               ],
+                       ],
+                       '400 Bad Request - custom error' => [
+                               'http' => [
+                                       'code' => 400,
+                                       'reason' => 'Bad Request',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => 'No good reason',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       'No good reason',
+                                       true // retry
+                               ],
+                       ],
+               ];
+       }
+
+       /**
+        * @covers EtcdConfig::fetchAllFromEtcdServer
+        * @covers EtcdConfig::unserialize
+        * @dataProvider provideFetchFromServer
+        */
+       public function testFetchFromServer( array $httpResponse, array $expected ) {
+               $http = $this->getMockBuilder( MultiHttpClient::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $http->expects( $this->once() )->method( 'run' )
+                       ->willReturn( array_values( $httpResponse ) );
+
+               $conf = $this->getMockBuilder( EtcdConfig::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               // Access for protected member and method
+               $conf = TestingAccessWrapper::newFromObject( $conf );
+               $conf->http = $http;
+
+               $this->assertSame(
+                       $expected,
+                       $conf->fetchAllFromEtcdServer( 'etcd-tcp.example.net' )
+               );
+       }
 }
index b9ce997..d0996e3 100644 (file)
@@ -103,16 +103,16 @@ more stuff
 
        public static function dataGetSection() {
                return [
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "0",
                                "Intro"
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "2",
                                "== test ==
 just a test"
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "8",
                                false
                        ],
@@ -138,38 +138,38 @@ just a test"
 
        public static function dataReplaceSection() {
                return [
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "0",
                                "No more",
                                null,
-                               trim( preg_replace( '/^Intro/sm', 'No more', WikitextContentTest::$sections ) )
+                               trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "",
                                "No more",
                                null,
                                "No more"
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "2",
                                "== TEST ==\nmore fun",
                                null,
                                trim( preg_replace(
                                        '/^== test ==.*== foo ==/sm', "== TEST ==\nmore fun\n\n== foo ==",
-                                       WikitextContentTest::$sections
+                                       self::$sections
                                ) )
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "8",
                                "No more",
                                null,
-                               WikitextContentTest::$sections
+                               self::$sections
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "new",
                                "No more",
                                "New",
-                               trim( WikitextContentTest::$sections ) . "\n\n\n== New ==\n\nNo more"
+                               trim( self::$sections ) . "\n\n\n== New ==\n\nNo more"
                        ],
                ];
        }
diff --git a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
deleted file mode 100644 (file)
index 3dc810c..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-<?php
-/**
- * Holds tests for DatabaseMysqlBase MediaWiki class.
- *
- * 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
- * @author Antoine Musso
- * @copyright © 2013 Antoine Musso
- * @copyright © 2013 Wikimedia Foundation and contributors
- */
-
-use Wikimedia\Rdbms\TransactionProfiler;
-use Wikimedia\Rdbms\DatabaseDomain;
-use Wikimedia\Rdbms\MySQLMasterPos;
-use Wikimedia\Rdbms\DatabaseMysqlBase;
-
-/**
- * Fake class around abstract class so we can call concrete methods.
- */
-class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
-       // From Database
-       function __construct() {
-               $this->profiler = new ProfilerStub( [] );
-               $this->trxProfiler = new TransactionProfiler();
-               $this->cliMode = true;
-               $this->connLogger = new \Psr\Log\NullLogger();
-               $this->queryLogger = new \Psr\Log\NullLogger();
-               $this->errorLogger = function ( Exception $e ) {
-                       wfWarn( get_class( $e ) . ": {$e->getMessage()}" );
-               };
-               $this->currentDomain = DatabaseDomain::newUnspecified();
-       }
-
-       protected function closeConnection() {
-       }
-
-       protected function doQuery( $sql ) {
-       }
-
-       // From DatabaseMysql
-       protected function mysqlConnect( $realServer ) {
-       }
-
-       protected function mysqlSetCharset( $charset ) {
-       }
-
-       protected function mysqlFreeResult( $res ) {
-       }
-
-       protected function mysqlFetchObject( $res ) {
-       }
-
-       protected function mysqlFetchArray( $res ) {
-       }
-
-       protected function mysqlNumRows( $res ) {
-       }
-
-       protected function mysqlNumFields( $res ) {
-       }
-
-       protected function mysqlFieldName( $res, $n ) {
-       }
-
-       protected function mysqlFieldType( $res, $n ) {
-       }
-
-       protected function mysqlDataSeek( $res, $row ) {
-       }
-
-       protected function mysqlError( $conn = null ) {
-       }
-
-       protected function mysqlFetchField( $res, $n ) {
-       }
-
-       protected function mysqlRealEscapeString( $s ) {
-       }
-
-       function insertId() {
-       }
-
-       function lastErrno() {
-       }
-
-       function affectedRows() {
-       }
-
-       function getServerVersion() {
-       }
-}
-
-class DatabaseMysqlBaseTest extends MediaWikiTestCase {
-       /**
-        * @dataProvider provideDiapers
-        * @covers DatabaseMysqlBase::addIdentifierQuotes
-        */
-       public function testAddIdentifierQuotes( $expected, $in ) {
-               $db = new FakeDatabaseMysqlBase();
-               $quoted = $db->addIdentifierQuotes( $in );
-               $this->assertEquals( $expected, $quoted );
-       }
-
-       /**
-        * Feeds testAddIdentifierQuotes
-        *
-        * Named per T22281 convention.
-        */
-       function provideDiapers() {
-               return [
-                       // Format: expected, input
-                       [ '``', '' ],
-
-                       // Yeah I really hate loosely typed PHP idiocies nowadays
-                       [ '``', null ],
-
-                       // Dear codereviewer, guess what addIdentifierQuotes()
-                       // will return with thoses:
-                       [ '``', false ],
-                       [ '`1`', true ],
-
-                       // We never know what could happen
-                       [ '`0`', 0 ],
-                       [ '`1`', 1 ],
-
-                       // Whatchout! Should probably use something more meaningful
-                       [ "`'`", "'" ],  # single quote
-                       [ '`"`', '"' ],  # double quote
-                       [ '````', '`' ], # backtick
-                       [ '`’`', '’' ],  # apostrophe (look at your encyclopedia)
-
-                       // sneaky NUL bytes are lurking everywhere
-                       [ '``', "\0" ],
-                       [ '`xyzzy`', "\0x\0y\0z\0z\0y\0" ],
-
-                       // unicode chars
-                       [
-                               self::createUnicodeString( '`\u0001a\uFFFFb`' ),
-                               self::createUnicodeString( '\u0001a\uFFFFb' )
-                       ],
-                       [
-                               self::createUnicodeString( '`\u0001\uFFFF`' ),
-                               self::createUnicodeString( '\u0001\u0000\uFFFF\u0000' )
-                       ],
-                       [ '`☃`', '☃' ],
-                       [ '`メインページ`', 'メインページ' ],
-                       [ '`Басты_бет`', 'Басты_бет' ],
-
-                       // Real world:
-                       [ '`Alix`', 'Alix' ],  # while( ! $recovered ) { sleep(); }
-                       [ '`Backtick: ```', 'Backtick: `' ],
-                       [ '`This is a test`', 'This is a test' ],
-               ];
-       }
-
-       private static function createUnicodeString( $str ) {
-               return json_decode( '"' . $str . '"' );
-       }
-
-       function getMockForViews() {
-               $db = $this->getMockBuilder( 'DatabaseMysqli' )
-                       ->disableOriginalConstructor()
-                       ->setMethods( [ 'fetchRow', 'query' ] )
-                       ->getMock();
-
-               $db->method( 'query' )
-                       ->with( $this->anything() )
-                       ->willReturn( new FakeResultWrapper( [
-                               (object)[ 'Tables_in_' => 'view1' ],
-                               (object)[ 'Tables_in_' => 'view2' ],
-                               (object)[ 'Tables_in_' => 'myview' ]
-                       ] ) );
-
-               return $db;
-       }
-       /**
-        * @covers DatabaseMysqlBase::listViews
-        */
-       function testListviews() {
-               $db = $this->getMockForViews();
-
-               $this->assertEquals( [ 'view1', 'view2', 'myview' ],
-                       $db->listViews() );
-
-               // Prefix filtering
-               $this->assertEquals( [ 'view1', 'view2' ],
-                       $db->listViews( 'view' ) );
-               $this->assertEquals( [ 'myview' ],
-                       $db->listViews( 'my' ) );
-               $this->assertEquals( [],
-                       $db->listViews( 'UNUSED_PREFIX' ) );
-               $this->assertEquals( [ 'view1', 'view2', 'myview' ],
-                       $db->listViews( '' ) );
-       }
-
-       /**
-        * @dataProvider provideComparePositions
-        */
-       function testHasReached( MySQLMasterPos $lowerPos, MySQLMasterPos $higherPos, $match ) {
-               if ( $match ) {
-                       $this->assertTrue( $lowerPos->channelsMatch( $higherPos ) );
-
-                       $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
-                       $this->assertTrue( $higherPos->hasReached( $higherPos ) );
-                       $this->assertTrue( $lowerPos->hasReached( $lowerPos ) );
-                       $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
-               } else { // channels don't match
-                       $this->assertFalse( $lowerPos->channelsMatch( $higherPos ) );
-
-                       $this->assertFalse( $higherPos->hasReached( $lowerPos ) );
-                       $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
-               }
-       }
-
-       function provideComparePositions() {
-               return [
-                       // Binlog style
-                       [
-                               new MySQLMasterPos( 'db1034-bin.000976', '843431247' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '843431248' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '1000' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1035-bin.000976', '1000' ),
-                               false
-                       ],
-                       // MySQL GTID style
-                       [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:23' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '3E11FA47-71CA-11E1-9E33-C80AA9429562:24' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:99' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '3E11FA47-71CA-11E1-9E33-C80AA9429562:100' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:99' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '1E11FA47-71CA-11E1-9E33-C80AA9429562:100' ),
-                               false
-                       ],
-                       // MariaDB GTID style
-                       [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-23' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '255-11-24' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-99' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '255-11-100' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-999' ),
-                               new MySQLMasterPos( 'db1-bin.2', '2', '254-11-1000' ),
-                               false
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideChannelPositions
-        */
-       function testChannelsMatch( MySQLMasterPos $pos1, MySQLMasterPos $pos2, $matches ) {
-               $this->assertEquals( $matches, $pos1->channelsMatch( $pos2 ) );
-               $this->assertEquals( $matches, $pos2->channelsMatch( $pos1 ) );
-       }
-
-       function provideChannelPositions() {
-               return [
-                       [
-                               new MySQLMasterPos( 'db1034-bin.000876', '44' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '74' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1052-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1052-bin.000976', '1000' ),
-                               true
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
-                               new MySQLMasterPos( 'db1035-bin.000976', '10000' ),
-                               false
-                       ],
-                       [
-                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
-                               new MySQLMasterPos( 'trump2016.000976', '10000' ),
-                               false
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideLagAmounts
-        */
-       function testPtHeartbeat( $lag ) {
-               $db = $this->getMockBuilder( 'DatabaseMysqli' )
-                       ->disableOriginalConstructor()
-                       ->setMethods( [
-                               'getLagDetectionMethod', 'getHeartbeatData', 'getMasterServerInfo' ] )
-                       ->getMock();
-
-               $db->method( 'getLagDetectionMethod' )
-                       ->willReturn( 'pt-heartbeat' );
-
-               $db->method( 'getMasterServerInfo' )
-                       ->willReturn( [ 'serverId' => 172, 'asOf' => time() ] );
-
-               // Fake the current time.
-               list( $nowSecFrac, $nowSec ) = explode( ' ', microtime() );
-               $now = (float)$nowSec + (float)$nowSecFrac;
-               // Fake the heartbeat time.
-               // Work arounds for weak DataTime microseconds support.
-               $ptTime = $now - $lag;
-               $ptSec = (int)$ptTime;
-               $ptSecFrac = ( $ptTime - $ptSec );
-               $ptDateTime = new DateTime( "@$ptSec" );
-               $ptTimeISO = $ptDateTime->format( 'Y-m-d\TH:i:s' );
-               $ptTimeISO .= ltrim( number_format( $ptSecFrac, 6 ), '0' );
-
-               $db->method( 'getHeartbeatData' )
-                       ->with( [ 'server_id' => 172 ] )
-                       ->willReturn( [ $ptTimeISO, $now ] );
-
-               $db->setLBInfo( 'clusterMasterHost', 'db1052' );
-               $lagEst = $db->getLag();
-
-               $this->assertGreaterThan( $lag - .010, $lagEst, "Correct heatbeat lag" );
-               $this->assertLessThan( $lag + .010, $lagEst, "Correct heatbeat lag" );
-       }
-
-       function provideLagAmounts() {
-               return [
-                       [ 0 ],
-                       [ 0.3 ],
-                       [ 6.5 ],
-                       [ 10.1 ],
-                       [ 200.2 ],
-                       [ 400.7 ],
-                       [ 600.22 ],
-                       [ 1000.77 ],
-               ];
-       }
-}
diff --git a/tests/phpunit/includes/db/DatabaseSQLTest.php b/tests/phpunit/includes/db/DatabaseSQLTest.php
deleted file mode 100644 (file)
index 2b587db..0000000
+++ /dev/null
@@ -1,1075 +0,0 @@
-<?php
-
-use Wikimedia\Rdbms\LikeMatch;
-
-/**
- * Test the abstract database layer
- * This is a non DBMS depending test.
- */
-class DatabaseSQLTest extends MediaWikiTestCase {
-       /** @var DatabaseTestHelper */
-       private $database;
-
-       protected function setUp() {
-               parent::setUp();
-               $this->database = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => true ] );
-       }
-
-       protected function assertLastSql( $sqlText ) {
-               $this->assertEquals(
-                       $sqlText,
-                       $this->database->getLastSqls()
-               );
-       }
-
-       protected function assertLastSqlDb( $sqlText, $db ) {
-               $this->assertEquals( $sqlText, $db->getLastSqls() );
-       }
-
-       /**
-        * @dataProvider provideSelect
-        * @covers Database::select
-        */
-       public function testSelect( $sql, $sqlText ) {
-               $this->database->select(
-                       $sql['tables'],
-                       $sql['fields'],
-                       isset( $sql['conds'] ) ? $sql['conds'] : [],
-                       __METHOD__,
-                       isset( $sql['options'] ) ? $sql['options'] : [],
-                       isset( $sql['join_conds'] ) ? $sql['join_conds'] : []
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideSelect() {
-               return [
-                       [
-                               [
-                                       'tables' => 'table',
-                                       'fields' => [ 'field', 'alias' => 'field2' ],
-                                       'conds' => [ 'alias' => 'text' ],
-                               ],
-                               "SELECT field,field2 AS alias " .
-                                       "FROM table " .
-                                       "WHERE alias = 'text'"
-                       ],
-                       [
-                               [
-                                       'tables' => 'table',
-                                       'fields' => [ 'field', 'alias' => 'field2' ],
-                                       'conds' => [ 'alias' => 'text' ],
-                                       'options' => [ 'LIMIT' => 1, 'ORDER BY' => 'field' ],
-                               ],
-                               "SELECT field,field2 AS alias " .
-                                       "FROM table " .
-                                       "WHERE alias = 'text' " .
-                                       "ORDER BY field " .
-                                       "LIMIT 1"
-                       ],
-                       [
-                               [
-                                       'tables' => [ 'table', 't2' => 'table2' ],
-                                       'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
-                                       'conds' => [ 'alias' => 'text' ],
-                                       'options' => [ 'LIMIT' => 1, 'ORDER BY' => 'field' ],
-                                       'join_conds' => [ 't2' => [
-                                               'LEFT JOIN', 'tid = t2.id'
-                                       ] ],
-                               ],
-                               "SELECT tid,field,field2 AS alias,t2.id " .
-                                       "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
-                                       "WHERE alias = 'text' " .
-                                       "ORDER BY field " .
-                                       "LIMIT 1"
-                       ],
-                       [
-                               [
-                                       'tables' => [ 'table', 't2' => 'table2' ],
-                                       'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
-                                       'conds' => [ 'alias' => 'text' ],
-                                       'options' => [ 'LIMIT' => 1, 'GROUP BY' => 'field', 'HAVING' => 'COUNT(*) > 1' ],
-                                       'join_conds' => [ 't2' => [
-                                               'LEFT JOIN', 'tid = t2.id'
-                                       ] ],
-                               ],
-                               "SELECT tid,field,field2 AS alias,t2.id " .
-                                       "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
-                                       "WHERE alias = 'text' " .
-                                       "GROUP BY field HAVING COUNT(*) > 1 " .
-                                       "LIMIT 1"
-                       ],
-                       [
-                               [
-                                       'tables' => [ 'table', 't2' => 'table2' ],
-                                       'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
-                                       'conds' => [ 'alias' => 'text' ],
-                                       'options' => [
-                                               'LIMIT' => 1,
-                                               'GROUP BY' => [ 'field', 'field2' ],
-                                               'HAVING' => [ 'COUNT(*) > 1', 'field' => 1 ]
-                                       ],
-                                       'join_conds' => [ 't2' => [
-                                               'LEFT JOIN', 'tid = t2.id'
-                                       ] ],
-                               ],
-                               "SELECT tid,field,field2 AS alias,t2.id " .
-                                       "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
-                                       "WHERE alias = 'text' " .
-                                       "GROUP BY field,field2 HAVING (COUNT(*) > 1) AND field = '1' " .
-                                       "LIMIT 1"
-                       ],
-                       [
-                               [
-                                       'tables' => [ 'table' ],
-                                       'fields' => [ 'alias' => 'field' ],
-                                       'conds' => [ 'alias' => [ 1, 2, 3, 4 ] ],
-                               ],
-                               "SELECT field AS alias " .
-                                       "FROM table " .
-                                       "WHERE alias IN ('1','2','3','4')"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideUpdate
-        * @covers Database::update
-        */
-       public function testUpdate( $sql, $sqlText ) {
-               $this->database->update(
-                       $sql['table'],
-                       $sql['values'],
-                       $sql['conds'],
-                       __METHOD__,
-                       isset( $sql['options'] ) ? $sql['options'] : []
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideUpdate() {
-               return [
-                       [
-                               [
-                                       'table' => 'table',
-                                       'values' => [ 'field' => 'text', 'field2' => 'text2' ],
-                                       'conds' => [ 'alias' => 'text' ],
-                               ],
-                               "UPDATE table " .
-                                       "SET field = 'text'" .
-                                       ",field2 = 'text2' " .
-                                       "WHERE alias = 'text'"
-                       ],
-                       [
-                               [
-                                       'table' => 'table',
-                                       'values' => [ 'field = other', 'field2' => 'text2' ],
-                                       'conds' => [ 'id' => '1' ],
-                               ],
-                               "UPDATE table " .
-                                       "SET field = other" .
-                                       ",field2 = 'text2' " .
-                                       "WHERE id = '1'"
-                       ],
-                       [
-                               [
-                                       'table' => 'table',
-                                       'values' => [ 'field = other', 'field2' => 'text2' ],
-                                       'conds' => '*',
-                               ],
-                               "UPDATE table " .
-                                       "SET field = other" .
-                                       ",field2 = 'text2'"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideDelete
-        * @covers Database::delete
-        */
-       public function testDelete( $sql, $sqlText ) {
-               $this->database->delete(
-                       $sql['table'],
-                       $sql['conds'],
-                       __METHOD__
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideDelete() {
-               return [
-                       [
-                               [
-                                       'table' => 'table',
-                                       'conds' => [ 'alias' => 'text' ],
-                               ],
-                               "DELETE FROM table " .
-                                       "WHERE alias = 'text'"
-                       ],
-                       [
-                               [
-                                       'table' => 'table',
-                                       'conds' => '*',
-                               ],
-                               "DELETE FROM table"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideUpsert
-        * @covers Database::upsert
-        */
-       public function testUpsert( $sql, $sqlText ) {
-               $this->database->upsert(
-                       $sql['table'],
-                       $sql['rows'],
-                       $sql['uniqueIndexes'],
-                       $sql['set'],
-                       __METHOD__
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideUpsert() {
-               return [
-                       [
-                               [
-                                       'table' => 'upsert_table',
-                                       'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
-                                       'uniqueIndexes' => [ 'field' ],
-                                       'set' => [ 'field' => 'set' ],
-                               ],
-                               "BEGIN; " .
-                                       "UPDATE upsert_table " .
-                                       "SET field = 'set' " .
-                                       "WHERE ((field = 'text')); " .
-                                       "INSERT IGNORE INTO upsert_table " .
-                                       "(field,field2) " .
-                                       "VALUES ('text','text2'); " .
-                                       "COMMIT"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideDeleteJoin
-        * @covers Database::deleteJoin
-        */
-       public function testDeleteJoin( $sql, $sqlText ) {
-               $this->database->deleteJoin(
-                       $sql['delTable'],
-                       $sql['joinTable'],
-                       $sql['delVar'],
-                       $sql['joinVar'],
-                       $sql['conds'],
-                       __METHOD__
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideDeleteJoin() {
-               return [
-                       [
-                               [
-                                       'delTable' => 'table',
-                                       'joinTable' => 'table_join',
-                                       'delVar' => 'field',
-                                       'joinVar' => 'field_join',
-                                       'conds' => [ 'alias' => 'text' ],
-                               ],
-                               "DELETE FROM table " .
-                                       "WHERE field IN (" .
-                                       "SELECT field_join FROM table_join WHERE alias = 'text'" .
-                                       ")"
-                       ],
-                       [
-                               [
-                                       'delTable' => 'table',
-                                       'joinTable' => 'table_join',
-                                       'delVar' => 'field',
-                                       'joinVar' => 'field_join',
-                                       'conds' => '*',
-                               ],
-                               "DELETE FROM table " .
-                                       "WHERE field IN (" .
-                                       "SELECT field_join FROM table_join " .
-                                       ")"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideInsert
-        * @covers Database::insert
-        */
-       public function testInsert( $sql, $sqlText ) {
-               $this->database->insert(
-                       $sql['table'],
-                       $sql['rows'],
-                       __METHOD__,
-                       isset( $sql['options'] ) ? $sql['options'] : []
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideInsert() {
-               return [
-                       [
-                               [
-                                       'table' => 'table',
-                                       'rows' => [ 'field' => 'text', 'field2' => 2 ],
-                               ],
-                               "INSERT INTO table " .
-                                       "(field,field2) " .
-                                       "VALUES ('text','2')"
-                       ],
-                       [
-                               [
-                                       'table' => 'table',
-                                       'rows' => [ 'field' => 'text', 'field2' => 2 ],
-                                       'options' => 'IGNORE',
-                               ],
-                               "INSERT IGNORE INTO table " .
-                                       "(field,field2) " .
-                                       "VALUES ('text','2')"
-                       ],
-                       [
-                               [
-                                       'table' => 'table',
-                                       'rows' => [
-                                               [ 'field' => 'text', 'field2' => 2 ],
-                                               [ 'field' => 'multi', 'field2' => 3 ],
-                                       ],
-                                       'options' => 'IGNORE',
-                               ],
-                               "INSERT IGNORE INTO table " .
-                                       "(field,field2) " .
-                                       "VALUES " .
-                                       "('text','2')," .
-                                       "('multi','3')"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideInsertSelect
-        * @covers Database::insertSelect
-        */
-       public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
-               $this->database->insertSelect(
-                       $sql['destTable'],
-                       $sql['srcTable'],
-                       $sql['varMap'],
-                       $sql['conds'],
-                       __METHOD__,
-                       isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
-                       isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : [],
-                       isset( $sql['selectJoinConds'] ) ? $sql['selectJoinConds'] : []
-               );
-               $this->assertLastSql( $sqlTextNative );
-
-               $dbWeb = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => false ] );
-               $dbWeb->forceNextResult( [
-                       array_flip( array_keys( $sql['varMap'] ) )
-               ] );
-               $dbWeb->insertSelect(
-                       $sql['destTable'],
-                       $sql['srcTable'],
-                       $sql['varMap'],
-                       $sql['conds'],
-                       __METHOD__,
-                       isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
-                       isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : [],
-                       isset( $sql['selectJoinConds'] ) ? $sql['selectJoinConds'] : []
-               );
-               $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, $sqlInsert ] ), $dbWeb );
-       }
-
-       public static function provideInsertSelect() {
-               return [
-                       [
-                               [
-                                       'destTable' => 'insert_table',
-                                       'srcTable' => 'select_table',
-                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
-                                       'conds' => '*',
-                               ],
-                               "INSERT INTO insert_table " .
-                                       "(field_insert,field) " .
-                                       "SELECT field_select,field2 " .
-                                       "FROM select_table WHERE *",
-                               "SELECT field_select AS field_insert,field2 AS field " .
-                               "FROM select_table WHERE *   FOR UPDATE",
-                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
-                       ],
-                       [
-                               [
-                                       'destTable' => 'insert_table',
-                                       'srcTable' => 'select_table',
-                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
-                                       'conds' => [ 'field' => 2 ],
-                               ],
-                               "INSERT INTO insert_table " .
-                                       "(field_insert,field) " .
-                                       "SELECT field_select,field2 " .
-                                       "FROM select_table " .
-                                       "WHERE field = '2'",
-                               "SELECT field_select AS field_insert,field2 AS field FROM " .
-                               "select_table WHERE field = '2'   FOR UPDATE",
-                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
-                       ],
-                       [
-                               [
-                                       'destTable' => 'insert_table',
-                                       'srcTable' => 'select_table',
-                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
-                                       'conds' => [ 'field' => 2 ],
-                                       'insertOptions' => 'IGNORE',
-                                       'selectOptions' => [ 'ORDER BY' => 'field' ],
-                               ],
-                               "INSERT IGNORE INTO insert_table " .
-                                       "(field_insert,field) " .
-                                       "SELECT field_select,field2 " .
-                                       "FROM select_table " .
-                                       "WHERE field = '2' " .
-                                       "ORDER BY field",
-                               "SELECT field_select AS field_insert,field2 AS field " .
-                               "FROM select_table WHERE field = '2' ORDER BY field  FOR UPDATE",
-                               "INSERT IGNORE INTO insert_table (field_insert,field) VALUES ('0','1')"
-                       ],
-                       [
-                               [
-                                       'destTable' => 'insert_table',
-                                       'srcTable' => [ 'select_table1', 'select_table2' ],
-                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
-                                       'conds' => [ 'field' => 2 ],
-                                       'selectOptions' => [ 'ORDER BY' => 'field', 'FORCE INDEX' => [ 'select_table1' => 'index1' ] ],
-                                       'selectJoinConds' => [
-                                               'select_table2' => [ 'LEFT JOIN', [ 'select_table1.foo = select_table2.bar' ] ],
-                                       ],
-                               ],
-                               "INSERT INTO insert_table " .
-                                       "(field_insert,field) " .
-                                       "SELECT field_select,field2 " .
-                                       "FROM select_table1 LEFT JOIN select_table2 ON ((select_table1.foo = select_table2.bar)) " .
-                                       "WHERE field = '2' " .
-                                       "ORDER BY field",
-                               "SELECT field_select AS field_insert,field2 AS field " .
-                               "FROM select_table1 LEFT JOIN select_table2 ON ((select_table1.foo = select_table2.bar)) " .
-                               "WHERE field = '2' ORDER BY field  FOR UPDATE",
-                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideReplace
-        * @covers Database::replace
-        */
-       public function testReplace( $sql, $sqlText ) {
-               $this->database->replace(
-                       $sql['table'],
-                       $sql['uniqueIndexes'],
-                       $sql['rows'],
-                       __METHOD__
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideReplace() {
-               return [
-                       [
-                               [
-                                       'table' => 'replace_table',
-                                       'uniqueIndexes' => [ 'field' ],
-                                       'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
-                               ],
-                               "DELETE FROM replace_table " .
-                                       "WHERE ( field='text' ); " .
-                                       "INSERT INTO replace_table " .
-                                       "(field,field2) " .
-                                       "VALUES ('text','text2')"
-                       ],
-                       [
-                               [
-                                       'table' => 'module_deps',
-                                       'uniqueIndexes' => [ [ 'md_module', 'md_skin' ] ],
-                                       'rows' => [
-                                               'md_module' => 'module',
-                                               'md_skin' => 'skin',
-                                               'md_deps' => 'deps',
-                                       ],
-                               ],
-                               "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module' AND md_skin='skin' ); " .
-                                       "INSERT INTO module_deps " .
-                                       "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module','skin','deps')"
-                       ],
-                       [
-                               [
-                                       'table' => 'module_deps',
-                                       'uniqueIndexes' => [ [ 'md_module', 'md_skin' ] ],
-                                       'rows' => [
-                                               [
-                                                       'md_module' => 'module',
-                                                       'md_skin' => 'skin',
-                                                       'md_deps' => 'deps',
-                                               ], [
-                                                       'md_module' => 'module2',
-                                                       'md_skin' => 'skin2',
-                                                       'md_deps' => 'deps2',
-                                               ],
-                                       ],
-                               ],
-                               "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module' AND md_skin='skin' ); " .
-                                       "INSERT INTO module_deps " .
-                                       "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module','skin','deps'); " .
-                                       "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module2' AND md_skin='skin2' ); " .
-                                       "INSERT INTO module_deps " .
-                                       "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module2','skin2','deps2')"
-                       ],
-                       [
-                               [
-                                       'table' => 'module_deps',
-                                       'uniqueIndexes' => [ 'md_module', 'md_skin' ],
-                                       'rows' => [
-                                               [
-                                                       'md_module' => 'module',
-                                                       'md_skin' => 'skin',
-                                                       'md_deps' => 'deps',
-                                               ], [
-                                                       'md_module' => 'module2',
-                                                       'md_skin' => 'skin2',
-                                                       'md_deps' => 'deps2',
-                                               ],
-                                       ],
-                               ],
-                               "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module' ) OR ( md_skin='skin' ); " .
-                                       "INSERT INTO module_deps " .
-                                       "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module','skin','deps'); " .
-                                       "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module2' ) OR ( md_skin='skin2' ); " .
-                                       "INSERT INTO module_deps " .
-                                       "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module2','skin2','deps2')"
-                       ],
-                       [
-                               [
-                                       'table' => 'module_deps',
-                                       'uniqueIndexes' => [],
-                                       'rows' => [
-                                               'md_module' => 'module',
-                                               'md_skin' => 'skin',
-                                               'md_deps' => 'deps',
-                                       ],
-                               ],
-                               "INSERT INTO module_deps " .
-                                       "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module','skin','deps')"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideNativeReplace
-        * @covers Database::nativeReplace
-        */
-       public function testNativeReplace( $sql, $sqlText ) {
-               $this->database->nativeReplace(
-                       $sql['table'],
-                       $sql['rows'],
-                       __METHOD__
-               );
-               $this->assertLastSql( $sqlText );
-       }
-
-       public static function provideNativeReplace() {
-               return [
-                       [
-                               [
-                                       'table' => 'replace_table',
-                                       'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
-                               ],
-                               "REPLACE INTO replace_table " .
-                                       "(field,field2) " .
-                                       "VALUES ('text','text2')"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideConditional
-        * @covers Database::conditional
-        */
-       public function testConditional( $sql, $sqlText ) {
-               $this->assertEquals( trim( $this->database->conditional(
-                       $sql['conds'],
-                       $sql['true'],
-                       $sql['false']
-               ) ), $sqlText );
-       }
-
-       public static function provideConditional() {
-               return [
-                       [
-                               [
-                                       'conds' => [ 'field' => 'text' ],
-                                       'true' => 1,
-                                       'false' => 'NULL',
-                               ],
-                               "(CASE WHEN field = 'text' THEN 1 ELSE NULL END)"
-                       ],
-                       [
-                               [
-                                       'conds' => [ 'field' => 'text', 'field2' => 'anothertext' ],
-                                       'true' => 1,
-                                       'false' => 'NULL',
-                               ],
-                               "(CASE WHEN field = 'text' AND field2 = 'anothertext' THEN 1 ELSE NULL END)"
-                       ],
-                       [
-                               [
-                                       'conds' => 'field=1',
-                                       'true' => 1,
-                                       'false' => 'NULL',
-                               ],
-                               "(CASE WHEN field=1 THEN 1 ELSE NULL END)"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideBuildConcat
-        * @covers Database::buildConcat
-        */
-       public function testBuildConcat( $stringList, $sqlText ) {
-               $this->assertEquals( trim( $this->database->buildConcat(
-                       $stringList
-               ) ), $sqlText );
-       }
-
-       public static function provideBuildConcat() {
-               return [
-                       [
-                               [ 'field', 'field2' ],
-                               "CONCAT(field,field2)"
-                       ],
-                       [
-                               [ "'test'", 'field2' ],
-                               "CONCAT('test',field2)"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideBuildLike
-        * @covers Database::buildLike
-        */
-       public function testBuildLike( $array, $sqlText ) {
-               $this->assertEquals( trim( $this->database->buildLike(
-                       $array
-               ) ), $sqlText );
-       }
-
-       public static function provideBuildLike() {
-               return [
-                       [
-                               'text',
-                               "LIKE 'text' ESCAPE '`'"
-                       ],
-                       [
-                               [ 'text', new LikeMatch( '%' ) ],
-                               "LIKE 'text%' ESCAPE '`'"
-                       ],
-                       [
-                               [ 'text', new LikeMatch( '%' ), 'text2' ],
-                               "LIKE 'text%text2' ESCAPE '`'"
-                       ],
-                       [
-                               [ 'text', new LikeMatch( '_' ) ],
-                               "LIKE 'text_' ESCAPE '`'"
-                       ],
-                       [
-                               'more_text',
-                               "LIKE 'more`_text' ESCAPE '`'"
-                       ],
-                       [
-                               [ 'C:\\Windows\\', new LikeMatch( '%' ) ],
-                               "LIKE 'C:\\Windows\\%' ESCAPE '`'"
-                       ],
-                       [
-                               [ 'accent`_test`', new LikeMatch( '%' ) ],
-                               "LIKE 'accent```_test``%' ESCAPE '`'"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideUnionQueries
-        * @covers Database::unionQueries
-        */
-       public function testUnionQueries( $sql, $sqlText ) {
-               $this->assertEquals( trim( $this->database->unionQueries(
-                       $sql['sqls'],
-                       $sql['all']
-               ) ), $sqlText );
-       }
-
-       public static function provideUnionQueries() {
-               return [
-                       [
-                               [
-                                       'sqls' => [ 'RAW SQL', 'RAW2SQL' ],
-                                       'all' => true,
-                               ],
-                               "(RAW SQL) UNION ALL (RAW2SQL)"
-                       ],
-                       [
-                               [
-                                       'sqls' => [ 'RAW SQL', 'RAW2SQL' ],
-                                       'all' => false,
-                               ],
-                               "(RAW SQL) UNION (RAW2SQL)"
-                       ],
-                       [
-                               [
-                                       'sqls' => [ 'RAW SQL', 'RAW2SQL', 'RAW3SQL' ],
-                                       'all' => false,
-                               ],
-                               "(RAW SQL) UNION (RAW2SQL) UNION (RAW3SQL)"
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideUnionConditionPermutations
-        * @covers Database::unionConditionPermutations
-        */
-       public function testUnionConditionPermutations( $params, $expect ) {
-               if ( isset( $params['unionSupportsOrderAndLimit'] ) ) {
-                       $this->database->setUnionSupportsOrderAndLimit( $params['unionSupportsOrderAndLimit'] );
-               }
-
-               $sql = trim( $this->database->unionConditionPermutations(
-                       $params['table'],
-                       $params['vars'],
-                       $params['permute_conds'],
-                       isset( $params['extra_conds'] ) ? $params['extra_conds'] : '',
-                       'FNAME',
-                       isset( $params['options'] ) ? $params['options'] : [],
-                       isset( $params['join_conds'] ) ? $params['join_conds'] : []
-               ) );
-               $this->assertEquals( $expect, $sql );
-       }
-
-       public static function provideUnionConditionPermutations() {
-               return [
-                       // @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
-                       [
-                               [
-                                       'table' => [ 'table1', 'table2' ],
-                                       'vars' => [ 'field1', 'alias' => 'field2' ],
-                                       'permute_conds' => [
-                                               'field3' => [ 1, 2, 3 ],
-                                               'duplicates' => [ 4, 5, 4 ],
-                                               'empty' => [],
-                                               'single' => [ 0 ],
-                                       ],
-                                       'extra_conds' => 'table2.bar > 23',
-                                       'options' => [
-                                               'ORDER BY' => [ 'field1', 'alias' ],
-                                               'INNER ORDER BY' => [ 'field1', 'field2' ],
-                                               'LIMIT' => 100,
-                                       ],
-                                       'join_conds' => [
-                                               'table2' => [ 'JOIN', 'table1.foo_id = table2.foo_id' ],
-                                       ],
-                               ],
-                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '1' AND duplicates = '4' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
-                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '1' AND duplicates = '5' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
-                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '2' AND duplicates = '4' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
-                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '2' AND duplicates = '5' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
-                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '3' AND duplicates = '4' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
-                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '3' AND duplicates = '5' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) " .
-                               "ORDER BY field1,alias LIMIT 100"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [
-                                               'bar' => [ 1, 2, 3 ],
-                                       ],
-                                       'extra_conds' => [ 'baz' => null ],
-                                       'options' => [
-                                               'NOTALL',
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                       ],
-                               ],
-                               "(SELECT  foo_id  FROM foo    WHERE bar = '1' AND baz IS NULL  ORDER BY foo_id LIMIT 25  ) UNION " .
-                               "(SELECT  foo_id  FROM foo    WHERE bar = '2' AND baz IS NULL  ORDER BY foo_id LIMIT 25  ) UNION " .
-                               "(SELECT  foo_id  FROM foo    WHERE bar = '3' AND baz IS NULL  ORDER BY foo_id LIMIT 25  ) " .
-                               "ORDER BY foo_id LIMIT 25"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [
-                                               'bar' => [ 1, 2, 3 ],
-                                       ],
-                                       'extra_conds' => [ 'baz' => null ],
-                                       'options' => [
-                                               'NOTALL' => true,
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                       ],
-                                       'unionSupportsOrderAndLimit' => false,
-                               ],
-                               "(SELECT  foo_id  FROM foo    WHERE bar = '1' AND baz IS NULL  ) UNION " .
-                               "(SELECT  foo_id  FROM foo    WHERE bar = '2' AND baz IS NULL  ) UNION " .
-                               "(SELECT  foo_id  FROM foo    WHERE bar = '3' AND baz IS NULL  ) " .
-                               "ORDER BY foo_id LIMIT 25"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [],
-                                       'extra_conds' => [ 'baz' => null ],
-                                       'options' => [
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                       ],
-                               ],
-                               "SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY foo_id LIMIT 25"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [
-                                               'bar' => [],
-                                       ],
-                                       'extra_conds' => [ 'baz' => null ],
-                                       'options' => [
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                       ],
-                               ],
-                               "SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY foo_id LIMIT 25"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [
-                                               'bar' => [ 1 ],
-                                       ],
-                                       'options' => [
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                               'OFFSET' => 150,
-                                       ],
-                               ],
-                               "SELECT  foo_id  FROM foo    WHERE bar = '1'  ORDER BY foo_id LIMIT 150,25"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [],
-                                       'extra_conds' => [ 'baz' => null ],
-                                       'options' => [
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                               'OFFSET' => 150,
-                                               'INNER ORDER BY' => [ 'bar_id' ],
-                                       ],
-                               ],
-                               "(SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY bar_id LIMIT 175  ) ORDER BY foo_id LIMIT 150,25"
-                       ],
-                       [
-                               [
-                                       'table' => 'foo',
-                                       'vars' => [ 'foo_id' ],
-                                       'permute_conds' => [],
-                                       'extra_conds' => [ 'baz' => null ],
-                                       'options' => [
-                                               'ORDER BY' => [ 'foo_id' ],
-                                               'LIMIT' => 25,
-                                               'OFFSET' => 150,
-                                               'INNER ORDER BY' => [ 'bar_id' ],
-                                       ],
-                                       'unionSupportsOrderAndLimit' => false,
-                               ],
-                               "SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY foo_id LIMIT 150,25"
-                       ],
-                       // @codingStandardsIgnoreEnd
-               ];
-       }
-
-       /**
-        * @covers Database::commit
-        */
-       public function testTransactionCommit() {
-               $this->database->begin( __METHOD__ );
-               $this->database->commit( __METHOD__ );
-               $this->assertLastSql( 'BEGIN; COMMIT' );
-       }
-
-       /**
-        * @covers Database::rollback
-        */
-       public function testTransactionRollback() {
-               $this->database->begin( __METHOD__ );
-               $this->database->rollback( __METHOD__ );
-               $this->assertLastSql( 'BEGIN; ROLLBACK' );
-       }
-
-       /**
-        * @covers Database::dropTable
-        */
-       public function testDropTable() {
-               $this->database->setExistingTables( [ 'table' ] );
-               $this->database->dropTable( 'table', __METHOD__ );
-               $this->assertLastSql( 'DROP TABLE table CASCADE' );
-       }
-
-       /**
-        * @covers Database::dropTable
-        */
-       public function testDropNonExistingTable() {
-               $this->assertFalse(
-                       $this->database->dropTable( 'non_existing', __METHOD__ )
-               );
-       }
-
-       /**
-        * @dataProvider provideMakeList
-        * @covers Database::makeList
-        */
-       public function testMakeList( $list, $mode, $sqlText ) {
-               $this->assertEquals( trim( $this->database->makeList(
-                       $list, $mode
-               ) ), $sqlText );
-       }
-
-       public static function provideMakeList() {
-               return [
-                       [
-                               [ 'value', 'value2' ],
-                               LIST_COMMA,
-                               "'value','value2'"
-                       ],
-                       [
-                               [ 'field', 'field2' ],
-                               LIST_NAMES,
-                               "field,field2"
-                       ],
-                       [
-                               [ 'field' => 'value', 'field2' => 'value2' ],
-                               LIST_AND,
-                               "field = 'value' AND field2 = 'value2'"
-                       ],
-                       [
-                               [ 'field' => null, "field2 != 'value2'" ],
-                               LIST_AND,
-                               "field IS NULL AND (field2 != 'value2')"
-                       ],
-                       [
-                               [ 'field' => [ 'value', null, 'value2' ], 'field2' => 'value2' ],
-                               LIST_AND,
-                               "(field IN ('value','value2')  OR field IS NULL) AND field2 = 'value2'"
-                       ],
-                       [
-                               [ 'field' => [ null ], 'field2' => null ],
-                               LIST_AND,
-                               "field IS NULL AND field2 IS NULL"
-                       ],
-                       [
-                               [ 'field' => 'value', 'field2' => 'value2' ],
-                               LIST_OR,
-                               "field = 'value' OR field2 = 'value2'"
-                       ],
-                       [
-                               [ 'field' => 'value', 'field2' => null ],
-                               LIST_OR,
-                               "field = 'value' OR field2 IS NULL"
-                       ],
-                       [
-                               [ 'field' => [ 'value', 'value2' ], 'field2' => [ 'value' ] ],
-                               LIST_OR,
-                               "field IN ('value','value2')  OR field2 = 'value'"
-                       ],
-                       [
-                               [ 'field' => [ null, 'value', null, 'value2' ], "field2 != 'value2'" ],
-                               LIST_OR,
-                               "(field IN ('value','value2')  OR field IS NULL) OR (field2 != 'value2')"
-                       ],
-                       [
-                               [ 'field' => 'value', 'field2' => 'value2' ],
-                               LIST_SET,
-                               "field = 'value',field2 = 'value2'"
-                       ],
-                       [
-                               [ 'field' => 'value', 'field2' => null ],
-                               LIST_SET,
-                               "field = 'value',field2 = NULL"
-                       ],
-                       [
-                               [ 'field' => 'value', "field2 != 'value2'" ],
-                               LIST_SET,
-                               "field = 'value',field2 != 'value2'"
-                       ],
-               ];
-       }
-
-       public function testSessionTempTables() {
-               $temp1 = $this->database->tableName( 'tmp_table_1' );
-               $temp2 = $this->database->tableName( 'tmp_table_2' );
-               $temp3 = $this->database->tableName( 'tmp_table_3' );
-
-               $this->database->query( "CREATE TEMPORARY TABLE $temp1 LIKE orig_tbl", __METHOD__ );
-               $this->database->query( "CREATE TEMPORARY TABLE $temp2 LIKE orig_tbl", __METHOD__ );
-               $this->database->query( "CREATE TEMPORARY TABLE $temp3 LIKE orig_tbl", __METHOD__ );
-
-               $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
-               $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
-               $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
-
-               $this->database->dropTable( 'tmp_table_1', __METHOD__ );
-               $this->database->dropTable( 'tmp_table_2', __METHOD__ );
-               $this->database->dropTable( 'tmp_table_3', __METHOD__ );
-
-               $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
-               $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
-               $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
-
-               $this->database->query( "CREATE TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ );
-               $this->database->query( "CREATE TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ );
-               $this->database->query( "CREATE TEMPORARY TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ );
-
-               $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
-               $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
-               $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
-
-               $this->database->query( "DROP TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ );
-               $this->database->query( "DROP TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ );
-               $this->database->query( "DROP TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ );
-
-               $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
-               $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
-               $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
-       }
-}
index b90b1ad..ae61070 100644 (file)
@@ -87,6 +87,10 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                                new Blob( "hello" ),
                                "x'68656c6c6f'",
                        ],
+                       [ // #5: null
+                               null,
+                               "''",
+                       ],
                ];
        }
 
diff --git a/tests/phpunit/includes/db/DatabaseTest.php b/tests/phpunit/includes/db/DatabaseTest.php
deleted file mode 100644 (file)
index 45791e2..0000000
+++ /dev/null
@@ -1,415 +0,0 @@
-<?php
-
-use Wikimedia\Rdbms\IDatabase;
-
-/**
- * @group Database
- * @group Database
- */
-class DatabaseTest extends MediaWikiTestCase {
-       /**
-        * @var Database
-        */
-       protected $db;
-
-       private $functionTest = false;
-
-       protected function setUp() {
-               parent::setUp();
-               $this->db = wfGetDB( DB_MASTER );
-       }
-
-       protected function tearDown() {
-               parent::tearDown();
-               if ( $this->functionTest ) {
-                       $this->dropFunctions();
-                       $this->functionTest = false;
-               }
-               $this->db->restoreFlags( IDatabase::RESTORE_INITIAL );
-       }
-
-       /**
-        * @covers Database::dropTable
-        */
-       public function testAddQuotesNull() {
-               $check = "NULL";
-               if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) {
-                       $check = "''";
-               }
-               $this->assertEquals( $check, $this->db->addQuotes( null ) );
-       }
-
-       public function testAddQuotesInt() {
-               # returning just "1234" should be ok too, though...
-               # maybe
-               $this->assertEquals(
-                       "'1234'",
-                       $this->db->addQuotes( 1234 ) );
-       }
-
-       public function testAddQuotesFloat() {
-               # returning just "1234.5678" would be ok too, though
-               $this->assertEquals(
-                       "'1234.5678'",
-                       $this->db->addQuotes( 1234.5678 ) );
-       }
-
-       public function testAddQuotesString() {
-               $this->assertEquals(
-                       "'string'",
-                       $this->db->addQuotes( 'string' ) );
-       }
-
-       public function testAddQuotesStringQuote() {
-               $check = "'string''s cause trouble'";
-               if ( $this->db->getType() === 'mysql' ) {
-                       $check = "'string\'s cause trouble'";
-               }
-               $this->assertEquals(
-                       $check,
-                       $this->db->addQuotes( "string's cause trouble" ) );
-       }
-
-       private function getSharedTableName( $table, $database, $prefix, $format = 'quoted' ) {
-               global $wgSharedDB, $wgSharedTables, $wgSharedPrefix, $wgSharedSchema;
-
-               $this->db->setTableAliases( [
-                       $table => [
-                               'dbname' => $database,
-                               'schema' => null,
-                               'prefix' => $prefix
-                       ]
-               ] );
-
-               $ret = $this->db->tableName( $table, $format );
-
-               $this->db->setTableAliases( array_fill_keys(
-                       $wgSharedDB ? $wgSharedTables : [],
-                       [
-                               'dbname' => $wgSharedDB,
-                               'schema' => $wgSharedSchema,
-                               'prefix' => $wgSharedPrefix
-                       ]
-               ) );
-
-               return $ret;
-       }
-
-       private function prefixAndQuote( $table, $database = null, $prefix = null, $format = 'quoted' ) {
-               if ( $this->db->getType() === 'sqlite' || $format !== 'quoted' ) {
-                       $quote = '';
-               } elseif ( $this->db->getType() === 'mysql' ) {
-                       $quote = '`';
-               } elseif ( $this->db->getType() === 'oracle' ) {
-                       $quote = '/*Q*/';
-               } else {
-                       $quote = '"';
-               }
-
-               if ( $database !== null ) {
-                       if ( $this->db->getType() === 'oracle' ) {
-                               $database = $quote . $database . '.';
-                       } else {
-                               $database = $quote . $database . $quote . '.';
-                       }
-               }
-
-               if ( $prefix === null ) {
-                       $prefix = $this->dbPrefix();
-               }
-
-               if ( $this->db->getType() === 'oracle' ) {
-                       return strtoupper( $database . $quote . $prefix . $table );
-               } else {
-                       return $database . $quote . $prefix . $table . $quote;
-               }
-       }
-
-       public function testTableNameLocal() {
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename' ),
-                       $this->db->tableName( 'tablename' )
-               );
-       }
-
-       public function testTableNameRawLocal() {
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', null, null, 'raw' ),
-                       $this->db->tableName( 'tablename', 'raw' )
-               );
-       }
-
-       public function testTableNameShared() {
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_' ),
-                       $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_' )
-               );
-
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', 'sharedatabase', null ),
-                       $this->getSharedTableName( 'tablename', 'sharedatabase', null )
-               );
-       }
-
-       public function testTableNameRawShared() {
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_', 'raw' ),
-                       $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_', 'raw' )
-               );
-
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', 'sharedatabase', null, 'raw' ),
-                       $this->getSharedTableName( 'tablename', 'sharedatabase', null, 'raw' )
-               );
-       }
-
-       public function testTableNameForeign() {
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', 'databasename', '' ),
-                       $this->db->tableName( 'databasename.tablename' )
-               );
-       }
-
-       public function testTableNameRawForeign() {
-               $this->assertEquals(
-                       $this->prefixAndQuote( 'tablename', 'databasename', '', 'raw' ),
-                       $this->db->tableName( 'databasename.tablename', 'raw' )
-               );
-       }
-
-       public function testStoredFunctions() {
-               if ( !in_array( wfGetDB( DB_MASTER )->getType(), [ 'mysql', 'postgres' ] ) ) {
-                       $this->markTestSkipped( 'MySQL or Postgres required' );
-               }
-               global $IP;
-               $this->dropFunctions();
-               $this->functionTest = true;
-               $this->assertTrue(
-                       $this->db->sourceFile( "$IP/tests/phpunit/data/db/{$this->db->getType()}/functions.sql" )
-               );
-               $res = $this->db->query( 'SELECT mw_test_function() AS test', __METHOD__ );
-               $this->assertEquals( 42, $res->fetchObject()->test );
-       }
-
-       private function dropFunctions() {
-               $this->db->query( 'DROP FUNCTION IF EXISTS mw_test_function'
-                       . ( $this->db->getType() == 'postgres' ? '()' : '' )
-               );
-       }
-
-       public function testUnknownTableCorruptsResults() {
-               $res = $this->db->select( 'page', '*', [ 'page_id' => 1 ] );
-               $this->assertFalse( $this->db->tableExists( 'foobarbaz' ) );
-               $this->assertInternalType( 'int', $res->numRows() );
-       }
-
-       public function testTransactionIdle() {
-               $db = $this->db;
-
-               $db->setFlag( DBO_TRX );
-               $called = false;
-               $flagSet = null;
-               $db->onTransactionIdle(
-                       function () use ( $db, &$flagSet, &$called ) {
-                               $called = true;
-                               $flagSet = $db->getFlag( DBO_TRX );
-                       },
-                       __METHOD__
-               );
-               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
-               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
-               $this->assertTrue( $called, 'Callback reached' );
-
-               $db->clearFlag( DBO_TRX );
-               $flagSet = null;
-               $db->onTransactionIdle(
-                       function () use ( $db, &$flagSet ) {
-                               $flagSet = $db->getFlag( DBO_TRX );
-                       },
-                       __METHOD__
-               );
-               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
-               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
-
-               $db->clearFlag( DBO_TRX );
-               $db->onTransactionIdle(
-                       function () use ( $db ) {
-                               $db->setFlag( DBO_TRX );
-                       },
-                       __METHOD__
-               );
-               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
-       }
-
-       public function testTransactionResolution() {
-               $db = $this->db;
-
-               $db->clearFlag( DBO_TRX );
-               $db->begin( __METHOD__ );
-               $called = false;
-               $db->onTransactionResolution( function () use ( $db, &$called ) {
-                       $called = true;
-                       $db->setFlag( DBO_TRX );
-               } );
-               $db->commit( __METHOD__ );
-               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
-               $this->assertTrue( $called, 'Callback reached' );
-
-               $db->clearFlag( DBO_TRX );
-               $db->begin( __METHOD__ );
-               $called = false;
-               $db->onTransactionResolution( function () use ( $db, &$called ) {
-                       $called = true;
-                       $db->setFlag( DBO_TRX );
-               } );
-               $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
-               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
-               $this->assertTrue( $called, 'Callback reached' );
-       }
-
-       /**
-        * @covers Database::setTransactionListener()
-        */
-       public function testTransactionListener() {
-               $db = $this->db;
-
-               $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
-                       $called = true;
-               } );
-
-               $called = false;
-               $db->begin( __METHOD__ );
-               $db->commit( __METHOD__ );
-               $this->assertTrue( $called, 'Callback reached' );
-
-               $called = false;
-               $db->begin( __METHOD__ );
-               $db->commit( __METHOD__ );
-               $this->assertTrue( $called, 'Callback still reached' );
-
-               $called = false;
-               $db->begin( __METHOD__ );
-               $db->rollback( __METHOD__ );
-               $this->assertTrue( $called, 'Callback reached' );
-
-               $db->setTransactionListener( 'ping', null );
-               $called = false;
-               $db->begin( __METHOD__ );
-               $db->commit( __METHOD__ );
-               $this->assertFalse( $called, 'Callback not reached' );
-       }
-
-       /**
-        * @covers Database::flushSnapshot()
-        */
-       public function testFlushSnapshot() {
-               $db = $this->db;
-
-               $db->flushSnapshot( __METHOD__ ); // ok
-               $db->flushSnapshot( __METHOD__ ); // ok
-
-               $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
-               $db->query( 'SELECT 1', __METHOD__ );
-               $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
-               $db->flushSnapshot( __METHOD__ ); // ok
-               $db->restoreFlags( $db::RESTORE_PRIOR );
-
-               $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
-       }
-
-       public function testGetScopedLock() {
-               $db = $this->db;
-
-               $db->setFlag( DBO_TRX );
-               try {
-                       $this->badLockingMethodImplicit( $db );
-               } catch ( RunTimeException $e ) {
-                       $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
-               }
-               $db->clearFlag( DBO_TRX );
-               $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
-               $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
-
-               try {
-                       $this->badLockingMethodExplicit( $db );
-               } catch ( RunTimeException $e ) {
-                       $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
-               }
-               $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
-               $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
-       }
-
-       private function badLockingMethodImplicit( IDatabase $db ) {
-               $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
-               $db->query( "SELECT 1" ); // trigger DBO_TRX
-               throw new RunTimeException( "Uh oh!" );
-       }
-
-       private function badLockingMethodExplicit( IDatabase $db ) {
-               $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
-               $db->begin( __METHOD__ );
-               throw new RunTimeException( "Uh oh!" );
-       }
-
-       /**
-        * @covers Database::getFlag(
-        * @covers Database::setFlag()
-        * @covers Database::restoreFlags()
-        */
-       public function testFlagSetting() {
-               $db = $this->db;
-               $origTrx = $db->getFlag( DBO_TRX );
-               $origSsl = $db->getFlag( DBO_SSL );
-
-               $origTrx
-                       ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
-                       : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
-               $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
-
-               $origSsl
-                       ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
-                       : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
-               $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
-
-               $db->restoreFlags( $db::RESTORE_INITIAL );
-               $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
-               $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
-
-               $origTrx
-                       ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
-                       : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
-               $origSsl
-                       ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
-                       : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
-
-               $db->restoreFlags();
-               $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
-               $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
-
-               $db->restoreFlags();
-               $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
-               $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
-       }
-
-       /**
-        * @covers Database::tablePrefix()
-        * @covers Database::dbSchema()
-        */
-       public function testMutators() {
-               $old = $this->db->tablePrefix();
-               $this->assertType( 'string', $old, 'Prefix is string' );
-               $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
-               $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
-               $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
-               $this->db->tablePrefix( $old );
-               $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
-
-               $old = $this->db->dbSchema();
-               $this->assertType( 'string', $old, 'Schema is string' );
-               $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
-               $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
-               $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
-               $this->db->dbSchema( $old );
-               $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
-       }
-}
index d0121b1..1a15c26 100644 (file)
@@ -5,6 +5,10 @@
  * @author Timo Tijhof
  */
 
+/**
+ * @group ResourceLoader
+ * @group CSSMin
+ */
 class CSSMinTest extends MediaWikiTestCase {
 
        protected function setUp() {
@@ -233,6 +237,11 @@ class CSSMinTest extends MediaWikiTestCase {
                                [ 'foo { prop: url(/w/skin/images/bar.png); }', false, 'http://example.org/quux', false ],
                                'foo { prop: url(http://doc.example.org/w/skin/images/bar.png); }',
                        ],
+                       [
+                               "Don't barf at behavior: url(#default#behaviorName) - T162973",
+                               [ 'foo { behavior: url(#default#bar); }', false, '/w/', false ],
+                               'foo { behavior: url("#default#bar"); }',
+                       ],
                ];
        }
 
index 775709f..4a9f6cc 100644 (file)
@@ -81,22 +81,26 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
         */
        public function testSetDelayed() {
                $key = wfRandomString();
-               $value = wfRandomString();
+               $value = (object)[ 'v' => wfRandomString() ];
+               $expectValue = clone $value;
 
                // XXX: DeferredUpdates bound to transactions in CLI mode
                $dbw = wfGetDB( DB_MASTER );
                $dbw->begin();
                $this->cache->set( $key, $value );
 
+               // Test that later changes to $value don't affect the saved value (e.g. T168040)
+               $value->v = 'bogus';
+
                // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               $this->assertEquals( $expectValue, $this->cache1->get( $key ), 'Written to tier 1' );
                // Not yet set in tier 2
                $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
 
                $dbw->commit();
 
                // Set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+               $this->assertEquals( $expectValue, $this->cache2->get( $key ), 'Written to tier 2' );
        }
 
        /**
index 3dc7e28..a8dbdd3 100644 (file)
@@ -8,16 +8,20 @@ use Wikimedia\Rdbms\DatabaseDomain;
 class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
        public static function provideConstruct() {
                return [
-                       // All strings
-                       [ 'foo', 'bar', 'baz', 'foo-bar-baz' ],
-                       // Nothing
-                       [ null, null, '', '' ],
-                       // Invalid $database
-                       [ 0, 'bar', '', '', true ],
-                       // - in one of the fields
-                       [ 'foo-bar', 'baz', 'baa', 'foo?hbar-baz-baa' ],
-                       // ? in one of the fields
-                       [ 'foo?bar', 'baz', 'baa', 'foo??bar-baz-baa' ],
+                       'All strings' =>
+                               [ 'foo', 'bar', 'baz', 'foo-bar-baz' ],
+                       'Nothing' =>
+                               [ null, null, '', '' ],
+                       'Invalid $database' =>
+                               [ 0, 'bar', '', '', true ],
+                       'Invalid $schema' =>
+                               [ 'foo', 0, '', '', true ],
+                       'Invalid $prefix' =>
+                               [ 'foo', 'bar', 0, '', true ],
+                       'Dash' =>
+                               [ 'foo-bar', 'baz', 'baa', 'foo?hbar-baz-baa' ],
+                       'Question mark' =>
+                               [ 'foo?bar', 'baz', 'baa', 'foo??bar-baz-baa' ],
                ];
        }
 
@@ -27,6 +31,8 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
        public function testConstruct( $db, $schema, $prefix, $id, $exception = false ) {
                if ( $exception ) {
                        $this->setExpectedException( InvalidArgumentException::class );
+                       new DatabaseDomain( $db, $schema, $prefix );
+                       return;
                }
 
                $domain = new DatabaseDomain( $db, $schema, $prefix );
@@ -35,23 +41,27 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $schema, $domain->getSchema() );
                $this->assertEquals( $prefix, $domain->getTablePrefix() );
                $this->assertEquals( $id, $domain->getId() );
+               $this->assertEquals( $id, strval( $domain ), 'toString' );
        }
 
        public static function provideNewFromId() {
                return [
-                       // basic
-                       [ 'foo', 'foo', null, '' ],
-                       // <database>-<prefix>
-                       [ 'foo-bar', 'foo', null, 'bar' ],
-                       [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
-                       // ?h -> -
-                       [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
-                       // ?? -> ?
-                       [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
-                       // ? is left alone
-                       [ 'foo?bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
-                       // too many parts
-                       [ 'foo-bar-baz-baa', '', '', '', true ],
+                       'Basic' =>
+                               [ 'foo', 'foo', null, '' ],
+                       'db+prefix' =>
+                               [ 'foo-bar', 'foo', null, 'bar' ],
+                       'db+schema+prefix' =>
+                               [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
+                       '?h -> -' =>
+                               [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
+                       '?? -> ?' =>
+                               [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       '? is left alone' =>
+                               [ 'foo?bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       'too many parts' =>
+                               [ 'foo-bar-baz-baa', '', '', '', true ],
+                       'from instance' =>
+                               [ DatabaseDomain::newUnspecified(), null, null, '' ],
                ];
        }
 
@@ -61,6 +71,8 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
        public function testNewFromId( $id, $db, $schema, $prefix, $exception = false ) {
                if ( $exception ) {
                        $this->setExpectedException( InvalidArgumentException::class );
+                       DatabaseDomain::newFromId( $id );
+                       return;
                }
                $domain = DatabaseDomain::newFromId( $id );
                $this->assertInstanceOf( DatabaseDomain::class, $domain );
@@ -68,4 +80,50 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $schema, $domain->getSchema() );
                $this->assertEquals( $prefix, $domain->getTablePrefix() );
        }
+
+       public static function provideEquals() {
+               return [
+                       'Basic' =>
+                               [ 'foo', 'foo', null, '' ],
+                       'db+prefix' =>
+                               [ 'foo-bar', 'foo', null, 'bar' ],
+                       'db+schema+prefix' =>
+                               [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
+                       '?h -> -' =>
+                               [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
+                       '?? -> ?' =>
+                               [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       'Nothing' =>
+                               [ '', null, null, '' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideEquals
+        * @covers Wikimedia\Rdbms\DatabaseDomain::equals
+        */
+       public function testEquals( $id, $db, $schema, $prefix ) {
+               $fromId = DatabaseDomain::newFromId( $id );
+               $this->assertInstanceOf( DatabaseDomain::class, $fromId );
+
+               $constructed = new DatabaseDomain( $db, $schema, $prefix );
+
+               $this->assertTrue( $constructed->equals( $id ), 'constructed equals string' );
+               $this->assertTrue( $fromId->equals( $id ), 'fromId equals string' );
+
+               $this->assertTrue( $constructed->equals( $fromId ), 'compare constructed to newId' );
+               $this->assertTrue( $fromId->equals( $constructed ), 'compare newId to constructed' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\DatabaseDomain::newUnspecified
+        */
+       public function testNewUnspecified() {
+               $domain = DatabaseDomain::newUnspecified();
+               $this->assertInstanceOf( DatabaseDomain::class, $domain );
+               $this->assertTrue( $domain->equals( '' ) );
+               $this->assertSame( null, $domain->getDatabase() );
+               $this->assertSame( null, $domain->getSchema() );
+               $this->assertSame( '', $domain->getTablePrefix() );
+       }
 }
diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
new file mode 100644 (file)
index 0000000..b564310
--- /dev/null
@@ -0,0 +1,371 @@
+<?php
+/**
+ * Holds tests for DatabaseMysqlBase class.
+ *
+ * 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
+ * @author Antoine Musso
+ * @copyright © 2013 Antoine Musso
+ * @copyright © 2013 Wikimedia Foundation and contributors
+ */
+
+use Wikimedia\Rdbms\TransactionProfiler;
+use Wikimedia\Rdbms\DatabaseDomain;
+use Wikimedia\Rdbms\MySQLMasterPos;
+use Wikimedia\Rdbms\DatabaseMysqlBase;
+
+/**
+ * Fake class around abstract class so we can call concrete methods.
+ */
+class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
+       // From Database
+       function __construct() {
+               $this->profiler = new ProfilerStub( [] );
+               $this->trxProfiler = new TransactionProfiler();
+               $this->cliMode = true;
+               $this->connLogger = new \Psr\Log\NullLogger();
+               $this->queryLogger = new \Psr\Log\NullLogger();
+               $this->errorLogger = function ( Exception $e ) {
+                       wfWarn( get_class( $e ) . ": {$e->getMessage()}" );
+               };
+               $this->currentDomain = DatabaseDomain::newUnspecified();
+       }
+
+       protected function closeConnection() {
+       }
+
+       protected function doQuery( $sql ) {
+       }
+
+       // From DatabaseMysql
+       protected function mysqlConnect( $realServer ) {
+       }
+
+       protected function mysqlSetCharset( $charset ) {
+       }
+
+       protected function mysqlFreeResult( $res ) {
+       }
+
+       protected function mysqlFetchObject( $res ) {
+       }
+
+       protected function mysqlFetchArray( $res ) {
+       }
+
+       protected function mysqlNumRows( $res ) {
+       }
+
+       protected function mysqlNumFields( $res ) {
+       }
+
+       protected function mysqlFieldName( $res, $n ) {
+       }
+
+       protected function mysqlFieldType( $res, $n ) {
+       }
+
+       protected function mysqlDataSeek( $res, $row ) {
+       }
+
+       protected function mysqlError( $conn = null ) {
+       }
+
+       protected function mysqlFetchField( $res, $n ) {
+       }
+
+       protected function mysqlRealEscapeString( $s ) {
+       }
+
+       function insertId() {
+       }
+
+       function lastErrno() {
+       }
+
+       function affectedRows() {
+       }
+
+       function getServerVersion() {
+       }
+}
+
+class DatabaseMysqlBaseTest extends PHPUnit_Framework_TestCase {
+       /**
+        * @dataProvider provideDiapers
+        * @covers Wikimedia\Rdbms\DatabaseMysqlBase::addIdentifierQuotes
+        */
+       public function testAddIdentifierQuotes( $expected, $in ) {
+               $db = new FakeDatabaseMysqlBase();
+               $quoted = $db->addIdentifierQuotes( $in );
+               $this->assertEquals( $expected, $quoted );
+       }
+
+       /**
+        * Feeds testAddIdentifierQuotes
+        *
+        * Named per T22281 convention.
+        */
+       public static function provideDiapers() {
+               return [
+                       // Format: expected, input
+                       [ '``', '' ],
+
+                       // Yeah I really hate loosely typed PHP idiocies nowadays
+                       [ '``', null ],
+
+                       // Dear codereviewer, guess what addIdentifierQuotes()
+                       // will return with thoses:
+                       [ '``', false ],
+                       [ '`1`', true ],
+
+                       // We never know what could happen
+                       [ '`0`', 0 ],
+                       [ '`1`', 1 ],
+
+                       // Whatchout! Should probably use something more meaningful
+                       [ "`'`", "'" ],  # single quote
+                       [ '`"`', '"' ],  # double quote
+                       [ '````', '`' ], # backtick
+                       [ '`’`', '’' ],  # apostrophe (look at your encyclopedia)
+
+                       // sneaky NUL bytes are lurking everywhere
+                       [ '``', "\0" ],
+                       [ '`xyzzy`', "\0x\0y\0z\0z\0y\0" ],
+
+                       // unicode chars
+                       [
+                               self::createUnicodeString( '`\u0001a\uFFFFb`' ),
+                               self::createUnicodeString( '\u0001a\uFFFFb' )
+                       ],
+                       [
+                               self::createUnicodeString( '`\u0001\uFFFF`' ),
+                               self::createUnicodeString( '\u0001\u0000\uFFFF\u0000' )
+                       ],
+                       [ '`☃`', '☃' ],
+                       [ '`メインページ`', 'メインページ' ],
+                       [ '`Басты_бет`', 'Басты_бет' ],
+
+                       // Real world:
+                       [ '`Alix`', 'Alix' ],  # while( ! $recovered ) { sleep(); }
+                       [ '`Backtick: ```', 'Backtick: `' ],
+                       [ '`This is a test`', 'This is a test' ],
+               ];
+       }
+
+       private static function createUnicodeString( $str ) {
+               return json_decode( '"' . $str . '"' );
+       }
+
+       private function getMockForViews() {
+               $db = $this->getMockBuilder( 'DatabaseMysqli' )
+                       ->disableOriginalConstructor()
+                       ->setMethods( [ 'fetchRow', 'query' ] )
+                       ->getMock();
+
+               $db->method( 'query' )
+                       ->with( $this->anything() )
+                       ->willReturn( new FakeResultWrapper( [
+                               (object)[ 'Tables_in_' => 'view1' ],
+                               (object)[ 'Tables_in_' => 'view2' ],
+                               (object)[ 'Tables_in_' => 'myview' ]
+                       ] ) );
+
+               return $db;
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\DatabaseMysqlBase::listViews
+        */
+       public function testListviews() {
+               $db = $this->getMockForViews();
+
+               $this->assertEquals( [ 'view1', 'view2', 'myview' ],
+                       $db->listViews() );
+
+               // Prefix filtering
+               $this->assertEquals( [ 'view1', 'view2' ],
+                       $db->listViews( 'view' ) );
+               $this->assertEquals( [ 'myview' ],
+                       $db->listViews( 'my' ) );
+               $this->assertEquals( [],
+                       $db->listViews( 'UNUSED_PREFIX' ) );
+               $this->assertEquals( [ 'view1', 'view2', 'myview' ],
+                       $db->listViews( '' ) );
+       }
+
+       /**
+        * @dataProvider provideComparePositions
+        * @covers Wikimedia\Rdbms\MySQLMasterPos
+        */
+       public function testHasReached( MySQLMasterPos $lowerPos, MySQLMasterPos $higherPos, $match ) {
+               if ( $match ) {
+                       $this->assertTrue( $lowerPos->channelsMatch( $higherPos ) );
+
+                       $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
+                       $this->assertTrue( $higherPos->hasReached( $higherPos ) );
+                       $this->assertTrue( $lowerPos->hasReached( $lowerPos ) );
+                       $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
+               } else { // channels don't match
+                       $this->assertFalse( $lowerPos->channelsMatch( $higherPos ) );
+
+                       $this->assertFalse( $higherPos->hasReached( $lowerPos ) );
+                       $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
+               }
+       }
+
+       public static function provideComparePositions() {
+               return [
+                       // Binlog style
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000976', '843431247' ),
+                               new MySQLMasterPos( 'db1034-bin.000976', '843431248' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
+                               new MySQLMasterPos( 'db1034-bin.000976', '1000' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
+                               new MySQLMasterPos( 'db1035-bin.000976', '1000' ),
+                               false
+                       ],
+                       // MySQL GTID style
+                       [
+                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:23' ),
+                               new MySQLMasterPos( 'db1-bin.2', '2', '3E11FA47-71CA-11E1-9E33-C80AA9429562:24' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:99' ),
+                               new MySQLMasterPos( 'db1-bin.2', '2', '3E11FA47-71CA-11E1-9E33-C80AA9429562:100' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1-bin.2', '1', '3E11FA47-71CA-11E1-9E33-C80AA9429562:99' ),
+                               new MySQLMasterPos( 'db1-bin.2', '2', '1E11FA47-71CA-11E1-9E33-C80AA9429562:100' ),
+                               false
+                       ],
+                       // MariaDB GTID style
+                       [
+                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-23' ),
+                               new MySQLMasterPos( 'db1-bin.2', '2', '255-11-24' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-99' ),
+                               new MySQLMasterPos( 'db1-bin.2', '2', '255-11-100' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1-bin.2', '1', '255-11-999' ),
+                               new MySQLMasterPos( 'db1-bin.2', '2', '254-11-1000' ),
+                               false
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideChannelPositions
+        * @covers Wikimedia\Rdbms\MySQLMasterPos
+        */
+       public function testChannelsMatch( MySQLMasterPos $pos1, MySQLMasterPos $pos2, $matches ) {
+               $this->assertEquals( $matches, $pos1->channelsMatch( $pos2 ) );
+               $this->assertEquals( $matches, $pos2->channelsMatch( $pos1 ) );
+       }
+
+       public static function provideChannelPositions() {
+               return [
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000876', '44' ),
+                               new MySQLMasterPos( 'db1034-bin.000976', '74' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1052-bin.000976', '999' ),
+                               new MySQLMasterPos( 'db1052-bin.000976', '1000' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
+                               new MySQLMasterPos( 'db1035-bin.000976', '10000' ),
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
+                               new MySQLMasterPos( 'trump2016.000976', '10000' ),
+                               false
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideLagAmounts
+        * @covers Wikimedia\Rdbms\DatabaseMysqlBase::getLag
+        * @covers Wikimedia\Rdbms\DatabaseMysqlBase::getLagFromPtHeartbeat
+        */
+       public function testPtHeartbeat( $lag ) {
+               $db = $this->getMockBuilder( 'DatabaseMysqli' )
+                       ->disableOriginalConstructor()
+                       ->setMethods( [
+                               'getLagDetectionMethod', 'getHeartbeatData', 'getMasterServerInfo' ] )
+                       ->getMock();
+
+               $db->method( 'getLagDetectionMethod' )
+                       ->willReturn( 'pt-heartbeat' );
+
+               $db->method( 'getMasterServerInfo' )
+                       ->willReturn( [ 'serverId' => 172, 'asOf' => time() ] );
+
+               // Fake the current time.
+               list( $nowSecFrac, $nowSec ) = explode( ' ', microtime() );
+               $now = (float)$nowSec + (float)$nowSecFrac;
+               // Fake the heartbeat time.
+               // Work arounds for weak DataTime microseconds support.
+               $ptTime = $now - $lag;
+               $ptSec = (int)$ptTime;
+               $ptSecFrac = ( $ptTime - $ptSec );
+               $ptDateTime = new DateTime( "@$ptSec" );
+               $ptTimeISO = $ptDateTime->format( 'Y-m-d\TH:i:s' );
+               $ptTimeISO .= ltrim( number_format( $ptSecFrac, 6 ), '0' );
+
+               $db->method( 'getHeartbeatData' )
+                       ->with( [ 'server_id' => 172 ] )
+                       ->willReturn( [ $ptTimeISO, $now ] );
+
+               $db->setLBInfo( 'clusterMasterHost', 'db1052' );
+               $lagEst = $db->getLag();
+
+               $this->assertGreaterThan( $lag - .010, $lagEst, "Correct heatbeat lag" );
+               $this->assertLessThan( $lag + .010, $lagEst, "Correct heatbeat lag" );
+       }
+
+       public static function provideLagAmounts() {
+               return [
+                       [ 0 ],
+                       [ 0.3 ],
+                       [ 6.5 ],
+                       [ 10.1 ],
+                       [ 200.2 ],
+                       [ 400.7 ],
+                       [ 600.22 ],
+                       [ 1000.77 ],
+               ];
+       }
+}
diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
new file mode 100644 (file)
index 0000000..f519772
--- /dev/null
@@ -0,0 +1,1131 @@
+<?php
+
+use Wikimedia\Rdbms\LikeMatch;
+
+/**
+ * Test the parts of the Database abstract class that deal
+ * with creating SQL text.
+ */
+class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
+       /** @var DatabaseTestHelper */
+       private $database;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->database = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => true ] );
+       }
+
+       protected function assertLastSql( $sqlText ) {
+               $this->assertEquals(
+                       $sqlText,
+                       $this->database->getLastSqls()
+               );
+       }
+
+       protected function assertLastSqlDb( $sqlText, DatabaseTestHelper $db ) {
+               $this->assertEquals( $sqlText, $db->getLastSqls() );
+       }
+
+       /**
+        * @dataProvider provideSelect
+        * @covers Wikimedia\Rdbms\Database::select
+        * @covers Wikimedia\Rdbms\Database::selectSQLText
+        * @covers Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN
+        * @covers Wikimedia\Rdbms\Database::makeSelectOptions
+        * @covers Wikimedia\Rdbms\Database::makeOrderBy
+        * @covers Wikimedia\Rdbms\Database::makeGroupByWithHaving
+        */
+       public function testSelect( $sql, $sqlText ) {
+               $this->database->select(
+                       $sql['tables'],
+                       $sql['fields'],
+                       isset( $sql['conds'] ) ? $sql['conds'] : [],
+                       __METHOD__,
+                       isset( $sql['options'] ) ? $sql['options'] : [],
+                       isset( $sql['join_conds'] ) ? $sql['join_conds'] : []
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideSelect() {
+               return [
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field', 'alias' => 'field2' ],
+                                       'conds' => [ 'alias' => 'text' ],
+                               ],
+                               "SELECT field,field2 AS alias " .
+                                       "FROM table " .
+                                       "WHERE alias = 'text'"
+                       ],
+                       [
+                               [
+                                       // 'tables' with space prepended indicates pre-escaped table name
+                                       'tables' => ' table LEFT JOIN table2',
+                                       'fields' => [ 'field' ],
+                                       'conds' => [ 'field' => 'text' ],
+                               ],
+                               "SELECT field FROM  table LEFT JOIN table2 WHERE field = 'text'"
+                       ],
+                       [
+                               [
+                                       // Empty 'tables' is allowed
+                                       'tables' => '',
+                                       'fields' => [ 'SPECIAL_QUERY()' ],
+                               ],
+                               "SELECT SPECIAL_QUERY()"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field', 'alias' => 'field2' ],
+                                       'conds' => [ 'alias' => 'text' ],
+                                       'options' => [ 'LIMIT' => 1, 'ORDER BY' => 'field' ],
+                               ],
+                               "SELECT field,field2 AS alias " .
+                                       "FROM table " .
+                                       "WHERE alias = 'text' " .
+                                       "ORDER BY field " .
+                                       "LIMIT 1"
+                       ],
+                       [
+                               [
+                                       'tables' => [ 'table', 't2' => 'table2' ],
+                                       'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
+                                       'conds' => [ 'alias' => 'text' ],
+                                       'options' => [ 'LIMIT' => 1, 'ORDER BY' => 'field' ],
+                                       'join_conds' => [ 't2' => [
+                                               'LEFT JOIN', 'tid = t2.id'
+                                       ] ],
+                               ],
+                               "SELECT tid,field,field2 AS alias,t2.id " .
+                                       "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
+                                       "WHERE alias = 'text' " .
+                                       "ORDER BY field " .
+                                       "LIMIT 1"
+                       ],
+                       [
+                               [
+                                       'tables' => [ 'table', 't2' => 'table2' ],
+                                       'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
+                                       'conds' => [ 'alias' => 'text' ],
+                                       'options' => [ 'LIMIT' => 1, 'GROUP BY' => 'field', 'HAVING' => 'COUNT(*) > 1' ],
+                                       'join_conds' => [ 't2' => [
+                                               'LEFT JOIN', 'tid = t2.id'
+                                       ] ],
+                               ],
+                               "SELECT tid,field,field2 AS alias,t2.id " .
+                                       "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
+                                       "WHERE alias = 'text' " .
+                                       "GROUP BY field HAVING COUNT(*) > 1 " .
+                                       "LIMIT 1"
+                       ],
+                       [
+                               [
+                                       'tables' => [ 'table', 't2' => 'table2' ],
+                                       'fields' => [ 'tid', 'field', 'alias' => 'field2', 't2.id' ],
+                                       'conds' => [ 'alias' => 'text' ],
+                                       'options' => [
+                                               'LIMIT' => 1,
+                                               'GROUP BY' => [ 'field', 'field2' ],
+                                               'HAVING' => [ 'COUNT(*) > 1', 'field' => 1 ]
+                                       ],
+                                       'join_conds' => [ 't2' => [
+                                               'LEFT JOIN', 'tid = t2.id'
+                                       ] ],
+                               ],
+                               "SELECT tid,field,field2 AS alias,t2.id " .
+                                       "FROM table LEFT JOIN table2 t2 ON ((tid = t2.id)) " .
+                                       "WHERE alias = 'text' " .
+                                       "GROUP BY field,field2 HAVING (COUNT(*) > 1) AND field = '1' " .
+                                       "LIMIT 1"
+                       ],
+                       [
+                               [
+                                       'tables' => [ 'table' ],
+                                       'fields' => [ 'alias' => 'field' ],
+                                       'conds' => [ 'alias' => [ 1, 2, 3, 4 ] ],
+                               ],
+                               "SELECT field AS alias " .
+                                       "FROM table " .
+                                       "WHERE alias IN ('1','2','3','4')"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'DISTINCT', 'LOCK IN SHARE MODE' ],
+                               ],
+                               "SELECT DISTINCT field FROM table      LOCK IN SHARE MODE"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'EXPLAIN' => true ],
+                               ],
+                               'EXPLAIN SELECT field FROM table'
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'FOR UPDATE' ],
+                               ],
+                               "SELECT field FROM table      FOR UPDATE"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideUpdate
+        * @covers Wikimedia\Rdbms\Database::update
+        * @covers Wikimedia\Rdbms\Database::makeUpdateOptions
+        * @covers Wikimedia\Rdbms\Database::makeUpdateOptionsArray
+        */
+       public function testUpdate( $sql, $sqlText ) {
+               $this->database->update(
+                       $sql['table'],
+                       $sql['values'],
+                       $sql['conds'],
+                       __METHOD__,
+                       isset( $sql['options'] ) ? $sql['options'] : []
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideUpdate() {
+               return [
+                       [
+                               [
+                                       'table' => 'table',
+                                       'values' => [ 'field' => 'text', 'field2' => 'text2' ],
+                                       'conds' => [ 'alias' => 'text' ],
+                               ],
+                               "UPDATE table " .
+                                       "SET field = 'text'" .
+                                       ",field2 = 'text2' " .
+                                       "WHERE alias = 'text'"
+                       ],
+                       [
+                               [
+                                       'table' => 'table',
+                                       'values' => [ 'field = other', 'field2' => 'text2' ],
+                                       'conds' => [ 'id' => '1' ],
+                               ],
+                               "UPDATE table " .
+                                       "SET field = other" .
+                                       ",field2 = 'text2' " .
+                                       "WHERE id = '1'"
+                       ],
+                       [
+                               [
+                                       'table' => 'table',
+                                       'values' => [ 'field = other', 'field2' => 'text2' ],
+                                       'conds' => '*',
+                               ],
+                               "UPDATE table " .
+                                       "SET field = other" .
+                                       ",field2 = 'text2'"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideDelete
+        * @covers Wikimedia\Rdbms\Database::delete
+        */
+       public function testDelete( $sql, $sqlText ) {
+               $this->database->delete(
+                       $sql['table'],
+                       $sql['conds'],
+                       __METHOD__
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideDelete() {
+               return [
+                       [
+                               [
+                                       'table' => 'table',
+                                       'conds' => [ 'alias' => 'text' ],
+                               ],
+                               "DELETE FROM table " .
+                                       "WHERE alias = 'text'"
+                       ],
+                       [
+                               [
+                                       'table' => 'table',
+                                       'conds' => '*',
+                               ],
+                               "DELETE FROM table"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideUpsert
+        * @covers Wikimedia\Rdbms\Database::upsert
+        */
+       public function testUpsert( $sql, $sqlText ) {
+               $this->database->upsert(
+                       $sql['table'],
+                       $sql['rows'],
+                       $sql['uniqueIndexes'],
+                       $sql['set'],
+                       __METHOD__
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideUpsert() {
+               return [
+                       [
+                               [
+                                       'table' => 'upsert_table',
+                                       'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
+                                       'uniqueIndexes' => [ 'field' ],
+                                       'set' => [ 'field' => 'set' ],
+                               ],
+                               "BEGIN; " .
+                                       "UPDATE upsert_table " .
+                                       "SET field = 'set' " .
+                                       "WHERE ((field = 'text')); " .
+                                       "INSERT IGNORE INTO upsert_table " .
+                                       "(field,field2) " .
+                                       "VALUES ('text','text2'); " .
+                                       "COMMIT"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideDeleteJoin
+        * @covers Wikimedia\Rdbms\Database::deleteJoin
+        */
+       public function testDeleteJoin( $sql, $sqlText ) {
+               $this->database->deleteJoin(
+                       $sql['delTable'],
+                       $sql['joinTable'],
+                       $sql['delVar'],
+                       $sql['joinVar'],
+                       $sql['conds'],
+                       __METHOD__
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideDeleteJoin() {
+               return [
+                       [
+                               [
+                                       'delTable' => 'table',
+                                       'joinTable' => 'table_join',
+                                       'delVar' => 'field',
+                                       'joinVar' => 'field_join',
+                                       'conds' => [ 'alias' => 'text' ],
+                               ],
+                               "DELETE FROM table " .
+                                       "WHERE field IN (" .
+                                       "SELECT field_join FROM table_join WHERE alias = 'text'" .
+                                       ")"
+                       ],
+                       [
+                               [
+                                       'delTable' => 'table',
+                                       'joinTable' => 'table_join',
+                                       'delVar' => 'field',
+                                       'joinVar' => 'field_join',
+                                       'conds' => '*',
+                               ],
+                               "DELETE FROM table " .
+                                       "WHERE field IN (" .
+                                       "SELECT field_join FROM table_join " .
+                                       ")"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideInsert
+        * @covers Wikimedia\Rdbms\Database::insert
+        * @covers Wikimedia\Rdbms\Database::makeInsertOptions
+        */
+       public function testInsert( $sql, $sqlText ) {
+               $this->database->insert(
+                       $sql['table'],
+                       $sql['rows'],
+                       __METHOD__,
+                       isset( $sql['options'] ) ? $sql['options'] : []
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideInsert() {
+               return [
+                       [
+                               [
+                                       'table' => 'table',
+                                       'rows' => [ 'field' => 'text', 'field2' => 2 ],
+                               ],
+                               "INSERT INTO table " .
+                                       "(field,field2) " .
+                                       "VALUES ('text','2')"
+                       ],
+                       [
+                               [
+                                       'table' => 'table',
+                                       'rows' => [ 'field' => 'text', 'field2' => 2 ],
+                                       'options' => 'IGNORE',
+                               ],
+                               "INSERT IGNORE INTO table " .
+                                       "(field,field2) " .
+                                       "VALUES ('text','2')"
+                       ],
+                       [
+                               [
+                                       'table' => 'table',
+                                       'rows' => [
+                                               [ 'field' => 'text', 'field2' => 2 ],
+                                               [ 'field' => 'multi', 'field2' => 3 ],
+                                       ],
+                                       'options' => 'IGNORE',
+                               ],
+                               "INSERT IGNORE INTO table " .
+                                       "(field,field2) " .
+                                       "VALUES " .
+                                       "('text','2')," .
+                                       "('multi','3')"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideInsertSelect
+        * @covers Wikimedia\Rdbms\Database::insertSelect
+        * @covers Wikimedia\Rdbms\Database::nativeInsertSelect
+        */
+       public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
+               $this->database->insertSelect(
+                       $sql['destTable'],
+                       $sql['srcTable'],
+                       $sql['varMap'],
+                       $sql['conds'],
+                       __METHOD__,
+                       isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
+                       isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : [],
+                       isset( $sql['selectJoinConds'] ) ? $sql['selectJoinConds'] : []
+               );
+               $this->assertLastSql( $sqlTextNative );
+
+               $dbWeb = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => false ] );
+               $dbWeb->forceNextResult( [
+                       array_flip( array_keys( $sql['varMap'] ) )
+               ] );
+               $dbWeb->insertSelect(
+                       $sql['destTable'],
+                       $sql['srcTable'],
+                       $sql['varMap'],
+                       $sql['conds'],
+                       __METHOD__,
+                       isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
+                       isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : [],
+                       isset( $sql['selectJoinConds'] ) ? $sql['selectJoinConds'] : []
+               );
+               $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, $sqlInsert ] ), $dbWeb );
+       }
+
+       public static function provideInsertSelect() {
+               return [
+                       [
+                               [
+                                       'destTable' => 'insert_table',
+                                       'srcTable' => 'select_table',
+                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
+                                       'conds' => '*',
+                               ],
+                               "INSERT INTO insert_table " .
+                                       "(field_insert,field) " .
+                                       "SELECT field_select,field2 " .
+                                       "FROM select_table WHERE *",
+                               "SELECT field_select AS field_insert,field2 AS field " .
+                               "FROM select_table WHERE *   FOR UPDATE",
+                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
+                       ],
+                       [
+                               [
+                                       'destTable' => 'insert_table',
+                                       'srcTable' => 'select_table',
+                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
+                                       'conds' => [ 'field' => 2 ],
+                               ],
+                               "INSERT INTO insert_table " .
+                                       "(field_insert,field) " .
+                                       "SELECT field_select,field2 " .
+                                       "FROM select_table " .
+                                       "WHERE field = '2'",
+                               "SELECT field_select AS field_insert,field2 AS field FROM " .
+                               "select_table WHERE field = '2'   FOR UPDATE",
+                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
+                       ],
+                       [
+                               [
+                                       'destTable' => 'insert_table',
+                                       'srcTable' => 'select_table',
+                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
+                                       'conds' => [ 'field' => 2 ],
+                                       'insertOptions' => 'IGNORE',
+                                       'selectOptions' => [ 'ORDER BY' => 'field' ],
+                               ],
+                               "INSERT IGNORE INTO insert_table " .
+                                       "(field_insert,field) " .
+                                       "SELECT field_select,field2 " .
+                                       "FROM select_table " .
+                                       "WHERE field = '2' " .
+                                       "ORDER BY field",
+                               "SELECT field_select AS field_insert,field2 AS field " .
+                               "FROM select_table WHERE field = '2' ORDER BY field  FOR UPDATE",
+                               "INSERT IGNORE INTO insert_table (field_insert,field) VALUES ('0','1')"
+                       ],
+                       [
+                               [
+                                       'destTable' => 'insert_table',
+                                       'srcTable' => [ 'select_table1', 'select_table2' ],
+                                       'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
+                                       'conds' => [ 'field' => 2 ],
+                                       'selectOptions' => [ 'ORDER BY' => 'field', 'FORCE INDEX' => [ 'select_table1' => 'index1' ] ],
+                                       'selectJoinConds' => [
+                                               'select_table2' => [ 'LEFT JOIN', [ 'select_table1.foo = select_table2.bar' ] ],
+                                       ],
+                               ],
+                               "INSERT INTO insert_table " .
+                                       "(field_insert,field) " .
+                                       "SELECT field_select,field2 " .
+                                       "FROM select_table1 LEFT JOIN select_table2 ON ((select_table1.foo = select_table2.bar)) " .
+                                       "WHERE field = '2' " .
+                                       "ORDER BY field",
+                               "SELECT field_select AS field_insert,field2 AS field " .
+                               "FROM select_table1 LEFT JOIN select_table2 ON ((select_table1.foo = select_table2.bar)) " .
+                               "WHERE field = '2' ORDER BY field  FOR UPDATE",
+                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideReplace
+        * @covers Wikimedia\Rdbms\Database::replace
+        */
+       public function testReplace( $sql, $sqlText ) {
+               $this->database->replace(
+                       $sql['table'],
+                       $sql['uniqueIndexes'],
+                       $sql['rows'],
+                       __METHOD__
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideReplace() {
+               return [
+                       [
+                               [
+                                       'table' => 'replace_table',
+                                       'uniqueIndexes' => [ 'field' ],
+                                       'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
+                               ],
+                               "DELETE FROM replace_table " .
+                                       "WHERE ( field='text' ); " .
+                                       "INSERT INTO replace_table " .
+                                       "(field,field2) " .
+                                       "VALUES ('text','text2')"
+                       ],
+                       [
+                               [
+                                       'table' => 'module_deps',
+                                       'uniqueIndexes' => [ [ 'md_module', 'md_skin' ] ],
+                                       'rows' => [
+                                               'md_module' => 'module',
+                                               'md_skin' => 'skin',
+                                               'md_deps' => 'deps',
+                                       ],
+                               ],
+                               "DELETE FROM module_deps " .
+                                       "WHERE ( md_module='module' AND md_skin='skin' ); " .
+                                       "INSERT INTO module_deps " .
+                                       "(md_module,md_skin,md_deps) " .
+                                       "VALUES ('module','skin','deps')"
+                       ],
+                       [
+                               [
+                                       'table' => 'module_deps',
+                                       'uniqueIndexes' => [ [ 'md_module', 'md_skin' ] ],
+                                       'rows' => [
+                                               [
+                                                       'md_module' => 'module',
+                                                       'md_skin' => 'skin',
+                                                       'md_deps' => 'deps',
+                                               ], [
+                                                       'md_module' => 'module2',
+                                                       'md_skin' => 'skin2',
+                                                       'md_deps' => 'deps2',
+                                               ],
+                                       ],
+                               ],
+                               "DELETE FROM module_deps " .
+                                       "WHERE ( md_module='module' AND md_skin='skin' ); " .
+                                       "INSERT INTO module_deps " .
+                                       "(md_module,md_skin,md_deps) " .
+                                       "VALUES ('module','skin','deps'); " .
+                                       "DELETE FROM module_deps " .
+                                       "WHERE ( md_module='module2' AND md_skin='skin2' ); " .
+                                       "INSERT INTO module_deps " .
+                                       "(md_module,md_skin,md_deps) " .
+                                       "VALUES ('module2','skin2','deps2')"
+                       ],
+                       [
+                               [
+                                       'table' => 'module_deps',
+                                       'uniqueIndexes' => [ 'md_module', 'md_skin' ],
+                                       'rows' => [
+                                               [
+                                                       'md_module' => 'module',
+                                                       'md_skin' => 'skin',
+                                                       'md_deps' => 'deps',
+                                               ], [
+                                                       'md_module' => 'module2',
+                                                       'md_skin' => 'skin2',
+                                                       'md_deps' => 'deps2',
+                                               ],
+                                       ],
+                               ],
+                               "DELETE FROM module_deps " .
+                                       "WHERE ( md_module='module' ) OR ( md_skin='skin' ); " .
+                                       "INSERT INTO module_deps " .
+                                       "(md_module,md_skin,md_deps) " .
+                                       "VALUES ('module','skin','deps'); " .
+                                       "DELETE FROM module_deps " .
+                                       "WHERE ( md_module='module2' ) OR ( md_skin='skin2' ); " .
+                                       "INSERT INTO module_deps " .
+                                       "(md_module,md_skin,md_deps) " .
+                                       "VALUES ('module2','skin2','deps2')"
+                       ],
+                       [
+                               [
+                                       'table' => 'module_deps',
+                                       'uniqueIndexes' => [],
+                                       'rows' => [
+                                               'md_module' => 'module',
+                                               'md_skin' => 'skin',
+                                               'md_deps' => 'deps',
+                                       ],
+                               ],
+                               "INSERT INTO module_deps " .
+                                       "(md_module,md_skin,md_deps) " .
+                                       "VALUES ('module','skin','deps')"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideNativeReplace
+        * @covers Wikimedia\Rdbms\Database::nativeReplace
+        */
+       public function testNativeReplace( $sql, $sqlText ) {
+               $this->database->nativeReplace(
+                       $sql['table'],
+                       $sql['rows'],
+                       __METHOD__
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideNativeReplace() {
+               return [
+                       [
+                               [
+                                       'table' => 'replace_table',
+                                       'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
+                               ],
+                               "REPLACE INTO replace_table " .
+                                       "(field,field2) " .
+                                       "VALUES ('text','text2')"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideConditional
+        * @covers Wikimedia\Rdbms\Database::conditional
+        */
+       public function testConditional( $sql, $sqlText ) {
+               $this->assertEquals( trim( $this->database->conditional(
+                       $sql['conds'],
+                       $sql['true'],
+                       $sql['false']
+               ) ), $sqlText );
+       }
+
+       public static function provideConditional() {
+               return [
+                       [
+                               [
+                                       'conds' => [ 'field' => 'text' ],
+                                       'true' => 1,
+                                       'false' => 'NULL',
+                               ],
+                               "(CASE WHEN field = 'text' THEN 1 ELSE NULL END)"
+                       ],
+                       [
+                               [
+                                       'conds' => [ 'field' => 'text', 'field2' => 'anothertext' ],
+                                       'true' => 1,
+                                       'false' => 'NULL',
+                               ],
+                               "(CASE WHEN field = 'text' AND field2 = 'anothertext' THEN 1 ELSE NULL END)"
+                       ],
+                       [
+                               [
+                                       'conds' => 'field=1',
+                                       'true' => 1,
+                                       'false' => 'NULL',
+                               ],
+                               "(CASE WHEN field=1 THEN 1 ELSE NULL END)"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideBuildConcat
+        * @covers Wikimedia\Rdbms\Database::buildConcat
+        */
+       public function testBuildConcat( $stringList, $sqlText ) {
+               $this->assertEquals( trim( $this->database->buildConcat(
+                       $stringList
+               ) ), $sqlText );
+       }
+
+       public static function provideBuildConcat() {
+               return [
+                       [
+                               [ 'field', 'field2' ],
+                               "CONCAT(field,field2)"
+                       ],
+                       [
+                               [ "'test'", 'field2' ],
+                               "CONCAT('test',field2)"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideBuildLike
+        * @covers Wikimedia\Rdbms\Database::buildLike
+        * @covers Wikimedia\Rdbms\Database::escapeLikeInternal
+        */
+       public function testBuildLike( $array, $sqlText ) {
+               $this->assertEquals( trim( $this->database->buildLike(
+                       $array
+               ) ), $sqlText );
+       }
+
+       public static function provideBuildLike() {
+               return [
+                       [
+                               'text',
+                               "LIKE 'text' ESCAPE '`'"
+                       ],
+                       [
+                               [ 'text', new LikeMatch( '%' ) ],
+                               "LIKE 'text%' ESCAPE '`'"
+                       ],
+                       [
+                               [ 'text', new LikeMatch( '%' ), 'text2' ],
+                               "LIKE 'text%text2' ESCAPE '`'"
+                       ],
+                       [
+                               [ 'text', new LikeMatch( '_' ) ],
+                               "LIKE 'text_' ESCAPE '`'"
+                       ],
+                       [
+                               'more_text',
+                               "LIKE 'more`_text' ESCAPE '`'"
+                       ],
+                       [
+                               [ 'C:\\Windows\\', new LikeMatch( '%' ) ],
+                               "LIKE 'C:\\Windows\\%' ESCAPE '`'"
+                       ],
+                       [
+                               [ 'accent`_test`', new LikeMatch( '%' ) ],
+                               "LIKE 'accent```_test``%' ESCAPE '`'"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideUnionQueries
+        * @covers Wikimedia\Rdbms\Database::unionQueries
+        */
+       public function testUnionQueries( $sql, $sqlText ) {
+               $this->assertEquals( trim( $this->database->unionQueries(
+                       $sql['sqls'],
+                       $sql['all']
+               ) ), $sqlText );
+       }
+
+       public static function provideUnionQueries() {
+               return [
+                       [
+                               [
+                                       'sqls' => [ 'RAW SQL', 'RAW2SQL' ],
+                                       'all' => true,
+                               ],
+                               "(RAW SQL) UNION ALL (RAW2SQL)"
+                       ],
+                       [
+                               [
+                                       'sqls' => [ 'RAW SQL', 'RAW2SQL' ],
+                                       'all' => false,
+                               ],
+                               "(RAW SQL) UNION (RAW2SQL)"
+                       ],
+                       [
+                               [
+                                       'sqls' => [ 'RAW SQL', 'RAW2SQL', 'RAW3SQL' ],
+                                       'all' => false,
+                               ],
+                               "(RAW SQL) UNION (RAW2SQL) UNION (RAW3SQL)"
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideUnionConditionPermutations
+        * @covers Wikimedia\Rdbms\Database::unionConditionPermutations
+        */
+       public function testUnionConditionPermutations( $params, $expect ) {
+               if ( isset( $params['unionSupportsOrderAndLimit'] ) ) {
+                       $this->database->setUnionSupportsOrderAndLimit( $params['unionSupportsOrderAndLimit'] );
+               }
+
+               $sql = trim( $this->database->unionConditionPermutations(
+                       $params['table'],
+                       $params['vars'],
+                       $params['permute_conds'],
+                       isset( $params['extra_conds'] ) ? $params['extra_conds'] : '',
+                       'FNAME',
+                       isset( $params['options'] ) ? $params['options'] : [],
+                       isset( $params['join_conds'] ) ? $params['join_conds'] : []
+               ) );
+               $this->assertEquals( $expect, $sql );
+       }
+
+       public static function provideUnionConditionPermutations() {
+               return [
+                       // @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
+                       [
+                               [
+                                       'table' => [ 'table1', 'table2' ],
+                                       'vars' => [ 'field1', 'alias' => 'field2' ],
+                                       'permute_conds' => [
+                                               'field3' => [ 1, 2, 3 ],
+                                               'duplicates' => [ 4, 5, 4 ],
+                                               'empty' => [],
+                                               'single' => [ 0 ],
+                                       ],
+                                       'extra_conds' => 'table2.bar > 23',
+                                       'options' => [
+                                               'ORDER BY' => [ 'field1', 'alias' ],
+                                               'INNER ORDER BY' => [ 'field1', 'field2' ],
+                                               'LIMIT' => 100,
+                                       ],
+                                       'join_conds' => [
+                                               'table2' => [ 'JOIN', 'table1.foo_id = table2.foo_id' ],
+                                       ],
+                               ],
+                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '1' AND duplicates = '4' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
+                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '1' AND duplicates = '5' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
+                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '2' AND duplicates = '4' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
+                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '2' AND duplicates = '5' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
+                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '3' AND duplicates = '4' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) UNION ALL " .
+                               "(SELECT  field1,field2 AS alias  FROM table1 JOIN table2 ON ((table1.foo_id = table2.foo_id))   WHERE field3 = '3' AND duplicates = '5' AND single = '0' AND (table2.bar > 23)  ORDER BY field1,field2 LIMIT 100  ) " .
+                               "ORDER BY field1,alias LIMIT 100"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [
+                                               'bar' => [ 1, 2, 3 ],
+                                       ],
+                                       'extra_conds' => [ 'baz' => null ],
+                                       'options' => [
+                                               'NOTALL',
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                       ],
+                               ],
+                               "(SELECT  foo_id  FROM foo    WHERE bar = '1' AND baz IS NULL  ORDER BY foo_id LIMIT 25  ) UNION " .
+                               "(SELECT  foo_id  FROM foo    WHERE bar = '2' AND baz IS NULL  ORDER BY foo_id LIMIT 25  ) UNION " .
+                               "(SELECT  foo_id  FROM foo    WHERE bar = '3' AND baz IS NULL  ORDER BY foo_id LIMIT 25  ) " .
+                               "ORDER BY foo_id LIMIT 25"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [
+                                               'bar' => [ 1, 2, 3 ],
+                                       ],
+                                       'extra_conds' => [ 'baz' => null ],
+                                       'options' => [
+                                               'NOTALL' => true,
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                       ],
+                                       'unionSupportsOrderAndLimit' => false,
+                               ],
+                               "(SELECT  foo_id  FROM foo    WHERE bar = '1' AND baz IS NULL  ) UNION " .
+                               "(SELECT  foo_id  FROM foo    WHERE bar = '2' AND baz IS NULL  ) UNION " .
+                               "(SELECT  foo_id  FROM foo    WHERE bar = '3' AND baz IS NULL  ) " .
+                               "ORDER BY foo_id LIMIT 25"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [],
+                                       'extra_conds' => [ 'baz' => null ],
+                                       'options' => [
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                       ],
+                               ],
+                               "SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY foo_id LIMIT 25"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [
+                                               'bar' => [],
+                                       ],
+                                       'extra_conds' => [ 'baz' => null ],
+                                       'options' => [
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                       ],
+                               ],
+                               "SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY foo_id LIMIT 25"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [
+                                               'bar' => [ 1 ],
+                                       ],
+                                       'options' => [
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                               'OFFSET' => 150,
+                                       ],
+                               ],
+                               "SELECT  foo_id  FROM foo    WHERE bar = '1'  ORDER BY foo_id LIMIT 150,25"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [],
+                                       'extra_conds' => [ 'baz' => null ],
+                                       'options' => [
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                               'OFFSET' => 150,
+                                               'INNER ORDER BY' => [ 'bar_id' ],
+                                       ],
+                               ],
+                               "(SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY bar_id LIMIT 175  ) ORDER BY foo_id LIMIT 150,25"
+                       ],
+                       [
+                               [
+                                       'table' => 'foo',
+                                       'vars' => [ 'foo_id' ],
+                                       'permute_conds' => [],
+                                       'extra_conds' => [ 'baz' => null ],
+                                       'options' => [
+                                               'ORDER BY' => [ 'foo_id' ],
+                                               'LIMIT' => 25,
+                                               'OFFSET' => 150,
+                                               'INNER ORDER BY' => [ 'bar_id' ],
+                                       ],
+                                       'unionSupportsOrderAndLimit' => false,
+                               ],
+                               "SELECT  foo_id  FROM foo    WHERE baz IS NULL  ORDER BY foo_id LIMIT 150,25"
+                       ],
+                       // @codingStandardsIgnoreEnd
+               ];
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::commit
+        * @covers Wikimedia\Rdbms\Database::doCommit
+        */
+       public function testTransactionCommit() {
+               $this->database->begin( __METHOD__ );
+               $this->database->commit( __METHOD__ );
+               $this->assertLastSql( 'BEGIN; COMMIT' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::rollback
+        * @covers Wikimedia\Rdbms\Database::doRollback
+        */
+       public function testTransactionRollback() {
+               $this->database->begin( __METHOD__ );
+               $this->database->rollback( __METHOD__ );
+               $this->assertLastSql( 'BEGIN; ROLLBACK' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::dropTable
+        */
+       public function testDropTable() {
+               $this->database->setExistingTables( [ 'table' ] );
+               $this->database->dropTable( 'table', __METHOD__ );
+               $this->assertLastSql( 'DROP TABLE table CASCADE' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::dropTable
+        */
+       public function testDropNonExistingTable() {
+               $this->assertFalse(
+                       $this->database->dropTable( 'non_existing', __METHOD__ )
+               );
+       }
+
+       /**
+        * @dataProvider provideMakeList
+        * @covers Wikimedia\Rdbms\Database::makeList
+        */
+       public function testMakeList( $list, $mode, $sqlText ) {
+               $this->assertEquals( trim( $this->database->makeList(
+                       $list, $mode
+               ) ), $sqlText );
+       }
+
+       public static function provideMakeList() {
+               return [
+                       [
+                               [ 'value', 'value2' ],
+                               LIST_COMMA,
+                               "'value','value2'"
+                       ],
+                       [
+                               [ 'field', 'field2' ],
+                               LIST_NAMES,
+                               "field,field2"
+                       ],
+                       [
+                               [ 'field' => 'value', 'field2' => 'value2' ],
+                               LIST_AND,
+                               "field = 'value' AND field2 = 'value2'"
+                       ],
+                       [
+                               [ 'field' => null, "field2 != 'value2'" ],
+                               LIST_AND,
+                               "field IS NULL AND (field2 != 'value2')"
+                       ],
+                       [
+                               [ 'field' => [ 'value', null, 'value2' ], 'field2' => 'value2' ],
+                               LIST_AND,
+                               "(field IN ('value','value2')  OR field IS NULL) AND field2 = 'value2'"
+                       ],
+                       [
+                               [ 'field' => [ null ], 'field2' => null ],
+                               LIST_AND,
+                               "field IS NULL AND field2 IS NULL"
+                       ],
+                       [
+                               [ 'field' => 'value', 'field2' => 'value2' ],
+                               LIST_OR,
+                               "field = 'value' OR field2 = 'value2'"
+                       ],
+                       [
+                               [ 'field' => 'value', 'field2' => null ],
+                               LIST_OR,
+                               "field = 'value' OR field2 IS NULL"
+                       ],
+                       [
+                               [ 'field' => [ 'value', 'value2' ], 'field2' => [ 'value' ] ],
+                               LIST_OR,
+                               "field IN ('value','value2')  OR field2 = 'value'"
+                       ],
+                       [
+                               [ 'field' => [ null, 'value', null, 'value2' ], "field2 != 'value2'" ],
+                               LIST_OR,
+                               "(field IN ('value','value2')  OR field IS NULL) OR (field2 != 'value2')"
+                       ],
+                       [
+                               [ 'field' => 'value', 'field2' => 'value2' ],
+                               LIST_SET,
+                               "field = 'value',field2 = 'value2'"
+                       ],
+                       [
+                               [ 'field' => 'value', 'field2' => null ],
+                               LIST_SET,
+                               "field = 'value',field2 = NULL"
+                       ],
+                       [
+                               [ 'field' => 'value', "field2 != 'value2'" ],
+                               LIST_SET,
+                               "field = 'value',field2 != 'value2'"
+                       ],
+               ];
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::registerTempTableOperation
+        */
+       public function testSessionTempTables() {
+               $temp1 = $this->database->tableName( 'tmp_table_1' );
+               $temp2 = $this->database->tableName( 'tmp_table_2' );
+               $temp3 = $this->database->tableName( 'tmp_table_3' );
+
+               $this->database->query( "CREATE TEMPORARY TABLE $temp1 LIKE orig_tbl", __METHOD__ );
+               $this->database->query( "CREATE TEMPORARY TABLE $temp2 LIKE orig_tbl", __METHOD__ );
+               $this->database->query( "CREATE TEMPORARY TABLE $temp3 LIKE orig_tbl", __METHOD__ );
+
+               $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+               $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+               $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+
+               $this->database->dropTable( 'tmp_table_1', __METHOD__ );
+               $this->database->dropTable( 'tmp_table_2', __METHOD__ );
+               $this->database->dropTable( 'tmp_table_3', __METHOD__ );
+
+               $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+               $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+               $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+
+               $this->database->query( "CREATE TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ );
+               $this->database->query( "CREATE TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ );
+               $this->database->query( "CREATE TEMPORARY TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ );
+
+               $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+               $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+               $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+
+               $this->database->query( "DROP TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ );
+               $this->database->query( "DROP TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ );
+               $this->database->query( "DROP TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ );
+
+               $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+               $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+               $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+       }
+}
diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
new file mode 100644 (file)
index 0000000..9bea7ff
--- /dev/null
@@ -0,0 +1,355 @@
+<?php
+
+use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\TransactionProfiler;
+use Wikimedia\TestingAccessWrapper;
+
+class DatabaseTest extends PHPUnit_Framework_TestCase {
+
+       protected function setUp() {
+               $this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
+       }
+
+       public static function provideAddQuotes() {
+               return [
+                       [ null, 'NULL' ],
+                       [ 1234, "'1234'" ],
+                       [ 1234.5678, "'1234.5678'" ],
+                       [ 'string', "'string'" ],
+                       [ 'string\'s cause trouble', "'string\'s cause trouble'" ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideAddQuotes
+        * @covers Wikimedia\Rdbms\Database::addQuotes
+        */
+       public function testAddQuotes( $input, $expected ) {
+               $this->assertEquals( $expected, $this->db->addQuotes( $input ) );
+       }
+
+       public static function provideTableName() {
+               // Formatting is mostly ignored since addIdentifierQuotes is abstract.
+               // For testing of addIdentifierQuotes, see actual Database subclas tests.
+               return [
+                       'local' => [
+                               'tablename',
+                               'tablename',
+                               'quoted',
+                       ],
+                       'local-raw' => [
+                               'tablename',
+                               'tablename',
+                               'raw',
+                       ],
+                       'shared' => [
+                               'sharedb.tablename',
+                               'tablename',
+                               'quoted',
+                               [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
+                       ],
+                       'shared-raw' => [
+                               'sharedb.tablename',
+                               'tablename',
+                               'raw',
+                               [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
+                       ],
+                       'shared-prefix' => [
+                               'sharedb.sh_tablename',
+                               'tablename',
+                               'quoted',
+                               [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
+                       ],
+                       'shared-prefix-raw' => [
+                               'sharedb.sh_tablename',
+                               'tablename',
+                               'raw',
+                               [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
+                       ],
+                       'foreign' => [
+                               'databasename.tablename',
+                               'databasename.tablename',
+                               'quoted',
+                       ],
+                       'foreign-raw' => [
+                               'databasename.tablename',
+                               'databasename.tablename',
+                               'raw',
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideTableName
+        * @covers Wikimedia\Rdbms\Database::tableName
+        */
+       public function testTableName( $expected, $table, $format, array $alias = null ) {
+               if ( $alias ) {
+                       $this->db->setTableAliases( [ $table => $alias ] );
+               }
+               $this->assertEquals(
+                       $expected,
+                       $this->db->tableName( $table, $format ?: 'quoted' )
+               );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionIdle
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
+        */
+       public function testTransactionIdle() {
+               $db = $this->db;
+
+               $db->setFlag( DBO_TRX );
+               $called = false;
+               $flagSet = null;
+               $db->onTransactionIdle(
+                       function () use ( $db, &$flagSet, &$called ) {
+                               $called = true;
+                               $flagSet = $db->getFlag( DBO_TRX );
+                       },
+                       __METHOD__
+               );
+               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
+               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $db->clearFlag( DBO_TRX );
+               $flagSet = null;
+               $db->onTransactionIdle(
+                       function () use ( $db, &$flagSet ) {
+                               $flagSet = $db->getFlag( DBO_TRX );
+                       },
+                       __METHOD__
+               );
+               $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+
+               $db->clearFlag( DBO_TRX );
+               $db->onTransactionIdle(
+                       function () use ( $db ) {
+                               $db->setFlag( DBO_TRX );
+                       },
+                       __METHOD__
+               );
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionResolution
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
+        */
+       public function testTransactionResolution() {
+               $db = $this->db;
+
+               $db->clearFlag( DBO_TRX );
+               $db->begin( __METHOD__ );
+               $called = false;
+               $db->onTransactionResolution( function () use ( $db, &$called ) {
+                       $called = true;
+                       $db->setFlag( DBO_TRX );
+               } );
+               $db->commit( __METHOD__ );
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $db->clearFlag( DBO_TRX );
+               $db->begin( __METHOD__ );
+               $called = false;
+               $db->onTransactionResolution( function () use ( $db, &$called ) {
+                       $called = true;
+                       $db->setFlag( DBO_TRX );
+               } );
+               $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+               $this->assertTrue( $called, 'Callback reached' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::setTransactionListener
+        */
+       public function testTransactionListener() {
+               $db = $this->db;
+
+               $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
+                       $called = true;
+               } );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Callback still reached' );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->rollback( __METHOD__ );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $db->setTransactionListener( 'ping', null );
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertFalse( $called, 'Callback not reached' );
+       }
+
+       /**
+        * Use this mock instead of DatabaseTestHelper for cases where
+        * DatabaseTestHelper is too inflexibile due to mocking too much
+        * or being too restrictive about fname matching (e.g. for tests
+        * that assert behaviour when the name is a mismatch, we need to
+        * catch the error here instead of there).
+        *
+        * @return Database
+        */
+       private function getMockDB( $methods = [] ) {
+               static $abstractMethods = [
+                       'affectedRows',
+                       'closeConnection',
+                       'dataSeek',
+                       'doQuery',
+                       'fetchObject', 'fetchRow',
+                       'fieldInfo', 'fieldName',
+                       'getSoftwareLink', 'getServerVersion',
+                       'getType',
+                       'indexInfo',
+                       'insertId',
+                       'lastError', 'lastErrno',
+                       'numFields', 'numRows',
+                       'open',
+                       'strencode',
+               ];
+               $db = $this->getMockBuilder( Database::class )
+                       ->disableOriginalConstructor()
+                       ->setMethods( array_values( array_unique( array_merge(
+                               $abstractMethods,
+                               $methods
+                       ) ) ) )
+                       ->getMock();
+               $wdb = TestingAccessWrapper::newFromObject( $db );
+               $wdb->trxProfiler = new TransactionProfiler();
+               $wdb->connLogger = new \Psr\Log\NullLogger();
+               $wdb->queryLogger = new \Psr\Log\NullLogger();
+               return $db;
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::flushSnapshot
+        */
+       public function testFlushSnapshot() {
+               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+
+               $db->flushSnapshot( __METHOD__ ); // ok
+               $db->flushSnapshot( __METHOD__ ); // ok
+
+               $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               $db->query( 'SELECT 1', __METHOD__ );
+               $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
+               $db->flushSnapshot( __METHOD__ ); // ok
+               $db->restoreFlags( $db::RESTORE_PRIOR );
+
+               $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
+       }
+
+       public function testGetScopedLock() {
+               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+
+               $db->setFlag( DBO_TRX );
+               try {
+                       $this->badLockingMethodImplicit( $db );
+               } catch ( RunTimeException $e ) {
+                       $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
+               }
+               $db->clearFlag( DBO_TRX );
+               $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
+               $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
+
+               try {
+                       $this->badLockingMethodExplicit( $db );
+               } catch ( RunTimeException $e ) {
+                       $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
+               }
+               $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
+               $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
+       }
+
+       private function badLockingMethodImplicit( IDatabase $db ) {
+               $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
+               $db->query( "SELECT 1" ); // trigger DBO_TRX
+               throw new RunTimeException( "Uh oh!" );
+       }
+
+       private function badLockingMethodExplicit( IDatabase $db ) {
+               $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
+               $db->begin( __METHOD__ );
+               throw new RunTimeException( "Uh oh!" );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::getFlag
+        * @covers Wikimedia\Rdbms\Database::setFlag
+        * @covers Wikimedia\Rdbms\Database::restoreFlags
+        */
+       public function testFlagSetting() {
+               $db = $this->db;
+               $origTrx = $db->getFlag( DBO_TRX );
+               $origSsl = $db->getFlag( DBO_SSL );
+
+               $origTrx
+                       ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
+                       : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
+
+               $origSsl
+                       ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
+                       : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
+               $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
+
+               $db->restoreFlags( $db::RESTORE_INITIAL );
+               $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
+               $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
+
+               $origTrx
+                       ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
+                       : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               $origSsl
+                       ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
+                       : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
+
+               $db->restoreFlags();
+               $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
+               $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
+
+               $db->restoreFlags();
+               $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
+               $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::tablePrefix
+        * @covers Wikimedia\Rdbms\Database::dbSchema
+        */
+       public function testMutators() {
+               $old = $this->db->tablePrefix();
+               $this->assertInternalType( 'string', $old, 'Prefix is string' );
+               $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
+               $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
+               $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
+               $this->db->tablePrefix( $old );
+               $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
+
+               $old = $this->db->dbSchema();
+               $this->assertInternalType( 'string', $old, 'Schema is string' );
+               $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
+               $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
+               $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
+               $this->db->dbSchema( $old );
+               $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
+       }
+}
index 556a348..3079d8f 100644 (file)
@@ -625,15 +625,15 @@ more stuff
                return [
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "0",
                                "No more",
                                null,
-                               trim( preg_replace( '/^Intro/sm', 'No more', WikiPageTest::$sections ) )
+                               trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "",
                                "No more",
                                null,
@@ -641,29 +641,29 @@ more stuff
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "2",
                                "== TEST ==\nmore fun",
                                null,
                                trim( preg_replace( '/^== test ==.*== foo ==/sm',
                                        "== TEST ==\nmore fun\n\n== foo ==",
-                                       WikiPageTest::$sections ) )
+                                       self::$sections ) )
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "8",
                                "No more",
                                null,
-                               trim( WikiPageTest::$sections )
+                               trim( self::$sections )
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "new",
                                "No more",
                                "New",
-                               trim( WikiPageTest::$sections ) . "\n\n== New ==\n\nNo more"
+                               trim( self::$sections ) . "\n\n== New ==\n\nNo more"
                        ],
                ];
        }
index 3e0d883..3530d3c 100644 (file)
@@ -43,6 +43,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'test.top' => [ 'position' => 'top' ],
                        'test.private.top' => [ 'group' => 'private', 'position' => 'top' ],
                        'test.private.bottom' => [ 'group' => 'private', 'position' => 'bottom' ],
+                       'test.shouldembed' => [ 'shouldEmbed' => true ],
 
                        'test.styles.pure' => [ 'type' => ResourceLoaderModule::LOAD_STYLES ],
                        'test.styles.mixed' => [],
@@ -64,12 +65,24 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                                'group' => 'private',
                                'styles' => '.private{}',
                        ],
+                       'test.styles.shouldembed' => [
+                               'type' => ResourceLoaderModule::LOAD_STYLES,
+                               'shouldEmbed' => true,
+                               'styles' => '.shouldembed{}',
+                       ],
 
                        'test.scripts' => [],
                        'test.scripts.top' => [ 'position' => 'top' ],
                        'test.scripts.user' => [ 'group' => 'user' ],
                        'test.scripts.user.empty' => [ 'group' => 'user', 'isKnownEmpty' => true ],
                        'test.scripts.raw' => [ 'isRaw' => true ],
+                       'test.scripts.shouldembed' => [ 'shouldEmbed' => true ],
+
+                       'test.ordering.a' => [ 'shouldEmbed' => false ],
+                       'test.ordering.b' => [ 'shouldEmbed' => false ],
+                       'test.ordering.c' => [ 'shouldEmbed' => true, 'styles' => '.orderingC{}' ],
+                       'test.ordering.d' => [ 'shouldEmbed' => true, 'styles' => '.orderingD{}' ],
+                       'test.ordering.e' => [ 'shouldEmbed' => false ],
                ];
                return array_map( function ( $options ) {
                        return self::makeModule( $options );
@@ -102,6 +115,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'test.private.bottom',
                        'test.private.top',
                        'test.top',
+                       'test.shouldembed',
                        'test.unregistered',
                ] );
                $client->setModuleStyles( [
@@ -109,12 +123,14 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'test.styles.user.empty',
                        'test.styles.private',
                        'test.styles.pure',
+                       'test.styles.shouldembed',
                        'test.unregistered.styles',
                ] );
                $client->setModuleScripts( [
                        'test.scripts',
                        'test.scripts.user.empty',
                        'test.scripts.top',
+                       'test.scripts.shouldembed',
                        'test.unregistered.scripts',
                ] );
 
@@ -122,12 +138,15 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'states' => [
                                'test.private.top' => 'loading',
                                'test.private.bottom' => 'loading',
+                               'test.shouldembed' => 'loading',
                                'test.styles.pure' => 'ready',
                                'test.styles.user.empty' => 'ready',
                                'test.styles.private' => 'ready',
+                               'test.styles.shouldembed' => 'ready',
                                'test.scripts' => 'loading',
                                'test.scripts.top' => 'loading',
                                'test.scripts.user.empty' => 'ready',
+                               'test.scripts.shouldembed' => 'loading',
                        ],
                        'general' => [
                                'test',
@@ -139,12 +158,14 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'scripts' => [
                                'test.scripts',
                                'test.scripts.top',
+                               'test.scripts.shouldembed',
                        ],
                        'embed' => [
-                               'styles' => [ 'test.styles.private' ],
+                               'styles' => [ 'test.styles.private', 'test.styles.shouldembed' ],
                                'general' => [
                                        'test.private.bottom',
                                        'test.private.top',
+                                       'test.shouldembed',
                                ],
                        ],
                ];
@@ -276,6 +297,47 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                                'only' => ResourceLoaderModule::TYPE_STYLES,
                                'output' => '<noscript><link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.styles.noscript&amp;only=styles&amp;skin=fallback"/></noscript>',
                        ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_COMBINED,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.implement("test.shouldembed@09p30q0",function($,jQuery,require,module){},{"css":[]});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.styles.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' => '<style>.shouldembed{}</style>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.scripts.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_SCRIPTS,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.state({"test.scripts.shouldembed":"ready"});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test', 'test.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_COMBINED,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test\u0026skin=fallback");mw.loader.implement("test.shouldembed@09p30q0",function($,jQuery,require,module){},{"css":[]});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.styles.pure', 'test.styles.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' =>
+                                       '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.styles.pure&amp;only=styles&amp;skin=fallback"/>' . "\n"
+                                       . '<style>.shouldembed{}</style>'
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.ordering.a', 'test.ordering.e', 'test.ordering.b', 'test.ordering.d', 'test.ordering.c' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' =>
+                                       '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.ordering.a%2Cb&amp;only=styles&amp;skin=fallback"/>' . "\n"
+                                       . '<style>.orderingC{}.orderingD{}</style>' . "\n"
+                                       . '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.ordering.e&amp;only=styles&amp;skin=fallback"/>'
+                       ],
                        // @codingStandardsIgnoreEnd
                ];
        }
index 1e09e92..03a609b 100644 (file)
@@ -400,6 +400,10 @@ mw.loader.register( [
                $module = new ResourceLoaderStartUpModule();
                $out = ltrim( $case['out'], "\n" );
 
+               // Disable log from getModuleRegistrations via MWExceptionHandler
+               // for case where getVersionHash() is expected to throw.
+               $this->setLogger( 'exception', new Psr\Log\NullLogger() );
+
                $this->assertEquals(
                        self::expandPlaceholders( $out ),
                        $module->getModuleRegistrations( $context ),
index 1422adc..e6f709d 100644 (file)
@@ -588,7 +588,9 @@ mw.example();
         * @covers ResourceLoader::getCombinedVersion
         */
        public function testGetCombinedVersion() {
-               $rl = new EmptyResourceLoader();
+               $rl = $this->getMockBuilder( EmptyResourceLoader::class )
+                       // Disable log from outputErrorAndLog
+                       ->setMethods( [ 'outputErrorAndLog' ] )->getMock();
                $rl->register( [
                        'foo' => self::getSimpleModuleMock(),
                        'ferry' => self::getFailFerryMock(),
@@ -720,6 +722,9 @@ mw.example();
                        $rl
                );
 
+               // Disable log from makeModuleResponse via outputErrorAndLog
+               $this->setLogger( 'exception', new Psr\Log\NullLogger() );
+
                $response = $rl->makeModuleResponse( $context, $modules );
                $errors = $rl->getErrors();
 
@@ -764,6 +769,9 @@ mw.example();
                        'getModuleNames'
                );
 
+               // Disable log from makeModuleResponse via outputErrorAndLog
+               $this->setLogger( 'exception', new Psr\Log\NullLogger() );
+
                $modules = [ 'startup' => $rl->getModule( 'startup' ) ];
                $response = $rl->makeModuleResponse( $context, $modules );
                $errors = $rl->getErrors();
index 6597906..39c3421 100644 (file)
@@ -109,6 +109,6 @@ class TestSites {
        public static function insertIntoDb() {
                $sitesTable = \MediaWiki\MediaWikiServices::getInstance()->getSiteStore();
                $sitesTable->clear();
-               $sitesTable->saveSites( TestSites::getSites() );
+               $sitesTable->saveSites( self::getSites() );
        }
 }
index e2209eb..4070bc0 100644 (file)
@@ -37,11 +37,8 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                return $mock;
        }
 
-       /** helper to test SpecialRecentchanges::buildMainQueryConds() */
-       private function assertConditions(
-               $expected,
+       private function buildQuery(
                $requestOptions = null,
-               $message = '',
                $user = null
        ) {
                $context = new RequestContext;
@@ -81,6 +78,18 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                        'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
                );
 
+               return $queryConditions;
+       }
+
+       /** helper to test SpecialRecentchanges::buildQuery() */
+       private function assertConditions(
+               $expected,
+               $requestOptions = null,
+               $message = '',
+               $user = null
+       ) {
+               $queryConditions = $this->buildQuery( $requestOptions, $user );
+
                $this->assertEquals(
                        self::normalizeCondition( $expected ),
                        self::normalizeCondition( $queryConditions ),
@@ -373,6 +382,104 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
        }
 
+       public function testFilterUserExpLevelAll() {
+               $this->assertConditions(
+                       [
+                               # expected
+                       ],
+                       [
+                               'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
+                       ],
+                       "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
+               );
+       }
+
+       public function testFilterUserExpLevelRegisteredUnregistered() {
+               $this->assertConditions(
+                       [
+                               # expected
+                       ],
+                       [
+                               'userExpLevel' => 'registered;unregistered',
+                       ],
+                       "rc conditions: userExpLevel=registered;unregistered"
+               );
+       }
+
+       public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
+               $this->assertConditions(
+                       [
+                               # expected
+                       ],
+                       [
+                               'userExpLevel' => 'registered;unregistered;learner',
+                       ],
+                       "rc conditions: userExpLevel=registered;unregistered;learner"
+               );
+       }
+
+       public function testFilterUserExpLevelAllExperienceLevels() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user != 0',
+                       ],
+                       [
+                               'userExpLevel' => 'newcomer;learner;experienced',
+                       ],
+                       "rc conditions: userExpLevel=newcomer;learner;experienced"
+               );
+       }
+
+       public function testFilterUserExpLevelRegistrered() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user != 0',
+                       ],
+                       [
+                               'userExpLevel' => 'registered',
+                       ],
+                       "rc conditions: userExpLevel=registered"
+               );
+       }
+
+       public function testFilterUserExpLevelUnregistrered() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user' => 0,
+                       ],
+                       [
+                               'userExpLevel' => 'unregistered',
+                       ],
+                       "rc conditions: userExpLevel=unregistered"
+               );
+       }
+
+       public function testFilterUserExpLevelRegistreredOrLearner() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               'rc_user != 0',
+                       ],
+                       [
+                               'userExpLevel' => 'registered;learner',
+                       ],
+                       "rc conditions: userExpLevel=registered;learner"
+               );
+       }
+
+       public function testFilterUserExpLevelUnregistreredOrExperienced() {
+               $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
+
+               $this->assertRegExp(
+                       '/\(rc_user = 0\) OR \(\(user_editcount >= 500\) AND \(user_registration <= \'\d+\'\)\)/',
+                       reset( $conds ),
+                       "rc conditions: userExpLevel=unregistered;experienced"
+               );
+       }
+
        public function testFilterUserExpLevel() {
                $now = time();
                $this->setMwGlobals( [
@@ -438,18 +545,6 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                        $this->fetchUsers( [ 'learner', 'experienced' ], $now ),
                        'Learner and more experienced'
                );
-
-               // newcomers, learner, and more experienced
-               // TOOD: Fix test.  This needs to test that anons are excluded,
-               // and right now the join fails.
-               /* $this->assertArrayEquals( */
-               /*      [ */
-               /*              'Newcomer1', 'Newcomer2', 'Newcomer3', */
-               /*              'Learner1', 'Learner2', 'Learner3', 'Learner4', */
-               /*              'Experienced1', */
-               /*      ], */
-               /*      $this->fetchUsers( [ 'newcomer', 'learner', 'experienced' ], $now ) */
-               /* ); */
        }
 
        private function createUsers( $specs, $now ) {
@@ -798,7 +893,7 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                                        "hideliu" => true,
                                        "userExpLevel" => "newcomer",
                                ],
-                               "expectedConflicts" => true,
+                               "expectedConflicts" => false,
                        ],
                        [
                                "parameters" => [
index 85becff..a9a612d 100644 (file)
@@ -27,6 +27,8 @@ class SpecialRecentchangesTest extends AbstractChangesListSpecialPageTestCase {
 
                        [ 'days=3', [ 'days' => '3' ] ],
 
+                       [ 'days=0.25', [ 'days' => '0.25'] ],
+
                        [ 'namespace=5', [ 'namespace' => '5' ] ],
 
                        [ 'namespace=5|3', [ 'namespace' => '5|3' ] ],
diff --git a/tests/phpunit/structure/DatabaseIntegrationTest.php b/tests/phpunit/structure/DatabaseIntegrationTest.php
new file mode 100644 (file)
index 0000000..b0c1c8f
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\Database;
+
+/**
+ * @group Database
+ */
+class DatabaseIntegrationTest extends MediaWikiTestCase {
+       /**
+        * @var Database
+        */
+       protected $db;
+
+       private $functionTest = false;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->db = wfGetDB( DB_MASTER );
+       }
+
+       protected function tearDown() {
+               parent::tearDown();
+               if ( $this->functionTest ) {
+                       $this->dropFunctions();
+                       $this->functionTest = false;
+               }
+               $this->db->restoreFlags( IDatabase::RESTORE_INITIAL );
+       }
+
+       public function testStoredFunctions() {
+               if ( !in_array( wfGetDB( DB_MASTER )->getType(), [ 'mysql', 'postgres' ] ) ) {
+                       $this->markTestSkipped( 'MySQL or Postgres required' );
+               }
+               global $IP;
+               $this->dropFunctions();
+               $this->functionTest = true;
+               $this->assertTrue(
+                       $this->db->sourceFile( "$IP/tests/phpunit/data/db/{$this->db->getType()}/functions.sql" )
+               );
+               $res = $this->db->query( 'SELECT mw_test_function() AS test', __METHOD__ );
+               $this->assertEquals( 42, $res->fetchObject()->test );
+       }
+
+       private function dropFunctions() {
+               $this->db->query( 'DROP FUNCTION IF EXISTS mw_test_function'
+                       . ( $this->db->getType() == 'postgres' ? '()' : '' )
+               );
+       }
+
+       public function testUnknownTableCorruptsResults() {
+               $res = $this->db->select( 'page', '*', [ 'page_id' => 1 ] );
+               $this->assertFalse( $this->db->tableExists( 'foobarbaz' ) );
+               $this->assertInternalType( 'int', $res->numRows() );
+       }
+}
index 7babcac..251a4a2 100644 (file)
@@ -33,6 +33,7 @@
                <testsuite name="skins">
                        <directory>skins</directory>
                        <directory>structure</directory>
+                       <file>suites/ExtensionsTestSuite.php</file>
                        <file>suites/LessTestSuite.php</file>
                </testsuite>
                <!-- As there is a class Maintenance, we cannot use the name "maintenance" directly -->
@@ -67,7 +68,6 @@
                        <directory suffix=".php">../../includes</directory>
                        <directory suffix=".php">../../languages</directory>
                        <directory suffix=".php">../../maintenance</directory>
-                       <directory suffix=".php">../../skins</directory>
                </whitelist>
        </filter>
 </phpunit>
diff --git a/tests/qunit/data/callMwLoaderTestCallback.js b/tests/qunit/data/callMwLoaderTestCallback.js
deleted file mode 100644 (file)
index dd03411..0000000
+++ /dev/null
@@ -1 +0,0 @@
-mediaWiki.loader.testCallback();
diff --git a/tests/qunit/data/mwLoaderTestCallback.js b/tests/qunit/data/mwLoaderTestCallback.js
new file mode 100644 (file)
index 0000000..dd03411
--- /dev/null
@@ -0,0 +1 @@
+mediaWiki.loader.testCallback();
diff --git a/tests/qunit/data/qunitOkCall.js b/tests/qunit/data/qunitOkCall.js
deleted file mode 100644 (file)
index 3ed5514..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-QUnit.start();
-QUnit.assert.ok( true, 'Successfully loaded!' );
index d7da5a0..e944ef0 100644 (file)
@@ -2,7 +2,23 @@
 ( function ( $, mw, QUnit ) {
        'use strict';
 
-       var addons;
+       var addons, nested;
+
+       /**
+        * Make a safe copy of localEnv:
+        * - Creates a copy so that when the same object reference to module hooks is
+        *   used by multipe test hooks, our QUnit.module extension will not wrap the
+        *   callbacks multiple times. Instead, they wrap using a new object.
+        * - Normalise setup/teardown to avoid having to repeat this in each extension
+        *   (deprecated in QUnit 1.16, removed in QUnit 2).
+        * - Strip any other properties.
+        */
+       function makeSafeEnv( localEnv ) {
+               return {
+                       beforeEach: localEnv.setup || localEnv.beforeEach,
+                       afterEach: localEnv.teardown || localEnv.afterEach
+               };
+       }
 
        /**
         * Add bogus to url to prevent IE crazy caching
@@ -42,9 +58,6 @@
         *
         * Glue code for nicer integration with QUnit setup/teardown
         * Inspired by http://sinonjs.org/releases/sinon-qunit-1.0.0.js
-        * Fixes:
-        * - Work properly with asynchronous QUnit by using module setup/teardown
-        *   instead of synchronously wrapping QUnit.test.
         */
        sinon.assert.fail = function ( msg ) {
                QUnit.assert.ok( false, msg );
                useFakeTimers: false,
                useFakeServer: false
        };
+       // Extend QUnit.module to provide a Sinon sandbox.
        ( function () {
                var orgModule = QUnit.module;
-
                QUnit.module = function ( name, localEnv, executeNow ) {
+                       var orgBeforeEach, orgAfterEach, orgExecute;
+                       if ( nested ) {
+                               // In a nested module, don't re-run our handlers.
+                               return orgModule.apply( this, arguments );
+                       }
                        if ( arguments.length === 2 && typeof localEnv === 'function' ) {
                                executeNow = localEnv;
                                localEnv = undefined;
                        }
+                       if ( executeNow ) {
+                               // Wrap executeNow() so that we can detect nested modules
+                               orgExecute = executeNow;
+                               executeNow = function () {
+                                       var ret;
+                                       nested = true;
+                                       ret = orgExecute.apply( this, arguments );
+                                       nested = false;
+                                       return ret;
+                               };
+                       }
 
                        localEnv = localEnv || {};
-                       orgModule( name, {
-                               setup: function () {
-                                       var config = sinon.getConfig( sinon.config );
-                                       config.injectInto = this;
-                                       sinon.sandbox.create( config );
-
-                                       if ( localEnv.setup ) {
-                                               localEnv.setup.call( this );
-                                       }
-                               },
-                               teardown: function () {
-                                       if ( localEnv.teardown ) {
-                                               localEnv.teardown.call( this );
-                                       }
-
-                                       if ( this.sandbox ) {
-                                               this.sandbox.verifyAndRestore();
-                                       }
+                       orgBeforeEach = localEnv.beforeEach;
+                       orgAfterEach = localEnv.afterEach;
+                       localEnv.beforeEach = function () {
+                               var config = sinon.getConfig( sinon.config );
+                               config.injectInto = this;
+                               sinon.sandbox.create( config );
+
+                               if ( orgBeforeEach ) {
+                                       return orgBeforeEach.apply( this, arguments );
+                               }
+                       };
+                       localEnv.afterEach = function () {
+                               var ret;
+                               if ( orgAfterEach ) {
+                                       ret = orgAfterEach.apply( this, arguments );
                                }
-                       }, executeNow );
+
+                               this.sandbox.verifyAndRestore();
+                               return ret;
+                       };
+                       return orgModule( name, localEnv, executeNow );
                };
        }() );
 
        // Extend QUnit.module to provide a fixture element.
        ( function () {
                var orgModule = QUnit.module;
-
                QUnit.module = function ( name, localEnv, executeNow ) {
-                       var fixture;
-
+                       var orgBeforeEach, orgAfterEach;
+                       if ( nested ) {
+                               // In a nested module, don't re-run our handlers.
+                               return orgModule.apply( this, arguments );
+                       }
                        if ( arguments.length === 2 && typeof localEnv === 'function' ) {
                                executeNow = localEnv;
                                localEnv = undefined;
                        }
 
                        localEnv = localEnv || {};
-                       orgModule( name, {
-                               setup: function () {
-                                       fixture = document.createElement( 'div' );
-                                       fixture.id = 'qunit-fixture';
-                                       document.body.appendChild( fixture );
-
-                                       if ( localEnv.setup ) {
-                                               localEnv.setup.call( this );
-                                       }
-                               },
-                               teardown: function () {
-                                       if ( localEnv.teardown ) {
-                                               localEnv.teardown.call( this );
-                                       }
-
-                                       fixture.parentNode.removeChild( fixture );
+                       orgBeforeEach = localEnv.beforeEach;
+                       orgAfterEach = localEnv.afterEach;
+                       localEnv.beforeEach = function () {
+                               this.fixture = document.createElement( 'div' );
+                               this.fixture.id = 'qunit-fixture';
+                               document.body.appendChild( this.fixture );
+
+                               if ( orgBeforeEach ) {
+                                       return orgBeforeEach.apply( this, arguments );
                                }
-                       }, executeNow );
+                       };
+                       localEnv.afterEach = function () {
+                               var ret;
+                               if ( orgAfterEach ) {
+                                       ret = orgAfterEach.apply( this, arguments );
+                               }
+
+                               this.fixture.parentNode.removeChild( this.fixture );
+                               return ret;
+                       };
+                       return orgModule( name, localEnv, executeNow );
+               };
+       }() );
+
+       // Extend QUnit.module to normalise localEnv.
+       // NOTE: This MUST be the last QUnit.module extension so that the above extensions
+       // may safely modify the object and assume beforeEach/afterEach.
+       ( function () {
+               var orgModule = QUnit.module;
+               QUnit.module = function ( name, localEnv, executeNow ) {
+                       if ( typeof localEnv === 'object' ) {
+                               localEnv = makeSafeEnv( localEnv );
+                       }
+                       return orgModule( name, localEnv, executeNow );
                };
        }() );
 
                        ajaxRequests.push( { xhr: jqXHR, options: ajaxOptions } );
                }
 
-               return function ( localEnv ) {
-                       localEnv = $.extend( {
-                               // QUnit
-                               setup: $.noop,
-                               teardown: $.noop,
-                               // MediaWiki
-                               config: {},
-                               messages: {}
-                       }, localEnv );
+               return function ( orgEnv ) {
+                       var localEnv = orgEnv ? makeSafeEnv( orgEnv ) : {};
+                       // MediaWiki env testing
+                       localEnv.config = orgEnv && orgEnv.config || {};
+                       localEnv.messages = orgEnv && orgEnv.messages || {};
 
                        return {
-                               setup: function () {
+                               beforeEach: function () {
                                        // Greetings, mock environment!
                                        mw.config = new MwMap();
                                        mw.config.set( freshConfigCopy( localEnv.config ) );
                                        // Start tracking ajax requests
                                        $( document ).on( 'ajaxSend', trackAjax );
 
-                                       localEnv.setup.call( this );
+                                       if ( localEnv.beforeEach ) {
+                                               return localEnv.beforeEach.apply( this, arguments );
+                                       }
                                },
 
-                               teardown: function () {
-                                       var timers, pending, $activeLen;
+                               afterEach: function () {
+                                       var timers, pending, $activeLen, ret;
 
-                                       localEnv.teardown.call( this );
+                                       if ( localEnv.afterEach ) {
+                                               ret = localEnv.afterEach.apply( this, arguments );
+                                       }
 
                                        // Stop tracking ajax requests
                                        $( document ).off( 'ajaxSend', trackAjax );
 
                                                throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' );
                                        }
+
+                                       return ret;
                                }
                        };
                };
 
                // Expect boolean true
                assertTrue: function ( actual, message ) {
-                       QUnit.push( actual === true, actual, true, message );
+                       this.pushResult( {
+                               result: actual === true,
+                               actual: actual,
+                               expected: true,
+                               message: message
+                       } );
                },
 
                // Expect boolean false
                assertFalse: function ( actual, message ) {
-                       QUnit.push( actual === false, actual, false, message );
+                       this.pushResult( {
+                               result: actual === false,
+                               actual: actual,
+                               expected: false,
+                               message: message
+                       } );
                },
 
                // Expect numerical value less than X
                lt: function ( actual, expected, message ) {
-                       QUnit.push( actual < expected, actual, 'less than ' + expected, message );
+                       this.pushResult( {
+                               result: actual < expected,
+                               actual: actual,
+                               expected: 'less than ' + expected,
+                               message: message
+                       } );
                },
 
                // Expect numerical value less than or equal to X
                ltOrEq: function ( actual, expected, message ) {
-                       QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message );
+                       this.pushResult( {
+                               result: actual <= expected,
+                               actual: actual,
+                               expected: 'less than or equal to ' + expected,
+                               message: message
+                       } );
                },
 
                // Expect numerical value greater than X
                gt: function ( actual, expected, message ) {
-                       QUnit.push( actual > expected, actual, 'greater than ' + expected, message );
+                       this.pushResult( {
+                               result: actual > expected,
+                               actual: actual,
+                               expected: 'greater than ' + expected,
+                               message: message
+                       } );
                },
 
                // Expect numerical value greater than or equal to X
                gtOrEq: function ( actual, expected, message ) {
-                       QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message );
+                       this.pushResult( {
+                               result: actual >= true,
+                               actual: actual,
+                               expected: 'greater than or equal to ' + expected,
+                               message: message
+                       } );
                },
 
                /**
                htmlEqual: function ( actualHtml, expectedHtml, message ) {
                        var actual = getHtmlStructure( actualHtml ),
                                expected = getHtmlStructure( expectedHtml );
-
-                       QUnit.push(
-                               QUnit.equiv(
-                                       actual,
-                                       expected
-                               ),
-                               actual,
-                               expected,
-                               message
-                       );
+                       this.pushResult( {
+                               result: QUnit.equiv( actual, expected ),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       } );
                },
 
                /**
                        var actual = getHtmlStructure( actualHtml ),
                                expected = getHtmlStructure( expectedHtml );
 
-                       QUnit.push(
-                               !QUnit.equiv(
-                                       actual,
-                                       expected
-                               ),
-                               actual,
-                               expected,
-                               message
-                       );
+                       this.pushResult( {
+                               result: !QUnit.equiv( actual, expected ),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       } );
                }
        };
 
         * Small test suite to confirm proper functionality of the utilities and
         * initializations defined above in this file.
         */
-       QUnit.module( 'test.mediawiki.qunit.testrunner', QUnit.newMwEnvironment( {
+       QUnit.module( 'testrunner', QUnit.newMwEnvironment( {
                setup: function () {
                        this.mwHtmlLive = mw.html;
                        mw.html = {
                assert.deepEqual( missing, [], 'Modules in missing state' );
        } );
 
-       QUnit.test( 'htmlEqual', function ( assert ) {
+       QUnit.test( 'assert.htmlEqual', function ( assert ) {
                assert.htmlEqual(
                        '<div><p class="some classes" data-length="10">Child paragraph with <a href="http://example.com">A link</a></p>Regular text<span>A span</span></div>',
                        '<div><p data-length=\'10\'  class=\'some classes\'>Child paragraph with <a href=\'http://example.com\' >A link</a></p>Regular text<span>A span</span></div>',
                        'foo<a href="http://example.com">example</a>quux',
                        'Outer text nodes are compared (last text node different)'
                );
-
        } );
 
-       QUnit.module( 'test.mediawiki.qunit.testrunner-after', QUnit.newMwEnvironment() );
+       QUnit.module( 'testrunner-after', QUnit.newMwEnvironment() );
 
        QUnit.test( 'Teardown', function ( assert ) {
                assert.equal( mw.html.escape( '<' ), '&lt;', 'teardown() callback was ran.' );
                assert.equal( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' );
        } );
 
+       QUnit.module( 'testrunner-each', {
+               beforeEach: function () {
+                       this.mwHtmlLive = mw.html;
+               },
+               afterEach: function () {
+                       mw.html = this.mwHtmlLive;
+               }
+       } );
+       QUnit.test( 'beforeEach', function ( assert ) {
+               assert.ok( this.mwHtmlLive, 'setup() ran' );
+               mw.html = null;
+       } );
+       QUnit.test( 'afterEach', function ( assert ) {
+               assert.equal( mw.html.escape( '<' ), '&lt;', 'afterEach() ran' );
+       } );
+
+       QUnit.module( 'testrunner-each-compat', {
+               setup: function () {
+                       this.mwHtmlLive = mw.html;
+               },
+               teardown: function () {
+                       mw.html = this.mwHtmlLive;
+               }
+       } );
+       QUnit.test( 'setup', function ( assert ) {
+               assert.ok( this.mwHtmlLive, 'setup() ran' );
+               mw.html = null;
+       } );
+       QUnit.test( 'teardown', function ( assert ) {
+               assert.equal( mw.html.escape( '<' ), '&lt;', 'teardown() ran' );
+       } );
+
+       // Regression test for 'this.sandbox undefined' error, fixed by
+       // ensuring Sinon setup/teardown is not re-run on inner module.
+       QUnit.module( 'testrunner-nested', function () {
+               QUnit.module( 'testrunner-nested-inner', function () {
+                       QUnit.test( 'Dummy', function ( assert ) {
+                               assert.ok( true, 'Nested modules supported' );
+                       } );
+               } );
+       } );
+
 }( jQuery, mediaWiki, QUnit ) );
index 5c1be67..8555a7e 100644 (file)
        byteLimitTest( {
                description: 'Input filter that increases the length',
                $input: $( '<input>' ).attr( 'type', 'text' )
-               .byteLimit( 10, function ( text ) {
-                       return 'prefix' + text;
-               } ),
+                       .byteLimit( 10, function ( text ) {
+                               return 'prefix' + text;
+                       } ),
                sample: simpleSample,
                // Prefix adds 6 characters, limit is reached after 4
                expected: '1234'
        byteLimitTest( {
                description: 'Input filter of which the base exceeds the limit',
                $input: $( '<input>' ).attr( 'type', 'text' )
-               .byteLimit( 3, function ( text ) {
-                       return 'prefix' + text;
-               } ),
+                       .byteLimit( 3, function ( text ) {
+                               return 'prefix' + text;
+                       } ),
                sample: simpleSample,
                hasLimit: true,
                limit: 6, // 'prefix' length
index 53d29cf..0c91e43 100644 (file)
 
        QUnit.test( 'mw-made-collapsible data added', function ( assert ) {
                var $collapsible = prepareCollapsible(
-                               '<div>' + loremIpsum + '</div>'
-                       );
+                       '<div>' + loremIpsum + '</div>'
+               );
 
                assert.equal( $collapsible.data( 'mw-made-collapsible' ), true, 'mw-made-collapsible data present' );
        } );
 
        QUnit.test( 'mw-collapsible added when missing', function ( assert ) {
                var $collapsible = prepareCollapsible(
-                               '<div>' + loremIpsum + '</div>'
-                       );
+                       '<div>' + loremIpsum + '</div>'
+               );
 
                assert.assertTrue( $collapsible.hasClass( 'mw-collapsible' ), 'mw-collapsible class present' );
        } );
        QUnit.test( 'mw-collapsed added when missing', function ( assert ) {
                var $collapsible = prepareCollapsible(
                        '<div>' + loremIpsum + '</div>',
-                               { collapsed: true }
-                       );
+                       { collapsed: true }
+               );
 
                assert.assertTrue( $collapsible.hasClass( 'mw-collapsed' ), 'mw-collapsed class present' );
        } );
index 5b973f6..c88941e 100644 (file)
         * @param {function($table)} callback something to do with the table before we compare
         */
        function tableTest( msg, header, data, expected, callback ) {
-               QUnit.test( msg, 1, function ( assert ) {
+               QUnit.test( msg, function ( assert ) {
                        var extracted,
                                $table = tableCreate( header, data );
 
         * @param {function($table)} callback Something to do with the table before we compare
         */
        function tableTestHTML( msg, html, expected, callback ) {
-               QUnit.test( msg, 1, function ( assert ) {
+               QUnit.test( msg, function ( assert ) {
                        var extracted,
                                $table = $( html );
 
index 854e4b1..4ee8038 100644 (file)
                                'action=options&format=json&formatversion=2&optionname=foo%7Cbar%3Dquux&token=%2B%5C'
                        ] ) !== -1 ) {
                                assert.ok( true, 'Repond to ' + request.requestBody );
-                               request.respond( 200, { 'Content-Type': 'application/json' },
-                                               '{ "options": "success" }' );
+                               request.respond(
+                                       200,
+                                       { 'Content-Type': 'application/json' },
+                                       '{ "options": "success" }'
+                               );
                        } else {
                                assert.ok( false, 'Unexpected request: ' + request.requestBody );
                        }
index e10a7fa..2361f70 100644 (file)
                                        'X-Foo': 'Bar'
                                }
                        }
-               )
-               .then( function () {
+               ).then( function () {
                        assert.equal( test.server.requests[ 0 ].requestHeaders[ 'X-Foo' ], 'Bar', 'Header sent' );
 
                        return api.postWithToken( 'csrf',
                                        assert.ok( false, 'This parameter cannot be a callback' );
                                }
                        );
-               } )
-               .then( function ( data ) {
+               } ).then( function ( data ) {
                        assert.equal( data.example, 'quux' );
 
                        assert.equal( test.server.requests.length, 2, 'Request made' );
index 5212ee9..da7bafd 100644 (file)
                }, {
                        name: 'group4',
                        type: 'single_option',
-                       default: 'option1',
+                       default: 'option2',
                        filters: [
                                { name: 'option1', label: 'group4option1-label', description: 'group4option1-desc' },
                                { name: 'option2', label: 'group4option2-label', description: 'group4option2-desc' },
                                { name: 'option3', label: 'group4option3-label', description: 'group4option3-desc' }
                        ]
+               }, {
+                       name: 'group5',
+                       type: 'single_option',
+                       filters: [
+                               { name: 'option1', label: 'group5option1-label', description: 'group5option1-desc' },
+                               { name: 'option2', label: 'group5option2-label', description: 'group5option2-desc' },
+                               { name: 'option3', label: 'group5option3-label', description: 'group5option3-desc' }
+                       ]
+               }, {
+                       name: 'group6',
+                       type: 'boolean',
+                       filters: [
+                               { name: 'group6option1', label: 'group6option1-label', description: 'group5option1-desc' },
+                               { name: 'group6option2', label: 'group6option2-label', description: 'group5option2-desc', default: true, useDefaultAsBaseValue: true },
+                               { name: 'group6option3', label: 'group6option3-label', description: 'group5option3-desc', default: true }
+                       ]
                } ],
                viewsDefinition = {
                        namespaces: {
                        filter5: '1',
                        filter6: '0',
                        group3: 'filter8',
-                       group4: 'option1',
+                       group4: 'option2',
+                       group5: 'option1',
+                       group6option1: '0',
+                       group6option2: '1',
+                       group6option3: '1',
                        namespace: ''
                },
                baseParamRepresentation = {
                        filter5: '0',
                        filter6: '0',
                        group3: '',
-                       group4: '',
+                       group4: 'option2',
+                       group5: 'option1',
+                       group6option1: '0',
+                       group6option2: '1',
+                       group6option3: '0',
                        namespace: ''
                },
                baseFilterRepresentation = {
                        group3__filter7: false,
                        group3__filter8: false,
                        group3__filter9: false,
+                       // The 'single_value' type of group can't have empty value; it's either
+                       // the default given or the first item that will get the truthy value
                        group4__option1: false,
-                       group4__option2: false,
+                       group4__option2: true, // Default
                        group4__option3: false,
+                       group5__option1: true, // No default set, first item is default value
+                       group5__option2: false,
+                       group5__option3: false,
+                       group6__group6option1: false,
+                       group6__group6option2: true,
+                       group6__group6option3: false,
                        namespace__0: false,
                        namespace__1: false,
                        namespace__2: false,
                        group3__filter8: { selected: false, conflicted: false, included: false },
                        group3__filter9: { selected: false, conflicted: false, included: false },
                        group4__option1: { selected: false, conflicted: false, included: false },
-                       group4__option2: { selected: false, conflicted: false, included: false },
+                       group4__option2: { selected: true, conflicted: false, included: false },
                        group4__option3: { selected: false, conflicted: false, included: false },
+                       group5__option1: { selected: true, conflicted: false, included: false },
+                       group5__option2: { selected: false, conflicted: false, included: false },
+                       group5__option3: { selected: false, conflicted: false, included: false },
+                       group6__group6option1: { selected: false, conflicted: false, included: false },
+                       group6__group6option2: { selected: true, conflicted: false, included: false },
+                       group6__group6option3: { selected: false, conflicted: false, included: false },
                        namespace__0: { selected: false, conflicted: false, included: false },
                        namespace__1: { selected: false, conflicted: false, included: false },
                        namespace__2: { selected: false, conflicted: false, included: false },
                assert.deepEqual(
                        model.getFiltersFromParameters( {} ),
                        baseFilterRepresentation,
-                       'Empty parameter query results in an object representing all filters set to false'
+                       'Empty parameter query results in an object representing all filters set to their base state'
                );
 
                assert.deepEqual(
                assert.deepEqual(
                        model.getSelectedState(),
                        $.extend( {}, baseFilterRepresentation, {
-                               group4__option1: true
+                               group4__option1: true,
+                               group4__option2: false
                        } ),
                        'A \'single_option\' parameter reflects a single selected value.'
                );
index 3c77a00..db51fb3 100644 (file)
                );
                assert.equal(
                        formatParse( 'external-link-plural', 2, 'http://example.org' ),
-                       'Foo <a href=\"http://example.org\">two</a> things.',
+                       'Foo <a href="http://example.org">two</a> things.',
                        'Link is expanded inside an explicit plural form and is not escaped html'
                );
                assert.equal(
index a00b3cb..985ff92 100644 (file)
@@ -90,7 +90,7 @@
                        isAwesomeDone = true;
                };
 
-               mw.loader.implement( 'test.callback', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
+               mw.loader.implement( 'test.callback', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/mwLoaderTestCallback.js' ) ] );
 
                return mw.loader.using( 'test.callback', function () {
                        assert.strictEqual( isAwesomeDone, true, 'test.callback module should\'ve caused isAwesomeDone to be true' );
                        isAwesomeDone = true;
                };
 
-               mw.loader.implement( 'hasOwnProperty', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ], {}, {} );
+               mw.loader.implement( 'hasOwnProperty', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/mwLoaderTestCallback.js' ) ], {}, {} );
 
                return mw.loader.using( 'hasOwnProperty', function () {
                        assert.strictEqual( isAwesomeDone, true, 'hasOwnProperty module should\'ve caused isAwesomeDone to be true' );
                        isAwesomeDone = true;
                };
 
-               mw.loader.implement( 'test.promise', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
+               mw.loader.implement( 'test.promise', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/mwLoaderTestCallback.js' ) ] );
 
                return mw.loader.using( 'test.promise' )
-               .done( function () {
-                       assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
-                       delete mw.loader.testCallback;
-               } )
-               .fail( function () {
-                       assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
-               } );
+                       .done( function () {
+                               assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
+                               delete mw.loader.testCallback;
+                       } )
+                       .fail( function () {
+                               assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
+                       } );
        } );
 
        // Covers mw.loader#sortDependencies (with native Set if available)
                                assert.ok( /Circular/.test( String( e ) ), 'Detect circular dependency' );
                        }
                )
-               .always( done );
+                       .always( done );
        } );
 
        // @covers mw.loader#sortDependencies (with fallback shim)
                                assert.ok( /Circular/.test( String( e ) ), 'Detect circular dependency' );
                        }
                )
-               .always( done );
+                       .always( done );
        } );
 
        QUnit.test( '.load() - Error: Circular dependency', function ( assert ) {
                );
        } );
 
-       QUnit.asyncTest( '.load( "//protocol-relative" ) - T32825', function ( assert ) {
-               // This bug was actually already fixed in 1.18 and later when discovered in 1.17.
-               // Test is for regressions!
+       // This bug was actually already fixed in 1.18 and later when discovered in 1.17.
+       QUnit.test( '.load( "//protocol-relative" ) - T32825', function ( assert ) {
+               var target,
+                       done = assert.async();
 
-               // Forge a URL to the test callback script
-               var target = QUnit.fixurl(
-                       mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
+               // URL to the callback script
+               target = QUnit.fixurl(
+                       mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/mwLoaderTestCallback.js'
                );
-
-               // Confirm that mw.loader.load() works with protocol-relative URLs
+               // Ensure a protocol-relative URL for this test
                target = target.replace( /https?:/, '' );
+               assert.equal( target.slice( 0, 2 ), '//', 'URL is protocol-relative' );
 
-               assert.equal( target.slice( 0, 2 ), '//',
-                       'URL must be relative to test relative URLs!'
-               );
+               mw.loader.testCallback = function () {
+                       delete mw.loader.testCallback;
+                       assert.ok( true, 'callback' );
+                       done();
+               };
 
-               // Async!
-               // The target calls QUnit.start
+               // Go!
                mw.loader.load( target );
        } );
 
-       QUnit.asyncTest( '.load( "/absolute-path" )', function ( assert ) {
-               // Forge a URL to the test callback script
-               var target = QUnit.fixurl(
-                       mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
-               );
+       QUnit.test( '.load( "/absolute-path" )', function ( assert ) {
+               var target,
+                       done = assert.async();
 
-               // Confirm that mw.loader.load() works with absolute-paths (relative to current hostname)
+               // URL to the callback script
+               target = QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/mwLoaderTestCallback.js' );
                assert.equal( target.slice( 0, 1 ), '/', 'URL is relative to document root' );
 
-               // Async!
-               // The target calls QUnit.start
+               mw.loader.testCallback = function () {
+                       delete mw.loader.testCallback;
+                       assert.ok( true, 'callback' );
+                       done();
+               };
+
+               // Go!
                mw.loader.load( target );
        } );
 
                                }
                        };
                } );
-               return mw.loader.using( [ 'test.require1', 'test.require2', 'test.require3', 'test.require4' ] )
-               .then( function ( require ) {
+               return mw.loader.using( [ 'test.require1', 'test.require2', 'test.require3', 'test.require4' ] ).then( function ( require ) {
                        var module1, module2, module3, module4;
 
                        module1 = require( 'test.require1' );
index 477550b..b20b68f 100644 (file)
@@ -6,7 +6,7 @@
                }
        } ) );
 
-       QUnit.asyncTest( 'toggleToc', function ( assert ) {
+       QUnit.test( 'toggleToc', function ( assert ) {
                var tocHtml, $toc, $toggleLink, $tocList;
 
                assert.strictEqual( $( '.toc' ).length, 0, 'There is no table of contents on the page at the beginning' );
                assert.strictEqual( $tocList.is( ':hidden' ), false, 'The table of contents is now visible' );
 
                $toggleLink.click();
-               $tocList.promise().done( function () {
+               return $tocList.promise().then( function () {
                        assert.strictEqual( $tocList.is( ':hidden' ), true, 'The table of contents is now hidden' );
 
                        $toggleLink.click();
-                       $tocList.promise().done( function () {
-                               QUnit.start();
-                       } );
+                       return $tocList.promise();
                } );
        } );
 }( mediaWiki, jQuery ) );
index c8fb8a7..f3e4877 100644 (file)
@@ -1,7 +1,6 @@
-/* eslint comma-dangle: 0 */
-/* eslint no-undef: "error" */
-/* eslint no-console: 0 */
 /* eslint-env node */
+/* eslint no-undef: "error" */
+/* eslint-disable no-console, comma-dangle */
 'use strict';
 
 const path = require( 'path' );
@@ -114,12 +113,12 @@ exports.config = {
        // with "/", then the base url gets prepended.
        baseUrl: (
                process.env.MW_SERVER === undefined ?
-               'http://127.0.0.1:8080' :
-               process.env.MW_SERVER
+                       'http://127.0.0.1:8080' :
+                       process.env.MW_SERVER
        ) + (
                process.env.MW_SCRIPT_PATH === undefined ?
-               '/w' :
-               process.env.MW_SCRIPT_PATH
+                       '/w' :
+                       process.env.MW_SCRIPT_PATH
        ),
        //
        // Default timeout for all waitFor* commands.